Pull request #634: RED-4515 - Swagger multitenancy

Merge in RED/persistence-service from RED-4515-swagger to master

* commit '8561bca4ba0948c822c304057eb8ddbc82ed8fd0':
  RED-4515 - Swagger multitenancy
  RED-4515 - Swagger multitenancy
This commit is contained in:
Timo Bejan 2023-03-22 13:09:38 +01:00
commit f476693409
7 changed files with 136 additions and 8 deletions

View File

@ -14,8 +14,10 @@ import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.DeploymentKeyService;
import com.iqser.red.service.persistence.management.v1.processor.service.TenantManagementService;
import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
import com.iqser.red.service.persistence.service.v1.api.external.resource.TenantsResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.SimpleTenantResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.TenantRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.TenantResponse;
@ -53,4 +55,9 @@ public class TenantsController implements TenantsResource {
return JSONPrimitive.of(deploymentKeyService.getDeploymentKey(tenantId));
}
public List<SimpleTenantResponse> getSimpleTenants() {
return MagicConverter.convert(tenantManagementService.getTenants(), SimpleTenantResponse.class);
}
}

View File

@ -50,10 +50,7 @@ public class SwaggerAutoConfiguration {
private static final String DESCRIPTION = "Description for redaction";
private static final String VERSION = "1.0";
private static final String OAUTH_NAME = "RED-OAUTH";
private static final String PROTOCOL_URL_FORMAT = "/auth/realms/%s/protocol/openid-connect";
@Autowired
private KeycloakSpringBootProperties keycloakProperties;
private static final String PROTOCOL_URL_FORMAT = "/auth/realms/<tenantId>/protocol/openid-connect";
@Bean
@ -128,9 +125,7 @@ public class SwaggerAutoConfiguration {
private OAuthFlow createAuthorizationCodeFlow() {
var protocolUrl = String.format(PROTOCOL_URL_FORMAT, keycloakProperties.getRealm());
return new OAuthFlow().authorizationUrl(protocolUrl + "/auth").tokenUrl(protocolUrl + "/token");
return new OAuthFlow().authorizationUrl(PROTOCOL_URL_FORMAT + "/auth").tokenUrl(PROTOCOL_URL_FORMAT + "/token");
}
}

View File

@ -0,0 +1,54 @@
package com.iqser.red.persistence.service.v1.external.api.impl.swagger;
import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springdoc.webmvc.api.OpenApiWebMvcResource;
import org.springdoc.webmvc.ui.SwaggerConfigResource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ExternalApi;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@RestController
@RequiredArgsConstructor
public class SwaggerCustomDocsController {
private final OpenApiWebMvcResource openApiWebMvcResource;
private final SwaggerConfigResource swaggerConfigResource;
@Value("${springdoc.api-docs.path:#{T(org.springdoc.core.Constants).DEFAULT_API_DOCS_URL}}")
private String apiDocsUrl;
@SneakyThrows
@Operation(hidden = true)
@GetMapping(ExternalApi.BASE_PATH + "/docs/tenant")
public String getDocs(HttpServletRequest request, @RequestParam(value = "tenantId", required = false, defaultValue = "redaction") String tenantId) {
var response = openApiWebMvcResource.openapiJson(request, apiDocsUrl, Locale.getDefault());
return response.replace("<tenantId>", tenantId);
}
@Operation(hidden = true)
@GetMapping(value = "/redaction-gateway-v1/docs/swagger-config", produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> getSwaggerUiConfig(HttpServletRequest request) {
var map = swaggerConfigResource.openapiJson(request);
map.put("url", apiDocsUrl);
return map;
}
}

View File

@ -0,0 +1,47 @@
package com.iqser.red.persistence.service.v1.external.api.impl.swagger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ExternalApi;
@Configuration
@EnableWebMvc
public class SwaggerTenantMvcConfigurer implements WebMvcConfigurer {
@Value("${springdoc.api-docs.path:#{T(org.springdoc.core.Constants).DEFAULT_API_DOCS_URL}}")
private String path;
@Override
public void addInterceptors(InterceptorRegistry registry) {
var cleanedUpPath = path.contains("?") ? path.substring(0, path.indexOf("?")) : path;
registry.addInterceptor(new DocsInterceptor()).addPathPatterns(cleanedUpPath);
}
public static class DocsInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
var query = "";
if (!StringUtils.isBlank(request.getQueryString())) {
query = "?" + request.getQueryString();
}
response.sendRedirect(ExternalApi.BASE_PATH + "/docs/tenant" + query);
return false;
}
}
}

View File

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.SimpleTenantResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.TenantRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.TenantResponse;
@ -36,6 +37,11 @@ public interface TenantsResource {
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<TenantResponse> getTenants();
@GetMapping(value = ExternalApi.BASE_PATH + "/tenants/simple", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenant in a simplified format", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<SimpleTenantResponse> getSimpleTenants();
@GetMapping(value = ExternalApi.BASE_PATH + "/deploymentKey" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the deployment key for a tenant", description = "None")

View File

@ -157,8 +157,9 @@ springdoc:
client-id: swagger-ui-client
doc-expansion: none
enabled: ${swagger.enabled}
config-url: /redaction-gateway-v1/docs/swagger-config
api-docs:
path: /redaction-gateway-v1/docs
path: /redaction-gateway-v1/docs?tenantId=redaction
enabled: ${swagger.enabled}
pre-loading-enabled: true
packages-to-scan: ['com.iqser.red.persistence.service.v1.external.api']

View File

@ -0,0 +1,18 @@
package com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SimpleTenantResponse {
private String tenantId;
private String displayName;
private String guid;
}