diff --git a/docs/antora.yml b/docs/antora.yml
new file mode 100644
index 0000000..c966d4f
--- /dev/null
+++ b/docs/antora.yml
@@ -0,0 +1,3 @@
+name: onecx-shell
+title: Shell bff
+version: latest
\ No newline at end of file
diff --git a/docs/modules/onecx-shell-bff/nav.adoc b/docs/modules/onecx-shell-bff/nav.adoc
new file mode 100644
index 0000000..5eecaf9
--- /dev/null
+++ b/docs/modules/onecx-shell-bff/nav.adoc
@@ -0,0 +1 @@
+* xref:onecx-shell-bff:index.adoc[Shell Bff]
\ No newline at end of file
diff --git a/docs/modules/onecx-shell-bff/pages/index.adoc b/docs/modules/onecx-shell-bff/pages/index.adoc
new file mode 100644
index 0000000..15ca5ad
--- /dev/null
+++ b/docs/modules/onecx-shell-bff/pages/index.adoc
@@ -0,0 +1,10 @@
+include::onecx-shell-bff-attributes.adoc[opts=optional]
+
+== onecx-shell-bff
+
+include::docs.adoc[opts=optional]
+
+=== Configuration
+include::onecx-shell-bff.adoc[opts=optional]
+
+include::onecx-shell-bff-docs.adoc[opts=optional]
diff --git a/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-attributes.adoc b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-attributes.adoc
new file mode 100644
index 0000000..26c1d08
--- /dev/null
+++ b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-attributes.adoc
@@ -0,0 +1,5 @@
+
+:docker-registry: https://github.com/onecx/onecx-shell-bff/pkgs/container/onecx-shell-bff
+:helm-registry: https://github.com/onecx/onecx-shell-bff/pkgs/container/charts%2Fonecx-shell-bff
+:properties-file: src/main/resources/application.properties
+:helm-file: src/main/helm/values.yaml
\ No newline at end of file
diff --git a/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-docs.adoc b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-docs.adoc
new file mode 100644
index 0000000..cd46a1d
--- /dev/null
+++ b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-docs.adoc
@@ -0,0 +1,80 @@
+
+include::onecx-shell-bff-attributes.adoc[opts=optional]
+
+=== Default properties
+
+.{properties-file}
+[%collapsible%open]
+====
+[source,properties,subs=attributes+]
+----
+quarkus.http.auth.permission.health.paths=/q/*
+quarkus.http.auth.permission.health.policy=permit
+quarkus.http.auth.permission.default.paths=/*
+quarkus.http.auth.permission.default.policy=authenticated
+onecx.permissions.application-id=${quarkus.application.name}
+onecx.shell.permissions.cache-enabled=true
+onecx.shell.permissions.key-separator=#
+org.eclipse.microprofile.rest.client.propagateHeaders=apm-principal-token
+%prod.quarkus.rest-client.onecx_workspace_svc.url=http://onecx-workspace-svc:8080
+%prod.quarkus.rest-client.onecx_theme_svc.url=http://onecx-theme-svc:8080
+%prod.quarkus.rest-client.onecx_product_store_svc.url=http://onecx-product-store-svc:8080
+%prod.quarkus.rest-client.onecx_permission_svc.url=http://onecx-permission-svc:8080
+%prod.quarkus.rest-client.onecx_user_profile_svc.url=http://onecx-user-profile-svc:8080
+onecx.component.mock.keys[0]=portalmenu
+%prod.quarkus.oidc-client.client-id=${quarkus.application.name}
+quarkus.openapi-generator.codegen.input-base-dir=target/tmp/openapi
+quarkus.openapi-generator.codegen.spec.onecx_workspace_svc_external_v1_yaml.config-key=onecx_workspace_svc
+quarkus.openapi-generator.codegen.spec.onecx_workspace_svc_external_v1_yaml.base-package=gen.org.tkit.onecx.workspace.client
+quarkus.openapi-generator.codegen.spec.onecx_workspace_svc_external_v1_yaml.return-response=true
+quarkus.openapi-generator.codegen.spec.onecx_workspace_svc_external_v1_yaml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+quarkus.openapi-generator.codegen.spec.onecx_workspace_svc_external_v1_yaml.additional-model-type-annotations=@io.quarkus.runtime.annotations.RegisterForReflection;
+quarkus.openapi-generator.codegen.spec.onecx_theme_svc_external_v1_yaml.config-key=onecx_theme_svc
+quarkus.openapi-generator.codegen.spec.onecx_theme_svc_external_v1_yaml.base-package=gen.org.tkit.onecx.theme.client
+quarkus.openapi-generator.codegen.spec.onecx_theme_svc_external_v1_yaml.return-response=true
+quarkus.openapi-generator.codegen.spec.onecx_theme_svc_external_v1_yaml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+quarkus.openapi-generator.codegen.spec.onecx_theme_svc_external_v1_yaml.additional-model-type-annotations=@io.quarkus.runtime.annotations.RegisterForReflection;
+quarkus.openapi-generator.codegen.spec.onecx_product_store_svc_v1_yaml.config-key=onecx_product_store_svc
+quarkus.openapi-generator.codegen.spec.onecx_product_store_svc_v1_yaml.base-package=gen.org.tkit.onecx.product.store.client
+quarkus.openapi-generator.codegen.spec.onecx_product_store_svc_v1_yaml.return-response=true
+quarkus.openapi-generator.codegen.spec.onecx_product_store_svc_v1_yaml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+quarkus.openapi-generator.codegen.spec.onecx_product_store_svc_v1_yaml.model-name-suffix=PSV1
+quarkus.openapi-generator.codegen.spec.onecx_product_store_svc_v1_yaml.additional-model-type-annotations=@io.quarkus.runtime.annotations.RegisterForReflection;
+quarkus.openapi-generator.codegen.spec.onecx_permission_svc_v1_yaml.config-key=onecx_permission_svc
+quarkus.openapi-generator.codegen.spec.onecx_permission_svc_v1_yaml.base-package=gen.org.tkit.onecx.permission.client
+quarkus.openapi-generator.codegen.spec.onecx_permission_svc_v1_yaml.return-response=true
+quarkus.openapi-generator.codegen.spec.onecx_permission_svc_v1_yaml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+quarkus.openapi-generator.codegen.spec.onecx_permission_svc_v1_yaml.additional-model-type-annotations=@io.quarkus.runtime.annotations.RegisterForReflection;
+quarkus.openapi-generator.codegen.spec.onecx_user_profile_svc_v1_yaml.config-key=onecx_user_profile_svc
+quarkus.openapi-generator.codegen.spec.onecx_user_profile_svc_v1_yaml.base-package=gen.org.tkit.onecx.user.profile.client
+quarkus.openapi-generator.codegen.spec.onecx_user_profile_svc_v1_yaml.return-response=true
+quarkus.openapi-generator.codegen.spec.onecx_user_profile_svc_v1_yaml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
+quarkus.openapi-generator.codegen.spec.onecx_user_profile_svc_v1_yaml.additional-model-type-annotations=@io.quarkus.runtime.annotations.RegisterForReflection;
+----
+====
+
+=== Extensions
+
+include::onecx-shell-bff-extensions.adoc[opts=optional]
+
+=== Container
+
+{docker-registry}[Docker registry]
+
+
+=== Helm
+
+{helm-registry}[Helm registry]
+
+Default values
+
+.{helm-file}
+[source,yaml]
+----
+app:
+ name: bff
+ image:
+ repository: "onecx/onecx-shell-bff"
+
+----
+
diff --git a/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-extensions.adoc b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-extensions.adoc
new file mode 100644
index 0000000..f726a3e
--- /dev/null
+++ b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff-extensions.adoc
@@ -0,0 +1,133 @@
+
+include::onecx-shell-bff-attributes.adoc[opts=optional]
+
+[.extension.table.searchable, cols="50,.^15,.^15,.^20"]
+|===
+h| Extensions
+h| Documentation
+h| Configuration
+h| Version
+
+| quarkus-rest
+
+| https://quarkus.io/guides/rest[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-rest.adoc[Link]
+| 3.9.3
+
+| quarkus-smallrye-openapi
+
+| https://quarkus.io/guides/openapi-swaggerui[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-smallrye-openapi.adoc[Link]
+| 3.9.3
+
+| quarkus-rest-jackson
+
+| https://quarkus.io/guides/rest-json[Link]
+|
+| 3.9.3
+
+| quarkus-smallrye-health
+
+| https://quarkus.io/guides/smallrye-health[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-smallrye-health.adoc[Link]
+| 3.9.3
+
+| quarkus-openapi-generator
+
+| https://docs.quarkiverse.io/quarkus-openapi-generator/dev/index.html[Link]
+| https://github.com/quarkiverse/quarkus-openapi-generator/blob/2.4.1/docs/modules/ROOT/pages/includes/quarkus-openapi-generator.adoc[Link]
+| 2.4.1
+
+| quarkus-rest-client-reactive-jackson
+
+| https://quarkus.io/guides/rest-client[Link]
+|
+| 3.9.3
+
+| tkit-quarkus-log-cdi
+
+| https://1000kit.github.io/tkit-quarkus/current/tkit-quarkus/tkit-quarkus-log-cdi.html[Link]
+| https://github.com/1000kit/tkit-quarkus/blob/2.21.0/docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-log-cdi.adoc[Link]
+| 2.21.0
+
+| tkit-quarkus-log-rs
+
+| https://1000kit.github.io/tkit-quarkus/current/tkit-quarkus/tkit-quarkus-log-rs.html[Link]
+| https://github.com/1000kit/tkit-quarkus/blob/2.21.0/docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-log-rs.adoc[Link]
+| 2.21.0
+
+| tkit-quarkus-log-json
+
+| https://1000kit.github.io/tkit-quarkus/current/tkit-quarkus/tkit-quarkus-log-json.html[Link]
+| https://github.com/1000kit/tkit-quarkus/blob/2.21.0/docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-log-json.adoc[Link]
+| 2.21.0
+
+| tkit-quarkus-rest
+
+| https://1000kit.github.io/tkit-quarkus/current/tkit-quarkus/tkit-quarkus-rest.html[Link]
+| https://github.com/1000kit/tkit-quarkus/blob/2.21.0/docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-rest.adoc[Link]
+| 2.21.0
+
+| tkit-quarkus-rest-context
+
+| https://1000kit.github.io/tkit-quarkus/current/tkit-quarkus/tkit-quarkus-rest-context.html[Link]
+| https://github.com/1000kit/tkit-quarkus/blob/2.21.0/docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-rest-context.adoc[Link]
+| 2.21.0
+
+| tkit-quarkus-jpa
+
+| https://1000kit.github.io/tkit-quarkus/current/tkit-quarkus/tkit-quarkus-jpa.html[Link]
+| https://github.com/1000kit/tkit-quarkus/blob/2.21.0/docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-jpa.adoc[Link]
+| 2.21.0
+
+| tkit-quarkus-security
+
+| https://1000kit.github.io/tkit-quarkus/current/tkit-quarkus/tkit-quarkus-security.html[Link]
+| https://github.com/1000kit/tkit-quarkus/blob/2.21.0/docs/modules/tkit-quarkus/pages/includes/tkit-quarkus-security.adoc[Link]
+| 2.21.0
+
+| quarkus-hibernate-validator
+
+| https://quarkus.io/guides/validation[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-hibernate-validator.adoc[Link]
+| 3.9.3
+
+| onecx-permissions
+
+| https://onecx.github.io/docs/onecx-quarkus/current/onecx-quarkus/onecx-permissions.html[Link]
+| https://github.com/onecx/onecx-quarkus/blob/0.16.0/docs/modules/onecx-quarkus/pages/includes/onecx-permissions.adoc[Link]
+| 0.16.0
+
+| quarkus-oidc
+
+| https://quarkus.io/guides/security-oidc-bearer-token-authentication-tutorial[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-oidc.adoc[Link]
+| 3.9.3
+
+| quarkus-oidc-client-reactive-filter
+
+| https://quarkus.io/guides/security-openid-connect-client-reference[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-oidc-client-reactive-filter.adoc[Link]
+| 3.9.3
+
+| onecx-core
+
+| https://onecx.github.io/docs/onecx-quarkus/current/onecx-quarkus/onecx-core.html[Link]
+|
+| 0.16.0
+
+| quarkus-arc
+
+| https://quarkus.io/guides/cdi-reference[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-arc.adoc[Link]
+| 3.9.3
+
+| quarkus-container-image-docker
+
+| https://quarkus.io/guides/container-image[Link]
+| https://github.com/quarkusio/quarkusio.github.io/blob/develop/_generated-doc/latest/config/quarkus-container-image-docker.adoc[Link]
+| 3.9.3
+
+
+
+|===
\ No newline at end of file
diff --git a/docs/modules/onecx-shell-bff/pages/onecx-shell-bff.adoc b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff.adoc
new file mode 100644
index 0000000..0f4d2b2
--- /dev/null
+++ b/docs/modules/onecx-shell-bff/pages/onecx-shell-bff.adoc
@@ -0,0 +1,46 @@
+
+:summaryTableId: onecx-shell-bff
+[.configuration-legend]
+icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime
+[.configuration-reference.searchable, cols="80,.^10,.^10"]
+|===
+
+h|[[onecx-shell-bff_configuration]]link:#onecx-shell-bff_configuration[Configuration property]
+
+h|Type
+h|Default
+
+a| [[onecx-shell-bff_onecx-shell-permissions-cache-enabled]]`link:#onecx-shell-bff_onecx-shell-permissions-cache-enabled[onecx.shell.permissions.cache-enabled]`
+
+
+[.description]
+--
+Enable or disable caching
+
+ifdef::add-copy-button-to-env-var[]
+Environment variable: env_var_with_copy_button:+++ONECX_SHELL_PERMISSIONS_CACHE_ENABLED+++[]
+endif::add-copy-button-to-env-var[]
+ifndef::add-copy-button-to-env-var[]
+Environment variable: `+++ONECX_SHELL_PERMISSIONS_CACHE_ENABLED+++`
+endif::add-copy-button-to-env-var[]
+--|boolean
+|`true`
+
+
+a| [[onecx-shell-bff_onecx-shell-permissions-key-separator]]`link:#onecx-shell-bff_onecx-shell-permissions-key-separator[onecx.shell.permissions.key-separator]`
+
+
+[.description]
+--
+select default key separator
+
+ifdef::add-copy-button-to-env-var[]
+Environment variable: env_var_with_copy_button:+++ONECX_SHELL_PERMISSIONS_KEY_SEPARATOR+++[]
+endif::add-copy-button-to-env-var[]
+ifndef::add-copy-button-to-env-var[]
+Environment variable: `+++ONECX_SHELL_PERMISSIONS_KEY_SEPARATOR+++`
+endif::add-copy-button-to-env-var[]
+--|string
+|`#`
+
+|===
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 96ca725..c4e3b66 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.tkit.onecx
onecx-quarkus3-parent
- 0.37.0
+ 0.46.0
onecx-shell-bff
@@ -15,7 +15,7 @@
io.quarkus
- quarkus-resteasy-reactive
+ quarkus-rest
io.quarkus
@@ -23,7 +23,7 @@
io.quarkus
- quarkus-resteasy-reactive-jackson
+ quarkus-rest-jackson
io.quarkus
diff --git a/src/main/java/org/tkit/onecx/shell/bff/rs/ShellConfig.java b/src/main/java/org/tkit/onecx/shell/bff/rs/ShellConfig.java
index c9353fa..f9a170c 100644
--- a/src/main/java/org/tkit/onecx/shell/bff/rs/ShellConfig.java
+++ b/src/main/java/org/tkit/onecx/shell/bff/rs/ShellConfig.java
@@ -1,20 +1,37 @@
package org.tkit.onecx.shell.bff.rs;
+import io.quarkus.runtime.annotations.ConfigDocFilename;
+import io.quarkus.runtime.annotations.ConfigPhase;
+import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
import io.smallrye.config.WithName;
+/**
+ * Shell bff configuration
+ */
+@ConfigDocFilename("onecx-shell-bff.adoc")
+@ConfigRoot(phase = ConfigPhase.RUN_TIME)
@ConfigMapping(prefix = "onecx.shell")
public interface ShellConfig {
+ /**
+ * permission configurations
+ */
@WithName("permissions")
PermissionConfig permissions();
interface PermissionConfig {
+ /**
+ * Enable or disable caching
+ */
@WithDefault("true")
@WithName("cache-enabled")
boolean cachingEnabled();
+ /**
+ * select default key separator
+ */
@WithDefault("#")
@WithName("key-separator")
String keySeparator();
diff --git a/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/PermissionRestController.java b/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/PermissionRestController.java
index 77b043c..b80a8ed 100644
--- a/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/PermissionRestController.java
+++ b/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/PermissionRestController.java
@@ -17,7 +17,7 @@
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
-import org.tkit.onecx.quarkus.permission.client.PermissionResponse;
+import org.tkit.onecx.quarkus.permission.service.PermissionResponse;
import org.tkit.onecx.shell.bff.rs.ShellConfig;
import org.tkit.onecx.shell.bff.rs.mappers.ExceptionMapper;
import org.tkit.onecx.shell.bff.rs.mappers.PermissionMapper;
diff --git a/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/WorkspaceConfigRestController.java b/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/WorkspaceConfigRestController.java
index a0aac6e..6b94349 100644
--- a/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/WorkspaceConfigRestController.java
+++ b/src/main/java/org/tkit/onecx/shell/bff/rs/controllers/WorkspaceConfigRestController.java
@@ -8,8 +8,10 @@
import jakarta.transaction.Transactional;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriInfo;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.reactive.RestResponse;
@@ -55,6 +57,9 @@ public class WorkspaceConfigRestController implements WorkspaceConfigApiService
@Inject
RemoteComponentMockConfig mockConfig;
+ @Context
+ UriInfo uriInfo;
+
@Override
public Response getWorkspaceConfig(GetWorkspaceConfigRequestDTO getWorkspaceConfigRequestDTO) {
GetWorkspaceConfigResponseDTO responseDTO = new GetWorkspaceConfigResponseDTO();
@@ -76,7 +81,8 @@ public Response getWorkspaceConfig(GetWorkspaceConfigRequestDTO getWorkspaceConf
try (Response psResponse = productStoreClient.getProductByName(p.getProductName())) {
var product = psResponse.readEntity(ProductPSV1.class);
product.getMicrofrontends()
- .forEach(mfe -> routes.add(mapper.mapRoute(mfe, product, p.getMicrofrontends())));
+ .forEach(mfe -> routes.add(mapper.mapRoute(mfe, product, p.getMicrofrontends(),
+ workspaceResponse.getBaseUrl())));
} catch (WebApplicationException ex) {
//skip
}
@@ -86,6 +92,12 @@ public Response getWorkspaceConfig(GetWorkspaceConfigRequestDTO getWorkspaceConf
//get theme info
try (Response themeResponse = themeClient.getThemeByName(workspaceResponse.getTheme())) {
var themeInfo = themeResponse.readEntity(Theme.class);
+ if (themeInfo.getFaviconUrl() == null) {
+ themeInfo.setFaviconUrl(uriInfo.getPath() + "/themes/" + themeInfo.getName() + "/favicon");
+ }
+ if (themeInfo.getLogoUrl() == null) {
+ themeInfo.setLogoUrl(uriInfo.getPath() + "/themes/" + themeInfo.getName() + "/logo");
+ }
responseDTO.setTheme(mapper.mapTheme(themeInfo));
}
//call remoteComponent Mocks => should be removed after implementation
@@ -118,6 +130,25 @@ public Response getThemeFaviconByName(String name) {
}
}
+ @Override
+ public Response getThemeLogoByName(String name) {
+ Response.ResponseBuilder responseBuilder;
+ try (Response response = themeClient.getThemeLogoByName(name)) {
+ var contentType = response.getHeaderString(HttpHeaders.CONTENT_TYPE);
+ var contentLength = response.getHeaderString(HttpHeaders.CONTENT_LENGTH);
+ var body = response.readEntity(byte[].class);
+ if (contentType != null && body.length != 0) {
+ responseBuilder = Response.status(response.getStatus())
+ .header(HttpHeaders.CONTENT_TYPE, contentType)
+ .header(HttpHeaders.CONTENT_LENGTH, contentLength)
+ .entity(body);
+ } else {
+ responseBuilder = Response.status(Response.Status.BAD_REQUEST);
+ }
+ return responseBuilder.build();
+ }
+ }
+
/**
* SHOULD BE REMOVED AFTER IMPLEMENTATION
*
diff --git a/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/PermissionMapper.java b/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/PermissionMapper.java
index f07dda3..54fbff1 100644
--- a/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/PermissionMapper.java
+++ b/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/PermissionMapper.java
@@ -2,7 +2,7 @@
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
-import org.tkit.onecx.quarkus.permission.client.PermissionResponse;
+import org.tkit.onecx.quarkus.permission.service.PermissionResponse;
import gen.org.tkit.onecx.shell.bff.rs.internal.model.GetPermissionsResponseDTO;
diff --git a/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/WorkspaceConfigMapper.java b/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/WorkspaceConfigMapper.java
index afc3e54..c114eba 100644
--- a/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/WorkspaceConfigMapper.java
+++ b/src/main/java/org/tkit/onecx/shell/bff/rs/mappers/WorkspaceConfigMapper.java
@@ -31,15 +31,16 @@ public interface WorkspaceConfigMapper {
ProductItemSearchCriteriaPSV1 map(WorkspaceAbstract workspaceInfo);
default RouteDTO mapRoute(MicrofrontendPSV1 mfe, ProductPSV1 product,
- List wsMfes) {
+ List wsMfes, String workspaceUrl) {
RouteDTO route = new RouteDTO();
route.setRemoteEntryUrl(mfe.getRemoteEntry());
route.setExposedModule(mfe.getExposedModule());
route.setProductName(product.getName());
+ route.setDisplayName(product.getDisplayName());
route.setAppId(mfe.getAppId());
route.setTechnology(TechnologiesDTO.ANGULAR);
var selectedMfe = wsMfes.stream().filter(microfrontend -> microfrontend.getMfeId().equals(mfe.getAppId())).findFirst();
- selectedMfe.ifPresent(microfrontend -> route.setBaseUrl(microfrontend.getBasePath()));
+ selectedMfe.ifPresent(microfrontend -> route.setBaseUrl(workspaceUrl + microfrontend.getBasePath()));
route.setUrl(mfe.getRemoteBaseUrl());
route.setPathMatch(PathMatchDTO.PREFIX);
if (mfe.getRemoteName() != null) {
diff --git a/src/main/openapi/openapi-bff.yaml b/src/main/openapi/openapi-bff.yaml
index a891056..4bcc388 100644
--- a/src/main/openapi/openapi-bff.yaml
+++ b/src/main/openapi/openapi-bff.yaml
@@ -60,6 +60,31 @@ paths:
404:
description: Not found
+ /workspaceConfig/themes/{name}/logo:
+ get:
+ tags:
+ - "WorkspaceConfig"
+ description: Load logo by theme name
+ operationId: getThemeLogoByName
+ parameters:
+ - name: name
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ 200:
+ description: OK
+ content:
+ image/*:
+ schema:
+ minimum: 1
+ maximum: 110000
+ type: string
+ format: binary
+ 404:
+ description: Not found
+
/userProfile:
get:
tags:
@@ -203,6 +228,7 @@ components:
- 'appId'
- 'productName'
- 'pathMatch'
+ - 'displayName'
properties:
url:
type: string
@@ -222,6 +248,8 @@ components:
$ref: '#/components/schemas/PathMatch'
remoteName:
type: string
+ displayName:
+ type: string
Technologies:
type: string
diff --git a/src/test/java/org/tkit/onecx/shell/bff/rs/WorkspaceConfigRestControllerTest.java b/src/test/java/org/tkit/onecx/shell/bff/rs/WorkspaceConfigRestControllerTest.java
index d9374cf..5039681 100644
--- a/src/test/java/org/tkit/onecx/shell/bff/rs/WorkspaceConfigRestControllerTest.java
+++ b/src/test/java/org/tkit/onecx/shell/bff/rs/WorkspaceConfigRestControllerTest.java
@@ -61,6 +61,92 @@ void getWorkspaceConfigByBaseUrlTest() {
new Microfrontend().basePath("/app1").mfeId("app1")));
loadResponse.setProducts(List.of(product1));
+ // create mock rest endpoint for load workspace by name
+ mockServerClient.when(request().withPath("/v1/workspaces/w1/load").withMethod(HttpMethod.GET))
+ .withId("mockWSLoad")
+ .respond(httpRequest -> response().withStatusCode(Response.Status.OK.getStatusCode())
+ .withContentType(MediaType.APPLICATION_JSON)
+ .withBody(JsonBody.json(loadResponse)));
+
+ ProductPSV1 productResponse = new ProductPSV1();
+ productResponse.basePath("/product1").name("product1").microfrontends(List.of(
+ new MicrofrontendPSV1().exposedModule("App1Module")
+ .appName("app1")
+ .remoteBaseUrl("/remoteBaseUrl")
+ .remoteEntry("/remoteEntry.js")
+ .technology("ANGULAR")));
+ // create mock rest endpoint for get product by name from product-store
+ mockServerClient.when(request().withPath("/v1/products/product1").withMethod(HttpMethod.GET))
+ .withId("mockPS")
+ .respond(httpRequest -> response().withStatusCode(Response.Status.OK.getStatusCode())
+ .withContentType(MediaType.APPLICATION_JSON)
+ .withBody(JsonBody.json(productResponse)));
+
+ Theme themeResponse = new Theme();
+ themeResponse.name("theme1").cssFile("cssfile").properties(new Object()).logoUrl("someLogoUrl")
+ .faviconUrl("someFavIconUrl");
+ // create mock rest endpoint for get theme by name from theme-svc
+ mockServerClient.when(request().withPath("/v1/themes/theme1").withMethod(HttpMethod.GET))
+ .withId("mockTheme")
+ .respond(httpRequest -> response().withStatusCode(Response.Status.OK.getStatusCode())
+ .withContentType(MediaType.APPLICATION_JSON)
+ .withBody(JsonBody.json(themeResponse)));
+
+ var input = new GetWorkspaceConfigRequestDTO();
+ input.setUrl("/w1Url");
+
+ var output = given()
+ .when()
+ .auth().oauth2(keycloakClient.getAccessToken(ADMIN))
+ .header(APM_HEADER_PARAM, ADMIN)
+ .contentType(APPLICATION_JSON)
+ .body(input)
+ .post()
+ .then()
+ .statusCode(Response.Status.OK.getStatusCode())
+ .contentType(APPLICATION_JSON)
+ .extract().as(GetWorkspaceConfigResponseDTO.class);
+
+ Assertions.assertNotNull(output);
+ Assertions.assertEquals("w1", output.getWorkspace().getName());
+ Assertions.assertEquals("theme1", output.getTheme().getName());
+ Assertions.assertEquals(1, output.getRoutes().size());
+
+ //CHECK FOR MOCKED REMOTE COMPONENTS
+ //SHOULD BE REMOVED AFTER IMPLEMENTATION
+ Assertions.assertEquals("PortalMenu", output.getRemoteComponents().get(0).getName());
+ Assertions.assertEquals("appId", output.getRemoteComponents().get(0).getAppId());
+ Assertions.assertEquals("menu", output.getShellRemoteComponents().get(0).getSlotName());
+
+ mockServerClient.clear("mockWS");
+ mockServerClient.clear("mockPS");
+ mockServerClient.clear("mockTheme");
+ mockServerClient.clear("mockWSLoad");
+ }
+
+ @Test
+ void getWorkspaceConfigByBaseUrlTest_emptyImageUrl_should_create_urls_test() {
+ GetWorkspaceByUrlRequest byUrlRequest = new GetWorkspaceByUrlRequest();
+ byUrlRequest.setUrl("/w1Url");
+ Workspace workspace = new Workspace();
+ workspace.name("w1").theme("theme1");
+
+ // create mock rest endpoint for workspace search
+ mockServerClient.when(request().withPath("/v1/workspaces/byUrl").withMethod(HttpMethod.POST)
+ .withContentType(MediaType.APPLICATION_JSON)
+ .withBody(JsonBody.json(byUrlRequest)))
+ .withId("mockWS")
+ .respond(httpRequest -> response().withStatusCode(Response.Status.OK.getStatusCode())
+ .withContentType(MediaType.APPLICATION_JSON)
+ .withBody(JsonBody.json(workspace)));
+
+ WorkspaceLoad loadResponse = new WorkspaceLoad();
+ loadResponse.setName("w1");
+ Product product1 = new Product();
+ product1.baseUrl("/product1").productName("product1").microfrontends(List.of(
+ new Microfrontend().basePath("/app1").mfeId("app1")));
+ loadResponse.setProducts(List.of(product1));
+
// create mock rest endpoint for load workspace by name
mockServerClient.when(request().withPath("/v1/workspaces/w1/load").withMethod(HttpMethod.GET))
.withId("mockWSLoad")
@@ -110,6 +196,8 @@ void getWorkspaceConfigByBaseUrlTest() {
Assertions.assertEquals("w1", output.getWorkspace().getName());
Assertions.assertEquals("theme1", output.getTheme().getName());
Assertions.assertEquals(1, output.getRoutes().size());
+ Assertions.assertNotNull(output.getTheme().getFaviconUrl());
+ Assertions.assertNotNull(output.getTheme().getLogoUrl());
//CHECK FOR MOCKED REMOTE COMPONENTS
//SHOULD BE REMOVED AFTER IMPLEMENTATION
@@ -372,7 +460,39 @@ void getThemeFaviconTest() {
}
@Test
- void getThemeFavicon_shouldReturnBadRequest_whenBodyEmpty() {
+ void getThemeLogoTest() {
+
+ byte[] bytesRes = new byte[] { (byte) 0xe0, 0x4f, (byte) 0xd0,
+ 0x20, (byte) 0xea, 0x3a, 0x69, 0x10, (byte) 0xa2, (byte) 0xd8, 0x08, 0x00, 0x2b,
+ 0x30, 0x30, (byte) 0x9d };
+
+ // create mock rest endpoint for get theme by name from theme-svc
+ mockServerClient.when(request().withPath("/v1/themes/theme1/logo").withMethod(HttpMethod.GET))
+ .withId("mockLogo")
+ .respond(httpRequest -> response().withStatusCode(OK.getStatusCode())
+ .withHeaders(
+ new Header(HttpHeaders.CONTENT_TYPE, "image/png"))
+ .withBody(bytesRes));
+
+ var output = given()
+ .when()
+ .auth().oauth2(keycloakClient.getAccessToken(ADMIN))
+ .header(APM_HEADER_PARAM, ADMIN)
+ .contentType(APPLICATION_JSON)
+ .pathParam("name", "theme1")
+ .get("/themes/{name}/logo")
+ .then()
+ .statusCode(OK.getStatusCode())
+ .header(HttpHeaders.CONTENT_TYPE, "image/png")
+ .extract().body().asByteArray();
+
+ Assertions.assertNotNull(output);
+ mockServerClient.clear("mockLogo");
+
+ }
+
+ @Test
+ void getThemeFaviconAndLogo_shouldReturnBadRequest_whenBodyEmpty() {
byte[] bytesRes = null;
@@ -384,6 +504,14 @@ void getThemeFavicon_shouldReturnBadRequest_whenBodyEmpty() {
new Header(HttpHeaders.CONTENT_TYPE, "image/png"))
.withBody(bytesRes));
+ mockServerClient.when(request().withPath("/v1/themes/theme1/logo").withMethod(HttpMethod.GET))
+ .withId("mockLogo")
+ .withPriority(100)
+ .respond(httpRequest -> response().withStatusCode(OK.getStatusCode())
+ .withHeaders(
+ new Header(HttpHeaders.CONTENT_TYPE, "image/png"))
+ .withBody(bytesRes));
+
given()
.when()
.auth().oauth2(keycloakClient.getAccessToken(ADMIN))
@@ -393,12 +521,23 @@ void getThemeFavicon_shouldReturnBadRequest_whenBodyEmpty() {
.get("/themes/{name}/favicon")
.then()
.statusCode(BAD_REQUEST.getStatusCode());
- mockServerClient.clear("mockFavicon");
+ given()
+ .when()
+ .auth().oauth2(keycloakClient.getAccessToken(ADMIN))
+ .header(APM_HEADER_PARAM, ADMIN)
+ .contentType(APPLICATION_JSON)
+ .pathParam("name", "theme1")
+ .get("/themes/{name}/logo")
+ .then()
+ .statusCode(BAD_REQUEST.getStatusCode());
+
+ mockServerClient.clear("mockFavicon");
+ mockServerClient.clear("mockLogo");
}
@Test
- void getThemeFavicon_shouldReturnBadRequest_whenContentTypeEmpty() {
+ void getThemeFaviconAndLogo_shouldReturnBadRequest_whenContentTypeEmpty() {
byte[] bytesRes = new byte[] { (byte) 0xe0, 0x4f, (byte) 0xd0,
0x20, (byte) 0xea, 0x3a, 0x69, 0x10, (byte) 0xa2, (byte) 0xd8, 0x08, 0x00, 0x2b,
@@ -409,6 +548,11 @@ void getThemeFavicon_shouldReturnBadRequest_whenContentTypeEmpty() {
.withPriority(100)
.respond(httpRequest -> response().withStatusCode(OK.getStatusCode())
.withBody(bytesRes));
+ mockServerClient.when(request().withPath("/v1/themes/theme1/logo").withMethod(HttpMethod.GET))
+ .withId("mockLogo")
+ .withPriority(100)
+ .respond(httpRequest -> response().withStatusCode(OK.getStatusCode())
+ .withBody(bytesRes));
given()
.when()
@@ -420,7 +564,18 @@ void getThemeFavicon_shouldReturnBadRequest_whenContentTypeEmpty() {
.then()
.statusCode(BAD_REQUEST.getStatusCode());
+ given()
+ .when()
+ .auth().oauth2(keycloakClient.getAccessToken(ADMIN))
+ .header(APM_HEADER_PARAM, ADMIN)
+ .contentType(APPLICATION_JSON)
+ .pathParam("name", "theme1")
+ .get("/themes/{name}/logo")
+ .then()
+ .statusCode(BAD_REQUEST.getStatusCode());
+
mockServerClient.clear("mockFavicon");
+ mockServerClient.clear("mockLogo");
}
@@ -432,6 +587,11 @@ void getImage_shouldReturnBadRequest_whenAllEmpty() {
.withPriority(100)
.respond(httpRequest -> response().withStatusCode(OK.getStatusCode()));
+ mockServerClient.when(request().withPath("/v1/themes/theme1/logo").withMethod(HttpMethod.GET))
+ .withId("mockLogo")
+ .withPriority(100)
+ .respond(httpRequest -> response().withStatusCode(OK.getStatusCode()));
+
given()
.when()
.auth().oauth2(keycloakClient.getAccessToken(ADMIN))
@@ -441,7 +601,18 @@ void getImage_shouldReturnBadRequest_whenAllEmpty() {
.get("/themes/{name}/favicon")
.then()
.statusCode(BAD_REQUEST.getStatusCode());
- mockServerClient.clear("mockFavicon");
+ given()
+ .when()
+ .auth().oauth2(keycloakClient.getAccessToken(ADMIN))
+ .header(APM_HEADER_PARAM, ADMIN)
+ .contentType(APPLICATION_JSON)
+ .pathParam("name", "theme1")
+ .get("/themes/{name}/logo")
+ .then()
+ .statusCode(BAD_REQUEST.getStatusCode());
+
+ mockServerClient.clear("mockFavicon");
+ mockServerClient.clear("mockLogo");
}
}