From 6d2d00ba484b8d2d6d730db2c0dc4d78eb930497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= <58428711+pj892031@users.noreply.github.com> Date: Thu, 3 Jul 2025 09:39:18 +0200 Subject: [PATCH] refactor: Removal of using eureka endoints and asynchronous caching (#4187) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .github/workflows/integration-tests.yml | 2 +- api-catalog-services/README.md | 82 --- .../AppReadyListener.java | 9 +- .../config/TomcatConfiguration.java | 1 + .../apiml/apicatalog/config/WebConfig.java | 1 + ...cController.java => ApiDocController.java} | 53 +- .../controllers/api/MockController.java | 45 -- .../controllers/api/OidcController.java | 46 ++ ...ontroller.java => ServicesController.java} | 104 ++-- .../ApiCatalogControllerExceptionHandler.java | 12 +- ...talogApiDocControllerExceptionHandler.java | 21 +- .../custom/CustomErrorStatusHandlingBean.java | 2 + .../ApiDiffNotAvailableException.java | 2 +- .../ApiDocNotFoundException.java | 7 +- .../ApiVersionNotFoundException.java | 6 +- ...=> ContainerStatusRetrievalException.java} | 7 +- .../ServiceNotFoundException.java | 2 +- .../health/ApiCatalogHealthIndicator.java | 1 + .../EurekaServiceInstanceRequest.java | 25 - .../instance/InstanceInitializeService.java | 157 ----- .../instance/InstanceRefreshService.java | 242 -------- .../instance/InstanceRetrievalService.java | 279 --------- .../apicatalog/model/SemanticVersion.java | 2 + .../ApiCatalogLogoutSuccessHandler.java | 1 + .../security/SecurityConfiguration.java | 4 +- .../services/cached/CachedApiDocService.java | 236 -------- .../cached/CachedServicesService.java | 74 --- .../services/cached/model/ApiDocCacheKey.java | 21 - .../status/APIServiceStatusService.java | 183 ------ .../status/OpenApiCompareProducer.java | 22 - .../model/ContainerStatusChangeEvent.java | 42 -- .../status/event/model/STATUS_EVENT_TYPE.java | 17 - .../status/event/model/StatusChangeEvent.java | 31 - .../listeners/GatewayLookupEventListener.java | 54 -- .../services/status/model/APIServiceInfo.java | 19 - .../status/model/APIServiceInstances.java | 20 - .../standalone/ApiDocTransformForMock.java | 47 -- .../apicatalog/standalone/ExampleService.java | 227 ------- .../StandaloneAPIDocRetrievalService.java | 52 -- .../standalone/StandaloneInitializer.java | 52 -- .../standalone/StandaloneLoaderService.java | 162 ----- .../standalone/StandaloneSecurityConfig.java | 50 -- ...cAPIRefreshControllerExceptionHandler.java | 2 +- .../cached/model => swagger}/ApiDocInfo.java | 4 +- .../swagger/ApiDocRetrievalService.java | 74 +++ .../ApiDocRetrievalServiceRest.java} | 124 ++-- .../ContainerService.java} | 411 +++++-------- .../swagger/TransformApiDocService.java | 3 +- .../swagger/api/AbstractApiDocService.java | 15 +- .../swagger/api/ApiDocV2Service.java | 2 +- .../swagger/api/ApiDocV3Service.java | 2 +- ...itional-spring-configuration-metadata.json | 15 - ...inerRetrievalTestContextConfiguration.java | 14 +- ...> ApiDocControllerApiDocNotFoundTest.java} | 5 +- ... ApiDocControllerServiceNotFoundTest.java} | 4 +- .../controllers/api/ApiDocControllerTest.java | 105 ++++ ...piDocNotFoundTestContextConfiguration.java | 20 +- ...rviceNotFoundTestContextConfiguration.java | 17 +- .../api/CatalogApiDocControllerTest.java | 92 --- .../controllers/api/MockControllerTest.java | 89 --- .../controllers/api/OidcControllerTest.java | 83 +++ ...icesControllerContainerRetrievalTest.java} | 4 +- ...ests.java => ServicesControllerTests.java} | 154 ++--- .../ApiDiffNotAvailableExceptionTest.java | 2 +- .../InstanceInitializeServiceTest.java | 231 ------- .../instance/InstanceRefreshServiceTest.java | 215 ------- .../InstanceRetrievalServiceTest.java | 316 ---------- .../apicatalog/services/ApiDocKeyTest.java | 38 -- .../cached/CachedApiDocServiceTest.java | 266 --------- .../CachedProductFamilyServiceTest.java | 562 ------------------ .../cached/CachedServicesServiceTest.java | 70 --- .../status/ApiServiceStatusServiceTest.java | 209 ------- .../GatewayLookupEventListenerTest.java | 31 - .../ApiDocTransformForMockTest.java | 39 -- .../standalone/ExampleServiceTest.java | 189 ------ .../StandaloneAPIDocRetrievalServiceTest.java | 74 --- .../standalone/StandaloneInitializerTest.java | 92 --- .../StandaloneLoaderServiceTest.java | 188 ------ .../StaticAPIRefreshControllerTest.java | 2 +- .../swagger/ContainerServiceTest.java | 269 +++++++++ .../LocalApiDocServiceTest.java | 225 +++---- .../swagger/TransformApiDocServiceTest.java | 1 - .../api/AbstractApiDocServiceTest.java | 2 +- .../swagger/api/ApiDocV2ServiceTest.java | 2 +- .../swagger/api/ApiDocV3ServiceTest.java | 2 +- .../swagger/api/dummy/DummyApiDocService.java | 2 +- .../util/ContainerServiceMockUtil.java | 133 ----- .../apicatalog/util/ServicesBuilder.java | 21 +- ...ervice1-instance1_zowe v1.0.0_default.json | 3 - .../invalid-apiDocName/apiDocs/service2.json | 1 - .../standalone/invalid-app/apps/service2.json | 3 - ...ervice1-instance1_zowe v1.0.0_default.json | 143 ----- ...ervice1-instance2_zowe v1.0.0_default.json | 143 ----- .../apiDocs/service2_zowe v1.0.0_default.json | 143 ----- .../apiDocs/service2_zowe v2.0.0.json | 143 ----- .../standalone/services/apps/service1.json | 85 --- .../standalone/services/apps/service2.json | 51 -- .../mocked-backend/assets/containers.json | 2 +- .../java/org/zowe/apiml/util/EurekaUtils.java | 54 +- config/docker/api-defs/staticclient.yml | 2 +- config/local/api-defs-http/staticclient.yml | 2 +- config/local/api-defs/staticclient.yml | 2 +- gateway-service/build.gradle | 5 +- .../apicatalog/ApiCatalogStandaloneTest.java | 127 ---- 104 files changed, 1155 insertions(+), 6596 deletions(-) delete mode 100644 api-catalog-services/README.md rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/status/listeners => config}/AppReadyListener.java (80%) rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/{CatalogApiDocController.java => ApiDocController.java} (76%) delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/MockController.java create mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/OidcController.java rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/{ApiCatalogController.java => ServicesController.java} (69%) rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/status/model => exceptions}/ApiDiffNotAvailableException.java (92%) rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/status/model => exceptions}/ApiDocNotFoundException.java (86%) rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/status/model => exceptions}/ApiVersionNotFoundException.java (84%) rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/{ContainerStatusRetrievalThrowable.java => ContainerStatusRetrievalException.java} (66%) rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/status/model => exceptions}/ServiceNotFoundException.java (90%) delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/EurekaServiceInstanceRequest.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/model/ApiDocCacheKey.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIServiceStatusService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/OpenApiCompareProducer.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/ContainerStatusChangeEvent.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/STATUS_EVENT_TYPE.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/StatusChangeEvent.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInfo.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInstances.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMock.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ExampleService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializer.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderService.java delete mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneSecurityConfig.java rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/cached/model => swagger}/ApiDocInfo.java (92%) create mode 100644 api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocRetrievalService.java rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/status/APIDocRetrievalService.java => swagger/ApiDocRetrievalServiceRest.java} (75%) rename api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/{services/cached/CachedProductFamilyService.java => swagger/ContainerService.java} (58%) delete mode 100644 api-catalog-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json rename api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/{CatalogApiDocControllerApiDocNotFoundTest.java => ApiDocControllerApiDocNotFoundTest.java} (94%) rename api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/{CatalogApiDocControllerServiceNotFoundTest.java => ApiDocControllerServiceNotFoundTest.java} (95%) create mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java create mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/OidcControllerTest.java rename api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/{ApiCatalogControllerContainerRetrievalTest.java => ServicesControllerContainerRetrievalTest.java} (95%) rename api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/{ApiCatalogControllerTests.java => ServicesControllerTests.java} (63%) rename api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/{services/status/model => exceptions}/ApiDiffNotAvailableExceptionTest.java (93%) delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/ApiDocKeyTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/ApiServiceStatusServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListenerTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMockTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ExampleServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalServiceTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderServiceTest.java create mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/ContainerServiceTest.java rename api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/{services/status => swagger}/LocalApiDocServiceTest.java (65%) delete mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ContainerServiceMockUtil.java delete mode 100644 api-catalog-services/src/test/resources/standalone/invalid-apiDoc/apiDocs/service1-instance1_zowe v1.0.0_default.json delete mode 100644 api-catalog-services/src/test/resources/standalone/invalid-apiDocName/apiDocs/service2.json delete mode 100644 api-catalog-services/src/test/resources/standalone/invalid-app/apps/service2.json delete mode 100644 api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance1_zowe v1.0.0_default.json delete mode 100644 api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance2_zowe v1.0.0_default.json delete mode 100644 api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v1.0.0_default.json delete mode 100644 api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v2.0.0.json delete mode 100644 api-catalog-services/src/test/resources/standalone/services/apps/service1.json delete mode 100644 api-catalog-services/src/test/resources/standalone/services/apps/service2.json delete mode 100644 integration-tests/src/test/java/org/zowe/apiml/functional/apicatalog/ApiCatalogStandaloneTest.java diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 297b34f592..6345d68075 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -6,7 +6,7 @@ on: paths-ignore: - '**.md' pull_request: - branches: [ v2.x.x, v3.x.x ] + branches: [ v2.x.x, v3.x.x, reboot/catalog-modulith ] paths-ignore: - '**.md' workflow_dispatch: diff --git a/api-catalog-services/README.md b/api-catalog-services/README.md deleted file mode 100644 index af8ffa2b98..0000000000 --- a/api-catalog-services/README.md +++ /dev/null @@ -1,82 +0,0 @@ - -# API Catalog Services - -- [Standalone mode](#standalone-mode) - - [Configuration](#configuration) - - [Service directory structure](#service-directory-structure) - - [Apps subdirectory](#apps-subdirectory) - - [ApiDocs subdirectory](#apidocs-subdirectory) - -## Standalone mode - -Standalone mode allows displaying, without the need for authentication, services -that are stored on the disk. Standalone API Catalog does not connect to any -other service. - -It requires building the UI module differently. For more information please follow -[the documentation about UI](../api-catalog-ui/frontend/README.md). - -### Configuration - -- `apiml.catalog.standalone.enabled` - specifies whether to enable the standalone mode - Default: false -- `apiml.catalog.standalone.servicesDirectory` - specifies a directory where service definitions are stored - Default: services - -### Service directory structure - -The service directory contains definitions of services that are visible in -the Catalog. - -It consists of the following subdirectories: - - apps - - apiDocs - -#### Apps subdirectory - -`apps` subdirectory contains files in JSON format that describes services. All -JSON files inside the directory are processed. The file name does not have any -specific format. - -The file contains the Eureka Instance descriptor of an API ML conformant -service. It is serialized `com.netflix.discovery.shared.Applications` object. -JSON `application` object contains a list of one or multiple services. - -You can use endpoints in the following link to obtain the file content: - - - -Example: -[service1.json](config/local/catalog-standalone-defs/apps/service1.json) - -#### ApiDocs subdirectory - -`apiDocs` subdirectory contains API documentation in JSON format for services. -All API ML supported formats are available, such as Swagger or openAPI. All JSON -files are processed. - -The file contains supported API documentation for one service and version. - -The file name has the following structure: - -`{serviceId}_{version}_default.json` - -or - -`{serviceId}_{version}.json` - -The `default` suffix is used for the API version that is displayed in -the Catalog as the default one. Make sure that it corresponds to the default -version specified in `apiInfo` section in the service metadata provided in `app` -subdirectory. Each service MUST have exactly one API documentation file with -a default suffix. - -Note: -The service ID and version cannot contain an underscore character. Otherwise, the definition will be not loaded. -You can find more information about apiInfo at - - -Example: -[service2_org.zowe v1.0.0_default.json](config/local/catalog-standalone-defs/apiDocs/service2_org.zowe v1.0.0_default.json) diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/AppReadyListener.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/AppReadyListener.java similarity index 80% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/AppReadyListener.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/AppReadyListener.java index 4a30573aa6..ce5271f8ed 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/AppReadyListener.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/AppReadyListener.java @@ -8,9 +8,8 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status.listeners; +package org.zowe.apiml.apicatalog.config; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @@ -20,11 +19,6 @@ * This class fires on ApplicationReadyEvent event during Spring context initialization */ @Component -@ConditionalOnProperty( - value = "apiml.catalog.standalone.enabled", - havingValue = "false", - matchIfMissing = true -) public class AppReadyListener { /** @@ -38,4 +32,5 @@ public void onApplicationEvent(ApplicationReadyEvent event) { new ServiceStartupEventHandler().onServiceStartup("API Catalog Service", ServiceStartupEventHandler.DEFAULT_DELAY_FACTOR); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/TomcatConfiguration.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/TomcatConfiguration.java index f61b16fca9..b035fe9d9b 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/TomcatConfiguration.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/TomcatConfiguration.java @@ -31,4 +31,5 @@ public ServletWebServerFactory servletContainer(List tomcat.addConnectorCustomizers(connectorCustomizers.toArray(new TomcatConnectorCustomizer[0])); return tomcat; } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/WebConfig.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/WebConfig.java index feb5d36f2b..c49cac621c 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/WebConfig.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/WebConfig.java @@ -42,4 +42,5 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { .setCacheControl(CacheControl.maxAge(Duration.ofDays(365L))) .addResourceLocations("/resources/", "/resources/static/", "/resources/templates/"); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocController.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocController.java similarity index 76% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocController.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocController.java index fa373e5691..5d1e5d51f7 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocController.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocController.java @@ -16,35 +16,33 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpStatus; +import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.output.HtmlRender; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.zowe.apiml.apicatalog.services.status.APIServiceStatusService; +import org.zowe.apiml.apicatalog.exceptions.ApiDiffNotAvailableException; +import org.zowe.apiml.apicatalog.swagger.ApiDocRetrievalService; /** * Main API for handling requests from the API Catalog UI, routed through the gateway */ +@Slf4j @RestController @RequestMapping("/apidoc") @Tag(name = "API Documentation") -public class CatalogApiDocController { - - private final APIServiceStatusService apiServiceStatusService; - - /** - * Create the controller and autowire in the repository services - * - * @param apiServiceStatusService repo service for registered services - */ - @Autowired - public CatalogApiDocController(APIServiceStatusService apiServiceStatusService) { - this.apiServiceStatusService = apiServiceStatusService; - } +@RequiredArgsConstructor +public class ApiDocController { + private final ApiDocRetrievalService apiDocRetrievalService; /** * Retrieve the api-doc info for this service @@ -72,7 +70,7 @@ public ResponseEntity getApiDocInfo( @PathVariable(value = "serviceId") String serviceId, @Parameter(name = "apiId", description = "The API ID and version, separated by a space, of the API documentation", required = true, example = "zowe.apiml.apicatalog v1.0.0") @PathVariable(value = "apiId") String apiId) { - return this.apiServiceStatusService.getServiceCachedApiDocInfo(serviceId, apiId); + return ResponseEntity.ok(apiDocRetrievalService.retrieveApiDoc(serviceId, apiId)); } /** @@ -97,7 +95,8 @@ public ResponseEntity getApiDocInfo( public ResponseEntity getDefaultApiDocInfo( @Parameter(name = "serviceId", description = "The unique identifier of the registered service", required = true, example = "apicatalog") @PathVariable(value = "serviceId") String serviceId) { - return this.apiServiceStatusService.getServiceCachedDefaultApiDocInfo(serviceId); + + return ResponseEntity.ok(apiDocRetrievalService.retrieveDefaultApiDoc(serviceId)); } @GetMapping(value = "/{serviceId}/{apiId1}/{apiId2}", produces = MediaType.TEXT_HTML_VALUE) @@ -120,6 +119,24 @@ public ResponseEntity getApiDiff( @PathVariable(value = "apiId1") String apiId1, @Parameter(name = "apiId2", description = "The API ID and version, separated by a space, of the API documentation", required = true, example = "zowe.apiml.apicatalog v2.0.0") @PathVariable(value = "apiId2") String apiId2) { - return this.apiServiceStatusService.getApiDiffInfo(serviceId, apiId1, apiId2); + + try { + String doc1 = apiDocRetrievalService.retrieveApiDoc(serviceId, apiId1); + String doc2 = apiDocRetrievalService.retrieveApiDoc(serviceId, apiId2); + ChangedOpenApi diff = OpenApiCompare.fromContents(doc1, doc2); + HtmlRender render = new HtmlRender(); + String result = render.render(diff); + //Remove external stylesheet + result = result.replace("", ""); + return ResponseEntity + .status(HttpStatus.SC_OK) + .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) + .body(result); + } catch (Exception e) { + String errorMessage = String.format("Error retrieving API diff for '%s' with versions '%s' and '%s'", serviceId, apiId1, apiId2); + log.error(errorMessage, e); + throw new ApiDiffNotAvailableException(errorMessage); + } } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/MockController.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/MockController.java deleted file mode 100644 index 2e9bd2357f..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/MockController.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.controllers.api; - -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.zowe.apiml.apicatalog.standalone.ExampleService; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * This controller simulates the responses of services that are not available in the standalone mode. - */ -@Controller -@RequestMapping("/mock") -@SuppressWarnings("squid:S3752") // this controller cannot be more accurate in definition, it should handle all requests -@Tag(name = "API Catalog") -@RequiredArgsConstructor -@ConditionalOnProperty(value = "apiml.catalog.standalone.enabled", havingValue = "true") -public class MockController { - - private final ExampleService exampleService; - - @RequestMapping("/**") - public void mockEndpoint(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException { - String path = httpServletRequest.getRequestURI(); - int index = path.indexOf("/mock"); - if (index >= 0) path = path.substring(index + "/mock".length()); - exampleService.replyExample(httpServletResponse, httpServletRequest.getMethod(), path); - } - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/OidcController.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/OidcController.java new file mode 100644 index 0000000000..a5e2cba8dc --- /dev/null +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/OidcController.java @@ -0,0 +1,46 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.apicatalog.controllers.api; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.zowe.apiml.apicatalog.security.OidcUtils; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Endpoints related to the OIDC integration + */ +@Slf4j +@RestController +@RequestMapping("/oidc") +@Tag(name = "OIDC integration") +public class OidcController { + + private AtomicReference> oidcProviderCache = new AtomicReference<>(); + + @GetMapping(value = "/provider", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getOidcProvider() { + if (oidcProviderCache.get() == null) { + oidcProviderCache.set(OidcUtils.getOidcProvider()); + } + + return new ResponseEntity<>(oidcProviderCache.get(), oidcProviderCache.get().isEmpty() ? HttpStatus.NO_CONTENT : HttpStatus.OK); + } + +} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogController.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ServicesController.java similarity index 69% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogController.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ServicesController.java index 6333b49116..65c0d9cce4 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogController.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ServicesController.java @@ -15,8 +15,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -24,19 +24,17 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.zowe.apiml.apicatalog.exceptions.ContainerStatusRetrievalThrowable; +import org.zowe.apiml.apicatalog.exceptions.ContainerStatusRetrievalException; import org.zowe.apiml.apicatalog.model.APIContainer; import org.zowe.apiml.apicatalog.model.APIService; -import org.zowe.apiml.apicatalog.security.OidcUtils; -import org.zowe.apiml.apicatalog.services.cached.CachedApiDocService; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; +import org.zowe.apiml.apicatalog.swagger.ApiDocRetrievalService; +import org.zowe.apiml.apicatalog.swagger.ContainerService; import org.zowe.apiml.message.log.ApimlLogger; import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.StreamSupport; /** @@ -46,39 +44,15 @@ @RestController @RequestMapping("/") @Tag(name = "API Catalog") -public class ApiCatalogController { +@RequiredArgsConstructor +public class ServicesController { - private final CachedProductFamilyService cachedProductFamilyService; - private final CachedApiDocService cachedApiDocService; + private final ContainerService containerService; + private final ApiDocRetrievalService apiDocRetrievalService; @InjectApimlLogger private final ApimlLogger apimlLog = ApimlLogger.empty(); - private AtomicReference> oidcProviderCache = new AtomicReference<>(); - - /** - * Create the controller and autowire in the repository services - * - * @param cachedProductFamilyService cached service for containers - * @param cachedApiDocService Cached state opf containers and services - */ - @Autowired - public ApiCatalogController(CachedProductFamilyService cachedProductFamilyService, - CachedApiDocService cachedApiDocService) { - this.cachedProductFamilyService = cachedProductFamilyService; - this.cachedApiDocService = cachedApiDocService; - } - - @GetMapping(value = "/oidc/provider", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getOidcProvider() { - if (oidcProviderCache.get() == null) { - oidcProviderCache.set(OidcUtils.getOidcProvider()); - } - - return new ResponseEntity<>(oidcProviderCache.get(), oidcProviderCache.get().isEmpty() ? HttpStatus.NO_CONTENT : HttpStatus.OK); - } - - /** * Get all containers * @@ -99,20 +73,17 @@ public ResponseEntity> getOidcProvider() { @ApiResponse(responseCode = "404", description = "URI not found"), @ApiResponse(responseCode = "500", description = "An unexpected condition occurred") }) - public ResponseEntity> getAllAPIContainers() throws ContainerStatusRetrievalThrowable { + public ResponseEntity> getAllAPIContainers() throws ContainerStatusRetrievalException { try { - Iterable allContainers = cachedProductFamilyService.getAllContainers(); + Iterable allContainers = containerService.getAllContainers(); List apiContainers = toList(allContainers); if (apiContainers == null || apiContainers.isEmpty()) { return new ResponseEntity<>(apiContainers, HttpStatus.NO_CONTENT); - } else { - // for each container, check the status of all it's services so it's overall status can be set here - apiContainers.forEach(cachedProductFamilyService::calculateContainerServiceValues); - return new ResponseEntity<>(apiContainers, HttpStatus.OK); } + return new ResponseEntity<>(apiContainers, HttpStatus.OK); } catch (Exception e) { apimlLog.log("org.zowe.apiml.apicatalog.containerCouldNotBeRetrieved", e.getMessage()); - throw new ContainerStatusRetrievalThrowable(e); + throw new ContainerStatusRetrievalException(e); } } @@ -135,26 +106,33 @@ public ResponseEntity> getAllAPIContainers() throws Container @ApiResponse(responseCode = "404", description = "URI not found"), @ApiResponse(responseCode = "500", description = "An unexpected condition occurred") }) - public ResponseEntity> getAPIContainerById(@PathVariable(value = "id") String id) throws ContainerStatusRetrievalThrowable { + public ResponseEntity> getAPIContainerById(@PathVariable(value = "id") String id) throws ContainerStatusRetrievalException { try { List apiContainers = new ArrayList<>(); - APIContainer containerById = cachedProductFamilyService.getContainerById(id); + APIContainer containerById = containerService.getContainerById(id); if (containerById != null) { apiContainers.add(containerById); } if (!apiContainers.isEmpty()) { - apiContainers.forEach(apiContainer -> { - // For this single container, check the status of all it's services so it's overall status can be set here - cachedProductFamilyService.calculateContainerServiceValues(apiContainer); + apiContainers.forEach( // add API Doc to the services to improve UI performance - setApiDocToService(apiContainer); - }); + this::setApiDocToService + ); } return new ResponseEntity<>(apiContainers, HttpStatus.OK); } catch (Exception e) { apimlLog.log("org.zowe.apiml.apicatalog.containerCouldNotBeRetrieved", e.getMessage()); - throw new ContainerStatusRetrievalThrowable(e); + throw new ContainerStatusRetrievalException(e); + } + } + + private String getApiDoc(String serviceId) { + try { + return apiDocRetrievalService.retrieveDefaultApiDoc(serviceId); + } catch (Exception e) { + log.debug("Cannot download api doc", e); } + return null; } /** @@ -177,28 +155,23 @@ public ResponseEntity> getAPIContainerById(@PathVariable(valu @ApiResponse(responseCode = "404", description = "URI not found"), @ApiResponse(responseCode = "500", description = "An unexpected condition occurred") }) - public ResponseEntity getAPIServicesById(@PathVariable(value = "id") String id) throws ContainerStatusRetrievalThrowable { + public ResponseEntity getAPIServicesById(@PathVariable(value = "id") String id) throws ContainerStatusRetrievalException { try { - - var services = cachedProductFamilyService.getServices(); - if (services == null) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - var service = services.get(id); + var service = containerService.getService(id); if (service == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - log.debug("Getting service by id {}", id); - String apiDoc = cachedApiDocService.getDefaultApiDocForService(id); - log.debug("Getting service: {} with status {}", service.getServiceId(), service.getStatus()); + log.debug("Getting service api doc by id {}", id); + String apiDoc = getApiDoc(id); + log.debug("Getting service: {} with status {}", service.getServiceId(), service.getStatus()); if (apiDoc != null) { log.debug("API doc was retrieved"); service.setApiDoc(apiDoc); - List apiVersions = cachedApiDocService.getApiVersionsForService(id); + List apiVersions = apiDocRetrievalService.retrieveApiVersions(id); service.setApiVersions(apiVersions); log.debug("Got API versions: {}", apiVersions != null ? apiVersions.size() : 0); - String defaultApiVersion = cachedApiDocService.getDefaultApiVersionForService(id); + String defaultApiVersion = apiDocRetrievalService.retrieveDefaultApiVersion(id); log.debug("Default API version: {}", defaultApiVersion); service.setDefaultApiVersion(defaultApiVersion); } else { @@ -207,7 +180,7 @@ public ResponseEntity getAPIServicesById(@PathVariable(value = "id") return new ResponseEntity<>(service, HttpStatus.OK); } catch (Exception e) { apimlLog.log("org.zowe.apiml.apicatalog.serviceCouldNotBeRetrieved", e.getMessage()); - throw new ContainerStatusRetrievalThrowable(e); + throw new ContainerStatusRetrievalException(e); } } @@ -217,15 +190,15 @@ private void setApiDocToService(APIContainer apiContainer) { // it may or may not be null String serviceId = apiService.getServiceId(); try { - String apiDoc = cachedApiDocService.getDefaultApiDocForService(serviceId); + String apiDoc = apiDocRetrievalService.retrieveDefaultApiDoc(serviceId); if (apiDoc != null) { apiService.setApiDoc(apiDoc); } - List apiVersions = cachedApiDocService.getApiVersionsForService(serviceId); + List apiVersions = apiDocRetrievalService.retrieveApiVersions(serviceId); apiService.setApiVersions(apiVersions); - String defaultApiVersion = cachedApiDocService.getDefaultApiVersionForService(serviceId); + String defaultApiVersion = apiDocRetrievalService.retrieveDefaultApiVersion(serviceId); apiService.setDefaultApiVersion(defaultApiVersion); } catch (Exception e) { log.debug("An error occurred when trying to fetch ApiDoc for service: {}, processing can continue but this service will not be able to display any Api Documentation.\nError:", serviceId, e); @@ -248,4 +221,5 @@ private List toList(final Iterable iterable) { return StreamSupport.stream(iterable.spliterator(), false) .toList(); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/ApiCatalogControllerExceptionHandler.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/ApiCatalogControllerExceptionHandler.java index 3167ff76af..9981f9ad54 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/ApiCatalogControllerExceptionHandler.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/ApiCatalogControllerExceptionHandler.java @@ -10,8 +10,8 @@ package org.zowe.apiml.apicatalog.controllers.handlers; -import org.zowe.apiml.apicatalog.controllers.api.ApiCatalogController; -import org.zowe.apiml.apicatalog.exceptions.ContainerStatusRetrievalThrowable; +import org.zowe.apiml.apicatalog.controllers.api.ServicesController; +import org.zowe.apiml.apicatalog.exceptions.ContainerStatusRetrievalException; import org.zowe.apiml.message.api.ApiMessageView; import org.zowe.apiml.message.core.Message; import org.zowe.apiml.message.core.MessageService; @@ -24,9 +24,10 @@ /** * This class creates responses for exceptional behavior of the ApiCatalogController */ -@ControllerAdvice(assignableTypes = {ApiCatalogController.class}) +@ControllerAdvice(assignableTypes = {ServicesController.class}) @RequiredArgsConstructor public class ApiCatalogControllerExceptionHandler { + private final MessageService messageService; /** @@ -35,11 +36,12 @@ public class ApiCatalogControllerExceptionHandler { * @param exception ContainerStatusRetrievalThrowable * @return 500 and the message 'Could not retrieve container statuses, {optional text}' */ - @ExceptionHandler(ContainerStatusRetrievalThrowable.class) - public ResponseEntity handleServiceNotFoundException(ContainerStatusRetrievalThrowable exception) { + @ExceptionHandler(ContainerStatusRetrievalException.class) + public ResponseEntity handleServiceNotFoundException(ContainerStatusRetrievalException exception) { Message message = messageService.createMessage("org.zowe.apiml.apicatalog.containerStatusRetrievalException", exception.getMessage()); return ResponseEntity .status(HttpStatus.INTERNAL_SERVER_ERROR) .body(message.mapToView()); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/CatalogApiDocControllerExceptionHandler.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/CatalogApiDocControllerExceptionHandler.java index 76fab5bc27..da459e5939 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/CatalogApiDocControllerExceptionHandler.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/CatalogApiDocControllerExceptionHandler.java @@ -10,31 +10,32 @@ package org.zowe.apiml.apicatalog.controllers.handlers; -import org.zowe.apiml.apicatalog.controllers.api.CatalogApiDocController; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; -import org.zowe.apiml.apicatalog.services.status.model.ServiceNotFoundException; -import org.zowe.apiml.message.api.ApiMessageView; -import org.zowe.apiml.message.core.Message; -import org.zowe.apiml.message.core.MessageService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.zowe.apiml.apicatalog.controllers.api.ApiDocController; +import org.zowe.apiml.apicatalog.exceptions.ApiDocNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ServiceNotFoundException; +import org.zowe.apiml.message.api.ApiMessageView; +import org.zowe.apiml.message.core.Message; +import org.zowe.apiml.message.core.MessageService; /** * This class creates responses for exceptional behavior of the CatalogApiDocController */ -@ControllerAdvice(assignableTypes = {CatalogApiDocController.class}) +@ControllerAdvice(assignableTypes = {ApiDocController.class}) @RequiredArgsConstructor public class CatalogApiDocControllerExceptionHandler { + private final MessageService messageService; /** * Could not retrieve the API Documentation * * @param exception InvalidFormatException - * @return 500 and the message 'TBD' + * @return 404 and the message 'API Documentation not retrieved...' */ @ExceptionHandler(ApiDocNotFoundException.class) public ResponseEntity handleApiDocNotFoundException(ApiDocNotFoundException exception) { @@ -49,15 +50,15 @@ public ResponseEntity handleApiDocNotFoundException(ApiDocNotFou * Could not retrieve the API Documentation as the Gateway was not available * * @param exception InvalidFormatException - * @return 404 and the message 'TBD' + * @return 404 and the message 'Service not located...' */ @ExceptionHandler(ServiceNotFoundException.class) public ResponseEntity handleServiceNotFoundException(ServiceNotFoundException exception) { - Message message = messageService.createMessage("org.zowe.apiml.apicatalog.serviceNotFound", exception.getMessage()); return ResponseEntity .status(HttpStatus.NOT_FOUND) .body(message.mapToView()); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/custom/CustomErrorStatusHandlingBean.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/custom/CustomErrorStatusHandlingBean.java index 9f5460d9e1..3cf01aa70b 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/custom/CustomErrorStatusHandlingBean.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/handlers/custom/CustomErrorStatusHandlingBean.java @@ -18,9 +18,11 @@ @Component public class CustomErrorStatusHandlingBean implements WebServerFactoryCustomizer { + @Override public void customize(ConfigurableServletWebServerFactory factory) { factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/not_found")); factory.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/internal_error")); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiDiffNotAvailableException.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiDiffNotAvailableException.java similarity index 92% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiDiffNotAvailableException.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiDiffNotAvailableException.java index 8b0de789ec..758e46cf1d 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiDiffNotAvailableException.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiDiffNotAvailableException.java @@ -8,7 +8,7 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status.model; +package org.zowe.apiml.apicatalog.exceptions; /** * Exception thrown when API diff is not available diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiDocNotFoundException.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiDocNotFoundException.java similarity index 86% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiDocNotFoundException.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiDocNotFoundException.java index bcf4ba20e2..e61fe7a5d3 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiDocNotFoundException.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiDocNotFoundException.java @@ -8,7 +8,7 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status.model; +package org.zowe.apiml.apicatalog.exceptions; /** * Exception thrown when API Doc is not accessible @@ -21,8 +21,8 @@ public ApiDocNotFoundException(String message, Throwable cause) { super(message, cause); } - public ApiDocNotFoundException(String s) { - super(s); + public ApiDocNotFoundException(String message) { + this(message, null); } /** @@ -33,4 +33,5 @@ public ApiDocNotFoundException(String s) { public synchronized Throwable fillInStackTrace() { return this; } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiVersionNotFoundException.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiVersionNotFoundException.java similarity index 84% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiVersionNotFoundException.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiVersionNotFoundException.java index 37451c7bcb..e786df4671 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ApiVersionNotFoundException.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ApiVersionNotFoundException.java @@ -8,9 +8,12 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status.model; +package org.zowe.apiml.apicatalog.exceptions; public class ApiVersionNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1187349522463958685L; + public ApiVersionNotFoundException(String s) { super(s); } @@ -22,4 +25,5 @@ public ApiVersionNotFoundException(String s) { public synchronized Throwable fillInStackTrace() { return this; } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ContainerStatusRetrievalThrowable.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ContainerStatusRetrievalException.java similarity index 66% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ContainerStatusRetrievalThrowable.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ContainerStatusRetrievalException.java index 7a1e203428..85676ee3c5 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ContainerStatusRetrievalThrowable.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ContainerStatusRetrievalException.java @@ -10,9 +10,12 @@ package org.zowe.apiml.apicatalog.exceptions; -public class ContainerStatusRetrievalThrowable extends Throwable { +public class ContainerStatusRetrievalException extends Exception { - public ContainerStatusRetrievalThrowable(Throwable e) { + private static final long serialVersionUID = 2060505088907324466L; + + public ContainerStatusRetrievalException(Throwable e) { super(e); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ServiceNotFoundException.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ServiceNotFoundException.java similarity index 90% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ServiceNotFoundException.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ServiceNotFoundException.java index 2d1a0d1a87..2f52d643ab 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/ServiceNotFoundException.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/exceptions/ServiceNotFoundException.java @@ -8,7 +8,7 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status.model; +package org.zowe.apiml.apicatalog.exceptions; /** * Exception thrown when a Service is not accessible diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/health/ApiCatalogHealthIndicator.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/health/ApiCatalogHealthIndicator.java index 365a16a107..c5fee29abf 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/health/ApiCatalogHealthIndicator.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/health/ApiCatalogHealthIndicator.java @@ -38,4 +38,5 @@ protected void doHealthCheck(Health.Builder builder) { .status(healthStatus) .withDetail(gatewayServiceId, healthStatus.getCode()); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/EurekaServiceInstanceRequest.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/EurekaServiceInstanceRequest.java deleted file mode 100644 index 900bfc7671..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/EurekaServiceInstanceRequest.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.instance; - -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class EurekaServiceInstanceRequest { - - private String serviceId; - private String eurekaRequestUrl; - private String username; - private String password; - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeService.java deleted file mode 100644 index 357669503b..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeService.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.instance; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.retry.RetryException; -import org.springframework.retry.annotation.Backoff; -import org.springframework.retry.annotation.Recover; -import org.springframework.retry.annotation.Retryable; -import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.model.APIContainer; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; -import org.zowe.apiml.message.log.ApimlLogger; -import org.zowe.apiml.product.constants.CoreService; -import org.zowe.apiml.product.gateway.GatewayNotAvailableException; -import org.zowe.apiml.product.instance.InstanceInitializationException; -import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; -import org.zowe.apiml.product.registry.CannotRegisterServiceException; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import static org.zowe.apiml.constants.EurekaMetadataDefinition.CATALOG_ID; - -/** - * Initialize the API catalog with the running instances. - */ -@Slf4j -@Service -@RequiredArgsConstructor -public class InstanceInitializeService { - - private final CachedProductFamilyService cachedProductFamilyService; - private final CachedServicesService cachedServicesService; - private final InstanceRetrievalService instanceRetrievalService; - private final InstanceRefreshService instanceRefreshService; - - @InjectApimlLogger - private final ApimlLogger apimlLog = ApimlLogger.empty(); - - /** - * Initialise the API Catalog with all current running instances - * The API Catalog itself must be UP before checking all other instances - * If the catalog is not up, or if the fetch fails, then wait for a defined period and retry up to a max of 5 times - * - * @throws CannotRegisterServiceException if the fetch fails or the catalog is not registered with the discovery - */ - @Retryable( - retryFor = {RetryException.class}, - noRetryFor = CannotRegisterServiceException.class, - maxAttempts = 5, - backoff = @Backoff(delayExpression = "#{${apiml.service-registry.serviceFetchDelayInMillis}}")) - public void retrieveAndRegisterAllInstancesWithCatalog() throws CannotRegisterServiceException { - log.info("Initialising API Catalog with Discovery services."); - try { - String serviceId = CoreService.API_CATALOG.getServiceId(); - InstanceInfo apiCatalogInstance = instanceRetrievalService.getInstanceInfo(serviceId); - if (apiCatalogInstance == null) { - String msg = "API Catalog Instance not retrieved from Discovery service"; - log.debug(msg); - throw new RetryException(msg); - } else { - log.info("API Catalog instance found, retrieving all services."); - getAllInstances(apiCatalogInstance); - instanceRefreshService.start(); - } - } catch (InstanceInitializationException | GatewayNotAvailableException e) { - throw new RetryException(e.getMessage()); - } catch (Exception e) { - String msg = "An unexpected exception occurred when trying to retrieve API Catalog instance from Discovery service"; - apimlLog.log("org.zowe.apiml.apicatalog.initializeAborted", e.getMessage()); - throw new CannotRegisterServiceException(msg, e); - } - } - - @Recover - public void recover(RetryException e) { - apimlLog.log("org.zowe.apiml.apicatalog.initializeFailed"); - } - - /** - * Query the discovery service for all running instances - */ - private void updateCacheWithAllInstances() { - Applications discoveryApplications = instanceRetrievalService.getAllInstancesFromDiscovery(false); - - // Only include services which have a instances - List listApplication = discoveryApplications.getRegisteredApplications() - .stream() - .filter(application -> !application.getInstances().isEmpty()) - .toList(); - - // Return an empty string if no services are found after filtering - if (listApplication.isEmpty()) { - log.info("No services found"); - return; - } - - log.debug("Found: " + listApplication.size() + " services on startup."); - String s = listApplication.stream() - .map(Application::getName).collect(Collectors.joining(", ")); - log.debug("Discovered Services: {}", s); - - // create containers for services - listApplication.forEach(this::createContainers); - listApplication.forEach(this::createServices); - // populate the cache - Collection containers = cachedProductFamilyService.getAllContainers(); - log.debug("Cache contains: " + containers.size() + " tiles."); - } - - - public void createContainers(Application application) { - cachedServicesService.updateService(application.getName(), application); - application.getInstances().forEach(instanceInfo -> { - String productFamilyId = instanceInfo.getMetadata().get(CATALOG_ID); - if (productFamilyId != null) { - log.debug("Initialising product family (creating tile for) : " + productFamilyId); - cachedProductFamilyService.saveContainerFromInstance(productFamilyId, instanceInfo); - } - - }); - } - - public void createServices(Application application) { - cachedServicesService.updateService(application.getName(), application); - application.getInstances().forEach(instanceInfo -> { - log.debug("Initialising service: {}", instanceInfo.getInstanceId()); - cachedProductFamilyService.addService(instanceInfo); - }); - } - - private void getAllInstances(InstanceInfo apiCatalogInstance) { - String productFamilyId = apiCatalogInstance.getMetadata().get(CATALOG_ID); - if (productFamilyId != null) { - log.debug("Initialising product family (creating tile for) : " + productFamilyId); - cachedProductFamilyService.saveContainerFromInstance(productFamilyId, apiCatalogInstance); - } - - updateCacheWithAllInstances(); - log.info("API Catalog initialised with running services.."); - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshService.java deleted file mode 100644 index 3631dea2d5..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshService.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.instance; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.model.APIContainer; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; -import org.zowe.apiml.message.log.ApimlLogger; -import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; - -import java.util.HashSet; -import java.util.Set; - -import static org.zowe.apiml.constants.EurekaMetadataDefinition.CATALOG_ID; - -/** - * Refresh the cache with the latest state of the discovery service - * Use deltas to get latest changes from Eureka - */ -@Slf4j -@Service -@RequiredArgsConstructor -public class InstanceRefreshService { - - // until versioning is implemented, only v1 API docs are supported - private final CachedProductFamilyService cachedProductFamilyService; - private final CachedServicesService cachedServicesService; - private final InstanceRetrievalService instanceRetrievalService; - private boolean isStarted = false; - - @InjectApimlLogger - private final ApimlLogger apimlLog = ApimlLogger.empty(); - - /** - * Starts refreshing the API Catalog cache - */ - public void start() { - this.isStarted = true; - log.info("InstanceRefreshService started"); - } - - /** - * Periodically refresh the container/service caches - * Depends on the GatewayClient: no refreshes happen when it's not initialized - */ - @Scheduled( - initialDelayString = "${apiml.service-registry.cacheRefreshInitialDelayInMillis}", - fixedDelayString = "${apiml.service-registry.cacheRefreshRetryDelayInMillis}") - public void refreshCacheFromDiscovery() { - if (!isStarted) { - log.debug("InstanceRefreshService is stopped. Skip refresh."); - return; - } - - log.debug("Refreshing API Catalog with the latest state of discovery service"); - - this.compareServices(); - } - - /** - * @return a list of changed services - */ - @SuppressWarnings("deprecation") - private Set compareServices() { - // Updated containers - Set containersUpdated = new HashSet<>(); - Applications cachedServices = cachedServicesService.getAllCachedServices(); - Applications deltaFromDiscovery = instanceRetrievalService.getAllInstancesFromDiscovery(true); - - if (deltaFromDiscovery != null && !deltaFromDiscovery.getRegisteredApplications().isEmpty()) { - // use the version to check if this delta has changed, it is deprecated and should be replaced as soon as a - // newer identifier is provided by Netflix - // if getVersion is removed then the process will be slightly more inefficient but will not need to change - if (cachedServicesService.getVersionDelta() != deltaFromDiscovery.getVersion()) { - containersUpdated = processServiceInstances(cachedServices, deltaFromDiscovery); - } - cachedServicesService.setVersionDelta(deltaFromDiscovery.getVersion()); - } - return containersUpdated; - } - - /** - * Check each delta instance and consider it for processing - * - * @param cachedServices the collection of cached services - * @param deltaFromDiscovery changed instances - */ - private Set processServiceInstances(Applications cachedServices, Applications deltaFromDiscovery) { - Set containersUpdated = new HashSet<>(); - Set updatedServices = updateDelta(deltaFromDiscovery); - updatedServices.forEach(instance -> { - try { - // check if this instance should be processed/updated - processServiceInstance(containersUpdated, cachedServices, deltaFromDiscovery, instance); - } catch (Exception e) { - log.debug("could not update cache for service: " + instance + ", processing will continue.", e); - } - }); - return containersUpdated; - } - - /** - * Get this instance service details and check if it should be processed - * - * @param containersUpdated containers, which were updated - * @param cachedServices existing services - * @param deltaFromDiscovery changed service instances - * @param instance this instance - */ - private void processServiceInstance(Set containersUpdated, Applications cachedServices, - Applications deltaFromDiscovery, InstanceInfo instance) { - Application application = null; - // Get the application which this instance belongs to - if (cachedServices != null && cachedServices.getRegisteredApplications() != null) { - application = cachedServices.getRegisteredApplications().stream() - .filter(service -> service.getName().equalsIgnoreCase(instance.getAppName())).findFirst().orElse(null); - } - // if its new then it will only be in the delta - if (application == null || application.getInstances().isEmpty()) { - application = deltaFromDiscovery.getRegisteredApplications().stream() - .filter(service -> service.getName().equalsIgnoreCase(instance.getAppName())).findFirst().orElse(null); - } - - // there's no chance which this case is not called. It's just double check - if (application == null || application.getInstances().isEmpty()) { - log.debug("Instance {} couldn't get details from cache and delta", instance.getAppName()); - return; - } - - processInstance(containersUpdated, instance, application); - } - - /** - * Go ahead and retrieve this instances API doc and update the cache - * - * @param containersUpdated containers, which were updated - * @param instance the instance - * @param application the service - */ - private void processInstance(Set containersUpdated, InstanceInfo instance, Application application) { - application.addInstance(instance); - - if (!InstanceInfo.InstanceStatus.DOWN.equals(instance.getStatus())) { - // update any containers which contain this service - updateContainer(containersUpdated, instance); - updateService(instance); - } - if (InstanceInfo.ActionType.DELETED.equals(instance.getActionType())) { - // remove instance which isn't available anymore - cachedProductFamilyService.removeInstance(instance.getMetadata().get(CATALOG_ID), instance); - cachedProductFamilyService.removeInstanceFromServices(instance); - return; - } - - // Update the service cache - updateService(instance.getAppName(), application); - } - - private void updateService(String serviceId, Application application) { - if (application == null) { - log.debug("Could not find Application object for serviceId: " + serviceId + " cache not updated with " + - "current values."); - } else { - cachedServicesService.updateService(serviceId, application); - log.debug("Updated service cache for service: " + serviceId); - } - } - - /** - * Update the container - * - * @param containersUpdated what containers were updated - * @param instanceInfo the instance - */ - private void updateContainer(Set containersUpdated, InstanceInfo instanceInfo) { - String productFamilyId = instanceInfo.getMetadata().get(CATALOG_ID); - if (productFamilyId == null) { - log.debug("Cannot create a tile without a parent id, the metadata for service '{}' must contain an entry for '{}'", - instanceInfo.getAppName(), CATALOG_ID); - } else { - APIContainer container = cachedProductFamilyService.saveContainerFromInstance(productFamilyId, instanceInfo); - log.debug("Created/Updated tile and updated cache for container: " + container.getId() + " @ " + container.getLastUpdatedTimestamp().getTime()); - containersUpdated.add(productFamilyId); - } - } - - /** - * Update the container - * - * @param instanceInfo the instance - */ - private void updateService(InstanceInfo instanceInfo) { - cachedProductFamilyService.addService(instanceInfo); - log.debug("Created/Updated service and updated cache: {}", instanceInfo.getInstanceId()); - } - - /** - * Compare cached instances against eureka delta to send back a change-list - * - * @param delta retrieved from Eureka - * @return changed instances - */ - private Set updateDelta(Applications delta) { - int deltaCount = 0; - Set updatedInstances = new HashSet<>(); - for (Application app : delta.getRegisteredApplications()) { - for (InstanceInfo instance : app.getInstances()) { - ++deltaCount; - if (InstanceInfo.ActionType.ADDED.equals(instance.getActionType())) { - log.debug("Added instance {} to the list of changed instances ", instance.getId()); - updatedInstances.add(instance); - } else if (InstanceInfo.ActionType.MODIFIED.equals(instance.getActionType())) { - log.debug("Modified instance {} added to the list of changed instances ", instance.getId()); - updatedInstances.add(instance); - } else if (InstanceInfo.ActionType.DELETED.equals(instance.getActionType())) { - log.debug("Deleted instance {} added to the list of changed instances ", instance.getId()); - instance.setStatus(InstanceInfo.InstanceStatus.DOWN); - updatedInstances.add(instance); - } - } - } - - log.debug("The total number of changed instances fetched by the delta processor : {}", deltaCount); - return updatedInstances; - } - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalService.java deleted file mode 100644 index f555306de6..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalService.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.instance; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.converters.jackson.EurekaJsonJacksonCodec; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import jakarta.validation.constraints.NotBlank; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpEntity; -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.io.entity.EntityUtils; -import org.apache.hc.core5.http.message.BasicHeader; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.discovery.DiscoveryConfigProperties; -import org.zowe.apiml.constants.EurekaMetadataDefinition; -import org.zowe.apiml.message.log.ApimlLogger; -import org.zowe.apiml.product.instance.InstanceInitializationException; -import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; -import org.zowe.apiml.product.registry.ApplicationWrapper; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Predicate; - -import static org.zowe.apiml.product.constants.CoreService.GATEWAY; - -/** - * Service for instance retrieval from Eureka - */ -@Slf4j -@Service -public class InstanceRetrievalService { - - private final DiscoveryConfigProperties discoveryConfigProperties; - private final CloseableHttpClient httpClient; - - private static final String APPS_ENDPOINT = "apps/"; - private static final String DELTA_ENDPOINT = "delta"; - private static final String UNKNOWN = "unknown"; - - @InjectApimlLogger - private final ApimlLogger apimlLog = ApimlLogger.empty(); - - private ObjectMapper mapper = new ObjectMapper(); - - @Autowired - public InstanceRetrievalService(DiscoveryConfigProperties discoveryConfigProperties, - CloseableHttpClient httpClient) { - this.discoveryConfigProperties = discoveryConfigProperties; - this.httpClient = httpClient; - } - - private InstanceInfo getInstanceInfo(String serviceId, AtomicBoolean instanceFound, Predicate selector) { - List eurekaServiceInstanceRequests = constructServiceInfoQueryRequest(serviceId, false); - // iterate over list of discovery services, return at first success - for (EurekaServiceInstanceRequest eurekaServiceInstanceRequest : eurekaServiceInstanceRequests) { - // call Eureka REST endpoint to fetch single or all Instances - try { - String responseBody = queryDiscoveryForInstances(eurekaServiceInstanceRequest); - if (responseBody != null) { - instanceFound.set(true); - return extractSingleInstanceFromApplication(serviceId, responseBody, selector); - } - } catch (Exception e) { - log.debug("Error obtaining instance information from {}, error message: {}", - eurekaServiceInstanceRequest.getEurekaRequestUrl(), e.getMessage()); - } - } - return null; - } - - /** - * Retrieves {@link InstanceInfo} of particular service - * - * @param serviceId the service to search for - * @return service instance - */ - public InstanceInfo getInstanceInfo(@NotBlank(message = "Service Id must be supplied") String serviceId) { - if (serviceId.equalsIgnoreCase(UNKNOWN)) { - return null; - } - - // identification if there was no instance or any error happened during fetching - AtomicBoolean instanceFound = new AtomicBoolean(false); - InstanceInfo instanceInfo = getInstanceInfo(serviceId, instanceFound, - ii -> EurekaMetadataDefinition.RegistrationType.of(ii.getMetadata()).isPrimary() - ); - if (instanceInfo == null) { - // maybe the input is apimlId, try to find the matching Gateway (multi-tenancy use case) - instanceInfo = getInstanceInfo(GATEWAY.getServiceId(), instanceFound, - ii -> EurekaMetadataDefinition.RegistrationType.of(ii.getMetadata()).isAdditional() - ); - } - - if (!instanceFound.get()) { - String msg = "An error occurred when trying to get instance info for: " + serviceId; - throw new InstanceInitializationException(msg); - } - - return instanceInfo; - } - - /** - * Retrieve instances from the discovery service - * - * @param delta filter the registry information to the just updated infos - * @return the Applications object that wraps all the registry information - */ - public Applications getAllInstancesFromDiscovery(boolean delta) { - - List requestInfoList = constructServiceInfoQueryRequest(null, delta); - for (EurekaServiceInstanceRequest requestInfo : requestInfoList) { - try { - String responseBody = queryDiscoveryForInstances(requestInfo); - return extractApplications(responseBody); - } catch (Exception e) { - log.debug("Not able to contact discovery service: {}", requestInfo.getEurekaRequestUrl(), e); - } - } - // call Eureka REST endpoint to fetch single or all Instances - return null; - } - - /** - * Parse information from the response and extract the Applications object which contains all the registry information returned by eureka server - * - * @param responseBody the http response body - * @return Applications object that wraps all the registry information - */ - private Applications extractApplications(String responseBody) { - Applications applications = null; - ObjectMapper mapper = new EurekaJsonJacksonCodec().getObjectMapper(Applications.class); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - try { - applications = mapper.readValue(responseBody, Applications.class); - } catch (IOException e) { - apimlLog.log("org.zowe.apiml.apicatalog.serviceRetrievalParsingFailed", e.getMessage()); - } - - return applications; - } - - /** - * Query Discovery - * - * @param eurekaServiceInstanceRequest information used to query the discovery service - * @return ResponseEntity query response - */ - String queryDiscoveryForInstances(EurekaServiceInstanceRequest eurekaServiceInstanceRequest) throws IOException { - HttpGet httpGet = new HttpGet(eurekaServiceInstanceRequest.getEurekaRequestUrl()); - for (Header header : createRequestHeader(eurekaServiceInstanceRequest)) { - httpGet.setHeader(header); - } - - return httpClient.execute(httpGet, response -> { - final int statusCode = response.getCode(); - final HttpEntity responseEntity = response.getEntity(); - - String responseBody = ""; - if (responseEntity != null) { - responseBody = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8); - } - - if (HttpStatus.valueOf(statusCode).is2xxSuccessful()) { - return responseBody; - } - - apimlLog.log("org.zowe.apiml.apicatalog.serviceRetrievalRequestFailed", - eurekaServiceInstanceRequest.getServiceId(), - eurekaServiceInstanceRequest.getEurekaRequestUrl(), - statusCode, - response.getReasonPhrase() != null ? response.getReasonPhrase() : responseBody - ); - - return null; - }); - } - - /** - * @param serviceId the service to search for - * @param responseBody the fetch attempt response body - * @return service instance - */ - InstanceInfo extractSingleInstanceFromApplication(String serviceId, String responseBody, Predicate selector) { - ApplicationWrapper application = null; - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - try { - application = mapper.readValue(responseBody, ApplicationWrapper.class); - } catch (IOException e) { - log.debug("Could not extract service: {} info from discovery --{}", serviceId, e.getMessage(), e); - } - - return Optional.ofNullable(application) - .map(ApplicationWrapper::getApplication) - .map(Application::getInstances) - .orElse(Collections.emptyList()) - .stream().filter(selector) - .findFirst() - .orElse(null); - } - - /** - * Construct a tuple used to query the discovery service - * - * @param serviceId optional service id - * @return request information - */ - private List constructServiceInfoQueryRequest(String serviceId, boolean getDelta) { - String[] discoveryServiceUrls = discoveryConfigProperties.getLocations(); - List eurekaServiceInstanceRequests = new ArrayList<>(discoveryServiceUrls.length); - for (String discoveryUrl : discoveryServiceUrls) { - String discoveryServiceLocatorUrl = discoveryUrl.endsWith("/") ? discoveryUrl + APPS_ENDPOINT : discoveryUrl + "/" + APPS_ENDPOINT; - if (getDelta) { - discoveryServiceLocatorUrl += DELTA_ENDPOINT; - } else { - if (serviceId != null) { - discoveryServiceLocatorUrl += serviceId.toLowerCase(); - } - } - - String eurekaUsername = discoveryConfigProperties.getEurekaUserName(); - String eurekaUserPassword = discoveryConfigProperties.getEurekaUserPassword(); - - log.debug("Querying instance information of the service {} from the URL {} with the user {} and password {}", - serviceId, discoveryServiceLocatorUrl, eurekaUsername, - StringUtils.isEmpty(eurekaUserPassword) ? "NO PASSWORD" : "*******"); - - EurekaServiceInstanceRequest eurekaServiceInstanceRequest = EurekaServiceInstanceRequest.builder() - .serviceId(serviceId) - .eurekaRequestUrl(discoveryServiceLocatorUrl) - .username(eurekaUsername) - .password(eurekaUserPassword) - .build(); - eurekaServiceInstanceRequests.add(eurekaServiceInstanceRequest); - } - - return eurekaServiceInstanceRequests; - } - - /** - * Create HTTP headers - * - * @return HTTP Headers - */ - private List
createRequestHeader(EurekaServiceInstanceRequest eurekaServiceInstanceRequest) { - List
headers = new ArrayList<>(); - if (eurekaServiceInstanceRequest != null && eurekaServiceInstanceRequest.getUsername() != null && eurekaServiceInstanceRequest.getPassword() != null) { - String basicToken = "Basic " + Base64.getEncoder().encodeToString((eurekaServiceInstanceRequest.getUsername() + ":" - + eurekaServiceInstanceRequest.getPassword()).getBytes()); - headers.add(new BasicHeader(HttpHeaders.AUTHORIZATION, basicToken)); - } - headers.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)); - headers.add(new BasicHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)); - return headers; - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/model/SemanticVersion.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/model/SemanticVersion.java index ae05f4692c..50fcdb8a0a 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/model/SemanticVersion.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/model/SemanticVersion.java @@ -15,6 +15,7 @@ @EqualsAndHashCode(callSuper = false) public class SemanticVersion implements Comparable { + @NonNull private final int[] numbers; @@ -38,4 +39,5 @@ public int compareTo(@NonNull SemanticVersion another) { } return 0; } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/ApiCatalogLogoutSuccessHandler.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/ApiCatalogLogoutSuccessHandler.java index 130f47b59b..984ab67e93 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/ApiCatalogLogoutSuccessHandler.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/ApiCatalogLogoutSuccessHandler.java @@ -58,4 +58,5 @@ public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletRe context.setAuthentication(null); SecurityContextHolder.clearContext(); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java index 5fa7838b50..0d9ab161b4 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java @@ -14,7 +14,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -69,8 +68,8 @@ @EnableApimlAuth @EnableMethodSecurity @EnableConfigurationProperties(SafSecurityConfigurationProperties.class) -@ConditionalOnProperty(value = "apiml.catalog.standalone.enabled", havingValue = "false", matchIfMissing = true) public class SecurityConfiguration { + private static final String APIDOC_ROUTES = "/apidoc/**"; private static final String STATIC_REFRESH_ROUTE = "/static-api/refresh"; @@ -86,6 +85,7 @@ public class SecurityConfiguration { private boolean isAttlsEnabled; @Value("${apiml.health.protected:true}") private boolean isHealthEndpointProtected; + /** * Filter chain for protecting /apidoc/** endpoints with MF credentials for client certificate. */ diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocService.java deleted file mode 100644 index 843d34f473..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocService.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.cached; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocCacheKey; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; -import org.zowe.apiml.apicatalog.services.status.APIDocRetrievalService; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; -import org.zowe.apiml.apicatalog.services.status.model.ApiVersionNotFoundException; -import org.zowe.apiml.apicatalog.swagger.TransformApiDocService; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import java.util.function.UnaryOperator; - -/** - * Caching service for API Doc Info - */ - -@Service -@Slf4j -public class CachedApiDocService { - public static final String DEFAULT_API_KEY = "default"; - - private static final Map serviceApiDocs = new HashMap<>(); - private static final Map> serviceApiVersions = new HashMap<>(); - private static final Map serviceApiDefaultVersions = new HashMap<>(); - - private final APIDocRetrievalService apiDocRetrievalService; - private final TransformApiDocService transformApiDocService; - - private static final UnaryOperator exceptionMessage = serviceId -> "No API Documentation was retrieved for the service " + serviceId + "."; - - public CachedApiDocService(APIDocRetrievalService apiDocRetrievalService, TransformApiDocService transformApiDocService) { - this.apiDocRetrievalService = apiDocRetrievalService; - this.transformApiDocService = transformApiDocService; - } - - /** - * Fetches API documentation for a given service and version. - * - * @param serviceId service identifier - * @param cacheKeySuffix version or default API key - * @param retrievalLogic supplier providing the API documentation retrieval logic - * @return the API documentation - */ - private String fetchApiDoc( - final String serviceId, - final String cacheKeySuffix, - final Supplier retrievalLogic - ) { - ApiDocCacheKey cacheKey = new ApiDocCacheKey(serviceId, cacheKeySuffix); - String errorMessage = ""; - Exception exception = null; - - // Try to fetch API doc from the retrieval logic - try { - ApiDocInfo apiDocInfo = retrievalLogic.get(); - if (apiDocInfo != null && apiDocInfo.getApiDocContent() != null) { - String apiDoc = transformApiDocService.transformApiDoc(serviceId, apiDocInfo); - CachedApiDocService.serviceApiDocs.put(cacheKey, apiDoc); - return apiDoc; - } else { - log.debug("No API Documentation found for the service {}", serviceId); - } - } catch (Exception e) { - log.debug("Exception updating API doc in cache for '{} {}'", serviceId, cacheKeySuffix, e); - errorMessage = e.getMessage(); - exception = e; - } - - // If no DS is available, try to use cached data - String apiDoc = CachedApiDocService.serviceApiDocs.get(cacheKey); - if (apiDoc != null) { - log.debug("Using cached API doc for service '{}'", serviceId); - return apiDoc; - } - - // Cannot obtain API doc, throw exception - log.error("No API doc available for '{} {}'", serviceId, cacheKeySuffix); - throw new ApiDocNotFoundException( - exceptionMessage.apply(serviceId) + " Root cause: " + errorMessage, exception - ); - } - - /** - * Update the api docs for this service - * - * @param serviceId service identifier - * @param apiVersion the version of the API - * @return api doc info for the requested service id - */ - public String getApiDocForService(final String serviceId, final String apiVersion) { - return fetchApiDoc( - serviceId, - apiVersion, - () -> apiDocRetrievalService.retrieveApiDoc(serviceId, apiVersion) - ); - } - - /** - * Update the api docs for this service - * This method should be executed if a new version of a service is discovered on renewal - * - * @param serviceId service identifier - * @param apiVersion the version of the API - * @param apiDoc API Doc info - */ - public void updateApiDocForService(final String serviceId, final String apiVersion, final String apiDoc) { - CachedApiDocService.serviceApiDocs.put(new ApiDocCacheKey(serviceId, apiVersion), apiDoc); - } - - /** - * Update the docs for the latest API version for this service - * - * @param serviceId service identifier - * @return api doc info for the latest API of the request service id - */ - public String getDefaultApiDocForService(final String serviceId) { - return fetchApiDoc( - serviceId, - DEFAULT_API_KEY, - () -> apiDocRetrievalService.retrieveDefaultApiDoc(serviceId) - ); - } - - /** - * Update the latest version api doc for this service. - * THis method should be executed if a new version of a service is discovered on renewal - * - * @param serviceId service identifier - * @param apiDoc API Doc info - */ - public void updateDefaultApiDocForService(final String serviceId, final String apiDoc) { - CachedApiDocService.serviceApiDocs.put(new ApiDocCacheKey(serviceId, DEFAULT_API_KEY), apiDoc); - } - - /** - * Update the api versions for this service - * - * @param serviceId service identifier - * @return List of API version strings for the requested service ID - */ - public List getApiVersionsForService(final String serviceId) { - // First try to fetch apiDoc from the DS - try { - List versions = apiDocRetrievalService.retrieveApiVersions(serviceId); - if (!versions.isEmpty()) { - CachedApiDocService.serviceApiVersions.put(serviceId, versions); - return versions; - } - } catch (Exception e) { - log.debug("Exception updating API versions in cache for {}", serviceId, e); - } - - // if no DS is available try to use cached data - List versions = CachedApiDocService.serviceApiVersions.get(serviceId); - if (versions != null) { - return versions; - } - - // Cannot obtain API doc, end with exception - log.error("No API versions available for service '{}'", serviceId); - throw new ApiVersionNotFoundException("No API versions were retrieved for the service " + serviceId + "."); - } - - /** - * Update the api versions for this service. - * This method should be executed if a new version of a service is discovered on renewal. - * - * @param serviceId service identifier - * @param apiVersions the API versions - */ - public void updateApiVersionsForService(final String serviceId, final List apiVersions) { - CachedApiDocService.serviceApiVersions.put(serviceId, apiVersions); - } - - /** - * Update the default API version for this service. - * - * @param serviceId service identifier - * @return default API version for given service id - */ - public String getDefaultApiVersionForService(final String serviceId) { - // First try to fetch apiDoc from the DS - try { - String version = apiDocRetrievalService.retrieveDefaultApiVersion(serviceId); - if (version != null) { - CachedApiDocService.serviceApiDefaultVersions.put(serviceId, version); - return version; - } - } catch (Exception e) { - log.error("No default API version available for service '{}'", serviceId, e); - } - - // if no DS is available try to use cached data - String version = CachedApiDocService.serviceApiDefaultVersions.get(serviceId); - if (version != null) { - return version; - } - - // cannot obtain apiDoc ends with exception - throw new ApiVersionNotFoundException("Error trying to find default API version"); - } - - /** - * Update the default api version for this service. - * This method should be executed if a new version of a service is discovered on renewal. - * - * @param serviceId service identifier - * @param apiVersion the default API version - */ - public void updateDefaultApiVersionForService(final String serviceId, final String apiVersion) { - CachedApiDocService.serviceApiDefaultVersions.put(serviceId, apiVersion); - } - - /** - * Reset the cache for this service - */ - public void resetCache() { - serviceApiDocs.clear(); - serviceApiVersions.clear(); - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesService.java deleted file mode 100644 index fa2392f4a9..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesService.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.cached; - -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import lombok.NonNull; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -/** - * Container for eureka services - */ -@Service -public class CachedServicesService { - - private Map services = new HashMap<>(); - private long versionDelta; - - /** - * return all cached service instances - * @return instances - */ - public Applications getAllCachedServices() { - if (services.isEmpty()) { - return null; - } else { - return new Applications(null, 1L, new ArrayList<>(services.values())); - } - } - - /** - * return all cached service instances - * @param serviceId the service identifier - * @return instances for this service (might be empty instances collection) - */ - public Application getService(@NonNull final String serviceId) { - return services.get(serviceId.toLowerCase()); - } - - /** - * Update this service with the application object - * @param serviceId the service name (lowercase) - * @param application updated application with running instances - */ - public void updateService(@NonNull final String serviceId, final Application application) { - services.put(serviceId.toLowerCase(), application); - } - /** - * Clear the cache and remove all entries from the map - */ - public void clearAllServices() { - services.clear(); - } - - public long getVersionDelta() { - return versionDelta; - } - - public void setVersionDelta(long versionDelta) { - this.versionDelta = versionDelta; - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/model/ApiDocCacheKey.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/model/ApiDocCacheKey.java deleted file mode 100644 index a527f2374e..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/model/ApiDocCacheKey.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.cached.model; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class ApiDocCacheKey { - private String serviceId; - private String apiVersion; -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIServiceStatusService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIServiceStatusService.java deleted file mode 100644 index b0155526b6..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIServiceStatusService.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Applications; -import lombok.AllArgsConstructor; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.openapitools.openapidiff.core.output.HtmlRender; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.model.APIContainer; -import org.zowe.apiml.apicatalog.services.cached.CachedApiDocService; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; -import org.zowe.apiml.apicatalog.services.status.event.model.ContainerStatusChangeEvent; -import org.zowe.apiml.apicatalog.services.status.event.model.STATUS_EVENT_TYPE; -import org.zowe.apiml.apicatalog.services.status.model.ApiDiffNotAvailableException; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -@Slf4j -@Service -@AllArgsConstructor -public class APIServiceStatusService { - - private final CachedProductFamilyService cachedProductFamilyService; - private final CachedServicesService cachedServicesService; - private final CachedApiDocService cachedApiDocService; - private final OpenApiCompareProducer openApiCompareProducer; - - /** - * Return a cached snapshot of services and instances as a response - * - * @return Applications from cache - */ - public ResponseEntity getCachedApplicationStateResponse() { - return new ResponseEntity<>(cachedServicesService.getAllCachedServices(), createHeaders(), HttpStatus.OK); - } - - /** - * Return a cached snapshot of services and instances - * - * @return Applications from cache - */ - public Applications getCachedApplicationState() { - return cachedServicesService.getAllCachedServices(); - } - - /** - * Retrieve all containers and return them as events - * - * @return container status as events - */ - public List getContainersStateAsEvents() { - log.debug("Retrieving all containers statuses as events"); - List events = new ArrayList<>(); - Iterable allContainers = cachedProductFamilyService.getAllContainers(); - allContainers.forEach(container -> { - cachedProductFamilyService.calculateContainerServiceValues(container); - addContainerEvent(events, container); - }); - return events; - } - - /** - * Return the cached API docs for a service - * - * @param serviceId the unique service id - * @param apiVersion the version of the API - * @return a version of an API Doc - */ - public ResponseEntity getServiceCachedApiDocInfo(@NonNull String serviceId, String apiVersion) { - return new ResponseEntity<>(cachedApiDocService.getApiDocForService(serviceId, apiVersion), createHeaders(), HttpStatus.OK); - } - - /** - * Return the cached default API doc for a service - * - * @param serviceId the unique service id - * @return the default version of an API Doc - */ - public ResponseEntity getServiceCachedDefaultApiDocInfo(@NonNull String serviceId) { - return new ResponseEntity<>(cachedApiDocService.getDefaultApiDocForService(serviceId), createHeaders(), HttpStatus.OK); - } - - /** - * Return the diff of two api versions - * @param serviceId the unique service id - * @param apiVersion1 the old version of the api - * @param apiVersion2 the new version of the api - * @return response containing HTML document detailing changes between api doc versions - */ - public ResponseEntity getApiDiffInfo(@NonNull String serviceId, String apiVersion1, String apiVersion2) { - try { - String doc1 = cachedApiDocService.getApiDocForService(serviceId, apiVersion1); - String doc2 = cachedApiDocService.getApiDocForService(serviceId, apiVersion2); - ChangedOpenApi diff = openApiCompareProducer.fromContents(doc1, doc2); - HtmlRender render = new HtmlRender(); - String result = render.render(diff); - //Remove external stylesheet - result = result.replace("", ""); - return new ResponseEntity<>(result, createHeaders(), HttpStatus.OK); - } catch (Exception e) { - String errorMessage = String.format("Error retrieving API diff for '%s' with versions '%s' and '%s'", serviceId, apiVersion1, apiVersion2); - log.error(errorMessage, e); - throw new ApiDiffNotAvailableException(errorMessage); - } - } - - /** - * Retrieve all containers which were updated inside a given threshold value and return them as events - * - * @return recent container status as events - */ - public List getRecentlyUpdatedContainersAsEvents() { - List recentEvents = new ArrayList<>(); - Iterable allContainers = cachedProductFamilyService.getRecentlyUpdatedContainers(); - allContainers.forEach(container -> { - cachedProductFamilyService.calculateContainerServiceValues(container); - addContainerEvent(recentEvents, container); - }); - if (!recentEvents.isEmpty()) { - log.debug("Recent events found: " + recentEvents.size()); - } - return recentEvents; - } - - /** - * Create an event based on the status of the instance - * - * @param events the list of events to return - * @param container the instance - */ - private void addContainerEvent(List events, APIContainer container) { - STATUS_EVENT_TYPE eventType; - if (InstanceInfo.InstanceStatus.DOWN.name().equalsIgnoreCase(container.getStatus())) { - eventType = STATUS_EVENT_TYPE.CANCEL; - } else if (container.getCreatedTimestamp().equals(container.getLastUpdatedTimestamp())) { - eventType = STATUS_EVENT_TYPE.CREATED_CONTAINER; - } else { - eventType = STATUS_EVENT_TYPE.RENEW; - } - events.add(new ContainerStatusChangeEvent( - container.getId(), - container.getTitle(), - container.getStatus(), - container.getTotalServices(), - container.getActiveServices(), - container.getServices(), - eventType) - ); - } - -// ============================== HELPER METHODS - - - /** - * HTTP headers - * - * @return headers for requests - */ - private HttpHeaders createHeaders() { - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - return headers; - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/OpenApiCompareProducer.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/OpenApiCompareProducer.java deleted file mode 100644 index 200f97e73d..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/OpenApiCompareProducer.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status; - -import org.openapitools.openapidiff.core.OpenApiCompare; -import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.springframework.stereotype.Service; - -@Service -public class OpenApiCompareProducer { - public ChangedOpenApi fromContents(String oldContent, String newContent) { - return OpenApiCompare.fromContents(oldContent, newContent); - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/ContainerStatusChangeEvent.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/ContainerStatusChangeEvent.java deleted file mode 100644 index 05e6b1ab2a..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/ContainerStatusChangeEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status.event.model; - -import org.zowe.apiml.apicatalog.model.APIService; -import lombok.Data; - -import java.util.Set; - -@Data -public class ContainerStatusChangeEvent implements StatusChangeEvent { - private final String containerId; - private final String title; - private final String status; - private final int totalServices; - private final int activeServices; - private Set services; - private final STATUS_EVENT_TYPE statusEventType; - private final String timeStamp; - - public ContainerStatusChangeEvent(String containerId, String title, String status, - int totalServices, int activeServices, - Set services, - STATUS_EVENT_TYPE statusEventType) { - this.containerId = containerId; - this.title = title; - this.status = status; - this.services = services; - this.totalServices = totalServices; - this.activeServices = activeServices; - this.statusEventType = statusEventType; - timeStamp = setTimeStamp(); - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/STATUS_EVENT_TYPE.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/STATUS_EVENT_TYPE.java deleted file mode 100644 index 4593f7e73a..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/STATUS_EVENT_TYPE.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status.event.model; - -public enum STATUS_EVENT_TYPE { - CREATED_CONTAINER, - RENEW, - CANCEL -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/StatusChangeEvent.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/StatusChangeEvent.java deleted file mode 100644 index 6886f0f313..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/event/model/StatusChangeEvent.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status.event.model; - -import java.sql.Timestamp; -import java.text.DateFormat; -import java.time.Instant; -import java.util.TimeZone; - -public interface StatusChangeEvent { - - /** - * Create a String time stamp for this event - * @return a timestamp - */ - default String setTimeStamp() { - Instant now = Instant.now(); - Timestamp current = Timestamp.from(now); - DateFormat df = DateFormat.getDateTimeInstance(); - df.setTimeZone(TimeZone.getTimeZone("UTC")); - return df.format(current); - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java deleted file mode 100644 index 0985f0de97..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status.listeners; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.event.EventListener; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.zowe.apiml.apicatalog.instance.InstanceInitializeService; -import org.zowe.apiml.product.gateway.GatewayLookupCompleteEvent; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * This class fires on GatewayLookupCompleteEvent event - * Initializes Catalog instances from Eureka - */ -@Slf4j -@Component -@ConditionalOnProperty( - value = "apiml.catalog.standalone.enabled", - havingValue = "false", - matchIfMissing = true) -@RequiredArgsConstructor -@Order(Ordered.HIGHEST_PRECEDENCE) -public class GatewayLookupEventListener { - - private final InstanceInitializeService instanceInitializeService; - private final AtomicBoolean hasRun = new AtomicBoolean(false); - - @EventListener(GatewayLookupCompleteEvent.class) - public void onApplicationEvent() { - if (!hasRun.get()) { - hasRun.set(true); - try { - instanceInitializeService.retrieveAndRegisterAllInstancesWithCatalog(); - } catch (Exception e) { - hasRun.set(false); - log.debug("Unexpected error occurred while initial retrieving of services: {}", e.getMessage()); - } - } - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInfo.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInfo.java deleted file mode 100644 index ec4cad83d0..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInfo.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status.model; - -import lombok.Data; - -@Data -public class APIServiceInfo { - private String instanceId; - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInstances.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInstances.java deleted file mode 100644 index cd6e9cd46f..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/model/APIServiceInstances.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status.model; - -import lombok.Data; - -import java.util.List; - -@Data -public class APIServiceInstances { - private List instances; -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMock.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMock.java deleted file mode 100644 index ec668752eb..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMock.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.zowe.apiml.product.instance.ServiceAddress; - -@Configuration -@ConditionalOnProperty(value = "apiml.catalog.standalone.enabled", havingValue = "true") -public class ApiDocTransformForMock { - - @Value("${server.hostname:localhost}") - private String hostname; - - @Value("${server.port}") - private String port; - - @Value("${service.schema:https}") - private String schema; - - @Value("${server.servlet.contextPath:/apicatalog}") - private String contextPath; - - @Bean - @Primary - public ServiceAddress gatewayConfigPropertiesForMock() { - String path = this.contextPath.endsWith("/") ? this.contextPath.substring(0, this.contextPath.lastIndexOf('/')) : this.contextPath; - - return ServiceAddress.builder() - .scheme(schema) - .hostname(String.format("%s:%s%s/mock", hostname, port, path)) - .build(); - } - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ExampleService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ExampleService.java deleted file mode 100644 index c8d2cbdb82..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/ExampleService.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import com.fasterxml.jackson.databind.module.SimpleModule; -import io.swagger.oas.inflector.examples.ExampleBuilder; -import io.swagger.oas.inflector.processors.JsonNodeExampleSerializer; -import io.swagger.parser.OpenAPIParser; -import io.swagger.v3.core.util.Json; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.oas.models.media.Content; -import io.swagger.v3.oas.models.media.MediaType; -import io.swagger.v3.oas.models.responses.ApiResponse; -import io.swagger.v3.parser.core.models.SwaggerParseResult; -import lombok.Builder; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Service; -import org.springframework.util.AntPathMatcher; - -import jakarta.servlet.http.HttpServletResponse; -import org.zowe.apiml.apicatalog.swagger.ApiDocTransformationException; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.*; - -/** - * This class is responsible for generating the examples for API Doc and can simulate the response for any endpoint. It - * is helpful for the standalone mode that mocks the loaded schemas. - * - * This class tries to generate a response for each endpoint in the API documentation. It focuses on the successful - * response with media-type JSON. If it is not possible to generate such content, it uses the default one (an empty - * JSON object). Examples are mapping by ANT matcher and it is possible to respond by this class (see method - * {@link ExampleService#replyExample(HttpServletResponse, String, String)}). - */ -@Slf4j -@Service -@ConditionalOnProperty(value = "apiml.catalog.standalone.enabled", havingValue = "true") -public class ExampleService { - - private static final Example DEFAULT_EXAMPLE = Example.builder() - .responseCode(200).content("{}").mediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE) - .build(); - - /** - * Collection of know responses. The key of the map represents method type. The list itself is all known - * examples of responses for the specific type. It is not possible to use map or a different structure, because - * the mapping uses Ant Patterns. Therefore, it is necessary to test mapping of each URL path. - */ - private static final Map> examples = Collections.synchronizedMap(new HashMap<>()); - - { - // init JSON library to generate example - SimpleModule simpleModule = new SimpleModule().addSerializer(new JsonNodeExampleSerializer()); - Json.mapper().registerModule(simpleModule); - } - - /** - * Construct class {@link ExampleService.Example} and put in {@link #examples} for the future using. - * @param method method of the request - * @param path ANT pattern to match URL path of request - * @param responseCode response code of example - * @param mediaType the media type of the example - * @param content response's content of example - */ - static void addExample(String method, String path, int responseCode, String mediaType, String content) { - Example example = Example.builder() - .method(method).path(path) - .responseCode(responseCode).mediaType(mediaType).content(content) - .build(); - - List byMethod = examples.computeIfAbsent(method, k -> Collections.synchronizedList(new ArrayList<>())); - byMethod.add(example); - - log.debug("Generated response example for request {}, {}, {}, {}:\n{}", method, path, responseCode, mediaType, content); - } - - /** - * Method returns first entry with response code 2xx if exists. Otherwise it return null (in case the operation - * does not contain any record) or the first entry. - * @param operation operation to analyze - * @return entry with successful response if exists, otherwise first entry or null if no one exists - */ - static Map.Entry getFirstApiResponses(Operation operation) { - Map.Entry first = null; - for (Map.Entry responseEntry : operation.getResponses().entrySet()) { - if (responseEntry.getKey().startsWith("2")) return responseEntry; - if (first == null) first = responseEntry; - } - return first; - } - - /** - * Method to translate response code in API doc to number. - * @param input the representation of response code in API doc - * @return translated response code or 0 if the input is unclear - */ - static int getResponseCode(String input) { - if (StringUtils.equals("default", input)) { - return 200; - } - if (StringUtils.isNumeric(input)) { - return Integer.parseInt(input); - } - return 0; - } - - private void generateExample(String serviceId, OpenAPI swagger, String method, Operation operation, String path) { - Map.Entry responseEntry = getFirstApiResponses(operation); - if (responseEntry == null) return; - Content content = responseEntry.getValue().getContent(); - if (content == null) return; - - for (Map.Entry mediaEntry : content.entrySet()) { - if (!mediaEntry.getKey().contains("json")) continue; - - Object example = mediaEntry.getValue().getExample(); - if (example == null) { - example = ExampleBuilder.fromSchema(mediaEntry.getValue().getSchema(), swagger.getComponents().getSchemas()); - } - - String uri = String.format("/%s%s", serviceId, path); - String exampleJson = Json.pretty(example); - - addExample(method, uri, getResponseCode(responseEntry.getKey()), mediaEntry.getKey(), exampleJson); - } - } - - /** - * It load, parse and analyze input file with API doc. The response is loaded examples in {@link #examples}. - * @param serviceId ID of service of API doc file - * @param apiDoc path of file with API doc to parse - */ - public void generateExamples(String serviceId, String apiDoc) { - - try { - SwaggerParseResult parseResult = new OpenAPIParser().readContents(apiDoc, null, null); - OpenAPI swagger = parseResult != null ? parseResult.getOpenAPI() : null; - Paths paths = swagger != null ? swagger.getPaths() : null; - if (paths != null) { - for (Map.Entry pathItemEntry : paths.entrySet()) { - for (Map.Entry operationEntry : pathItemEntry.getValue().readOperationsMap().entrySet()) { - generateExample(serviceId, swagger, operationEntry.getKey().name(), operationEntry.getValue(), pathItemEntry.getKey()); - } - } - } else { - throw new ApiDocTransformationException("Exception parsing null apiDoc " + apiDoc + - " paths is null: '" + paths + "'."); - } - } catch (Exception e) { - log.warn("Cannot generate example from API doc file {}", apiDoc, e); - } - } - /** - * To find a prepared example for specific endpoint defined by request method and URL path. If no example is found - * it returns the default one (empty JSON object). - * @param method HTTP method to find - * @param path URL path to find - * @return example generated by loaded API doc (see previous call of {@link #generateExamples(String, String)}) or - * the default one (empty JSON object) - */ - public Example getExample(String method, String path) { - List byMethod = examples.get(method); - if (byMethod == null) return DEFAULT_EXAMPLE; - - return byMethod.stream() - .filter(e -> e.isMatching(path)) - .findFirst() - .orElse(DEFAULT_EXAMPLE); - } - - /** - * It writes a response with an example for specified endpoint. - * @param httpServletResponse response to be handled - * @param method request method of the endpoint - * @param path URL path of the endpoint - * @throws IOException - */ - public void replyExample(HttpServletResponse httpServletResponse, String method, String path) throws IOException { - Example example = getExample(method, path); - httpServletResponse.setContentType(example.getMediaType()); - httpServletResponse.setStatus(example.getResponseCode()); - try (PrintWriter pw = httpServletResponse.getWriter()) { - pw.print(example.getContent()); - } - } - - /** - * Data object to define example - */ - @Builder - @Value - static class Example { - - private final AntPathMatcher pathMatcher = new AntPathMatcher(); - private final String path; - private final String method; - private final int responseCode; - private final String content; - private final String mediaType; - - /** - * Checking if the ant pattern of the example is matching with the requested path - * @param uri tested URI - * @return true if example is matching, otherwise false - */ - boolean isMatching(String uri) { - return pathMatcher.match(this.path, uri); - } - - } - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalService.java deleted file mode 100644 index 3a63453473..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalService.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import lombok.NonNull; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; -import org.zowe.apiml.apicatalog.services.status.APIDocRetrievalService; - -import java.util.Collections; -import java.util.List; - -@Service -@ConditionalOnProperty( - value = "apiml.catalog.standalone.enabled", - havingValue = "true") -public class StandaloneAPIDocRetrievalService extends APIDocRetrievalService { - - public StandaloneAPIDocRetrievalService() { - super(null, null, null); - } - - @Override - public ApiDocInfo retrieveApiDoc(@NonNull String serviceId, String apiVersion) { - return null; - } - - @Override - public ApiDocInfo retrieveDefaultApiDoc(@NonNull String serviceId) { - return null; - } - - @Override - public List retrieveApiVersions(@NonNull String serviceId) { - return Collections.emptyList(); - } - - @Override - public String retrieveDefaultApiVersion(@NonNull String serviceId) { - return null; - } - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializer.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializer.java deleted file mode 100644 index ff5a945f78..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import lombok.RequiredArgsConstructor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.event.EventListener; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.zowe.apiml.product.service.ServiceStartupEventHandler; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Initializes Catalog instances from files - */ -@Component -@ConditionalOnProperty( - value = "apiml.catalog.standalone.enabled", - havingValue = "true") -@RequiredArgsConstructor -@Order(Ordered.HIGHEST_PRECEDENCE) -public class StandaloneInitializer { - - private final StandaloneLoaderService standaloneLoaderService; - private final AtomicBoolean hasRun = new AtomicBoolean(false); - - @EventListener(ApplicationReadyEvent.class) - public void onApplicationEvent(ApplicationReadyEvent event) { - if (isStandalone(event.getApplicationContext()) && hasRun.compareAndSet(false, true)) { - standaloneLoaderService.initializeCache(); - new ServiceStartupEventHandler().onServiceStartup("API Catalog Service Standalone", - ServiceStartupEventHandler.DEFAULT_DELAY_FACTOR); - } - } - - private boolean isStandalone(ConfigurableApplicationContext context) { - return Boolean.parseBoolean(context.getEnvironment().getProperty("apiml.catalog.standalone.enabled", "false")); - } - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderService.java deleted file mode 100644 index 81d841565e..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderService.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Applications; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.instance.InstanceInitializeService; -import org.zowe.apiml.apicatalog.services.cached.CachedApiDocService; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; -import org.zowe.apiml.apicatalog.swagger.api.AbstractApiDocService; -import org.zowe.apiml.config.ApiInfo; -import org.zowe.apiml.product.instance.ServiceAddress; -import org.zowe.apiml.product.routing.RoutedServices; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.List; -import java.util.function.Function; - -@Slf4j -@Service -@ConditionalOnProperty( - value = "apiml.catalog.standalone.enabled", - havingValue = "true") -@RequiredArgsConstructor -public class StandaloneLoaderService { - - @Value("${apiml.catalog.standalone.servicesDirectory:services}") - private String servicesDirectory; - - private final ObjectMapper objectMapper; - private final InstanceInitializeService instanceInitializeService; - private final CachedApiDocService cachedApiDocService; - private final StandaloneAPIDocRetrievalService standaloneAPIDocRetrievalService; - private final Function> beanApiDocFactory; - private final ServiceAddress gatewayConfigProperties; - private final ExampleService exampleService; - - public void initializeCache() { - loadApplicationCache(); - loadOpenAPICache(); - } - - private void loadApplicationCache() { - File[] appFiles = getFiles(servicesDirectory + "/apps"); - log.debug("Found {} files", appFiles.length); - if (appFiles.length == 0) { - log.error("No service definition files found."); - return; - } - - for (File file : appFiles) { - log.debug("Processing file {}", file.getName()); - createContainerFromFile(file); - } - } - - private void loadOpenAPICache() { - File[] openAPIFiles = getFiles(servicesDirectory + "/apiDocs"); - log.debug("Found {} API Doc files", openAPIFiles.length); - if (openAPIFiles.length == 0) { - log.error("No apiDocs files found or I/O error occured."); - return; - } - - for (File openAPIFile : openAPIFiles) { - log.debug("Processing {}", openAPIFile.getName()); - loadApiDocCache(openAPIFile); - } - } - - private void createContainerFromFile(File file) { - log.info("Initialising services from '{}' file.", file.getName()); - - try { - Applications apps = objectMapper.readValue(file, Applications.class); - apps.getRegisteredApplications().forEach(app -> { - instanceInitializeService.createContainers(app); - // Uses metadata from the first instance like {@link InstanceRetrievalService#getInstanceInfo} - loadApiDocVersionCache(app.getInstances().get(0)); - }); - } catch (IOException e) { - log.error("Unable to parse service definition '{}' because {}", file.getName(), e.getMessage()); - } - } - - private ApiDocInfo createApiDocInfo(String serviceId, String apiDoc) { - ApiInfo apiInfo = new ApiInfo(); - apiInfo.setGatewayUrl(String.format("%s://%s/%s/", gatewayConfigProperties.getScheme(), gatewayConfigProperties.getHostname(), serviceId)); - RoutedServices routedServices = new RoutedServices(); - return new ApiDocInfo( - apiInfo, - apiDoc, - routedServices - ); - } - - private void loadApiDocCache(File file) { - try (InputStream inputStream = Files.newInputStream(file.toPath())) { - String[] name = FilenameUtils.removeExtension(file.getName()).split("_"); - if (name.length < 2 || name.length > 3 || (name.length == 3 && !"default".equals(name[2]))) { - log.warn("ApiDoc file has incorrect name format '{}'. The correct format is '{serviceId}_{version}(_default)'.", file.getName()); - return; - } - String serviceId = name[0]; - String apiVersion = name[1]; - - String apiDoc = IOUtils.toString(inputStream, StandardCharsets.UTF_8); - ApiDocInfo apiDocInfo = createApiDocInfo(serviceId, apiDoc); - apiDoc = beanApiDocFactory.apply(apiDoc).transformApiDoc(serviceId, apiDocInfo); - - if (name.length > 2 && name[2].equals("default")) { - cachedApiDocService.updateApiDocForService(serviceId, CachedApiDocService.DEFAULT_API_KEY, apiDoc); - } - - cachedApiDocService.updateApiDocForService(serviceId, apiVersion, apiDoc); - - exampleService.generateExamples(serviceId, apiDoc); - } catch (IOException e) { - log.error("Cannot read '{}' because {}", file.getName(), e.getMessage()); - } - } - - private void loadApiDocVersionCache(InstanceInfo instanceInfo) { - List apiVersions = standaloneAPIDocRetrievalService.retrieveApiVersions(instanceInfo.getMetadata()); - cachedApiDocService.updateApiVersionsForService(instanceInfo.getAppName(), apiVersions); - - String defaultApiVersion = standaloneAPIDocRetrievalService.retrieveDefaultApiVersion(instanceInfo.getMetadata()); - cachedApiDocService.updateDefaultApiVersionForService(instanceInfo.getAppName(), defaultApiVersion); - } - - private File[] getFiles(String directory) { - File dir = new File(directory); - if (!dir.isDirectory()) { - log.error("Directory '{}' does not exists.", directory); - return new File[0]; - } - var files = dir.listFiles(f -> f.isFile() && f.getName().endsWith(".json")); - if (files == null) { - return new File[0]; - } else return files; - } -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneSecurityConfig.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneSecurityConfig.java deleted file mode 100644 index fd806cea35..0000000000 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/standalone/StandaloneSecurityConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - - -import jakarta.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; -import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; -import org.springframework.security.web.SecurityFilterChain; -import org.zowe.apiml.product.constants.CoreService; - -@Configuration -@ConditionalOnProperty(value = "apiml.catalog.standalone.enabled", havingValue = "true") -@Slf4j -public class StandaloneSecurityConfig { - - @PostConstruct - void init() { - log.warn(CoreService.API_CATALOG.getServiceId() + " is running in standalone mode. Authentication is disabled. Do not use in production."); - } - - @Bean - @Order(Ordered.HIGHEST_PRECEDENCE) - public SecurityFilterChain permitAll(HttpSecurity http) throws Exception { - return http - .csrf(CsrfConfigurer::disable) // NOSONAR - .headers(httpSecurityHeadersConfigurer -> - httpSecurityHeadersConfigurer.httpStrictTransportSecurity(HeadersConfigurer.HstsConfig::disable) - .frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) - - .authorizeHttpRequests(matcherRegistry -> matcherRegistry.anyRequest().permitAll()) - .build(); - } - -} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerExceptionHandler.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerExceptionHandler.java index 5c8c8e1d16..eb016ac7f4 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerExceptionHandler.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerExceptionHandler.java @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.client.RestClientException; -import org.zowe.apiml.apicatalog.services.status.model.ServiceNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ServiceNotFoundException; import org.zowe.apiml.message.api.ApiMessageView; import org.zowe.apiml.message.core.Message; import org.zowe.apiml.message.core.MessageService; diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/model/ApiDocInfo.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocInfo.java similarity index 92% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/model/ApiDocInfo.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocInfo.java index 462eac0d84..fc7030b1ec 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/model/ApiDocInfo.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocInfo.java @@ -8,7 +8,7 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.cached.model; +package org.zowe.apiml.apicatalog.swagger; import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.product.routing.RoutedServices; @@ -24,7 +24,9 @@ @AllArgsConstructor @ToString public class ApiDocInfo { + ApiInfo apiInfo; String apiDocContent; RoutedServices routes; + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocRetrievalService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocRetrievalService.java new file mode 100644 index 0000000000..b9da406bad --- /dev/null +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocRetrievalService.java @@ -0,0 +1,74 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.apicatalog.swagger; + +import lombok.NonNull; +import org.zowe.apiml.apicatalog.exceptions.ApiDocNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ApiVersionNotFoundException; + +import java.util.List; + +public interface ApiDocRetrievalService { + + /** + * Retrieves the available API versions for a registered service. + * Takes the versions available in each 'apiml.service.apiInfo' element. + * + * @param serviceId the unique service ID + * @return a list of API version strings + * @throws ApiVersionNotFoundException if the API versions cannot be loaded + */ + List retrieveApiVersions(@NonNull String serviceId); + + /** + * Retrieves the default API version for a registered service. + * Uses 'apiml.service.apiInfo.defaultApi' field. + *

+ * Returns version in the format 'v{majorVersion|'}. If no API is set as default, null is returned. + * + * @param serviceId the unique service ID + * @return default API version in the format v{majorVersion}, or null. + */ + String retrieveDefaultApiVersion(@NonNull String serviceId); + + /** + * Retrieve the API docs for a registered service + *

+ * API doc URL is taken from the application metadata in the following + * order: + *

+ * 1. 'apiml.service.apiInfo.swaggerUrl' (preferred way) + * 2. 'apiml.service.apiInfo' is present and 'swaggerUrl' is not, ApiDoc info is automatically generated + * 3. URL is constructed from 'apiml.routes.api-doc.serviceUrl'. This method is deprecated and used for + * backwards compatibility only + * + * @param serviceId the unique service id + * @param apiVersion the version of the API + * @return the API doc + * @throws ApiDocNotFoundException if the response is error + */ + String retrieveApiDoc(@NonNull String serviceId, String apiVersion); + + /** + * Retrieve the default API docs for a registered service. + *

+ * Default API doc is selected via the configuration parameter 'apiml.service.apiInfo.isDefault'. + *

+ * If there are multiple apiInfo elements with isDefault set to 'true', or there are none set to 'true', + * then the high API version will be selected. + * + * @param serviceId the unique service id + * @return the default API doc + * @throws ApiDocNotFoundException if the response is error + */ + String retrieveDefaultApiDoc(@NonNull String serviceId); + +} diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIDocRetrievalService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocRetrievalServiceRest.java similarity index 75% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIDocRetrievalService.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocRetrievalServiceRest.java index b26f46dcde..b09ab6aa08 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIDocRetrievalService.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ApiDocRetrievalServiceRest.java @@ -8,9 +8,10 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status; +package org.zowe.apiml.apicatalog.swagger; import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,64 +20,54 @@ import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; -import org.zowe.apiml.apicatalog.instance.InstanceRetrievalService; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; -import org.zowe.apiml.apicatalog.services.status.model.ApiVersionNotFoundException; -import org.zowe.apiml.apicatalog.swagger.SubstituteSwaggerGenerator; +import org.zowe.apiml.apicatalog.exceptions.ApiDocNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ApiVersionNotFoundException; import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.eurekaservice.client.util.EurekaMetadataParser; import org.zowe.apiml.message.log.ApimlLogger; import org.zowe.apiml.product.gateway.GatewayClient; -import org.zowe.apiml.product.instance.InstanceInitializationException; import org.zowe.apiml.product.instance.ServiceAddress; import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; import org.zowe.apiml.product.routing.RoutedService; import org.zowe.apiml.product.routing.RoutedServices; +import org.zowe.apiml.util.EurekaUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.UnaryOperator; /** * Retrieves the API documentation for a registered service */ @Service -@ConditionalOnProperty( - value = "apiml.catalog.standalone.enabled", - havingValue = "false", - matchIfMissing = true) @RequiredArgsConstructor @Slf4j -public class APIDocRetrievalService { +public class ApiDocRetrievalServiceRest implements ApiDocRetrievalService { + + private static final UnaryOperator exceptionMessage = serviceId -> "No API Documentation was retrieved for the service " + serviceId + "."; @Qualifier("secureHttpClientWithoutKeystore") private final CloseableHttpClient secureHttpClientWithoutKeystore; - private final InstanceRetrievalService instanceRetrievalService; + private final EurekaClient eurekaClient; private final GatewayClient gatewayClient; private final EurekaMetadataParser metadataParser = new EurekaMetadataParser(); private final SubstituteSwaggerGenerator swaggerGenerator = new SubstituteSwaggerGenerator(); + private final TransformApiDocService transformApiDocService; + @InjectApimlLogger private ApimlLogger apimlLogger = ApimlLogger.empty(); - /** - * Retrieves the available API versions for a registered service. - * Takes the versions available in each 'apiml.service.apiInfo' element. - * - * @param serviceId the unique service ID - * @return a list of API version strings - * @throws ApiVersionNotFoundException if the API versions cannot be loaded - */ + @Override public List retrieveApiVersions(@NonNull String serviceId) { log.debug("Retrieving API versions for service '{}'", serviceId); InstanceInfo instanceInfo; @@ -93,7 +84,7 @@ public List retrieveApiVersions(@NonNull String serviceId) { return apiVersions; } - public List retrieveApiVersions(@NonNull Map metadata) { + private List retrieveApiVersions(@NonNull Map metadata) { List apiInfoList = metadataParser.parseApiInfo(metadata); List apiVersions = new ArrayList<>(); for (ApiInfo apiInfo : apiInfoList) { @@ -103,15 +94,7 @@ public List retrieveApiVersions(@NonNull Map metadata) { return apiVersions; } - /** - * Retrieves the default API version for a registered service. - * Uses 'apiml.service.apiInfo.defaultApi' field. - *

- * Returns version in the format 'v{majorVersion|'}. If no API is set as default, null is returned. - * - * @param serviceId the unique service ID - * @return default API version in the format v{majorVersion}, or null. - */ + @Override public String retrieveDefaultApiVersion(@NonNull String serviceId) { log.debug("Retrieving default API version for service '{}'", serviceId); InstanceInfo instanceInfo; @@ -128,7 +111,7 @@ public String retrieveDefaultApiVersion(@NonNull String serviceId) { return defaultVersion; } - public String retrieveDefaultApiVersion(@NonNull Map metadata) { + private String retrieveDefaultApiVersion(@NonNull Map metadata) { List apiInfoList = metadataParser.parseApiInfo(metadata); ApiInfo defaultApiInfo = getDefaultApiInfo(apiInfoList); @@ -139,23 +122,8 @@ public String retrieveDefaultApiVersion(@NonNull Map metadata) { return String.format("%s v%s", defaultApiInfo.getApiId(), defaultApiInfo.getVersion()); } - /** - * Retrieve the API docs for a registered service - *

- * API doc URL is taken from the application metadata in the following - * order: - *

- * 1. 'apiml.service.apiInfo.swaggerUrl' (preferred way) - * 2. 'apiml.service.apiInfo' is present and 'swaggerUrl' is not, ApiDoc info is automatically generated - * 3. URL is constructed from 'apiml.routes.api-doc.serviceUrl'. This method is deprecated and used for - * backwards compatibility only - * - * @param serviceId the unique service id - * @param apiVersion the version of the API - * @return the API doc and related information for transformation - * @throws ApiDocNotFoundException if the response is error - */ - public ApiDocInfo retrieveApiDoc(@NonNull String serviceId, String apiVersion) { + @Override + public String retrieveApiDoc(@NonNull String serviceId, String apiVersion) { log.debug("Retrieving API doc for '{} {}'", serviceId, apiVersion); InstanceInfo instanceInfo = getInstanceInfo(serviceId); @@ -165,38 +133,33 @@ public ApiDocInfo retrieveApiDoc(@NonNull String serviceId, String apiVersion) { return buildApiDocInfo(serviceId, apiInfo, instanceInfo); } - private ApiDocInfo buildApiDocInfo(String serviceId, ApiInfo apiInfo, InstanceInfo instanceInfo) { + String buildApiDocInfo(String serviceId, ApiInfo apiInfo, InstanceInfo instanceInfo) { RoutedServices routes = metadataParser.parseRoutes(instanceInfo.getMetadata()); String apiDocUrl = getApiDocUrl(apiInfo, instanceInfo, routes); + ApiDocInfo apiDocInfo; if (apiDocUrl == null) { log.warn("No api doc URL for '{} {} {}'", serviceId, apiInfo.getApiId(), apiInfo.getVersion()); - return getApiDocInfoBySubstituteSwagger(instanceInfo, routes, apiInfo); + apiDocInfo = getApiDocInfoBySubstituteSwagger(instanceInfo, routes, apiInfo); + } else { + String apiDocContent = ""; + try { + apiDocContent = getApiDocContentByUrl(serviceId, apiDocUrl); + apiDocInfo = new ApiDocInfo(apiInfo, apiDocContent, routes); + } catch (IOException e) { + apimlLogger.log("org.zowe.apiml.apicatalog.apiDocHostCommunication", serviceId, e.getMessage()); + log.debug("Error retrieving api doc for '{}'", serviceId, e); + throw new ApiDocNotFoundException( + exceptionMessage.apply(serviceId) + " Root cause: " + e.getMessage(), e + ); + } } - String apiDocContent = ""; - try { - apiDocContent = getApiDocContentByUrl(serviceId, apiDocUrl); - } catch (IOException e) { - apimlLogger.log("org.zowe.apiml.apicatalog.apiDocHostCommunication", serviceId, e.getMessage()); - log.debug("Error retrieving api doc for '{}'", serviceId, e); - } - return new ApiDocInfo(apiInfo, apiDocContent, routes); + return transformApiDocService.transformApiDoc(serviceId, apiDocInfo); } - /** - * Retrieve the default API docs for a registered service. - *

- * Default API doc is selected via the configuration parameter 'apiml.service.apiInfo.isDefault'. - *

- * If there are multiple apiInfo elements with isDefault set to 'true', or there are none set to 'true', - * then the high API version will be selected. - * - * @param serviceId the unique service id - * @return the default API doc and related information for transfer - * @throws ApiDocNotFoundException if the response is error - */ - public ApiDocInfo retrieveDefaultApiDoc(@NonNull String serviceId) { + @Override + public String retrieveDefaultApiDoc(@NonNull String serviceId) { log.debug("Retrieving default API doc for service '{}'", serviceId); InstanceInfo instanceInfo = getInstanceInfo(serviceId); @@ -375,19 +338,9 @@ private ApiInfo findApi(List apiInfos, String apiVersion) { return new ApiDocNotFoundException(errMessage); }); } - private InstanceInfo getInstanceInfo(String serviceId) { - String errMsg = "Could not load instance information for service " + serviceId + "."; - try { - InstanceInfo instanceInfo = instanceRetrievalService.getInstanceInfo(serviceId); - if (instanceInfo == null) { - throw new ApiDocNotFoundException(errMsg); - } - - return instanceInfo; - } catch (InstanceInitializationException e) { - throw new ApiDocNotFoundException(errMsg, e); - } + return EurekaUtils.getInstanceInfo(eurekaClient, serviceId) + .orElseThrow(() -> new ApiDocNotFoundException("Could not load instance information for service " + serviceId + ".")); } /** @@ -431,4 +384,5 @@ private String createApiDocUrlFromRouting(InstanceInfo instanceInfo, RoutedServi return uri.toUriString(); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ContainerService.java similarity index 58% rename from api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyService.java rename to api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ContainerService.java index 0c807fdba4..10af03a39a 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyService.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/ContainerService.java @@ -8,11 +8,13 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.cached; +package org.zowe.apiml.apicatalog.swagger; import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; import com.netflix.discovery.shared.Application; -import lombok.Getter; +import com.netflix.discovery.shared.Applications; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -31,37 +33,30 @@ import org.zowe.apiml.product.routing.ServiceType; import org.zowe.apiml.product.routing.transform.TransformService; import org.zowe.apiml.product.routing.transform.URLTransformationException; +import org.zowe.apiml.util.EurekaUtils; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.zowe.apiml.constants.EurekaMetadataDefinition.*; import static org.zowe.apiml.product.constants.CoreService.GATEWAY; /** - * Caching service for eureka services + * Initialize the API catalog with the running instances. */ @Slf4j @Service -public class CachedProductFamilyService { +@RequiredArgsConstructor +public class ContainerService { private static final String DEFAULT_APIINFO_KEY = "default"; - @InjectApimlLogger - private final ApimlLogger apimlLog = ApimlLogger.empty(); - - private final Integer cacheRefreshUpdateThresholdInMillis; - - private final CachedServicesService cachedServicesService; + private final AuthenticationSchemes schemes = new AuthenticationSchemes(); private final EurekaMetadataParser metadataParser = new EurekaMetadataParser(); - private final TransformService transformService; - private final Map products = new ConcurrentHashMap<>(); - @Getter - private final Map services = new ConcurrentHashMap<>(); - - private final AuthenticationSchemes schemes = new AuthenticationSchemes(); + private final EurekaClient eurekaClient; + private final TransformService transformService; private final CustomStyleConfig customStyleConfig; @Value("${apiml.catalog.hide.serviceInfo:false}") @@ -70,14 +65,18 @@ public class CachedProductFamilyService { @Value("${server.attls.enabled:false}") private boolean isAttlsEnabled; - public CachedProductFamilyService(CachedServicesService cachedServicesService, - TransformService transformService, - @Value("${apiml.service-registry.cacheRefreshUpdateThresholdInMillis}") Integer cacheRefreshUpdateThresholdInMillis, - CustomStyleConfig customStyleConfig) { - this.cachedServicesService = cachedServicesService; - this.transformService = transformService; - this.cacheRefreshUpdateThresholdInMillis = cacheRefreshUpdateThresholdInMillis; - this.customStyleConfig = customStyleConfig; + @InjectApimlLogger + private final ApimlLogger apimlLog = ApimlLogger.empty(); + + private Set getProductIds() { + return Optional.ofNullable(eurekaClient.getApplications()) + .map(Applications::getRegisteredApplications) + .map(List::stream).orElse(Stream.empty()) + .map(Application::getInstances) + .flatMap(Collection::stream) + .map(InstanceInfo::getMetadata) + .map(metadata -> metadata.get(CATALOG_ID)) + .collect(Collectors.toSet()); } /** @@ -86,211 +85,24 @@ public CachedProductFamilyService(CachedServicesService cachedServicesService, * @return instances */ public Collection getAllContainers() { - return products.values(); - } - - - /** - * return cached service instance by id - * - * @param id service identifier - * @return {@link APIContainer} - */ - public APIContainer getContainerById(String id) { - return products.get(id); + return getProductIds().stream() + .map(this::getContainerById) + .toList(); } - /** - * Retrieve any containers which have had their details updated after the threshold figure - * If performance is slow then possibly cache the result and evict after 'n' seconds - * - * @return recently updated containers - */ - public List getRecentlyUpdatedContainers() { - return this.products.values().stream().filter( - container -> { - boolean isRecent = container.isRecentUpdated(cacheRefreshUpdateThresholdInMillis); - if (isRecent) { - log.debug("Container: " + container.getId() + " last updated: " - + container.getLastUpdatedTimestamp().getTime() + - " was updated recently"); - } - return isRecent; - }).toList(); - } - - /** - * Add service to container - * - * @param productFamilyId the service identifier - * @param instanceInfo InstanceInfo - */ - public void addServiceToContainer(final String productFamilyId, final InstanceInfo instanceInfo) { - APIContainer apiContainer = products.get(productFamilyId); - // fix - throw error if null - apiContainer.addService(createAPIServiceFromInstance(instanceInfo)); - products.put(productFamilyId, apiContainer); - } - - /** - * Save a containers details using a service's metadata - * - * @param productFamilyId the product family id of the container - * @param instanceInfo the service instance - */ - public APIContainer saveContainerFromInstance(String productFamilyId, InstanceInfo instanceInfo) { - APIContainer container = products.get(productFamilyId); - if (container == null) { - container = createNewContainerFromService(productFamilyId, instanceInfo); - } else { - Set apiServices = container.getServices(); - APIService service = createAPIServiceFromInstance(instanceInfo); - - // Verify whether already exists - if (apiServices.contains(service)) { - apiServices.stream() - .filter(existingService -> existingService.equals(service)) - .forEach(existingService -> { - if (!existingService.getInstances().contains(instanceInfo.getInstanceId())) { - existingService.getInstances().add(instanceInfo.getInstanceId()); - } - }); // If the instance is in list, do nothing otherwise - } else { - apiServices.add(service); - } - - container.setServices(apiServices); - //update container - String versionFromInstance = instanceInfo.getMetadata().get(CATALOG_VERSION); - String title = instanceInfo.getMetadata().get(CATALOG_TITLE); - String description = instanceInfo.getMetadata().get(CATALOG_DESCRIPTION); - - container.setVersion(versionFromInstance); - container.setTitle(title); - container.setDescription(description); - container.updateLastUpdatedTimestamp(); - - products.put(productFamilyId, container); - } - - return container; - } - - /** - * Remove Instance which isn't available anymore. Based on what service the instance belongs to: - * 1) it will remove the whole APIContainer (Tile) if there is no instance of any service remaining - * 2) Remove the service from the containe if there is no instance of service remaining - * 3) Remove instance from the service - * - * @param removedInstanceFamilyId the product family id of the container - * @param removedInstance the service instance - */ - public void removeInstance(String removedInstanceFamilyId, InstanceInfo removedInstance) { - APIContainer containerWithInstance = products.get(removedInstanceFamilyId); - // There is nothing to do. - if (containerWithInstance == null) { - log.info("Remove product with id: {} instance {}", removedInstanceFamilyId, removedInstance.getInstanceId()); - return; - } - - APIService toBeRemoved = createAPIServiceFromInstance(removedInstance); - - Set currentServices = containerWithInstance.getServices(); - AtomicBoolean removeFullService = new AtomicBoolean(false); - currentServices.stream() - .filter(existingService -> existingService.equals(toBeRemoved)) - .forEach(existingService -> { - if (existingService.getInstances().size() == 1) { - removeFullService.set(true); - } else { - // Only remove one of the instances - existingService.getInstances().remove(removedInstance.getInstanceId()); - } - }); - - // Remove at least the full service - if (removeFullService.get()) { - currentServices.remove(toBeRemoved); - - // Remove the whole container (tile) - if (currentServices.isEmpty()) { - products.remove(removedInstanceFamilyId); - } - } - } - - /** - * Remove Instance which isn't available anymore. Based on what service the instance belongs to: - * 1) it will remove the whole APIContainer (Tile) if there is no instance of any service remaining - * 2) Remove the service from the containe if there is no instance of service remaining - * 3) Remove instance from the service - * - * @param removedInstance the service instance - */ - public void removeInstanceFromServices(InstanceInfo removedInstance) { - var serviceId = removedInstance.getAppName(); - var currentService = services.get(serviceId); - // There is nothing to do. - if (currentService == null) { - log.info("Service with id: {} instance {} not found", serviceId, removedInstance.getInstanceId()); - return; - } - if (currentService.getInstances().size() == 1) { - services.remove(serviceId); - } else { - currentService.getInstances().remove(removedInstance.getInstanceId()); - services.put(serviceId, currentService); - } - - } - - /** - * Update the summary totals, sso and API IDs info for a container based on it's running services - * - * @param apiContainer calculate totals for this container - */ - public void calculateContainerServiceValues(APIContainer apiContainer) { - if (apiContainer.getServices() == null) { - apiContainer.setServices(new HashSet<>()); - } - - int servicesCount = apiContainer.getServices().size(); - int activeServicesCount = 0; - boolean isSso = servicesCount > 0; - for (APIService apiService : apiContainer.getServices()) { - if (update(apiService)) { - activeServicesCount++; - } - isSso &= apiService.isSsoAllInstances(); - } - - setStatus(apiContainer, servicesCount, activeServicesCount); - apiContainer.setSso(isSso); - apiContainer.setHideServiceInfo(hideServiceInfo); - - // set metadata to customize the UI - if (customStyleConfig != null) { - setCustomUiConfig(apiContainer); - } - - } - - /** - * Map the configuration to customize the Catalog UI to the container - * - * @param apiContainer - */ - private void setCustomUiConfig(APIContainer apiContainer) { - apiContainer.setCustomStyleConfig(customStyleConfig); + private boolean isSso(InstanceInfo instanceInfo) { + Map eurekaMetadata = instanceInfo.getMetadata(); + return Authentication.builder() + .scheme(schemes.map(eurekaMetadata.get(AUTHENTICATION_SCHEME))) + .supportsSso(BooleanUtils.toBooleanObject(eurekaMetadata.get(AUTHENTICATION_SSO))) + .build() + .supportsSso(); } - /** - * Return the number of containers (used for checking if a new container was created) - * - * @return the number of containers - */ - public int getContainerCount() { - return products.size(); + private boolean hasHomePage(InstanceInfo instanceInfo) { + String instanceHomePage = instanceInfo.getHomePageUrl(); + return instanceHomePage != null + && !instanceHomePage.isEmpty(); } /** @@ -349,43 +161,6 @@ private String getApiBasePath(InstanceInfo instanceInfo) { return ""; } - private boolean hasHomePage(InstanceInfo instanceInfo) { - String instanceHomePage = instanceInfo.getHomePageUrl(); - return instanceHomePage != null - && !instanceHomePage.isEmpty(); - } - - /** - * Create a new container based on information in a new instance - * - * @param productFamilyId parent id - * @param instanceInfo instance - * @return a new container - */ - private APIContainer createNewContainerFromService(String productFamilyId, InstanceInfo instanceInfo) { - Map instanceInfoMetadata = instanceInfo.getMetadata(); - String title = instanceInfoMetadata.get(CATALOG_TITLE); - String description = instanceInfoMetadata.get(CATALOG_DESCRIPTION); - String version = instanceInfoMetadata.get(CATALOG_VERSION); - APIContainer container = new APIContainer(); - container.setStatus("UP"); - container.setId(productFamilyId); - container.setDescription(description); - container.setTitle(title); - container.setVersion(version); - log.debug("updated Container cache with product family: " + productFamilyId + ": " + title); - - // create API Service from instance and update container last changed date - container.addService(createAPIServiceFromInstance(instanceInfo)); - products.put(productFamilyId, container); - return container; - } - - public void addService(InstanceInfo instanceInfo) { - var serviceInfo = createAPIServiceFromInstance(instanceInfo); - services.put(serviceInfo.getServiceId(), serviceInfo); - } - /** * Create a APIService object using the instances metadata * @@ -443,17 +218,39 @@ APIService createAPIServiceFromInstance(InstanceInfo instanceInfo) { .build(); } - private boolean isSso(InstanceInfo instanceInfo) { - Map eurekaMetadata = instanceInfo.getMetadata(); - return Authentication.builder() - .scheme(schemes.map(eurekaMetadata.get(AUTHENTICATION_SCHEME))) - .supportsSso(BooleanUtils.toBooleanObject(eurekaMetadata.get(AUTHENTICATION_SSO))) - .build() - .supportsSso(); + /** + * Create a new container based on information in a new instance + * + * @param productFamilyId parent id + * @param instanceInfos all instances + * @return a new container + */ + private APIContainer createNewContainerFromService(String productFamilyId, InstanceInfo...instanceInfos) { + if (instanceInfos.length == 0) { + return null; + } + + Map instanceInfoMetadata = instanceInfos[0].getMetadata(); + String title = instanceInfoMetadata.get(CATALOG_TITLE); + String description = instanceInfoMetadata.get(CATALOG_DESCRIPTION); + String version = instanceInfoMetadata.get(CATALOG_VERSION); + APIContainer container = new APIContainer(); + container.setStatus("UP"); + container.setId(productFamilyId); + container.setDescription(description); + container.setTitle(title); + container.setVersion(version); + log.debug("updated Container cache with product family: " + productFamilyId + ": " + title); + + // create API Service from instance and update container last changed date + for (InstanceInfo instanceInfo : instanceInfos) { + container.addService(createAPIServiceFromInstance(instanceInfo)); + } + return container; } private boolean update(APIService apiService) { - Application application = cachedServicesService.getService(apiService.getServiceId()); + Application application = eurekaClient.getApplication(apiService.getServiceId()); // service has not cached yet, but count as alive if (application == null) return true; @@ -478,7 +275,77 @@ private void setStatus(APIContainer apiContainer, int servicesCount, int activeS } else { apiContainer.setStatus("WARNING"); } + } + + /** + * Map the configuration to customize the Catalog UI to the container + * + * @param apiContainer + */ + private void setCustomUiConfig(APIContainer apiContainer) { + apiContainer.setCustomStyleConfig(customStyleConfig); + } + + + /** + * Update the summary totals, sso and API IDs info for a container based on it's running services + * + * @param apiContainer calculate totals for this container + */ + public void calculateContainerServiceValues(APIContainer apiContainer) { + if (apiContainer.getServices() == null) { + apiContainer.setServices(new HashSet<>()); + } + + int servicesCount = apiContainer.getServices().size(); + int activeServicesCount = 0; + boolean isSso = servicesCount > 0; + for (APIService apiService : apiContainer.getServices()) { + if (update(apiService)) { + activeServicesCount++; + } + isSso &= apiService.isSsoAllInstances(); + } + + setStatus(apiContainer, servicesCount, activeServicesCount); + apiContainer.setSso(isSso); + apiContainer.setHideServiceInfo(hideServiceInfo); + + // set metadata to customize the UI + if (customStyleConfig != null) { + setCustomUiConfig(apiContainer); + } + + } + + /** + * return cached service instance by id + * + * @param id service identifier + * @return {@link APIContainer} + */ + public APIContainer getContainerById(String id) { + List instances = Optional.ofNullable(eurekaClient.getApplications()) + .map(Applications::getRegisteredApplications) + .map(List::stream).orElse(Stream.empty()) + .map(Application::getInstances) + .flatMap(Collection::stream) + .filter(instance -> StringUtils.equals(id, instance.getMetadata().get(CATALOG_ID))) + .toList(); + + if (instances.isEmpty()) { + return null; + } + + var container = createNewContainerFromService(id, instances.toArray(new InstanceInfo[0])); + calculateContainerServiceValues(container); + return container; + } + public APIService getService(String serviceId) { + return EurekaUtils.getInstanceInfo(eurekaClient, serviceId) + .map(this::createAPIServiceFromInstance) + .orElse(null); } } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocService.java index 08ba81b1f2..8c855014b0 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocService.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocService.java @@ -12,7 +12,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; import org.zowe.apiml.apicatalog.swagger.api.AbstractApiDocService; import jakarta.validation.UnexpectedTypeException; @@ -24,6 +23,7 @@ @Service @RequiredArgsConstructor public class TransformApiDocService { + private final Function> beanApiDocFactory; /** @@ -44,4 +44,5 @@ public String transformApiDoc(String serviceId, ApiDocInfo apiDocInfo) { return abstractApiDocService.transformApiDoc(serviceId, apiDocInfo); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocService.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocService.java index 22a2129c55..5f6d3b0cfe 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocService.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocService.java @@ -13,8 +13,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Pair; -import org.springframework.beans.factory.annotation.Value; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; +import org.zowe.apiml.apicatalog.swagger.ApiDocInfo; import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.product.gateway.GatewayClient; import org.zowe.apiml.product.routing.RoutedService; @@ -27,9 +26,6 @@ @Slf4j public abstract class AbstractApiDocService { - @Value("${apiml.catalog.standalone.enabled:false}") - protected boolean standalone; - protected final GatewayClient gatewayClient; protected static final String EXTERNAL_DOCUMENTATION = "External documentation"; @@ -42,14 +38,7 @@ public abstract class AbstractApiDocService { protected abstract void updateExternalDoc(T swaggerAPI, ApiDocInfo apiDocInfo); protected String getHostname(String serviceId) { - String hostname = gatewayClient.getGatewayConfigProperties().getHostname(); - if (!standalone) return hostname; - - StringBuilder sb = new StringBuilder(); - sb.append(hostname); - if (!hostname.endsWith("/")) sb.append('/'); - sb.append(serviceId); - return sb.toString(); + return gatewayClient.getGatewayConfigProperties().getHostname(); } protected void preparePath(N path, ApiDocPath apiDocPath, ApiDocInfo apiDocInfo, String basePath, String originalEndpoint, String serviceId) { diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2Service.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2Service.java index 6c813b635c..125b63ba91 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2Service.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2Service.java @@ -20,7 +20,7 @@ import jakarta.validation.UnexpectedTypeException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; +import org.zowe.apiml.apicatalog.swagger.ApiDocInfo; import org.zowe.apiml.apicatalog.swagger.ApiDocTransformationException; import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.product.gateway.GatewayClient; diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3Service.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3Service.java index 1f6ec5587a..d670d989b7 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3Service.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3Service.java @@ -32,7 +32,7 @@ import jakarta.validation.UnexpectedTypeException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; +import org.zowe.apiml.apicatalog.swagger.ApiDocInfo; import org.zowe.apiml.apicatalog.swagger.ApiDocTransformationException; import org.zowe.apiml.apicatalog.swagger.SecuritySchemeSerializer; import org.zowe.apiml.config.ApiInfo; diff --git a/api-catalog-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/api-catalog-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json deleted file mode 100644 index 8288380794..0000000000 --- a/api-catalog-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "properties": [ - { - "name": "apiml.catalog.standalone", - "type": "java.util.Map", - "description": "API Catalog standalone configuration.\nStandalone mode allows displaying, without the need for authentication, services that are stored on the disk. API Catalog does not connect to any other service." - }, - { - "name": "apiml.catalog.standalone.enabled", - "type": "java.lang.Boolean", - "defaultValue": "false", - "description": "Specifies whether to enable standalone mode.\nStandalone mode allows displaying, without the need for authentication, services that are stored on the disk. API Catalog does not connect to any other service." - } - ] -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java index bd9eb8cb12..8841093806 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java @@ -12,7 +12,7 @@ import org.springframework.context.annotation.Bean; import org.zowe.apiml.apicatalog.controllers.handlers.ApiCatalogControllerExceptionHandler; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; +import org.zowe.apiml.apicatalog.swagger.ContainerService; import org.zowe.apiml.message.core.MessageService; import org.zowe.apiml.message.yaml.YamlMessageService; @@ -21,18 +21,18 @@ class ApiCatalogControllerContainerRetrievalTestContextConfiguration { @Bean - public CachedProductFamilyService cachedProductFamilyService() { - return mock(CachedProductFamilyService.class); + public ContainerService instanceInitializeService() { + return mock(ContainerService.class); } @Bean - public ApiCatalogController apiCatalogController(CachedProductFamilyService cachedProductFamilyService) { - when(cachedProductFamilyService.getAllContainers()) + public ServicesController apiCatalogController(ContainerService containerService) { + when(containerService.getAllContainers()) .thenThrow(new NullPointerException()); - verify(cachedProductFamilyService, never()).getAllContainers(); + verify(containerService, never()).getAllContainers(); - return new ApiCatalogController(cachedProductFamilyService, null); + return new ServicesController(containerService, null); } @Bean diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerApiDocNotFoundTest.java similarity index 94% rename from api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTest.java rename to api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerApiDocNotFoundTest.java index b4dbc2d526..38b01f2263 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerApiDocNotFoundTest.java @@ -25,12 +25,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@WebMvcTest(controllers = {CatalogApiDocController.class}, +@WebMvcTest(controllers = {ApiDocController.class}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = WebSecurityConfigurer.class) }, excludeAutoConfiguration = { SecurityAutoConfiguration.class} ) @ContextConfiguration(classes = CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.class) -class CatalogApiDocControllerApiDocNotFoundTest { +class ApiDocControllerApiDocNotFoundTest { @Autowired private MockMvc mockMvc; @@ -43,5 +43,4 @@ void getApiDocAndFailThenThrowApiDocNotFoundException() throws Exception { hasItem("API Documentation not retrieved, Really bad stuff happened"))); } - } diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerServiceNotFoundTest.java similarity index 95% rename from api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTest.java rename to api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerServiceNotFoundTest.java index 23bba246fb..40a28782f6 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerServiceNotFoundTest.java @@ -28,12 +28,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(SpringExtension.class) -@WebMvcTest(controllers = {CatalogApiDocController.class}, +@WebMvcTest(controllers = {ApiDocController.class}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = WebSecurityConfigurer.class) }, excludeAutoConfiguration = { SecurityAutoConfiguration.class} ) @ContextConfiguration(classes = CatalogApiDocControllerServiceNotFoundTestContextConfiguration.class) -class CatalogApiDocControllerServiceNotFoundTest { +class ApiDocControllerServiceNotFoundTest { @Autowired private MockMvc mockMvc; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerTest.java new file mode 100644 index 0000000000..89c4c32cb9 --- /dev/null +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiDocControllerTest.java @@ -0,0 +1,105 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.apicatalog.controllers.api; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.springframework.http.ResponseEntity; +import org.zowe.apiml.apicatalog.exceptions.ApiDocNotFoundException; +import org.zowe.apiml.apicatalog.swagger.ApiDocRetrievalService; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class ApiDocControllerTest { + + private static final String API_DOC = "Some API Doc"; + + private ApiDocRetrievalService mockApiDocRetrievalService; + private ApiDocController underTest; + + @BeforeEach + void setup() { + mockApiDocRetrievalService = mock(ApiDocRetrievalService.class); + underTest = new ApiDocController(mockApiDocRetrievalService); + } + + @Test + void whenCreateController_thenItIsInstantiated() { + assertNotNull(underTest); + } + + @Nested + class GivenService { + @Nested + class WhenGetApiDocByVersion { + @Test + void givenApiDoc_thenReturnApiDoc() { + when(mockApiDocRetrievalService.retrieveApiDoc("service", "1.0.0")).thenReturn(API_DOC); + + ResponseEntity res = underTest.getApiDocInfo("service", "1.0.0"); + assertNotNull(res); + assertEquals(API_DOC, res.getBody()); + } + + @Test + void givenNoApiDoc_thenThrowException() { + when(mockApiDocRetrievalService.retrieveApiDoc("service", "1.0.0")).thenThrow(new ApiDocNotFoundException("error")); + assertThrows(ApiDocNotFoundException.class, () -> underTest.getApiDocInfo("service", "1.0.0")); + } + } + + @Nested + class WhenGetApiDocVersionDefault { + @Test + void givenApiDocExists_thenReturnIt() { + when(mockApiDocRetrievalService.retrieveDefaultApiDoc("service")).thenReturn(API_DOC); + + ResponseEntity res = underTest.getDefaultApiDocInfo("service"); + assertNotNull(res); + assertEquals(API_DOC, res.getBody()); + } + + @Test + void givenNoApiDocExists_thenThrowException() { + when(mockApiDocRetrievalService.retrieveDefaultApiDoc("service")).thenThrow(new ApiDocNotFoundException("error")); + assertThrows(ApiDocNotFoundException.class, () -> underTest.getDefaultApiDocInfo("service")); + } + } + + @Test + void whenGetApiDiff_thenReturnApiDiffHtml() { + ChangedOpenApi changedOpenApi = new ChangedOpenApi(OpenApiDiffOptions.builder().build()); + changedOpenApi.setChangedOperations(Collections.emptyList()); + changedOpenApi.setMissingEndpoints(Collections.emptyList()); + changedOpenApi.setNewEndpoints(Collections.emptyList()); + doReturn("doc1").when(mockApiDocRetrievalService).retrieveApiDoc("service", "v1"); + doReturn("doc2").when(mockApiDocRetrievalService).retrieveApiDoc("service", "v2"); + + try (MockedStatic openApiCompare = Mockito.mockStatic(OpenApiCompare.class)) { + openApiCompare.when(() -> OpenApiCompare.fromContents("doc1", "doc2")).thenReturn(changedOpenApi); + ResponseEntity res = underTest.getApiDiff("service", "v1", "v2"); + assertNotNull(res); + assertTrue(res.getBody().contains("Api Change Log")); + } + } + + } + +} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java index ac4ec2b36e..88eb0d176d 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java @@ -11,9 +11,10 @@ package org.zowe.apiml.apicatalog.controllers.api; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import org.zowe.apiml.apicatalog.controllers.handlers.CatalogApiDocControllerExceptionHandler; -import org.zowe.apiml.apicatalog.services.status.APIServiceStatusService; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ApiDocNotFoundException; +import org.zowe.apiml.apicatalog.swagger.ApiDocRetrievalService; import org.zowe.apiml.message.core.MessageService; import org.zowe.apiml.message.yaml.YamlMessageService; @@ -22,21 +23,22 @@ class CatalogApiDocControllerApiDocNotFoundTestContextConfiguration { @Bean - public APIServiceStatusService apiServiceStatusService() { - return mock(APIServiceStatusService.class); + @Primary + public ApiDocRetrievalService apiServiceStatusService() { + return mock(ApiDocRetrievalService.class); } @Bean - public CatalogApiDocController catalogApiDocController(APIServiceStatusService apiServiceStatusService) { - when(apiServiceStatusService.getServiceCachedApiDocInfo("service2", "v1")) + public ApiDocController catalogApiDocController(ApiDocRetrievalService apiDocRetrievalService) { + when(apiDocRetrievalService.retrieveApiDoc("service2", "v1")) .thenThrow(new ApiDocNotFoundException("Really bad stuff happened")); - when(apiServiceStatusService.getServiceCachedApiDocInfo("service2", null)) + when(apiDocRetrievalService.retrieveApiDoc("service2", null)) .thenThrow(new ApiDocNotFoundException("Really bad stuff happened")); - verify(apiServiceStatusService, never()).getServiceCachedApiDocInfo("service2", "v1"); + verify(apiDocRetrievalService, never()).retrieveApiDoc("service2", "v1"); - return new CatalogApiDocController(apiServiceStatusService); + return new ApiDocController(apiDocRetrievalService); } @Bean diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java index 65053eb455..2e16a01c8b 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java @@ -12,8 +12,8 @@ import org.springframework.context.annotation.Bean; import org.zowe.apiml.apicatalog.controllers.handlers.CatalogApiDocControllerExceptionHandler; -import org.zowe.apiml.apicatalog.services.status.APIServiceStatusService; -import org.zowe.apiml.apicatalog.services.status.model.ServiceNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ServiceNotFoundException; +import org.zowe.apiml.apicatalog.swagger.ApiDocRetrievalService; import org.zowe.apiml.message.core.MessageService; import org.zowe.apiml.message.yaml.YamlMessageService; @@ -22,18 +22,18 @@ class CatalogApiDocControllerServiceNotFoundTestContextConfiguration { @Bean - public APIServiceStatusService apiServiceStatusService() { - return mock(APIServiceStatusService.class); + public ApiDocRetrievalService apiServiceStatusService() { + return mock(ApiDocRetrievalService.class); } @Bean - public CatalogApiDocController catalogApiDocController(APIServiceStatusService apiServiceStatusService) { - when(apiServiceStatusService.getServiceCachedApiDocInfo("service1", "v1")) + public ApiDocController catalogApiDocController(ApiDocRetrievalService apiServiceStatusService) { + when(apiServiceStatusService.retrieveApiDoc("service1", "v1")) .thenThrow(new ServiceNotFoundException("API Documentation not retrieved, The service is running.")); - verify(apiServiceStatusService, never()).getServiceCachedApiDocInfo("service1", "v1"); + verify(apiServiceStatusService, never()).retrieveApiDoc("service1", "v1"); - return new CatalogApiDocController(apiServiceStatusService); + return new ApiDocController(apiServiceStatusService); } @Bean @@ -45,4 +45,5 @@ public MessageService messageService() { public CatalogApiDocControllerExceptionHandler catalogApiDocControllerExceptionHandler() { return new CatalogApiDocControllerExceptionHandler(messageService()); } + } diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerTest.java deleted file mode 100644 index f054a26fd3..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.controllers.api; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.zowe.apiml.apicatalog.services.status.APIServiceStatusService; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - -class CatalogApiDocControllerTest { - - private APIServiceStatusService mockApiServiceStatusService; - private CatalogApiDocController underTest; - - @BeforeEach - void setup() { - mockApiServiceStatusService = Mockito.mock(APIServiceStatusService.class); - underTest = new CatalogApiDocController(mockApiServiceStatusService); - } - - @Test - void whenCreateController_thenItIsInstantiated() { - assertNotNull(underTest); - } - - @Nested - class GivenService { - @Nested - class WhenGetApiDocByVersion { - @Test - void givenApiDoc_thenReturnApiDoc() { - ResponseEntity response = new ResponseEntity<>("Some API Doc", HttpStatus.OK); - when(mockApiServiceStatusService.getServiceCachedApiDocInfo("service", "1.0.0")).thenReturn(response); - - ResponseEntity res = underTest.getApiDocInfo("service", "1.0.0"); - assertNotNull(res); - assertEquals("Some API Doc", res.getBody()); - } - - @Test - void givenNoApiDoc_thenThrowException() { - when(mockApiServiceStatusService.getServiceCachedApiDocInfo("service", "1.0.0")).thenThrow(new ApiDocNotFoundException("error")); - assertThrows(ApiDocNotFoundException.class, () -> underTest.getApiDocInfo("service", "1.0.0")); - } - } - - @Nested - class WhenGetApiDocVersionDefault { - @Test - void givenApiDocExists_thenReturnIt() { - ResponseEntity response = new ResponseEntity<>("Some API Doc", HttpStatus.OK); - when(mockApiServiceStatusService.getServiceCachedDefaultApiDocInfo("service")).thenReturn(response); - - ResponseEntity res = underTest.getDefaultApiDocInfo("service"); - assertNotNull(res); - assertEquals("Some API Doc", res.getBody()); - } - - @Test - void givenNoApiDocExists_thenThrowException() { - when(mockApiServiceStatusService.getServiceCachedDefaultApiDocInfo("service")).thenThrow(new ApiDocNotFoundException("error")); - assertThrows(ApiDocNotFoundException.class, () -> underTest.getDefaultApiDocInfo("service")); - } - } - - @Test - void whenGetApiDiff_thenReturnApiDiffHtml() { - String responseString = "Some Diff"; - ResponseEntity response = new ResponseEntity<>("Some Diff", HttpStatus.OK); - - when(mockApiServiceStatusService.getApiDiffInfo("service", "v1", "v2")).thenReturn(response); - ResponseEntity res = underTest.getApiDiff("service", "v1", "v2"); - assertNotNull(res); - assertEquals(responseString, res.getBody()); - } - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java deleted file mode 100644 index 310774e38d..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.controllers.api; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.http.MediaType; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import org.zowe.apiml.apicatalog.standalone.ExampleService; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@WebMvcTest( - controllers = { MockController.class }, - excludeAutoConfiguration = { SecurityAutoConfiguration.class} -) -@ContextConfiguration(classes = MockControllerTest.Context.class) -@ActiveProfiles("test") -class MockControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private ExampleService exampleService; - - @Nested - class GivenEnabledController { - - @Test - void whenGetRequest() throws Exception { - mockMvc.perform(get("/mock/something")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(content().string("{}")); - verify(exampleService).replyExample(any(), eq("GET"), eq("/something")); - } - - @Test - void whenPostRequest() throws Exception { - mockMvc.perform(post("/mock/something/else")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(content().string("{}")); - verify(exampleService).replyExample(any(), eq("POST"), eq("/something/else")); - } - - } - - @Configuration - @Profile("test") - static class Context { - - @Bean - public ExampleService exampleService() { - return spy(new ExampleService()); - } - - @Bean - public MockController mockController(ExampleService exampleService) { - return new MockController(exampleService); - } - - } - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/OidcControllerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/OidcControllerTest.java new file mode 100644 index 0000000000..3dc09744a9 --- /dev/null +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/OidcControllerTest.java @@ -0,0 +1,83 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.apicatalog.controllers.api; + +import io.restassured.module.mockmvc.RestAssuredMockMvc; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static io.restassured.module.mockmvc.RestAssuredMockMvc.standaloneSetup; +import static org.junit.jupiter.api.Assertions.*; + +class OidcControllerTest { + + private OidcController oidcController = new OidcController(); + + @BeforeEach + void setUp() { + standaloneSetup(oidcController); + } + + @Nested + class OidcProviders { + + private String[] env = { + "ZWE_components_gateway_spring_security_oauth2_client_provider_oidc1_authorizationUri", + "ZWE_components_gateway_spring_security_oauth2_client_registration_oidc2_clientId", + "ZWE_components_gateway_spring_security_oauth2_client_provider_oidc1_tokenUri" + }; + + Map getEnvMap() { + try { + Class envVarClass = System.getenv().getClass(); + Field mField = envVarClass.getDeclaredField("m"); + mField.setAccessible(true); + return (Map) mField.get(System.getenv()); + } catch (NoSuchFieldException | IllegalAccessException e) { + fail(e); + return null; + } + } + + @AfterEach + void tearDown() { + Arrays.stream(env).forEach(k -> getEnvMap().remove(k)); + } + + @Test + void givenSystemEnv_whenInvokeOidcProviders_thenReturnTheList() { + Arrays.stream(env).forEach(k -> getEnvMap().put(k, "anyValue")); + List oidcProviders = RestAssuredMockMvc.given() + .when().get("/oidc/provider") + .getBody().jsonPath().getList("."); + assertEquals(2, oidcProviders.size()); + assertTrue(oidcProviders.contains("oidc1")); + assertTrue(oidcProviders.contains("oidc2")); + } + + @Test + void givenNoSystemEnv_whenInvokeOidcProviders_thenReturnAnEmptyList() { + List oidcProviders = RestAssuredMockMvc.given() + .when().get("/oidc/provider") + .getBody().jsonPath().getList("."); + assertEquals(0, oidcProviders.size()); + } + + } + +} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ServicesControllerContainerRetrievalTest.java similarity index 95% rename from api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTest.java rename to api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ServicesControllerContainerRetrievalTest.java index 41d75c16dc..0ba218f5b2 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ServicesControllerContainerRetrievalTest.java @@ -28,12 +28,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(SpringExtension.class) -@WebMvcTest(controllers = {ApiCatalogController.class}, +@WebMvcTest(controllers = {ServicesController.class}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = WebSecurityConfigurer.class) }, excludeAutoConfiguration = { SecurityAutoConfiguration.class} ) @ContextConfiguration(classes = ApiCatalogControllerContainerRetrievalTestContextConfiguration.class) -class ApiCatalogControllerContainerRetrievalTest { +class ServicesControllerContainerRetrievalTest { @Autowired private MockMvc mockMvc; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerTests.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ServicesControllerTests.java similarity index 63% rename from api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerTests.java rename to api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ServicesControllerTests.java index b008329481..65a8a241d5 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerTests.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ServicesControllerTests.java @@ -11,21 +11,26 @@ package org.zowe.apiml.apicatalog.controllers.api; import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; import com.netflix.discovery.shared.Application; import io.restassured.module.mockmvc.RestAssuredMockMvc; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.zowe.apiml.apicatalog.exceptions.ContainerStatusRetrievalThrowable; +import org.zowe.apiml.apicatalog.exceptions.ContainerStatusRetrievalException; +import org.zowe.apiml.apicatalog.swagger.ContainerService; import org.zowe.apiml.apicatalog.model.APIContainer; import org.zowe.apiml.apicatalog.model.APIService; -import org.zowe.apiml.apicatalog.services.cached.CachedApiDocService; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; +import org.zowe.apiml.apicatalog.model.CustomStyleConfig; +import org.zowe.apiml.apicatalog.swagger.ApiDocRetrievalService; +import org.zowe.apiml.product.gateway.GatewayClient; +import org.zowe.apiml.product.instance.ServiceAddress; +import org.zowe.apiml.product.routing.transform.TransformService; -import java.lang.reflect.Field; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import static io.restassured.module.mockmvc.RestAssuredMockMvc.standaloneSetup; import static org.hamcrest.MatcherAssert.assertThat; @@ -33,23 +38,29 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +class ServicesControllerTests { -class ApiCatalogControllerTests { private final String pathToContainers = "/containers"; - private CachedServicesService cachedServicesService; - private CachedProductFamilyService cachedProductFamilyService; - private CachedApiDocService cachedApiDocService; + private EurekaClient eurekaClient; + private ContainerService containerService; + private ApiDocRetrievalService apiDocRetrievalService; - private ApiCatalogController underTest; + private ServicesController underTest; @BeforeEach void setUp() { - cachedServicesService = mock(CachedServicesService.class); - cachedProductFamilyService = mock(CachedProductFamilyService.class); - cachedApiDocService = mock(CachedApiDocService.class); - - underTest = new ApiCatalogController(cachedProductFamilyService, cachedApiDocService); + eurekaClient = mock(EurekaClient.class); + containerService = spy(new ContainerService( + eurekaClient, + new TransformService(new GatewayClient(ServiceAddress.builder().scheme("https").hostname("localhost").build())), + new CustomStyleConfig()) + ); + apiDocRetrievalService = mock(ApiDocRetrievalService.class); + + underTest = new ServicesController(containerService, apiDocRetrievalService); standaloneSetup(underTest); } @@ -59,7 +70,7 @@ class GivenThereAreNoValidContainers { class WhenAllContainersAreRequested { @Test void thenReturnNoContent() { - given(cachedProductFamilyService.getAllContainers()).willReturn(null); + given(containerService.getAllContainers()).willReturn(null); RestAssuredMockMvc.given(). when(). @@ -74,7 +85,7 @@ class WhenSpecificContainerRequested { @Test void thenReturnOk() { String containerId = "service1"; - given(cachedProductFamilyService.getContainerById(containerId)).willReturn(null); + given(containerService.getContainerById(containerId)).willReturn(null); RestAssuredMockMvc.given(). when(). @@ -101,22 +112,22 @@ void prepareApplications() { apiVersions = Arrays.asList("1.0.0", "2.0.0"); - given(cachedServicesService.getService("service1")).willReturn(service1); - given(cachedApiDocService.getDefaultApiDocForService("service1")).willReturn("service1"); - given(cachedApiDocService.getApiVersionsForService("service1")).willReturn(apiVersions); + given(eurekaClient.getApplication("service1")).willReturn(service1); + given(apiDocRetrievalService.retrieveDefaultApiDoc("service1")).willReturn("service1"); + given(apiDocRetrievalService.retrieveApiVersions("service1")).willReturn(apiVersions); - given(cachedServicesService.getService("service2")).willReturn(service2); - given(cachedApiDocService.getDefaultApiDocForService("service2")).willReturn("service2"); - given(cachedApiDocService.getApiVersionsForService("service2")).willReturn(apiVersions); + given(eurekaClient.getApplication("service2")).willReturn(service2); + given(apiDocRetrievalService.retrieveDefaultApiDoc("service2")).willReturn("service2"); + given(apiDocRetrievalService.retrieveApiVersions("service2")).willReturn(apiVersions); - given(cachedProductFamilyService.getContainerById("api-one")).willReturn(createContainers().get(0)); + given(containerService.getContainerById("api-one")).willReturn(createContainers().get(0)); } @Nested class WhenGettingAllContainers { @Test void thenReturnContainersWithState() { - given(cachedProductFamilyService.getAllContainers()).willReturn(createContainers()); + given(containerService.getAllContainers()).willReturn(createContainers()); RestAssuredMockMvc.given(). when(). @@ -129,11 +140,11 @@ void thenReturnContainersWithState() { @Nested class WhenGettingSpecificContainer { @Test - void thenPopulateApiDocForServices() throws ContainerStatusRetrievalThrowable { + void thenPopulateApiDocForServices() throws ContainerStatusRetrievalException { String defaultApiVersion = "v1"; - given(cachedApiDocService.getDefaultApiVersionForService("service1")).willReturn(defaultApiVersion); - given(cachedApiDocService.getDefaultApiVersionForService("service2")).willReturn(defaultApiVersion); + given(apiDocRetrievalService.retrieveDefaultApiVersion("service1")).willReturn(defaultApiVersion); + given(apiDocRetrievalService.retrieveDefaultApiVersion("service2")).willReturn(defaultApiVersion); ResponseEntity> containers = underTest.getAPIContainerById("api-one"); @@ -146,8 +157,8 @@ void thenPopulateApiDocForServices() throws ContainerStatusRetrievalThrowable { } @Test - void thenPopulateApiDocForServicesExceptOneWhichFails() throws ContainerStatusRetrievalThrowable { - given(cachedApiDocService.getDefaultApiDocForService("service2")).willThrow(new RuntimeException()); + void thenPopulateApiDocForServicesExceptOneWhichFails() throws ContainerStatusRetrievalException { + given(apiDocRetrievalService.retrieveDefaultApiDoc("service2")).willThrow(new RuntimeException()); ResponseEntity> containers = underTest.getAPIContainerById("api-one"); assertThereIsOneContainer(containers); @@ -165,8 +176,8 @@ void thenPopulateApiDocForServicesExceptOneWhichFails() throws ContainerStatusRe } @Test - void thenPopulateApiVersionsForServicesExceptOneWhichFails() throws ContainerStatusRetrievalThrowable { - given(cachedApiDocService.getApiVersionsForService("service2")).willThrow(new RuntimeException()); + void thenPopulateApiVersionsForServicesExceptOneWhichFails() throws ContainerStatusRetrievalException { + given(apiDocRetrievalService.retrieveApiVersions("service2")).willThrow(new RuntimeException()); ResponseEntity> containers = underTest.getAPIContainerById("api-one"); assertThereIsOneContainer(containers); @@ -193,6 +204,7 @@ private void assertThereIsOneContainer(ResponseEntity> contai @Nested class WhenGettingSpecificService { + private final String serviceId = "service1"; private final APIService service = new APIService.Builder(serviceId) .secured(true) @@ -204,7 +216,7 @@ class WhenGettingSpecificService { @Test void thenReturnNotFound() { - given(cachedProductFamilyService.getServices()).willReturn(null); + given(eurekaClient.getApplications()).willReturn(null); String pathToServices = "/services"; RestAssuredMockMvc.given(). @@ -215,15 +227,12 @@ void thenReturnNotFound() { } @Test - void thenReturnOk() throws ContainerStatusRetrievalThrowable { + void thenReturnOk() throws ContainerStatusRetrievalException { String defaultApiVersion = "v1"; - Map services = new ConcurrentHashMap<>(); - services.put(serviceId, service); - given(cachedProductFamilyService.getServices()).willReturn(services); - - given(cachedApiDocService.getDefaultApiVersionForService(serviceId)).willReturn(defaultApiVersion); - given(cachedApiDocService.getDefaultApiDocForService(serviceId)).willReturn("mockApiDoc"); + given(containerService.getService(serviceId)).willReturn(service); + given(apiDocRetrievalService.retrieveDefaultApiVersion(serviceId)).willReturn(defaultApiVersion); + given(apiDocRetrievalService.retrieveDefaultApiDoc(serviceId)).willReturn("mockApiDoc"); ResponseEntity apiServicesById = underTest.getAPIServicesById(serviceId); assertEquals(HttpStatus.OK, apiServicesById.getStatusCode()); @@ -233,15 +242,12 @@ void thenReturnOk() throws ContainerStatusRetrievalThrowable { } @Test - void thenReturnOkWithApiDocNull() throws ContainerStatusRetrievalThrowable { + void thenReturnOkWithApiDocNull() throws ContainerStatusRetrievalException { String defaultApiVersion = "v1"; - Map services = new ConcurrentHashMap<>(); - services.put(serviceId, service); - given(cachedProductFamilyService.getServices()).willReturn(services); - - given(cachedApiDocService.getDefaultApiVersionForService(serviceId)).willReturn(defaultApiVersion); - given(cachedApiDocService.getDefaultApiDocForService(serviceId)).willReturn(null); + given(containerService.getService(serviceId)).willReturn(service); + given(apiDocRetrievalService.retrieveDefaultApiVersion(serviceId)).willReturn(defaultApiVersion); + given(apiDocRetrievalService.retrieveDefaultApiDoc(serviceId)).willReturn(null); ResponseEntity apiServicesById = underTest.getAPIServicesById(serviceId); assertEquals(HttpStatus.OK, apiServicesById.getStatusCode()); @@ -250,9 +256,6 @@ void thenReturnOkWithApiDocNull() throws ContainerStatusRetrievalThrowable { } } - - - // =========================================== Helper Methods =========================================== private List createContainers() { @@ -295,51 +298,4 @@ private InstanceInfo getStandardInstance(String serviceId, InstanceInfo.Instance null, null, null, null); } - @Nested - class OidcProviders { - - private String[] env = { - "ZWE_components_gateway_spring_security_oauth2_client_provider_oidc1_authorizationUri", - "ZWE_components_gateway_spring_security_oauth2_client_registration_oidc2_clientId", - "ZWE_components_gateway_spring_security_oauth2_client_provider_oidc1_tokenUri" - }; - - Map getEnvMap() { - try { - Class envVarClass = System.getenv().getClass(); - Field mField = envVarClass.getDeclaredField("m"); - mField.setAccessible(true); - return (Map) mField.get(System.getenv()); - } catch (NoSuchFieldException | IllegalAccessException e) { - fail(e); - return null; - } - } - - @AfterEach - void tearDown() { - Arrays.stream(env).forEach(k -> getEnvMap().remove(k)); - } - - @Test - void givenSystemEnv_whenInvokeOidcProviders_thenReturnTheList() { - Arrays.stream(env).forEach(k -> getEnvMap().put(k, "anyValue")); - List oidcProviders = RestAssuredMockMvc.given() - .when().get("/oidc/provider") - .getBody().jsonPath().getList("."); - assertEquals(2, oidcProviders.size()); - assertTrue(oidcProviders.contains("oidc1")); - assertTrue(oidcProviders.contains("oidc2")); - } - - @Test - void givenNoSystemEnv_whenInvokeOidcProviders_thenReturnAnEmptyList() { - List oidcProviders = RestAssuredMockMvc.given() - .when().get("/oidc/provider") - .getBody().jsonPath().getList("."); - assertEquals(0, oidcProviders.size()); - } - - } - } diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/model/ApiDiffNotAvailableExceptionTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/exceptions/ApiDiffNotAvailableExceptionTest.java similarity index 93% rename from api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/model/ApiDiffNotAvailableExceptionTest.java rename to api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/exceptions/ApiDiffNotAvailableExceptionTest.java index ae8c73f27f..1008195f28 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/model/ApiDiffNotAvailableExceptionTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/exceptions/ApiDiffNotAvailableExceptionTest.java @@ -8,7 +8,7 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status.model; +package org.zowe.apiml.apicatalog.exceptions; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeServiceTest.java deleted file mode 100644 index dbc05dcfae..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceInitializeServiceTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.instance; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.junit.jupiter.api.Assertions; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.retry.RetryException; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; -import org.zowe.apiml.product.constants.CoreService; -import org.zowe.apiml.product.gateway.GatewayNotAvailableException; -import org.zowe.apiml.product.instance.InstanceInitializationException; -import org.zowe.apiml.product.registry.CannotRegisterServiceException; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static org.mockito.Mockito.*; -import static org.zowe.apiml.constants.EurekaMetadataDefinition.*; - -@ExtendWith(MockitoExtension.class) -class InstanceInitializeServiceTest { - - @Mock - private CachedServicesService cachedServicesService; - - @Mock - private InstanceRetrievalService instanceRetrievalService; - - @Mock - private CachedProductFamilyService cachedProductFamilyService; - - @SuppressWarnings("unused") - @Mock - private InstanceRefreshService instanceRefreshService; - - @InjectMocks - private InstanceInitializeService instanceInitializeService; - - @Test - void testRetrieveAndRegisterAllInstancesWithCatalog() throws CannotRegisterServiceException { - Map instanceInfoMap = createInstances(); - - String catalogId = CoreService.API_CATALOG.getServiceId(); - InstanceInfo apiCatalogInstance = instanceInfoMap.get(catalogId.toUpperCase()); - when( - instanceRetrievalService.getInstanceInfo(catalogId) - ).thenReturn(apiCatalogInstance); - - Applications applications = new Applications(); - instanceInfoMap.values().forEach(f -> - applications.addApplication(new Application(f.getAppName(), Collections.singletonList(f))) - ); - - when( - instanceRetrievalService.getAllInstancesFromDiscovery(false) - ).thenReturn(applications); - - - instanceInitializeService.retrieveAndRegisterAllInstancesWithCatalog(); - - verify(cachedProductFamilyService, times(2)).saveContainerFromInstance( - apiCatalogInstance.getMetadata().get(CATALOG_ID), - apiCatalogInstance - ); - - - Optional catalogApplication = applications.getRegisteredApplications() - .stream() - .filter(f -> f.getName().equals(catalogId.toUpperCase())) - .findFirst(); - - Assertions.assertTrue(catalogApplication.isPresent()); - verify(cachedServicesService, times(2)).updateService(catalogApplication.get().getName(), catalogApplication.get()); - - - instanceInfoMap.values() - .stream() - .filter(f -> !f.getAppName().equals(catalogId.toUpperCase())) - .forEach(instanceInfo -> - verify(cachedProductFamilyService, times(1)).saveContainerFromInstance( - instanceInfo.getMetadata().get(CATALOG_ID), - instanceInfo - )); - } - - @Test - void shouldThrowExceptionWhenCatalogNotFound() throws CannotRegisterServiceException { - String catalogId = CoreService.API_CATALOG.getServiceId(); - when(instanceRetrievalService.getInstanceInfo(catalogId)).thenReturn(null); - - Exception exception = Assertions.assertThrows(CannotRegisterServiceException.class, () -> { - instanceInitializeService.retrieveAndRegisterAllInstancesWithCatalog(); - }); - Assertions.assertTrue(exception.getCause() instanceof RetryException); - } - - @Test - void shouldThrowRetryExceptionOnInstanceInitializationException() throws CannotRegisterServiceException { - String catalogId = CoreService.API_CATALOG.getServiceId(); - when(instanceRetrievalService.getInstanceInfo(catalogId)).thenThrow(new InstanceInitializationException("ERROR")); - - Exception exception = Assertions.assertThrows(RetryException.class, () -> { - instanceInitializeService.retrieveAndRegisterAllInstancesWithCatalog(); - }); - Assertions.assertEquals("ERROR", exception.getMessage()); - } - - @Test - void shouldThrowRetryExceptionOnGatewayNotAvailableException() throws CannotRegisterServiceException { - String catalogId = CoreService.API_CATALOG.getServiceId(); - when(instanceRetrievalService.getInstanceInfo(catalogId)).thenThrow(new GatewayNotAvailableException("ERROR")); - - Exception exception = Assertions.assertThrows(RetryException.class, () -> { - instanceInitializeService.retrieveAndRegisterAllInstancesWithCatalog(); - }); - Assertions.assertEquals("ERROR", exception.getMessage()); - } - - private Map createInstances() { - Map instanceInfoMap = new HashMap<>(); - - InstanceInfo instanceInfo = getStandardInstance( - CoreService.GATEWAY.getServiceId(), - InstanceInfo.InstanceStatus.UP, - getMetadataByCatalogUiTitleId("apimediationlayer", "/" + CoreService.GATEWAY.getServiceId()), - "gateway", - "https://localhost:9090/"); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance( - CoreService.ZAAS.getServiceId(), - InstanceInfo.InstanceStatus.UP, - getMetadataByCatalogUiTitleId("apimediationlayer", "/" + CoreService.ZAAS.getServiceId()), - "zaas", - "https://localhost:9090/"); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance( - CoreService.API_CATALOG.getServiceId(), - InstanceInfo.InstanceStatus.UP, - getMetadataByCatalogUiTitleId("apimediationlayer", "/" + CoreService.API_CATALOG.getServiceId()), - "apicatalog", - "https://localhost:9090/"); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance( - "STATICCLIENT", - InstanceInfo.InstanceStatus.UP, - getMetadataByCatalogUiTitleId("static", "/discoverableclient"), - "staticclient", - "https://localhost:9090/discoverableclient"); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance( - "STATICCLIENT2", - InstanceInfo.InstanceStatus.UP, - getMetadataByCatalogUiTitleId("static", "/discoverableclient"), - "staticclient2", - null); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance( - "ZOSMF1", - InstanceInfo.InstanceStatus.UP, - getMetadataByCatalogUiTitleId("zosmf", "/zosmf1"), - "zosmf1", - null); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance( - "ZOSMF2", - InstanceInfo.InstanceStatus.UP, - getMetadataByCatalogUiTitleId("zosmf", "/zosmf2"), - "zosmf2", - null); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - return instanceInfoMap; - } - - private HashMap getMetadataByCatalogUiTitleId(String catalogUiTileId, String uiRoute) { - HashMap metadata = new HashMap<>(); - metadata.put(CATALOG_ID, catalogUiTileId); - metadata.put(ROUTES + ".ui-v1." + ROUTES_SERVICE_URL, uiRoute); - metadata.put(ROUTES + ".ui-v1." + ROUTES_GATEWAY_URL, "ui/v1"); - metadata.put(ROUTES + ".api-v1." + ROUTES_SERVICE_URL, "api/v1"); - metadata.put(ROUTES + ".api-v1." + ROUTES_GATEWAY_URL, "/"); - - return metadata; - } - - - private InstanceInfo getStandardInstance(String serviceId, - InstanceInfo.InstanceStatus status, - HashMap metadata, - String vipAddress, - String homePageUrl) { - - return InstanceInfo.Builder.newBuilder() - .setInstanceId(serviceId) - .setAppName(serviceId) - .setIPAddr("192.168.0.1") - .enablePort(InstanceInfo.PortType.SECURE, true) - .setSecurePort(9090) - .setHostName("localhost") - .setHomePageUrl(homePageUrl, homePageUrl) - .setSecureVIPAddress("localhost") - .setMetadata(metadata) - .setVIPAddress(vipAddress) - .setStatus(status) - .build(); - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshServiceTest.java deleted file mode 100644 index 10b8cbc25d..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshServiceTest.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.instance; - - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.zowe.apiml.apicatalog.model.APIContainer; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; -import org.zowe.apiml.apicatalog.util.ContainerServiceMockUtil; -import org.zowe.apiml.apicatalog.util.ContainerServiceState; -import org.zowe.apiml.product.constants.CoreService; -import org.zowe.apiml.product.gateway.GatewayClient; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; - -import static org.mockito.Mockito.*; -import static org.zowe.apiml.constants.EurekaMetadataDefinition.CATALOG_ID; - -class InstanceRefreshServiceTest { - - private final ContainerServiceMockUtil containerServiceMockUtil = new ContainerServiceMockUtil(); - - private GatewayClient gatewayClient; - private CachedProductFamilyService cachedProductFamilyService; - private CachedServicesService cachedServicesService; - private InstanceRetrievalService instanceRetrievalService; - - private InstanceRefreshService underTest; - - @BeforeEach - void setup() { - gatewayClient = mock(GatewayClient.class); - cachedProductFamilyService = mock(CachedProductFamilyService.class); - cachedServicesService = mock(CachedServicesService.class); - instanceRetrievalService = mock(InstanceRetrievalService.class); - - underTest = new InstanceRefreshService(cachedProductFamilyService, cachedServicesService, instanceRetrievalService); - underTest.start(); - - addApiCatalogToCache(); - } - - @Nested - class WhenRefreshingCacheFromDiscovery { - @Nested - class GivenValidCache { - ContainerServiceState discoveredState; - ContainerServiceState cachedState; - - @BeforeEach - void prepareState() { - cachedState = containerServiceMockUtil.createContainersServicesAndInstances(); - containerServiceMockUtil.mockServiceRetrievalFromCache(cachedServicesService, cachedState.getApplications()); - - discoveredState = new ContainerServiceState(); - discoveredState.setServices(new ArrayList<>()); - discoveredState.setContainers(new ArrayList<>()); - discoveredState.setInstances(new ArrayList<>()); - discoveredState.setApplications(new ArrayList<>()); - } - - @Nested - class AndServiceDoesntExist { - InstanceInfo newInstanceOfService; - - @BeforeEach - void prepareApplication() { - // start up a new instance of service 5 and add it to the service1 application - HashMap metadata = new HashMap<>(); - metadata.put(CATALOG_ID, "api-five"); - newInstanceOfService - = containerServiceMockUtil.createInstance("service5", "service5:9999", InstanceInfo.InstanceStatus.UP, - InstanceInfo.ActionType.ADDED, metadata); - discoveredState.getInstances().add(newInstanceOfService); - Application service5 = new Application("service5", Collections.singletonList(newInstanceOfService)); - discoveredState.getApplications().add(service5); - - teachMocks(); - } - - @Test - void addServiceToCache() { - when(cachedProductFamilyService.saveContainerFromInstance("api-five", newInstanceOfService)) - .thenReturn(new APIContainer()); - - underTest.refreshCacheFromDiscovery(); - - verify(cachedProductFamilyService, times(1)) - .saveContainerFromInstance("api-five", newInstanceOfService); - } - } - - @Nested - class AndServiceAlreadyExists { - Application service3; - InstanceInfo changedInstanceOfService; - - @BeforeEach - void prepareService() { - service3 = cachedState.getApplications() - .stream() - .filter(application -> application.getName().equalsIgnoreCase("service3")) - .toList().get(0); - - changedInstanceOfService = service3.getInstances().get(0); - changedInstanceOfService.getMetadata().put(CATALOG_ID, "api-three"); - service3.getInstances().add(0, changedInstanceOfService); - discoveredState.getApplications().add(service3); - - teachMocks(); - - } - - @Test - void serviceIsRemovedFromCache() { - changedInstanceOfService.setActionType(InstanceInfo.ActionType.DELETED); - - underTest.refreshCacheFromDiscovery(); - - verify(cachedProductFamilyService, times(1)) - .removeInstance("api-three", changedInstanceOfService); - verify(cachedServicesService, never()).updateService(anyString(), any(Application.class)); - verify(cachedProductFamilyService, never()).saveContainerFromInstance("api-three", changedInstanceOfService); - } - - @Test - void serviceIsModifiedInCache() { - changedInstanceOfService.setActionType(InstanceInfo.ActionType.MODIFIED); - - APIContainer apiContainer3 = cachedState.getContainers() - .stream() - .filter(apiContainer -> apiContainer.getId().equals("api-three")) - .findFirst() - .orElse(new APIContainer()); - - when(cachedProductFamilyService.saveContainerFromInstance("api-three", changedInstanceOfService)) - .thenReturn(apiContainer3); - - underTest.refreshCacheFromDiscovery(); - - verify(cachedServicesService, times(1)).updateService(changedInstanceOfService.getAppName(), service3); - verify(cachedProductFamilyService, times(1)) - .saveContainerFromInstance("api-three", changedInstanceOfService); - } - } - - void teachMocks() { - // Mock the discovery and cached service query - Applications discoveredServices = new Applications("123", 2L, discoveredState.getApplications()); - when(instanceRetrievalService.getAllInstancesFromDiscovery(true)).thenReturn(discoveredServices); - Applications cachedServices = new Applications("456", 1L, cachedState.getApplications()); - when(cachedServicesService.getAllCachedServices()).thenReturn(cachedServices); - } - } - - @Nested - class GivenClientIsNotInitialized { - @Test - void cacheIsntUpdated() { - when(gatewayClient.isInitialized()).thenReturn(false); - - underTest.refreshCacheFromDiscovery(); - - verify(cachedServicesService, never()) - .updateService(anyString(), any(Application.class)); - } - } - - @Nested - class GivenApiCatalogIsntInCache { - @Test - void cacheIsntUpdated() { - when(cachedServicesService.getService(CoreService.API_CATALOG.getServiceId())).thenReturn(null); - - underTest.refreshCacheFromDiscovery(); - - verify(cachedServicesService, never()) - .updateService(anyString(), any(Application.class)); - } - } - } - - - private void addApiCatalogToCache() { - InstanceInfo apiCatalogInstance = containerServiceMockUtil.createInstance( - CoreService.API_CATALOG.getServiceId(), - "service:9999", - InstanceInfo.InstanceStatus.UP, - InstanceInfo.ActionType.ADDED, - new HashMap<>()); - - when(cachedServicesService.getService(CoreService.API_CATALOG.getServiceId())) - .thenReturn( - new Application(CoreService.API_CATALOG.getServiceId(), - Collections.singletonList(apiCatalogInstance) - ) - ); - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalServiceTest.java deleted file mode 100644 index 9675f70de0..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalServiceTest.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.instance; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import org.apache.commons.io.IOUtils; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.core5.http.ClassicHttpRequest; -import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.HttpClientResponseHandler; -import org.apache.hc.core5.http.io.entity.BasicHttpEntity; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.util.ReflectionTestUtils; -import org.zowe.apiml.apicatalog.discovery.DiscoveryConfigProperties; -import org.zowe.apiml.apicatalog.util.ApplicationsWrapper; -import org.zowe.apiml.constants.EurekaMetadataDefinition; -import org.zowe.apiml.product.constants.CoreService; -import org.zowe.apiml.product.instance.InstanceInitializationException; -import org.zowe.apiml.product.registry.ApplicationWrapper; -import org.zowe.apiml.util.HttpClientMockHelper; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.*; - -import static org.apache.hc.core5.http.ContentType.APPLICATION_JSON; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.*; -import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; -import static org.zowe.apiml.constants.EurekaMetadataDefinition.REGISTRATION_TYPE; - -class InstanceRetrievalServiceTest { - - private static final String UNKNOWN = "unknown"; - - private InstanceInfo getStandardInstance(String serviceId, InstanceInfo.InstanceStatus status) { - return InstanceInfo.Builder.newBuilder() - .setInstanceId(serviceId) - .setAppName(serviceId) - .setStatus(status) - .build(); - } - - @Nested - @ExtendWith(SpringExtension.class) - @TestPropertySource(locations = "/application.yml") - @ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class, classes = InstanceServicesContextConfiguration.class) - class SingleDomain { - - private InstanceRetrievalService instanceRetrievalService; - - @Autowired - private DiscoveryConfigProperties discoveryConfigProperties; - - @Mock - private CloseableHttpClient httpClient; - - @Mock - private CloseableHttpResponse response; - - @BeforeEach - void setup() { - HttpClientMockHelper.mockExecuteWithResponse(httpClient, response); - HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK); - - instanceRetrievalService = new InstanceRetrievalService(discoveryConfigProperties, httpClient); - } - - @Test - void whenDiscoveryServiceIsNotAvailable_thenTryOthersFromTheList() throws IOException { - when(response.getCode()).thenReturn(HttpStatus.SC_FORBIDDEN).thenReturn(HttpStatus.SC_OK); - - instanceRetrievalService.getAllInstancesFromDiscovery(false); - verify(httpClient, times(2)).execute(any(ClassicHttpRequest.class), any(HttpClientResponseHandler.class)); - } - - @Test - void testGetInstanceInfo_whenServiceIdIsUNKNOWN() { - InstanceInfo instanceInfo = instanceRetrievalService.getInstanceInfo(UNKNOWN); - assertNull(instanceInfo); - } - - @Test - void providedNoInstanceInfoIsReturned_thenInstanceInitializationExceptionIsThrown() { - String serviceId = CoreService.API_CATALOG.getServiceId(); - when(response.getCode()).thenReturn(HttpStatus.SC_FORBIDDEN); - - assertThrows(InstanceInitializationException.class, () -> instanceRetrievalService.getInstanceInfo(serviceId)); - } - - @Test - void testGetInstanceInfo_whenResponseHasEmptyBody() { - HttpClientMockHelper.mockResponse(response, ""); - InstanceInfo instanceInfo = instanceRetrievalService.getInstanceInfo(CoreService.API_CATALOG.getServiceId()); - assertNull(instanceInfo); - } - - @Test - void testGetInstanceInfo_whenResponseCodeIsSuccessWithUnParsedJsonText() { - HttpClientMockHelper.mockResponse(response, "UNPARSABLE_JSON"); - InstanceInfo instanceInfo = instanceRetrievalService.getInstanceInfo(CoreService.API_CATALOG.getServiceId()); - assertNull(instanceInfo); - } - - @Test - void testGetInstanceInfo() throws IOException { - InstanceInfo expectedInstanceInfo = getStandardInstance( - CoreService.API_CATALOG.getServiceId(), - InstanceInfo.InstanceStatus.UP - ); - - ObjectMapper mapper = new ObjectMapper(); - String bodyCatalog = mapper.writeValueAsString( - new ApplicationWrapper(new Application( - CoreService.API_CATALOG.getServiceId(), - Collections.singletonList(expectedInstanceInfo) - ))); - BasicHttpEntity responseEntity = new BasicHttpEntity(IOUtils.toInputStream(bodyCatalog, StandardCharsets.UTF_8), APPLICATION_JSON); - when(response.getEntity()).thenReturn(responseEntity); - - InstanceInfo actualInstanceInfo = instanceRetrievalService.getInstanceInfo(CoreService.API_CATALOG.getServiceId()); - - assertNotNull(actualInstanceInfo); - assertThat(actualInstanceInfo, hasProperty("instanceId", equalTo(expectedInstanceInfo.getInstanceId()))); - assertThat(actualInstanceInfo, hasProperty("appName", equalTo(expectedInstanceInfo.getAppName()))); - assertThat(actualInstanceInfo, hasProperty("status", equalTo(expectedInstanceInfo.getStatus()))); - } - - @Test - void testGetAllInstancesFromDiscovery_whenResponseCodeIsNotSuccess() { - when(response.getCode()).thenReturn(HttpStatus.SC_FORBIDDEN); - - Applications actualApplications = instanceRetrievalService.getAllInstancesFromDiscovery(false); - assertNull(actualApplications); - } - - @Test - void testGetAllInstancesFromDiscovery_whenResponseCodeIsSuccessWithUnParsedJsonText() { - Applications actualApplications = instanceRetrievalService.getAllInstancesFromDiscovery(false); - assertNull(actualApplications); - } - - @Test - void testGetAllInstancesFromDiscovery_whenNeedApplicationsWithoutFilter() throws IOException { - Map instanceInfoMap = createInstances(); - - - Applications expectedApplications = new Applications(); - instanceInfoMap.forEach((key, value) -> expectedApplications.addApplication(new Application(value.getAppName(), Collections.singletonList(value)))); - - ObjectMapper mapper = new ObjectMapper(); - String bodyAll = mapper.writeValueAsString(new ApplicationsWrapper(expectedApplications)); - BasicHttpEntity responseEntity = new BasicHttpEntity(IOUtils.toInputStream(bodyAll, StandardCharsets.UTF_8), APPLICATION_JSON); - when(response.getEntity()).thenReturn(responseEntity); - - Applications actualApplications = instanceRetrievalService.getAllInstancesFromDiscovery(false); - - assertEquals(expectedApplications.size(), actualApplications.size()); - - List actualApplicationList = - new ArrayList<>(actualApplications.getRegisteredApplications()); - - - expectedApplications - .getRegisteredApplications() - .forEach(expectedApplication -> - assertThat(actualApplicationList, hasItem(hasProperty("name", equalTo(expectedApplication.getName())))) - ); - } - - @Test - void testGetAllInstancesFromDiscovery_whenNeedApplicationsWithDeltaFilter() throws IOException { - Map instanceInfoMap = createInstances(); - - Applications expectedApplications = new Applications(); - instanceInfoMap.forEach((key, value) -> expectedApplications.addApplication(new Application(value.getAppName(), Collections.singletonList(value)))); - - ObjectMapper mapper = new ObjectMapper(); - String bodyAll = mapper.writeValueAsString(new ApplicationsWrapper(expectedApplications)); - BasicHttpEntity responseEntity = new BasicHttpEntity(IOUtils.toInputStream(bodyAll, StandardCharsets.UTF_8), APPLICATION_JSON); - when(response.getEntity()).thenReturn(responseEntity); - - Applications actualApplications = instanceRetrievalService.getAllInstancesFromDiscovery(true); - - assertEquals(expectedApplications.size(), actualApplications.size()); - - List actualApplicationList = - new ArrayList<>(actualApplications.getRegisteredApplications()); - - - expectedApplications - .getRegisteredApplications() - .forEach(expectedApplication -> - assertThat(actualApplicationList, hasItem(hasProperty("name", equalTo(expectedApplication.getName())))) - ); - } - - private Map createInstances() { - Map instanceInfoMap = new HashMap<>(); - - InstanceInfo instanceInfo = getStandardInstance(CoreService.GATEWAY.getServiceId(), InstanceInfo.InstanceStatus.UP); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance(CoreService.ZAAS.getServiceId(), InstanceInfo.InstanceStatus.UP); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance(CoreService.API_CATALOG.getServiceId(), InstanceInfo.InstanceStatus.UP); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - - instanceInfo = getStandardInstance("STATICCLIENT", InstanceInfo.InstanceStatus.UP); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - - instanceInfo = getStandardInstance("STATICCLIENT2", InstanceInfo.InstanceStatus.UP); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - - instanceInfo = getStandardInstance("ZOSMF1", InstanceInfo.InstanceStatus.UP); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - instanceInfo = getStandardInstance("ZOSMF2", InstanceInfo.InstanceStatus.UP); - instanceInfoMap.put(instanceInfo.getAppName(), instanceInfo); - - return instanceInfoMap; - } - - } - - @Nested - class MultiDomain { - - private static final String APIML_CENTRAL = "apimlidcentral"; - private static final String APIML_ID_1 = "apimlid1"; - - private InstanceRetrievalService instanceRetrievalService; - private ObjectMapper mapper = mock(ObjectMapper.class); - - @BeforeEach - void init() throws IOException { - DiscoveryConfigProperties discoveryConfig = new DiscoveryConfigProperties(); - ReflectionTestUtils.setField(discoveryConfig, "locations", new String[] { "https://ds:10011/eureka" }); - - instanceRetrievalService = spy(new InstanceRetrievalService(discoveryConfig, null)); - ReflectionTestUtils.setField(instanceRetrievalService, "mapper", mapper); - - // construct Eureka representation of Gateway instances - InstanceInfo centralApiml = getStandardInstance(CoreService.GATEWAY.getServiceId(), InstanceInfo.InstanceStatus.UP); - ReflectionTestUtils.setField(centralApiml, "instanceId", "centralApiml:instance:1"); - centralApiml.getMetadata().put(APIML_ID, APIML_CENTRAL); - centralApiml.getMetadata().put(REGISTRATION_TYPE, EurekaMetadataDefinition.RegistrationType.PRIMARY.getValue()); - InstanceInfo apiml1 = getStandardInstance(CoreService.GATEWAY.getServiceId(), InstanceInfo.InstanceStatus.UP); - ReflectionTestUtils.setField(apiml1, "instanceId", "domainApiml:instance:1"); - apiml1.getMetadata().put(APIML_ID, APIML_ID_1); - apiml1.getMetadata().put(REGISTRATION_TYPE, EurekaMetadataDefinition.RegistrationType.ADDITIONAL.getValue()); - Application application = new Application(CoreService.GATEWAY.getServiceId()); - application.addInstance(centralApiml); - application.addInstance(apiml1); - ApplicationWrapper applications = new ApplicationWrapper(application); - - // mock obtaining and mapping of APIML instance - doReturn("gatewayJson").when(instanceRetrievalService).queryDiscoveryForInstances(argThat(request -> - CoreService.GATEWAY.getServiceId().equalsIgnoreCase(request.getServiceId()) - )); - doReturn(applications).when(mapper).readValue("gatewayJson", ApplicationWrapper.class); - } - - @Test - void givenAdditionalRegistrationOfGateway_whenAskWithApimlId_thenFindIt() { - InstanceInfo instanceInfo = instanceRetrievalService.getInstanceInfo(APIML_ID_1); - assertEquals(CoreService.GATEWAY.getServiceId(), instanceInfo.getAppName().toLowerCase(Locale.ROOT)); - assertEquals(APIML_ID_1, instanceInfo.getMetadata().get(APIML_ID)); - assertEquals(EurekaMetadataDefinition.RegistrationType.ADDITIONAL.getValue(), instanceInfo.getMetadata().get(REGISTRATION_TYPE)); - } - - @Test - void givenUnknownServiceId_whenGetInstanceInfo_thenMakeTwoQueries() throws IOException { - instanceRetrievalService.getInstanceInfo("unknown-service"); - verify(instanceRetrievalService, times(2)).queryDiscoveryForInstances(any()); - } - - @Test - void givenMultipleGateways_whenAskForGatewayService_thenReturnThePrimaryOne() { - InstanceInfo instanceInfo = instanceRetrievalService.getInstanceInfo(CoreService.GATEWAY.getServiceId()); - assertEquals(CoreService.GATEWAY.getServiceId(), instanceInfo.getAppName().toLowerCase(Locale.ROOT)); - assertEquals(APIML_CENTRAL, instanceInfo.getMetadata().get(APIML_ID)); - assertEquals(EurekaMetadataDefinition.RegistrationType.PRIMARY.getValue(), instanceInfo.getMetadata().get(REGISTRATION_TYPE)); - } - - } - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/ApiDocKeyTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/ApiDocKeyTest.java deleted file mode 100644 index 4cef254f9b..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/ApiDocKeyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services; - -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocCacheKey; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class ApiDocKeyTest { - - @Test - void testCreationOfObjectAndValuesSet() { - ApiDocCacheKey apiDocCacheKey = new ApiDocCacheKey("service", "1.0.0"); - Assertions.assertNotNull(apiDocCacheKey); - Assertions.assertEquals("service", apiDocCacheKey.getServiceId()); - Assertions.assertEquals("1.0.0", apiDocCacheKey.getApiVersion()); - apiDocCacheKey.setApiVersion("2.0.0"); - apiDocCacheKey.setServiceId("service1"); - Assertions.assertEquals("service1", apiDocCacheKey.getServiceId()); - Assertions.assertEquals("2.0.0", apiDocCacheKey.getApiVersion()); - } - - @Test - void testEqualsAndHasCode() { - ApiDocCacheKey apiDocCacheKey = new ApiDocCacheKey("service", "1.0.0"); - ApiDocCacheKey apiDocCacheKey1 = new ApiDocCacheKey("service1", "2.0.0"); - Assertions.assertNotEquals(apiDocCacheKey, apiDocCacheKey1); - Assertions.assertNotEquals(apiDocCacheKey.hashCode(), apiDocCacheKey1.hashCode()); - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocServiceTest.java deleted file mode 100644 index 94b0f15a7e..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedApiDocServiceTest.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.cached; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; -import org.zowe.apiml.apicatalog.services.status.APIDocRetrievalService; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; -import org.zowe.apiml.apicatalog.services.status.model.ApiVersionNotFoundException; -import org.zowe.apiml.apicatalog.swagger.TransformApiDocService; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class CachedApiDocServiceTest { - private CachedApiDocService cachedApiDocService; - - @Mock - APIDocRetrievalService apiDocRetrievalService; - - @Mock - TransformApiDocService transformApiDocService; - - @BeforeEach - private void setUp() { - cachedApiDocService = new CachedApiDocService(apiDocRetrievalService, transformApiDocService); - cachedApiDocService.resetCache(); - } - - @Nested - class GivenValidApiDoc_thenReturnIt { - @Test - void whenRetrieving() { - String serviceId = "Service"; - String version = "v1"; - String expectedApiDoc = "This is some api doc"; - - ApiDocInfo apiDocInfo = new ApiDocInfo(null, expectedApiDoc, null); - - when(apiDocRetrievalService.retrieveApiDoc(serviceId, version)) - .thenReturn(apiDocInfo); - when(transformApiDocService.transformApiDoc(serviceId, apiDocInfo)) - .thenReturn(expectedApiDoc); - - String apiDoc = cachedApiDocService.getApiDocForService(serviceId, version); - - assertNotNull(apiDoc); - assertEquals(expectedApiDoc, apiDoc); - } - - @Test - void whenUpdating() { - String serviceId = "Service"; - String version = "v1"; - String expectedApiDoc = "This is some api doc"; - String updatedApiDoc = "This is some updated API Doc"; - - - ApiDocInfo apiDocInfo = new ApiDocInfo(null, expectedApiDoc, null); - - when(apiDocRetrievalService.retrieveApiDoc(serviceId, version)) - .thenReturn(apiDocInfo); - when(transformApiDocService.transformApiDoc(serviceId, apiDocInfo)) - .thenReturn(expectedApiDoc); - - String apiDoc = cachedApiDocService.getApiDocForService(serviceId, version); - - assertNotNull(apiDoc); - assertEquals(expectedApiDoc, apiDoc); - - cachedApiDocService.updateApiDocForService(serviceId, version, updatedApiDoc); - - apiDocInfo = new ApiDocInfo(null, updatedApiDoc, null); - - when(apiDocRetrievalService.retrieveApiDoc(serviceId, version)) - .thenReturn(apiDocInfo); - when(transformApiDocService.transformApiDoc(serviceId, apiDocInfo)) - .thenReturn(updatedApiDoc); - - apiDoc = cachedApiDocService.getApiDocForService(serviceId, version); - - assertNotNull(apiDoc); - assertEquals(updatedApiDoc, apiDoc); - } - } - - @Test - void givenInvalidApiDoc_whenRetrieving_thenThrowException() { - String serviceId = "Service"; - String version = "v1"; - - Exception exception = assertThrows(ApiDocNotFoundException.class, - () -> cachedApiDocService.getApiDocForService(serviceId, version), - "Expected exception is not ApiDocNotFoundException"); - assertEquals("No API Documentation was retrieved for the service Service. Root cause: ", exception.getMessage()); - } - - @Nested - class GivenValidApiVersions_thenReturnThem { - @Test - void whenRetrieving() { - String serviceId = "service"; - List expectedVersions = Arrays.asList("1.0.0", "2.0.0"); - - when(apiDocRetrievalService.retrieveApiVersions(serviceId)).thenReturn(expectedVersions); - - List versions = cachedApiDocService.getApiVersionsForService(serviceId); - assertEquals(expectedVersions, versions); - } - - @Test - void whenUpdating() { - String serviceId = "service"; - List initialVersions = Arrays.asList("1.0.0", "2.0.0"); - List updatedVersions = Arrays.asList("1.0.0", "2.0.0", "3.0.0"); - - when(apiDocRetrievalService.retrieveApiVersions(serviceId)).thenReturn(initialVersions); - - List versions = cachedApiDocService.getApiVersionsForService(serviceId); - assertEquals(initialVersions, versions); - - cachedApiDocService.updateApiVersionsForService(serviceId, updatedVersions); - - when(apiDocRetrievalService.retrieveApiVersions(serviceId)).thenReturn(updatedVersions); - - versions = cachedApiDocService.getApiVersionsForService(serviceId); - assertEquals(updatedVersions, versions); - } - } - - @Test - void givenInvalidApiVersion_whenRetrieving_thenThrowException() { - String serviceId = "service"; - - when(apiDocRetrievalService.retrieveApiVersions(serviceId)).thenReturn(Collections.emptyList()); - - Exception exception = assertThrows(ApiVersionNotFoundException.class, - () -> cachedApiDocService.getApiVersionsForService(serviceId), "Exception is not ApiVersionsNotFoundException"); - assertEquals("No API versions were retrieved for the service service.", exception.getMessage()); - } - - @Nested - class GivenValidApiDocs { - @Test - void whenRetrievingDefault_thenReturnLatestApi() { - String serviceId = "service"; - String expectedApiDoc = "This is some api doc"; - ApiDocInfo apiDocInfo = new ApiDocInfo(null, expectedApiDoc, null); - - when(apiDocRetrievalService.retrieveDefaultApiDoc(serviceId)).thenReturn(apiDocInfo); - when(transformApiDocService.transformApiDoc(serviceId, apiDocInfo)).thenReturn(expectedApiDoc); - - String apiDoc = cachedApiDocService.getDefaultApiDocForService(serviceId); - assertEquals(expectedApiDoc, apiDoc); - } - - @Test - void whenUpdatingDefault_thenRetrieveDefault() { - String serviceId = "service"; - String initialApiDoc = "This is some api doc"; - String updatedApiDoc = "This is some updated api doc"; - ApiDocInfo initialApiDocInfo = new ApiDocInfo(null, initialApiDoc, null); - ApiDocInfo updatedApiDocInfo = new ApiDocInfo(null, updatedApiDoc, null); - - when(apiDocRetrievalService.retrieveDefaultApiDoc(serviceId)).thenReturn(initialApiDocInfo); - when(transformApiDocService.transformApiDoc(serviceId, initialApiDocInfo)).thenReturn(initialApiDoc); - - String apiDoc = cachedApiDocService.getDefaultApiDocForService(serviceId); - assertEquals(initialApiDoc, apiDoc); - - cachedApiDocService.updateDefaultApiDocForService(serviceId, updatedApiDoc); - - when(apiDocRetrievalService.retrieveDefaultApiDoc(serviceId)).thenReturn(updatedApiDocInfo); - when(transformApiDocService.transformApiDoc(serviceId, updatedApiDocInfo)).thenReturn(updatedApiDoc); - - apiDoc = cachedApiDocService.getDefaultApiDocForService(serviceId); - assertEquals(updatedApiDoc, apiDoc); - } - - @Test - void whenRetrievingDefaultNocContent_thenException() { - String serviceId = "service"; - ApiDocInfo apiDocInfo = new ApiDocInfo(null, null, null); - - when(apiDocRetrievalService.retrieveDefaultApiDoc(serviceId)).thenReturn(apiDocInfo); - - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getDefaultApiDocForService(serviceId)); - verifyNoInteractions(transformApiDocService); - } - } - - @Test - void givenInvalidApiDoc_whenRetrievingDefault_thenThrowException() { - String serviceId = "service"; - - Exception exception = assertThrows(ApiDocNotFoundException.class, - () -> cachedApiDocService.getDefaultApiDocForService(serviceId), - "Expected exception is not ApiDocNotFoundException"); - assertEquals("No API Documentation was retrieved for the service service. Root cause: ", exception.getMessage()); - } - - @Nested - class GivenDefaultApiVersion_thenReturnIt { - @Test - void whenRetrieveDefaultVersion() { - String serviceId = "service"; - String expected = "v1"; - when(apiDocRetrievalService.retrieveDefaultApiVersion(serviceId)).thenReturn(expected); - - String actual = cachedApiDocService.getDefaultApiVersionForService(serviceId); - assertEquals(expected, actual); - } - - @Test - void whenUpdateDefaultVersion() { - String serviceId = "service"; - String initialVersion = "v1"; - String updatedVersion = "v2"; - - when(apiDocRetrievalService.retrieveDefaultApiVersion(serviceId)).thenReturn(initialVersion); - String version = cachedApiDocService.getDefaultApiVersionForService(serviceId); - assertEquals(initialVersion, version); - - cachedApiDocService.updateDefaultApiVersionForService(serviceId, updatedVersion); - - when(apiDocRetrievalService.retrieveDefaultApiVersion(serviceId)).thenReturn(null); - version = cachedApiDocService.getDefaultApiVersionForService(serviceId); - assertEquals(updatedVersion, version); - } - } - - @Test - void givenErrorRetrievingDefaultApiVersion_whenGetDefaultVersion_thenThrowException() { - String serviceId = "service"; - - when(apiDocRetrievalService.retrieveDefaultApiVersion(serviceId)).thenThrow(new RuntimeException("error")); - - Exception exception = assertThrows(ApiVersionNotFoundException.class, - () -> cachedApiDocService.getDefaultApiVersionForService(serviceId)); - assertEquals("Error trying to find default API version", exception.getMessage()); - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyServiceTest.java deleted file mode 100644 index 20839e7376..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyServiceTest.java +++ /dev/null @@ -1,562 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.cached; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import org.apache.commons.lang3.tuple.Pair; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.test.util.ReflectionTestUtils; -import org.zowe.apiml.apicatalog.model.APIContainer; -import org.zowe.apiml.apicatalog.model.APIService; -import org.zowe.apiml.apicatalog.model.CustomStyleConfig; -import org.zowe.apiml.apicatalog.util.ServicesBuilder; -import org.zowe.apiml.product.constants.CoreService; -import org.zowe.apiml.product.gateway.GatewayClient; -import org.zowe.apiml.product.instance.ServiceAddress; -import org.zowe.apiml.product.routing.RoutedServices; -import org.zowe.apiml.product.routing.ServiceType; -import org.zowe.apiml.product.routing.transform.TransformService; -import org.zowe.apiml.product.routing.transform.URLTransformationException; - -import java.util.*; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; -import static org.zowe.apiml.constants.EurekaMetadataDefinition.*; - -/** - * Container is used the same way as Tile - */ -@SuppressWarnings({ "squid:S2925" }) // replace with proper wait test library -class CachedProductFamilyServiceTest { - - private static final String SERVICE_ID = "service_test_id"; - - private final Integer cacheRefreshUpdateThresholdInMillis = 2000; - - private CachedProductFamilyService underTest; - - private TransformService transformService; - private CachedServicesService cachedServicesService; - private ServicesBuilder servicesBuilder; - private CustomStyleConfig customStyleConfig; - @BeforeEach - void setUp() { - cachedServicesService = mock(CachedServicesService.class); - transformService = mock(TransformService.class); - customStyleConfig = mock(CustomStyleConfig.class); - - underTest = new CachedProductFamilyService( - cachedServicesService, - transformService, - cacheRefreshUpdateThresholdInMillis, customStyleConfig); - - servicesBuilder = new ServicesBuilder(underTest); - } - - @Nested - class WhenCallSaveContainerFromInstance { - @Nested - class AndWhenCachedInstance { - private InstanceInfo instance; - private InstanceInfo updatedInstance; - - @BeforeEach - void prepareInstances() throws URLTransformationException { - Map metadata = new HashMap<>(); - metadata.put(CATALOG_ID, "demoapp"); - metadata.put(CATALOG_TITLE, "Title"); - metadata.put(CATALOG_DESCRIPTION, "Description"); - metadata.put(CATALOG_VERSION, "1.0.0"); - metadata.put(SERVICE_TITLE, "sTitle"); - metadata.put(SERVICE_DESCRIPTION, "sDescription"); - instance = servicesBuilder.createInstance("service1", InstanceInfo.InstanceStatus.UP, metadata); - - Map updatedMetadata = new HashMap<>(); - updatedMetadata.put(CATALOG_ID, "demoapp"); - updatedMetadata.put(CATALOG_TITLE, "Title2"); - updatedMetadata.put(CATALOG_DESCRIPTION, "Description2"); - updatedMetadata.put(CATALOG_VERSION, "2.0.0"); - updatedMetadata.put(SERVICE_TITLE, "sTitle2"); - updatedMetadata.put(SERVICE_DESCRIPTION, "sDescription2"); - updatedInstance = servicesBuilder.createInstance("service1", InstanceInfo.InstanceStatus.UP, - updatedMetadata); - - when(transformService.transformURL( - any(ServiceType.class), any(String.class), any(String.class), any(RoutedServices.class), eq(false))) - .thenReturn(instance.getHomePageUrl()); - } - - @Nested - class GivenInstanceIsNotInCache { - @Test - void createNew() { - APIContainer originalContainer = underTest.saveContainerFromInstance("demoapp", instance); - - List lsContainer = underTest.getRecentlyUpdatedContainers(); - assertThatContainerIsCorrect(lsContainer, originalContainer, instance); - } - - @Nested - class WhenCheckingIfAttlsEnabled { - @Test - void givenNoAttls_thenGetInstanceHomePageUrl() throws URLTransformationException { - when(transformService.transformURL( - any(ServiceType.class), any(String.class), any(String.class), any(RoutedServices.class), eq(true))) - .thenReturn(instance.getHomePageUrl()); - APIContainer originalContainer = underTest.saveContainerFromInstance("demoapp", instance); - - List lsContainer = underTest.getRecentlyUpdatedContainers(); - assertThatContainerIsCorrect(lsContainer, originalContainer, instance); - } - } - @Test - void givenAttlsEnabled_thenGetInstanceHomePageUrl() throws URLTransformationException { - ReflectionTestUtils.setField(underTest, "isAttlsEnabled", true); - when(transformService.transformURL( - any(ServiceType.class), any(String.class), any(String.class), any(RoutedServices.class), eq(true))) - .thenReturn(instance.getHomePageUrl()); - APIContainer originalContainer = underTest.saveContainerFromInstance("demoapp", instance); - - List lsContainer = underTest.getRecentlyUpdatedContainers(); - assertThatContainerIsCorrect(lsContainer, originalContainer, instance); - } - } - - @Nested - class GivenInstanceIsInTheCache { - @Test - void update() throws InterruptedException { - APIContainer originalContainer = underTest.saveContainerFromInstance("demoapp", instance); - Calendar createdTimestamp = originalContainer.getLastUpdatedTimestamp(); - - Thread.sleep(100); - - APIContainer updatedContainer = underTest.saveContainerFromInstance("demoapp", updatedInstance); - Calendar updatedTimestamp = updatedContainer.getLastUpdatedTimestamp(); - - assertThat(updatedTimestamp, is(not(createdTimestamp))); - - List lsContainer = underTest.getRecentlyUpdatedContainers(); - assertThatContainerIsCorrect(lsContainer, updatedContainer, updatedInstance); - } - } - } - - private void assertThatContainerIsCorrect(List lsContainer, APIContainer containerToVerify, - InstanceInfo instance) { - assertThat(lsContainer.size(), is(1)); - assertThatMetadataAreCorrect(containerToVerify, instance.getMetadata()); - - Set apiServices = containerToVerify.getServices(); - assertThat(apiServices.size(), is(1)); - assertThatInstanceIsCorrect(apiServices.iterator().next(), instance); - } - - private void assertThatInstanceIsCorrect(APIService result, InstanceInfo correct) { - assertThat(result.getServiceId(), is(correct.getAppName().toLowerCase())); - assertThat(result.isSecured(), is(correct.isPortEnabled(InstanceInfo.PortType.SECURE))); - assertThat(result.getHomePageUrl(), is(correct.getHomePageUrl())); - } - - private void assertThatMetadataAreCorrect(APIContainer result, Map correct) { - assertThat(result.getId(), is(correct.get(CATALOG_ID))); - assertThat(result.getTitle(), is(correct.get(CATALOG_TITLE))); - assertThat(result.getDescription(), is(correct.get(CATALOG_DESCRIPTION))); - assertThat(result.getVersion(), is(correct.get(CATALOG_VERSION))); - } - } - - @Nested - class WhenRemovingService { - @Nested - class GivenServiceExists { - InstanceInfo removedInstance; - String removedInstanceFamilyId = "service1"; - - @BeforeEach - void prepareExistingInstance() { - Map metadata = new HashMap<>(); - metadata.put(CATALOG_ID, "demoapp"); - metadata.put(CATALOG_TITLE, "Title"); - metadata.put(CATALOG_DESCRIPTION, "Description"); - metadata.put(CATALOG_VERSION, "1.0.0"); - metadata.put(SERVICE_TITLE, "sTitle"); - metadata.put(SERVICE_DESCRIPTION, "sDescription"); - removedInstance = servicesBuilder.createInstance("service1", InstanceInfo.InstanceStatus.UP, metadata); - - underTest.saveContainerFromInstance(removedInstanceFamilyId, removedInstance); - } - - @Nested - class AndWholeTileIsRemoved { - @Test - void tileIsntPresentInCache() { - underTest.removeInstance(removedInstanceFamilyId, removedInstance); - - // The container should be removed - APIContainer receivedContainer = underTest.getContainerById(removedInstanceFamilyId); - assertThat(receivedContainer, is(nullValue())); - } - } - - @Nested - class AndTileRemains { - InstanceInfo remainingInstance; - - @Nested - class AndTheInstancesAreFromTheSameService { - @BeforeEach - void prepareTileWithMultipleInstancesOfSameService() { - underTest.saveContainerFromInstance(removedInstanceFamilyId, removedInstance); - - Map metadata = new HashMap<>(); - metadata.put(CATALOG_ID, "demoapp"); - metadata.put(CATALOG_TITLE, "Title"); - metadata.put(CATALOG_DESCRIPTION, "Description"); - metadata.put(CATALOG_VERSION, "1.0.0"); - metadata.put(SERVICE_TITLE, "sTitle"); - metadata.put(SERVICE_DESCRIPTION, "sDescription"); - remainingInstance = servicesBuilder.createInstance("service1", InstanceInfo.InstanceStatus.UP, metadata); - underTest.saveContainerFromInstance(removedInstanceFamilyId, remainingInstance); - } - - @Test - void tileIsInCacheButServiceIsntInTile() { - underTest.removeInstance(removedInstanceFamilyId, removedInstance); - - APIContainer result = underTest.getContainerById(removedInstanceFamilyId); - assertThat(result, is(not(nullValue()))); - - Set remainingServices = result.getServices(); - assertThat(remainingServices.size(), is(1)); - APIService remainingService = remainingServices.iterator().next(); - assertThat(remainingService.getInstances().size(), is(1)); - assertThat(remainingService.getInstances().get(0), is("service13")); - } - } - - @Nested - class AndTheInstancesAreFromDifferentService { - @BeforeEach - void prepareTileWithMultipleInstancesOfSameService() { - underTest.saveContainerFromInstance(removedInstanceFamilyId, removedInstance); - - Map metadata = new HashMap<>(); - metadata.put(CATALOG_ID, "demoapp"); - metadata.put(CATALOG_TITLE, "Title"); - metadata.put(CATALOG_DESCRIPTION, "Description"); - metadata.put(CATALOG_VERSION, "1.0.0"); - metadata.put(SERVICE_TITLE, "sTitle"); - metadata.put(SERVICE_DESCRIPTION, "sDescription"); - remainingInstance = servicesBuilder.createInstance("service2", InstanceInfo.InstanceStatus.UP, metadata); - underTest.saveContainerFromInstance(removedInstanceFamilyId, remainingInstance); - } - - @Test - void tileIsInCacheButServiceIsntInTile() { - underTest.removeInstance(removedInstanceFamilyId, removedInstance); - - APIContainer result = underTest.getContainerById(removedInstanceFamilyId); - assertThat(result, is(not(nullValue()))); - - Set remainingServices = result.getServices(); - assertThat(remainingServices.size(), is(1)); - - APIService remainingService = remainingServices.iterator().next(); - assertThat(remainingService.getServiceId(), is("service2")); - } - } - } - - @Nested - class GivenRemovingNonExistentService { - @Test - void nothingHappens() { - underTest.removeInstance("nonexistent", removedInstance); - - APIContainer result = underTest.getContainerById(removedInstanceFamilyId); - assertThat(result, is(not(nullValue()))); - } - - } - } - } - - @Nested - class WhenRetrievingUpdatedContainers { - @Nested - class GivenExistingTilesAreValid { - @Test - void returnExistingTiles() { - underTest.saveContainerFromInstance("demoapp", servicesBuilder.instance1); - underTest.saveContainerFromInstance("demoapp2", servicesBuilder.instance2); - - Collection containers = underTest.getRecentlyUpdatedContainers(); - assertEquals(2, containers.size()); - } - } - - @Nested - class GivenOneTileIsTooOld { - @Test - void returnOnlyOneService() throws InterruptedException { - // To speed up the test, create instance which consider even 5 milliseconds as - // old. - underTest = new CachedProductFamilyService( - null, - transformService, - 5, null); - // This is considered as old update. - underTest.saveContainerFromInstance("demoapp", servicesBuilder.instance1); - - Thread.sleep(10); - - underTest.saveContainerFromInstance("demoapp2", servicesBuilder.instance2); - - Collection containers = underTest.getRecentlyUpdatedContainers(); - assertEquals(1, containers.size()); - } - } - } - - @Nested - class WhenCalculatingContainerTotals { - @Nested - class AndStatusIsInvolved { - InstanceInfo instance1; - InstanceInfo instance2; - - @BeforeEach - void prepareApplications() { - instance1 = servicesBuilder.createInstance("service1", "demoapp"); - instance2 = servicesBuilder.createInstance("service2", "demoapp"); - Application application1 = new Application(); - application1.addInstance(instance1); - Application application2 = new Application(); - application2.addInstance(instance2); - - when(cachedServicesService.getService("service1")).thenReturn(application1); - when(cachedServicesService.getService("service2")).thenReturn(application2); - underTest = new CachedProductFamilyService( - cachedServicesService, - transformService, - cacheRefreshUpdateThresholdInMillis, null); - } - - @Nested - class GivenAllServicesAreUp { - @Test - void containerStatusIsUp() { - underTest.saveContainerFromInstance("demoapp", instance1); - underTest.addServiceToContainer("demoapp", instance2); - - APIContainer container = underTest.getContainerById("demoapp"); - assertNotNull(container); - - underTest.calculateContainerServiceValues(container); - assertThatContainerHasValidState(container, "UP", 2); - } - } - - @Nested - class GivenAllServicesAreDown { - @Test - void containerStatusIsDown() { - instance1.setStatus(InstanceInfo.InstanceStatus.DOWN); - instance2.setStatus(InstanceInfo.InstanceStatus.DOWN); - - underTest.saveContainerFromInstance("demoapp", instance1); - underTest.addServiceToContainer("demoapp", instance2); - - APIContainer container = underTest.getContainerById("demoapp"); - assertNotNull(container); - - underTest.calculateContainerServiceValues(container); - assertThatContainerHasValidState(container, "DOWN", 0); - } - } - - @Nested - class GivenSomeServicesAreDown { - @Test - void containerStatusIsWarning() { - instance2.setStatus(InstanceInfo.InstanceStatus.DOWN); - - underTest.saveContainerFromInstance("demoapp", instance1); - underTest.addServiceToContainer("demoapp", instance2); - - APIContainer container = underTest.getContainerById("demoapp"); - assertNotNull(container); - - underTest.calculateContainerServiceValues(container); - assertThatContainerHasValidState(container, "WARNING", 1); - } - } - } - - @Nested - class GivenMultipleApiIds { - @Test - void groupThem() { - Application application = servicesBuilder.createApp( - SERVICE_ID, - servicesBuilder.createInstance(SERVICE_ID, "catalog1", - Pair.of("apiml.apiInfo.api-v1.apiId", "api1"), - Pair.of("apiml.apiInfo.api-v1.version", "1.0.0"), - Pair.of("apiml.apiInfo.api-v2.apiId", "api2"), - Pair.of("apiml.apiInfo.api-v2.version", "2"), - Pair.of("apiml.apiInfo.api-v3.apiId", "api3"))); - doReturn(application).when(cachedServicesService).getService(SERVICE_ID); - APIContainer apiContainer = underTest.getContainerById(SERVICE_ID); - underTest.calculateContainerServiceValues(apiContainer); - - APIService apiService = apiContainer.getServices().iterator().next(); - assertNotNull(apiService.getApis()); - assertEquals(3, apiService.getApis().size()); - assertNotNull(apiService.getApis().get("api1 v1.0.0")); - assertNotNull(apiService.getApis().get("api2 v2")); - assertNotNull(apiService.getApis().get("default")); - } - } - - @Nested - class AndSsoInvolved { - - @Nested - class GivenSsoAndNonSsoInstances { - @Test - void returnNonSso() { - Application application = servicesBuilder.createApp( - SERVICE_ID, - servicesBuilder.createInstance(SERVICE_ID, "catalog1", - Pair.of(AUTHENTICATION_SCHEME, "bypass")), - servicesBuilder.createInstance(SERVICE_ID, "catalog2", - Pair.of(AUTHENTICATION_SCHEME, "zoweJwt"))); - doReturn(application).when(cachedServicesService).getService(SERVICE_ID); - - APIContainer apiContainer = underTest.getContainerById(SERVICE_ID); - underTest.calculateContainerServiceValues(apiContainer); - - assertFalse(apiContainer.isSso()); - for (APIService apiService : apiContainer.getServices()) { - assertFalse(apiService.isSsoAllInstances()); - } - } - } - - @Nested - class GivenAllInstancesAreSso { - @Test - void returnSso() { - InstanceInfo instanceInfo = servicesBuilder.createInstance(SERVICE_ID, "catalog1", - Pair.of(AUTHENTICATION_SCHEME, "zoweJwt")); - doReturn(servicesBuilder.createApp(SERVICE_ID, instanceInfo)).when(cachedServicesService) - .getService(SERVICE_ID); - APIContainer apiContainer = underTest.getContainerById(SERVICE_ID); - underTest.calculateContainerServiceValues(apiContainer); - - assertTrue(apiContainer.isSso()); - for (APIService apiService : apiContainer.getServices()) { - assertTrue(apiService.isSso()); - assertTrue(apiService.isSsoAllInstances()); - } - } - } - } - - @Nested - class GivenHideServiceInfo { - @Test - void thenSetToApiService() { - InstanceInfo instanceInfo = servicesBuilder.createInstance(SERVICE_ID, "catalog1", - Pair.of(AUTHENTICATION_SCHEME, "zoweJwt")); - doReturn(servicesBuilder.createApp(SERVICE_ID, instanceInfo)).when(cachedServicesService) - .getService(SERVICE_ID); - APIContainer apiContainer = underTest.getContainerById(SERVICE_ID); - ReflectionTestUtils.setField(underTest, "hideServiceInfo", true); - underTest.calculateContainerServiceValues(apiContainer); - assertTrue(apiContainer.isHideServiceInfo()); - } - } - - void assertThatContainerHasValidState(APIContainer container, String state, int activeServices) { - assertNotNull(container); - - underTest.calculateContainerServiceValues(container); - assertEquals(state, container.getStatus()); - assertEquals(2, container.getTotalServices().intValue()); - assertEquals(activeServices, container.getActiveServices().intValue()); - } - } - - @Nested - class WhenGettingContainerWithoutServiceInstances { - @Test - void noServicesAreWithinTheContainer() { - assertThat(underTest.getContainerCount(), is(0)); - assertThat(underTest.getAllContainers().size(), is(0)); - } - } - - @Nested - class MultiTenancy { - - private CachedProductFamilyService cachedProductFamilyService; - - @BeforeEach - void init() { - cachedProductFamilyService = new CachedProductFamilyService( - new CachedServicesService(), - new TransformService(new GatewayClient(ServiceAddress.builder().scheme("https").hostname("localhost").build())), - 30000, - new CustomStyleConfig() - ); - } - - private APIService createDto(RegistrationType registrationType) { - Map metadata = new HashMap<>(); - metadata.put(APIML_ID, "apimlId"); - metadata.put(SERVICE_TITLE, "title"); - metadata.put(REGISTRATION_TYPE, registrationType.getValue()); - var service = InstanceInfo.Builder.newBuilder() - .setAppName(CoreService.GATEWAY.getServiceId()) - .setMetadata(metadata) - .build(); - return cachedProductFamilyService.createAPIServiceFromInstance(service); - } - - @Test - void givenPrimaryInstance_whenCreateDto_thenDoNotUpdateTitle() { - var dto = createDto(RegistrationType.ADDITIONAL); - assertEquals("title (apimlId)", dto.getTitle()); - assertEquals("apimlid", dto.getServiceId()); - assertEquals("/apimlid", dto.getBasePath()); - } - - @Test - void givenPrimaryInstance_whenCreateDto_thenAddApimlIdIntoTitle() { - var dto = createDto(RegistrationType.PRIMARY); - assertEquals("title", dto.getTitle()); - assertEquals("gateway", dto.getServiceId()); - assertEquals("/", dto.getBasePath()); - } - - } - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesServiceTest.java deleted file mode 100644 index 78cc6e0b85..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedServicesServiceTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.cached; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import com.netflix.discovery.shared.Applications; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; - -class CachedServicesServiceTest { - - @Test - void testReturningNoServices() { - CachedServicesService cachedServicesService = new CachedServicesService(); - cachedServicesService.clearAllServices(); - Applications services = cachedServicesService.getAllCachedServices(); - Assertions.assertNull(services); - } - - @Test - void testReturningOneService() { - CachedServicesService cachedServicesService = new CachedServicesService(); - cachedServicesService.clearAllServices(); - Applications applications = cachedServicesService.getAllCachedServices(); - Assertions.assertNull(applications); - - InstanceInfo instance = getStandardInstance("service", InstanceInfo.InstanceStatus.DOWN, null); - Application application = new Application("service"); - application.addInstance(instance); - cachedServicesService.updateService("service", application); - - applications = cachedServicesService.getAllCachedServices(); - Assertions.assertNotNull(applications.getRegisteredApplications()); - } - - @Test - void testGetAService() { - CachedServicesService cachedServicesService = new CachedServicesService(); - cachedServicesService.clearAllServices(); - Applications applications = cachedServicesService.getAllCachedServices(); - Assertions.assertNull(applications); - - InstanceInfo instance = getStandardInstance("service", InstanceInfo.InstanceStatus.DOWN, null); - Application application = new Application("service"); - application.addInstance(instance); - cachedServicesService.updateService("service", application); - - Application service = cachedServicesService.getService("service"); - Assertions.assertNotNull(service); - Assertions.assertEquals("service", service.getName()); - } - - private InstanceInfo getStandardInstance(String serviceId, InstanceInfo.InstanceStatus status, - HashMap metadata) { - return new InstanceInfo(serviceId, serviceId.toUpperCase(), null, "192.168.0.1", null, - new InstanceInfo.PortWrapper(true, 9090), null, null, null, null, null, null, null, 0, null, "hostname", - status, null, null, null, null, metadata, null, null, null, null); - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/ApiServiceStatusServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/ApiServiceStatusServiceTest.java deleted file mode 100644 index 060ccf0bde..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/ApiServiceStatusServiceTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status; - -import org.junit.jupiter.api.Nested; -import org.zowe.apiml.apicatalog.model.APIContainer; -import org.zowe.apiml.apicatalog.model.APIService; -import org.zowe.apiml.apicatalog.services.cached.CachedApiDocService; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; -import org.zowe.apiml.apicatalog.services.status.event.model.ContainerStatusChangeEvent; -import org.zowe.apiml.apicatalog.services.status.event.model.STATUS_EVENT_TYPE; -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Applications; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.zowe.apiml.apicatalog.services.status.model.ApiDiffNotAvailableException; - -import java.util.*; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class ApiServiceStatusServiceTest { - - @Mock - private CachedProductFamilyService cachedProductFamilyService; - - @Mock - private CachedServicesService cachedServicesService; - - @Mock - private CachedApiDocService cachedApiDocService; - - @Mock - private OpenApiCompareProducer openApiCompareProducer; - - @InjectMocks - private APIServiceStatusService apiServiceStatusService; - - @Test - void givenCachedServices_whenGetCachedApplicationsState_thenReturnState() { - when(cachedServicesService.getAllCachedServices()).thenReturn(new Applications()); - ResponseEntity responseEntity = apiServiceStatusService.getCachedApplicationStateResponse(); - assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); - } - - @Test - void givenContainers_whenGetContainersStateEvents_thenReturnEvents() { - List containers = new ArrayList<>(createContainers()); - when(cachedProductFamilyService.getAllContainers()).thenReturn(containers); - doNothing().when(this.cachedProductFamilyService).calculateContainerServiceValues(any(APIContainer.class)); - - List expectedEvents = new ArrayList<>(); - containers.forEach(container -> { - STATUS_EVENT_TYPE eventType; - if (InstanceInfo.InstanceStatus.DOWN.name().equalsIgnoreCase(container.getStatus())) { - eventType = STATUS_EVENT_TYPE.CANCEL; - } else if (container.getCreatedTimestamp().equals(container.getLastUpdatedTimestamp())) { - eventType = STATUS_EVENT_TYPE.CREATED_CONTAINER; - } else { - eventType = STATUS_EVENT_TYPE.RENEW; - } - expectedEvents.add(new ContainerStatusChangeEvent( - container.getId(), - container.getTitle(), - container.getStatus(), - container.getTotalServices(), - container.getActiveServices(), - container.getServices(), - eventType) - ); - }); - - List actualEvents = apiServiceStatusService.getContainersStateAsEvents(); - assertEquals(expectedEvents.size(), actualEvents.size()); - for (int eventIndex = 0; eventIndex < expectedEvents.size(); eventIndex++) { - assertEquals(expectedEvents.get(eventIndex), actualEvents.get(eventIndex)); - } - } - - @Nested - class GivenCachedApiDoc { - @Test - void whenGetApiDocForService_thenSuccessfulResponse() { - String apiDoc = "this is the api doc"; - when(cachedApiDocService.getApiDocForService(anyString(), anyString())).thenReturn(apiDoc); - - ResponseEntity expectedResponse = new ResponseEntity<>(apiDoc, HttpStatus.OK); - ResponseEntity actualResponse = apiServiceStatusService.getServiceCachedApiDocInfo("aaa", "v1"); - - assertEquals(expectedResponse.getStatusCode(), actualResponse.getStatusCode()); - assertEquals(expectedResponse.getBody(), actualResponse.getBody()); - } - - @Test - void whenGetDefaultApiDocForService_thenSuccessfulResponse() { - String apiDoc = "this is the api doc"; - when(cachedApiDocService.getDefaultApiDocForService(anyString())).thenReturn(apiDoc); - - ResponseEntity expectedResponse = new ResponseEntity<>(apiDoc, HttpStatus.OK); - ResponseEntity actualResponse = apiServiceStatusService.getServiceCachedDefaultApiDocInfo("aaa"); - - assertEquals(expectedResponse.getStatusCode(), actualResponse.getStatusCode()); - assertEquals(expectedResponse.getBody(), actualResponse.getBody()); - } - - @Test - void whenGetApiDiff_thenReturnsApiDiff() { - String apiDoc = "{}"; - when(cachedApiDocService.getApiDocForService(anyString(), anyString())).thenReturn(apiDoc); - OpenApiCompareProducer actualProducer = new OpenApiCompareProducer(); - when(openApiCompareProducer.fromContents(anyString(), anyString())).thenReturn(actualProducer.fromContents(apiDoc, apiDoc)); - ResponseEntity actualResponse = apiServiceStatusService.getApiDiffInfo("service", "v1", "v2"); - assertNotNull(actualResponse.getBody()); - assertTrue(actualResponse.getBody().contains("Api Change Log")); - assertEquals(HttpStatus.OK, actualResponse.getStatusCode()); - } - } - - @Test - void givenInvalidAPIs_whenDifferenceIsProduced_thenTheProperExceptionIsRaised() { - when(openApiCompareProducer.fromContents(anyString(), anyString())).thenThrow(new RuntimeException()); - Exception ex = assertThrows(ApiDiffNotAvailableException.class, () -> - apiServiceStatusService.getApiDiffInfo("service", "v1", "v2") - ); - assertEquals("Error retrieving API diff for 'service' with versions 'v1' and 'v2'", ex.getMessage()); - } - - @Test - void givenUpdatedContainers_whenGetRecentlyUpdatedContainers_thenUpdatedContainersAreReturned() { - List containers = createContainers(); - when(cachedProductFamilyService.getRecentlyUpdatedContainers()).thenReturn(containers); - doNothing().when(this.cachedProductFamilyService).calculateContainerServiceValues(any(APIContainer.class)); - List events = apiServiceStatusService.getRecentlyUpdatedContainersAsEvents(); - assertNotNull(events); - assertEquals(2, events.size()); - assertEquals("API One", events.get(0).getTitle()); - assertEquals("API Two", events.get(1).getTitle()); - } - - private List createContainers() { - Set services = new HashSet<>(); - - APIService service = new APIService.Builder("service1") - .title("service-1") - .description("service-1") - .secured(false) - .baseUrl("base") - .homePageUrl("home") - .basePath("base") - .sso(false) - .apis(Collections.emptyMap()) - .build(); - services.add(service); - - service = new APIService.Builder("service2") - .title("service-2") - .description("service-2") - .secured(true) - .baseUrl("base") - .homePageUrl("home") - .basePath("base") - .sso(false) - .apis(Collections.emptyMap()) - .build(); - services.add(service); - - APIContainer container = new APIContainer("api-one", "API One", "This is API One", services); - container.setTotalServices(2); - container.setActiveServices(2); - container.setStatus(InstanceInfo.InstanceStatus.UP.name()); - APIContainer container1 = new APIContainer("api-two", "API Two", "This is API Two", services); - container1.setTotalServices(2); - container1.setActiveServices(2); - container.setStatus(InstanceInfo.InstanceStatus.DOWN.name()); - return Arrays.asList(container, container1); - } - - private InstanceInfo getStandardInstance(String serviceId, InstanceInfo.InstanceStatus status, - HashMap metadata, String ipAddress, int port) { - return new InstanceInfo(serviceId + ":" + port, serviceId.toUpperCase(), null, ipAddress, null, - new InstanceInfo.PortWrapper(true, port), null, null, null, null, null, null, null, 0, null, "hostname", - status, null, null, null, null, metadata, null, null, null, null); - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListenerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListenerTest.java deleted file mode 100644 index 31351a8e0b..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListenerTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.services.status.listeners; - -import org.junit.jupiter.api.Test; -import org.zowe.apiml.apicatalog.instance.InstanceInitializeService; - -import static org.mockito.Mockito.*; - -class GatewayLookupEventListenerTest { - - @Test - void retrieveAndRegisterAllInstancesWithCatalog_onlyOnce() throws Exception { - InstanceInitializeService instanceInitializeService = mock(InstanceInitializeService.class); - GatewayLookupEventListener gatewayLookupEventListener = new GatewayLookupEventListener(instanceInitializeService); - for (int i = 0; i < 10; i++) { - gatewayLookupEventListener.onApplicationEvent(); - } - verify(instanceInitializeService, times(1)).retrieveAndRegisterAllInstancesWithCatalog(); - } - - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMockTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMockTest.java deleted file mode 100644 index 44a6db6796..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ApiDocTransformForMockTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.zowe.apiml.product.instance.ServiceAddress; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@SpringBootTest(classes = ApiDocTransformForMock.class, properties = { - "apiml.catalog.standalone.enabled=true", - "server.hostname=host", - "server.port=123", - "service.schema=http" -}) -class ApiDocTransformForMockTest { - - @Autowired - private ServiceAddress gatewayConfigProperties; - - @Test - void gatewayConfigPropertiesForMock() { - assertNotNull(gatewayConfigProperties); - assertEquals("host:123/apicatalog/mock", gatewayConfigProperties.getHostname()); - assertEquals("http", gatewayConfigProperties.getScheme()); - } - -} \ No newline at end of file diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ExampleServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ExampleServiceTest.java deleted file mode 100644 index ed911c0e2f..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/ExampleServiceTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import io.swagger.parser.OpenAPIParser; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.responses.ApiResponse; -import io.swagger.v3.oas.models.responses.ApiResponses; -import io.swagger.v3.parser.core.models.SwaggerParseResult; - -import org.apache.commons.io.IOUtils; -import org.json.JSONArray; -import org.json.JSONException; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.mockito.Mockito; -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; - -import java.io.IOException; -import java.nio.charset.Charset; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -class ExampleServiceTest { - - @Nested - @TestInstance(TestInstance.Lifecycle.PER_CLASS) - class ExampleRepository { - - private final ExampleService exampleService = new ExampleService(); - - @BeforeAll - void test1() throws IOException { - String apiDoc = IOUtils.toString( - new ClassPathResource("standalone/services/apiDocs/service2_zowe v2.0.0.json").getURL(), - Charset.forName("UTF-8") - ); - - ((Map>) ReflectionTestUtils.getField(exampleService, "examples")).clear(); - exampleService.generateExamples("testService", apiDoc); - } - - @Test - void existingGetExample() throws JSONException { - ExampleService.Example example = exampleService.getExample("GET", "/testService/pet/findByStatus"); - assertEquals("GET", example.getMethod()); - assertEquals("/testService/pet/findByStatus", example.getPath()); - assertEquals(200, example.getResponseCode()); - JSONArray json = new JSONArray(example.getContent()); - assertEquals(1, json.length()); - assertEquals(10, json.getJSONObject(0).get("id")); - assertEquals("doggie", json.getJSONObject(0).get("name")); - } - - @Test - void generateExampleDoesNotThrowException() throws IOException { - - String apiDoc = IOUtils.toString( - new ClassPathResource("standalone/services/apiDocs/service2_zowe v2.0.0.json").getURL(), - Charset.forName("UTF-8") - ); - assertDoesNotThrow( () -> exampleService.generateExamples("testService", apiDoc)); - } - - @Test - void generateExampleThrowsExceptionWhenPathIsNull() { - - OpenAPIParser openAPIParser = Mockito.mock(OpenAPIParser.class); - SwaggerParseResult swaggerParseResult = Mockito.mock(SwaggerParseResult.class); - OpenAPI openAPI = Mockito.mock(OpenAPI.class); - - Mockito.when(openAPIParser.readContents(any(),any(),any())).thenReturn(swaggerParseResult); - Mockito.when(swaggerParseResult.getOpenAPI()).thenReturn(openAPI); - Mockito.when(openAPI.getPaths()).thenReturn(null); - assertDoesNotThrow( () -> exampleService.generateExamples("testService", null)); - } - @Test - void nonExistingGetExample() { - ExampleService.Example example = exampleService.getExample("GET", "/unkwnown"); - assertEquals(200, example.getResponseCode()); - assertEquals("{}", example.getContent()); - } - - @Test - void replay() throws IOException { - MockHttpServletResponse response = new MockHttpServletResponse(); - exampleService.replyExample(response, "GET", "/unknown"); - assertEquals(200, response.getStatus()); - assertEquals("{}", response.getContentAsString()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, response.getHeaders("Content-Type").get(0)); - } - - } - - @Nested - class Example { - - @Test - void antMatcherIsWorking() { - ExampleService.Example example = ExampleService.Example.builder() - .path("/some/path/{id}") - .build(); - assertTrue(example.isMatching("/some/path/1")); - assertTrue(example.isMatching("/some/path/abc")); - assertFalse(example.isMatching("/some/otherPath/2")); - } - - } - - @Nested - class InternalMethods { - - @Nested - class GetResponseCode { - - @Test - void whenDefaultThenReturn200() { - assertEquals(200, ExampleService.getResponseCode("default")); - } - - @Test - void whenIsNumericThenParse() { - assertEquals(213, ExampleService.getResponseCode("213")); - } - - @Test - void whenIsNotNumericThenReturn0() { - assertEquals(0, ExampleService.getResponseCode("unknown")); - } - - } - - @Nested - class GetFirstApiRespones { - - private Operation createOperation(String...responseCodes) { - Map responses = new LinkedHashMap<>(); - for (String responseCode : responseCodes) { - responses.put(responseCode, mock(ApiResponse.class)); - } - - ApiResponses apiResponses = mock(ApiResponses.class); - Operation operation = mock(Operation.class); - - doReturn(responses.entrySet()).when(apiResponses).entrySet(); - doReturn(apiResponses).when(operation).getResponses(); - - return operation; - } - - @Test - void whenContainsSuccessResponseCode() { - assertEquals("200", ExampleService.getFirstApiResponses(createOperation("303", "200", "400")).getKey()); - } - - @Test - void whenDoesntContainsSuccessResponseCode() { - assertEquals("400", ExampleService.getFirstApiResponses(createOperation("400", "401", "404")).getKey()); - } - - @Test - void whenIsEmpty() { - assertNull(ExampleService.getFirstApiResponses(createOperation())); - } - - } - - } - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalServiceTest.java deleted file mode 100644 index a91192a5f5..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneAPIDocRetrievalServiceTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class StandaloneAPIDocRetrievalServiceTest { - - private final StandaloneAPIDocRetrievalService standaloneAPIDocRetrievalService = new StandaloneAPIDocRetrievalService(); - - @Nested - class ThenNothing { - - @Test - void whenRetrieveApiDoc() { - assertNull(standaloneAPIDocRetrievalService.retrieveApiDoc("service", null)); - } - - @Test - void whenRetrieveDefaultApiDoc() { - assertNull(standaloneAPIDocRetrievalService.retrieveDefaultApiDoc("service")); - } - - @Test - void whenRetrieveApiVersions() { - List apiVersions = standaloneAPIDocRetrievalService.retrieveApiVersions("service"); - assertTrue(apiVersions.isEmpty()); - } - - @Test - void whenRetrieveDefaultApiVersion() { - assertNull(standaloneAPIDocRetrievalService.retrieveDefaultApiVersion("service")); - } - } - - @Nested - class ThenItDoesntAcceptNullValues { - - @Test - void whenRetrieveApiDoc() { - assertThrows(NullPointerException.class, () -> standaloneAPIDocRetrievalService.retrieveApiDoc(null, null)); - } - - @Test - void whenRetrieveDefaultApiDoc() { - assertThrows(NullPointerException.class, () -> standaloneAPIDocRetrievalService.retrieveDefaultApiDoc(null)); - } - - @Test - void whenRetrieveApiVersions() { - assertThrows(NullPointerException.class, () -> standaloneAPIDocRetrievalService.retrieveApiVersions((String) null)); - } - - @Test - void whenRetrieveDefaultApiVersion() { - assertThrows(NullPointerException.class, () -> standaloneAPIDocRetrievalService.retrieveDefaultApiVersion((String )null)); - } - } -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java deleted file mode 100644 index 9fd0f06b09..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static org.mockito.Mockito.*; - -@ExtendWith(SpringExtension.class) -@ActiveProfiles("test") -class StandaloneInitializerTest { - - @Mock - SpringApplication application; - - @Mock - ConfigurableApplicationContext registry; - - @Autowired - private StandaloneLoaderService standaloneLoaderService; - - @Autowired - private ApplicationEventPublisher publisher; - - @Test - void testEventIsCalledOnlyOnce() { - ApplicationReadyEvent event = mock(ApplicationReadyEvent.class); - mockContext(event, true); - - publisher.publishEvent(event); - publisher.publishEvent(event); - - verify(standaloneLoaderService, times(1)).initializeCache(); - } - - @Test - void notCalledIfStandaloneDisabled() { - ApplicationReadyEvent event = mock(ApplicationReadyEvent.class); - mockContext(event, false); - - publisher.publishEvent(event); - - verifyNoInteractions(standaloneLoaderService); - } - - private ConfigurableApplicationContext mockContext(ApplicationReadyEvent event, boolean isStandalone) { - ConfigurableApplicationContext ctx = mock(ConfigurableApplicationContext.class); - ConfigurableEnvironment env = mock(ConfigurableEnvironment.class); - when(event.getApplicationContext()).thenReturn(ctx); - when(env.getProperty("apiml.catalog.standalone.enabled", "false")).thenReturn(String.valueOf(isStandalone)); - when(ctx.getEnvironment()).thenReturn(env); - return ctx; - } - - @Configuration - @Profile("test") - public static class TestConfiguration { - - @Bean - public StandaloneLoaderService standaloneLoaderService() { - return mock(StandaloneLoaderService.class); - } - - @Bean - public StandaloneInitializer getStandaloneInitializer(StandaloneLoaderService standaloneLoaderService) { - return new StandaloneInitializer(standaloneLoaderService); - } - - } - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderServiceTest.java deleted file mode 100644 index 1a12887559..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneLoaderServiceTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.standalone; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.io.IOUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.core.io.ClassPathResource; -import org.springframework.test.util.ReflectionTestUtils; -import org.zowe.apiml.apicatalog.instance.InstanceInitializeService; -import org.zowe.apiml.apicatalog.services.cached.CachedApiDocService; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; -import org.zowe.apiml.apicatalog.services.status.model.ApiVersionNotFoundException; -import org.zowe.apiml.apicatalog.swagger.api.AbstractApiDocService; -import org.zowe.apiml.product.gateway.GatewayClient; -import org.zowe.apiml.product.instance.ServiceAddress; -import org.zowe.apiml.product.routing.transform.TransformService; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -class StandaloneLoaderServiceTest { - - private StandaloneLoaderService standaloneLoaderService; - - @Nested - class WhenInitializeCache { - - private CachedServicesService cachedServicesService; - private CachedApiDocService cachedApiDocService; - private String testData; - - @BeforeEach - void init() throws IOException { - GatewayClient gatewayClient = new GatewayClient(null); - TransformService transformService = new TransformService(gatewayClient); - cachedServicesService = new CachedServicesService(); - CachedProductFamilyService cachedProductFamilyService = - new CachedProductFamilyService(cachedServicesService, transformService, 1000, null); - InstanceInitializeService instanceInitializeService = new InstanceInitializeService(cachedProductFamilyService, cachedServicesService, null, null); - StandaloneAPIDocRetrievalService standaloneAPIDocRetrievalService = new StandaloneAPIDocRetrievalService(); - cachedApiDocService = new CachedApiDocService(standaloneAPIDocRetrievalService, null); - standaloneLoaderService = new StandaloneLoaderService( - new ObjectMapper(), - instanceInitializeService, - cachedApiDocService, - standaloneAPIDocRetrievalService, - s -> { - AbstractApiDocService abstractApiDocService = mock(AbstractApiDocService.class); - doReturn(s).when(abstractApiDocService).transformApiDoc(any(), any()); - return abstractApiDocService; - }, - ServiceAddress.builder().scheme("https").hostname("localhost:10014").build(), - mock(ExampleService.class) - ); - - testData = setTestData("standalone/services"); - cachedApiDocService.resetCache(); - } - - @Test - void thenContainerCacheIsPopulated() { - assertNull(cachedServicesService.getAllCachedServices()); - - standaloneLoaderService.initializeCache(); - - assertEquals(3, cachedServicesService.getAllCachedServices().size()); - assertEquals("SERVICE1-INSTANCE1", cachedServicesService.getAllCachedServices().getRegisteredApplications("service1-instance1").getName()); - assertEquals("SERVICE1-INSTANCE2", cachedServicesService.getAllCachedServices().getRegisteredApplications("service1-instance2").getName()); - assertEquals("SERVICE2", cachedServicesService.getAllCachedServices().getRegisteredApplications("service2").getName()); - } - - @Test - void thenApiDocCacheIsPopulated() throws IOException { - String expectedService1Instance1apiDoc = readTestData("/apiDocs/service1-instance1_zowe v1.0.0_default.json"); - String expectedService1Instance2apiDoc = readTestData("/apiDocs/service1-instance2_zowe v1.0.0_default.json"); - String expectedService2apiDoc1 = readTestData("/apiDocs/service2_zowe v1.0.0_default.json"); - String expectedService2apiDoc2 = readTestData("/apiDocs/service2_zowe v2.0.0.json"); - - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getDefaultApiDocForService("service1-instance1")); - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getDefaultApiDocForService("service1-instance2")); - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getDefaultApiDocForService("service2")); - - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getApiDocForService("service1-instance1", "zowe v1.0.0")); - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getApiDocForService("service1-instance2", "zowe v1.0.0")); - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getApiDocForService("service2", "zowe v1.0.0")); - assertThrows(ApiDocNotFoundException.class, () -> cachedApiDocService.getApiDocForService("service2", "zowe v2.0.0")); - - standaloneLoaderService.initializeCache(); - - assertEquals(expectedService1Instance1apiDoc, cachedApiDocService.getDefaultApiDocForService("service1-instance1")); - assertEquals(expectedService1Instance2apiDoc, cachedApiDocService.getDefaultApiDocForService("service1-instance2")); - assertEquals(expectedService2apiDoc1, cachedApiDocService.getDefaultApiDocForService("service2")); - - assertEquals(expectedService1Instance1apiDoc, cachedApiDocService.getApiDocForService("service1-instance1", "zowe v1.0.0")); - assertEquals(expectedService1Instance2apiDoc, cachedApiDocService.getApiDocForService("service1-instance2", "zowe v1.0.0")); - assertEquals(expectedService2apiDoc1, cachedApiDocService.getApiDocForService("service2", "zowe v1.0.0")); - assertEquals(expectedService2apiDoc2, cachedApiDocService.getApiDocForService("service2", "zowe v2.0.0")); - } - - @Test - void thenVersionCacheIsPopulated() { - assertThrows(ApiVersionNotFoundException.class, () -> cachedApiDocService.getDefaultApiVersionForService("service1-instance1")); - assertThrows(ApiVersionNotFoundException.class, () -> cachedApiDocService.getDefaultApiVersionForService("service1-instance2")); - assertThrows(ApiVersionNotFoundException.class, () -> cachedApiDocService.getDefaultApiVersionForService("service2")); - - assertThrows(ApiVersionNotFoundException.class, () -> cachedApiDocService.getApiVersionsForService("service1-instance1")); - assertThrows(ApiVersionNotFoundException.class, () -> cachedApiDocService.getApiVersionsForService("service1-instance2")); - assertThrows(ApiVersionNotFoundException.class, () -> cachedApiDocService.getApiVersionsForService("service2")); - - standaloneLoaderService.initializeCache(); - - assertEquals("zowe v1.0.0", cachedApiDocService.getDefaultApiVersionForService("service1-instance1")); - assertEquals("zowe v1.0.0", cachedApiDocService.getDefaultApiVersionForService("service1-instance2")); - assertEquals("zowe v1.0.0", cachedApiDocService.getDefaultApiVersionForService("service2")); - - assertEquals(Collections.singletonList("zowe v1.0.0"), cachedApiDocService.getApiVersionsForService("service1-instance1")); - assertEquals(Collections.singletonList("zowe v1.0.0"), cachedApiDocService.getApiVersionsForService("service1-instance2")); - assertEquals(Arrays.asList("zowe v1.0.0", "zowe v2.0.0"), cachedApiDocService.getApiVersionsForService("service2")); - } - - private String readTestData(String fileName) throws IOException { - return IOUtils.toString(Files.newInputStream(new File(testData + fileName).toPath()), StandardCharsets.UTF_8); - } - - } - - @Nested - class thenNoException { - - @BeforeEach - void init() { - standaloneLoaderService = - new StandaloneLoaderService(new ObjectMapper(), null, null, null, null, null, null); - } - - @Test - void givenDefinitionDirectoryNotExist_whenInitializeCache() { - ReflectionTestUtils.setField(standaloneLoaderService, "servicesDirectory", "not-exists"); - - assertDoesNotThrow(standaloneLoaderService::initializeCache); - } - - @Test - void givenApiDocInvalidName_whenInitializeCache() throws IOException { - setTestData("standalone/invalid-apiDocName"); - - assertDoesNotThrow(standaloneLoaderService::initializeCache); - } - - @Test - void givenInvalidAppJson_whenInitializeCache() throws IOException { - setTestData("standalone/invalid-app"); - - assertDoesNotThrow(standaloneLoaderService::initializeCache); - } - - } - - private String setTestData(String data) throws IOException { - String testData = new ClassPathResource(data).getFile().getAbsolutePath(); - ReflectionTestUtils.setField(standaloneLoaderService, "servicesDirectory", testData); - - return testData; - } - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java index 9350f833e2..69587c5b21 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java @@ -21,7 +21,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.client.RestClientException; -import org.zowe.apiml.apicatalog.services.status.model.ServiceNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ServiceNotFoundException; import static org.hamcrest.Matchers.hasSize; import static org.mockito.Mockito.reset; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/ContainerServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/ContainerServiceTest.java new file mode 100644 index 0000000000..463e8b5313 --- /dev/null +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/ContainerServiceTest.java @@ -0,0 +1,269 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.apicatalog.swagger; + +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import com.netflix.discovery.shared.Application; +import com.netflix.discovery.shared.Applications; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.zowe.apiml.apicatalog.model.APIContainer; +import org.zowe.apiml.apicatalog.model.APIService; +import org.zowe.apiml.apicatalog.model.CustomStyleConfig; +import org.zowe.apiml.apicatalog.util.ServicesBuilder; +import org.zowe.apiml.product.constants.CoreService; +import org.zowe.apiml.product.gateway.GatewayClient; +import org.zowe.apiml.product.instance.ServiceAddress; +import org.zowe.apiml.product.routing.transform.TransformService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.*; + +@ExtendWith(MockitoExtension.class) +class ContainerServiceTest { + + @Nested + class WhenCalculatingContainerTotals { + + private static final String SERVICE_ID = "service_test_id"; + + private EurekaClient eurekaClient = mock(EurekaClient.class); + private TransformService transformService = new TransformService(new GatewayClient(ServiceAddress.builder().scheme("https").hostname("localhost").build())); + private CustomStyleConfig customStyleConfig = new CustomStyleConfig(); + + private InstanceInfo instance1; + private InstanceInfo instance2; + private ContainerService containerService; + + @BeforeEach + void prepareApplications() { + instance1 = ServicesBuilder.createInstance("service1", "demoapp"); + instance2 = ServicesBuilder.createInstance("service2", "demoapp"); + Application application1 = new Application("service1", Collections.singletonList(instance1)); + Application application2 = new Application("service2", Collections.singletonList(instance2)); + + when(eurekaClient.getApplication("service1")).thenReturn(application1); + when(eurekaClient.getApplication("service2")).thenReturn(application2); + when(eurekaClient.getApplications()).thenReturn(new Applications("hash", 0L, Arrays.asList(application1, application2))); + containerService = new ContainerService( + eurekaClient, + transformService, + customStyleConfig + ); + } + + @Nested + class AndStatusIsInvolved { + + void assertThatContainerHasValidState(APIContainer container, String state, int activeServices) { + assertNotNull(container); + + assertEquals(state, container.getStatus()); + assertEquals(2, container.getTotalServices().intValue()); + assertEquals(activeServices, container.getActiveServices().intValue()); + } + + @Nested + class GivenAllServicesAreUp { + + @Test + void containerStatusIsUp() { + APIContainer container = containerService.getContainerById("demoapp"); + assertNotNull(container); + + assertThatContainerHasValidState(container, "UP", 2); + } + + } + + @Nested + class GivenAllServicesAreDown { + + @Test + void containerStatusIsDown() { + instance1.setStatus(InstanceInfo.InstanceStatus.DOWN); + instance2.setStatus(InstanceInfo.InstanceStatus.DOWN); + + APIContainer container = containerService.getContainerById("demoapp"); + assertNotNull(container); + + assertThatContainerHasValidState(container, "DOWN", 0); + } + + } + + @Nested + class GivenSomeServicesAreDown { + @Test + void containerStatusIsWarning() { + instance2.setStatus(InstanceInfo.InstanceStatus.DOWN); + + APIContainer container = containerService.getContainerById("demoapp"); + assertNotNull(container); + + assertThatContainerHasValidState(container, "WARNING", 1); + } + } + + } + + @Nested + class GivenMultipleApiIds { + + @Test + void groupThem() { + Application application = ServicesBuilder.createApp( + SERVICE_ID, + ServicesBuilder.createInstance(SERVICE_ID, SERVICE_ID, + Pair.of("apiml.apiInfo.api-v1.apiId", "api1"), + Pair.of("apiml.apiInfo.api-v1.version", "1.0.0"), + Pair.of("apiml.apiInfo.api-v2.apiId", "api2"), + Pair.of("apiml.apiInfo.api-v2.version", "2"), + Pair.of("apiml.apiInfo.api-v3.apiId", "api3"))); + Applications applications = new Applications("hash", 0L, Collections.singletonList(application)); + doReturn(application).when(eurekaClient).getApplication(SERVICE_ID); + doReturn(applications).when(eurekaClient).getApplications(); + APIContainer apiContainer = containerService.getContainerById(SERVICE_ID); + + APIService apiService = apiContainer.getServices().iterator().next(); + assertNotNull(apiService.getApis()); + assertEquals(3, apiService.getApis().size()); + assertNotNull(apiService.getApis().get("api1 v1.0.0")); + assertNotNull(apiService.getApis().get("api2 v2")); + assertNotNull(apiService.getApis().get("default")); + } + + } + + @Nested + class AndSsoInvolved { + + @Nested + class GivenSsoAndNonSsoInstances { + + @Test + void returnNonSso() { + Application application = ServicesBuilder.createApp( + SERVICE_ID, + ServicesBuilder.createInstance(SERVICE_ID, SERVICE_ID, Pair.of(AUTHENTICATION_SCHEME, "bypass")), + ServicesBuilder.createInstance(SERVICE_ID, SERVICE_ID, Pair.of(AUTHENTICATION_SCHEME, "zoweJwt")) + ); + Applications applications = new Applications("hash", 0L, Collections.singletonList(application)); + doReturn(application).when(eurekaClient).getApplication(SERVICE_ID); + doReturn(applications).when(eurekaClient).getApplications(); + + APIContainer apiContainer = containerService.getContainerById(SERVICE_ID); + + assertFalse(apiContainer.isSso()); + for (APIService apiService : apiContainer.getServices()) { + assertFalse(apiService.isSsoAllInstances()); + } + } + + } + + @Nested + class GivenAllInstancesAreSso { + + @Test + void returnSso() { + InstanceInfo instanceInfo = ServicesBuilder.createInstance(SERVICE_ID, SERVICE_ID, Pair.of(AUTHENTICATION_SCHEME, "zoweJwt")); + Application application = ServicesBuilder.createApp(SERVICE_ID, instanceInfo); + Applications applications = new Applications("hash", 0L, Collections.singletonList(application)); + doReturn(application).when(eurekaClient).getApplication(SERVICE_ID); + doReturn(applications).when(eurekaClient).getApplications(); + APIContainer apiContainer = containerService.getContainerById(SERVICE_ID); + + assertTrue(apiContainer.isSso()); + for (APIService apiService : apiContainer.getServices()) { + assertTrue(apiService.isSso()); + assertTrue(apiService.isSsoAllInstances()); + } + } + + } + } + + @Nested + class GivenHideServiceInfo { + + @Test + void thenSetToApiService() { + InstanceInfo instanceInfo = ServicesBuilder.createInstance(SERVICE_ID, SERVICE_ID, Pair.of(AUTHENTICATION_SCHEME, "zoweJwt")); + Application application = ServicesBuilder.createApp(SERVICE_ID, instanceInfo); + Applications applications = new Applications("hash", 0L, Collections.singletonList(application)); + doReturn(applications).when(eurekaClient).getApplications(); + ReflectionTestUtils.setField(containerService, "hideServiceInfo", true); + APIContainer apiContainer = containerService.getContainerById(SERVICE_ID); + assertTrue(apiContainer.isHideServiceInfo()); + } + + } + + } + + @Nested + class MultiTenancy { + + private ContainerService containerService; + + @BeforeEach + void init() { + containerService = new ContainerService( + mock(EurekaClient.class), + new TransformService(new GatewayClient(ServiceAddress.builder().scheme("https").hostname("localhost").build())), + new CustomStyleConfig() + ); + } + + private APIService createDto(RegistrationType registrationType) { + Map metadata = new HashMap<>(); + metadata.put(APIML_ID, "apimlId"); + metadata.put(SERVICE_TITLE, "title"); + metadata.put(REGISTRATION_TYPE, registrationType.getValue()); + var service = InstanceInfo.Builder.newBuilder() + .setAppName(CoreService.GATEWAY.getServiceId()) + .setMetadata(metadata) + .build(); + return containerService.createAPIServiceFromInstance(service); + } + + @Test + void givenPrimaryInstance_whenCreateDto_thenDoNotUpdateTitle() { + var dto = createDto(RegistrationType.ADDITIONAL); + assertEquals("title (apimlId)", dto.getTitle()); + assertEquals("apimlid", dto.getServiceId()); + assertEquals("/apimlid", dto.getBasePath()); + } + + @Test + void givenPrimaryInstance_whenCreateDto_thenAddApimlIdIntoTitle() { + var dto = createDto(RegistrationType.PRIMARY); + assertEquals("title", dto.getTitle()); + assertEquals("gateway", dto.getServiceId()); + assertEquals("/", dto.getBasePath()); + } + + } + +} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/LocalApiDocServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/LocalApiDocServiceTest.java similarity index 65% rename from api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/LocalApiDocServiceTest.java rename to api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/LocalApiDocServiceTest.java index 35ee59fe3b..7e22e35d4f 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/LocalApiDocServiceTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/LocalApiDocServiceTest.java @@ -8,9 +8,11 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.apicatalog.services.status; +package org.zowe.apiml.apicatalog.swagger; import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import com.netflix.discovery.shared.Application; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.core5.http.HttpStatus; @@ -23,10 +25,10 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.springframework.test.util.ReflectionTestUtils; -import org.zowe.apiml.apicatalog.instance.InstanceRetrievalService; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; -import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException; -import org.zowe.apiml.apicatalog.services.status.model.ApiVersionNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ApiDocNotFoundException; +import org.zowe.apiml.apicatalog.exceptions.ApiVersionNotFoundException; +import org.zowe.apiml.apicatalog.util.ServicesBuilder; +import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.message.log.ApimlLogger; import org.zowe.apiml.product.gateway.GatewayClient; import org.zowe.apiml.product.instance.ServiceAddress; @@ -37,6 +39,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -45,6 +48,7 @@ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class LocalApiDocServiceTest { + private static final String SERVICE_ID = "service"; private static final String SERVICE_HOST = "service"; private static final int SERVICE_PORT = 8080; @@ -59,9 +63,9 @@ class LocalApiDocServiceTest { private static final String SWAGGER_URL = "https://service:8080/service/api-doc"; @Mock - private InstanceRetrievalService instanceRetrievalService; + private EurekaClient eurekaClient; - private APIDocRetrievalService apiDocRetrievalService; + private ApiDocRetrievalServiceRest apiDocRetrievalServiceRest; @Mock private ApimlLogger apimlLogger; @@ -72,16 +76,32 @@ class LocalApiDocServiceTest { @Mock private CloseableHttpResponse response; + private AtomicReference lastApiInfo = new AtomicReference<>(); + @BeforeEach void setup() { - HttpClientMockHelper.mockExecuteWithResponse(httpClient, response); + lastApiInfo.set(null); - apiDocRetrievalService = new APIDocRetrievalService( + HttpClientMockHelper.mockExecuteWithResponse(httpClient, response); + apiDocRetrievalServiceRest = new ApiDocRetrievalServiceRest( httpClient, - instanceRetrievalService, - new GatewayClient(getProperties())); + eurekaClient, + new GatewayClient(getProperties()), + new TransformApiDocService(null) { + @Override + public String transformApiDoc(String serviceId, ApiDocInfo apiDocInfo) { + return apiDocInfo.getApiDocContent(); + } + } + ) { + @Override + String buildApiDocInfo(String serviceId, ApiInfo apiInfo, InstanceInfo instanceInfo) { + lastApiInfo.set(apiInfo); + return super.buildApiDocInfo(serviceId, apiInfo, instanceInfo); + } + }; - ReflectionTestUtils.setField(apiDocRetrievalService, "apimlLogger", apimlLogger); + ReflectionTestUtils.setField(apiDocRetrievalServiceRest, "apimlLogger", apimlLogger); } @Nested @@ -90,30 +110,28 @@ class WhenGetApiDoc { void givenValidApiInfo_thenReturnApiDoc() { String responseBody = "api-doc body"; - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getStandardMetadata(), true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); - assertEquals(API_ID, actualResponse.getApiInfo().getApiId()); - assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl()); - assertEquals(SERVICE_VERSION, actualResponse.getApiInfo().getVersion()); - assertEquals(SWAGGER_URL, actualResponse.getApiInfo().getSwaggerUrl()); + assertNotNull(lastApiInfo.get()); + assertEquals(API_ID, lastApiInfo.get().getApiId()); + assertEquals(GATEWAY_URL, lastApiInfo.get().getGatewayUrl()); + assertEquals(SERVICE_VERSION, lastApiInfo.get().getVersion()); + assertEquals(SWAGGER_URL, lastApiInfo.get().getSwaggerUrl()); - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); - assertEquals(responseBody, actualResponse.getApiDocContent()); - - assertEquals("[api -> api=RoutedService(subServiceId=api-v1, gatewayUrl=api, serviceUrl=/)]", actualResponse.getRoutes().toString()); + assertNotNull(actualApiDoc); + assertEquals(responseBody, actualApiDoc); } @Nested class ThenThrowException { @Test void givenNoApiDocFoundForService() { - Exception exception = assertThrows(ApiDocNotFoundException.class, () -> apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V)); + Exception exception = assertThrows(ApiDocNotFoundException.class, () -> apiDocRetrievalServiceRest.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V)); assertEquals("Could not load instance information for service " + SERVICE_ID + ".", exception.getMessage()); } @@ -121,12 +139,12 @@ void givenNoApiDocFoundForService() { void givenServerErrorWhenRequestingSwaggerUrl() { String responseBody = "Server not found"; - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getStandardMetadata(), true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_INTERNAL_SERVER_ERROR, responseBody); - Exception exception = assertThrows(ApiDocNotFoundException.class, () -> apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V)); + Exception exception = assertThrows(ApiDocNotFoundException.class, () -> apiDocRetrievalServiceRest.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V)); assertEquals("No API Documentation was retrieved due to " + SERVICE_ID + " server error: '" + responseBody + "'.", exception.getMessage()); } @@ -134,12 +152,12 @@ void givenServerErrorWhenRequestingSwaggerUrl() { void givenNoInstanceMetadata() { String responseBody = "api-doc body"; - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(new HashMap<>(), true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - Exception exception = assertThrows(ApiDocNotFoundException.class, () -> apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V)); + Exception exception = assertThrows(ApiDocNotFoundException.class, () -> apiDocRetrievalServiceRest.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V)); assertEquals("No API Documentation defined for service " + SERVICE_ID + ".", exception.getMessage()); } @@ -181,75 +199,68 @@ void givenNoSwaggerUrl_thenReturnSubstituteApiDoc() { String responseBody = "api-doc body"; generatedResponseBody = generatedResponseBody.replaceAll("\\s+", ""); - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getMetadataWithoutSwaggerUrl(), true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); - - assertEquals(API_ID, actualResponse.getApiInfo().getApiId()); - assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl()); - assertEquals(SERVICE_VERSION, actualResponse.getApiInfo().getVersion()); - assertNull(actualResponse.getApiInfo().getSwaggerUrl()); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); - assertEquals(generatedResponseBody, actualResponse.getApiDocContent().replaceAll("\\s+", "")); + assertNotNull(lastApiInfo.get()); + assertEquals(API_ID, lastApiInfo.get().getApiId()); + assertEquals(GATEWAY_URL, lastApiInfo.get().getGatewayUrl()); + assertEquals(SERVICE_VERSION, lastApiInfo.get().getVersion()); + assertNull(lastApiInfo.get().getSwaggerUrl()); - assertEquals("[api -> api=RoutedService(subServiceId=api-v1, gatewayUrl=api, serviceUrl=/)]", actualResponse.getRoutes().toString()); + assertNotNull(actualApiDoc); + assertEquals(generatedResponseBody, actualApiDoc.replaceAll("\\s+", "")); } @Test void givenApiDocUrlInRouting_thenCreateApiDocUrlFromRoutingAndReturnApiDoc() { String responseBody = "api-doc body"; - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getMetadataWithoutApiInfo(), true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); - - assertEquals(responseBody, actualResponse.getApiDocContent()); + assertNotNull(actualApiDoc); + assertEquals(responseBody, actualApiDoc); } @Test void shouldCreateApiDocUrlFromRoutingAndUseHttp() { String responseBody = "api-doc body"; - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getMetadataWithoutApiInfo(), false)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); - - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V); - assertEquals(responseBody, actualResponse.getApiDocContent()); + assertNotNull(actualApiDoc); + assertEquals(responseBody, actualApiDoc); } @Test void givenServerCommunicationErrorWhenRequestingSwaggerUrl_thenLogCustomError() { - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getStandardMetadata(), true)); var exception = new IOException("Unable to reach the host"); HttpClientMockHelper.whenExecuteThenThrow(httpClient, exception); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveDefaultApiDoc(SERVICE_ID); + assertThrows(ApiDocNotFoundException.class, () -> apiDocRetrievalServiceRest.retrieveDefaultApiDoc(SERVICE_ID)); - assertEquals(API_ID, actualResponse.getApiInfo().getApiId()); - assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl()); - assertEquals(SERVICE_VERSION, actualResponse.getApiInfo().getVersion()); - assertEquals(SWAGGER_URL, actualResponse.getApiInfo().getSwaggerUrl()); - - assertEquals("", actualResponse.getApiDocContent()); + assertNotNull(lastApiInfo.get()); + assertEquals(API_ID, lastApiInfo.get().getApiId()); + assertEquals(GATEWAY_URL, lastApiInfo.get().getGatewayUrl()); + assertEquals(SERVICE_VERSION, lastApiInfo.get().getVersion()); + assertEquals(SWAGGER_URL, lastApiInfo.get().getSwaggerUrl()); verify(apimlLogger, times(1)).log("org.zowe.apiml.apicatalog.apiDocHostCommunication", SERVICE_ID, exception.getMessage()); } @@ -262,23 +273,21 @@ void givenDefaultApiDoc_thenReturnIt() { String responseBody = "api-doc body"; Map metadata = getMetadataWithMultipleApiInfo(); - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(metadata, true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveDefaultApiDoc(SERVICE_ID); - - assertEquals(API_ID, actualResponse.getApiInfo().getApiId()); - assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl()); - assertEquals(SERVICE_VERSION, actualResponse.getApiInfo().getVersion()); - assertEquals(SWAGGER_URL, actualResponse.getApiInfo().getSwaggerUrl()); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveDefaultApiDoc(SERVICE_ID); - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); - assertEquals(responseBody, actualResponse.getApiDocContent()); + assertNotNull(lastApiInfo.get()); + assertEquals(API_ID, lastApiInfo.get().getApiId()); + assertEquals(GATEWAY_URL, lastApiInfo.get().getGatewayUrl()); + assertEquals(SERVICE_VERSION, lastApiInfo.get().getVersion()); + assertEquals(SWAGGER_URL, lastApiInfo.get().getSwaggerUrl()); - assertEquals("[api -> api=RoutedService(subServiceId=api-v1, gatewayUrl=api, serviceUrl=/)]", actualResponse.getRoutes().toString()); + assertNotNull(actualApiDoc); + assertEquals(responseBody, actualApiDoc); } @Test @@ -287,63 +296,57 @@ void givenNoDefaultApiDoc_thenReturnHighestVersion() { Map metadata = getMetadataWithMultipleApiInfo(); metadata.remove(API_INFO + ".1." + API_INFO_IS_DEFAULT); // unset default API, so higher version becomes default - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(metadata, true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveDefaultApiDoc(SERVICE_ID); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveDefaultApiDoc(SERVICE_ID); - assertEquals(API_ID, actualResponse.getApiInfo().getApiId()); - assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl()); - assertEquals(HIGHER_SERVICE_VERSION, actualResponse.getApiInfo().getVersion()); - assertEquals(SWAGGER_URL, actualResponse.getApiInfo().getSwaggerUrl()); + assertNotNull(lastApiInfo.get()); + assertEquals(API_ID, lastApiInfo.get().getApiId()); + assertEquals(GATEWAY_URL, lastApiInfo.get().getGatewayUrl()); + assertEquals(HIGHER_SERVICE_VERSION, lastApiInfo.get().getVersion()); + assertEquals(SWAGGER_URL, lastApiInfo.get().getSwaggerUrl()); - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); - assertEquals(responseBody, actualResponse.getApiDocContent()); - - assertEquals("[api -> api=RoutedService(subServiceId=api-v1, gatewayUrl=api, serviceUrl=/)]", actualResponse.getRoutes().toString()); + assertNotNull(actualApiDoc); + assertEquals(responseBody, actualApiDoc); } @Test void givenNoDefaultApiDocAndDifferentVersionFormat_thenReturnHighestVersion() { String responseBody = "api-doc body"; - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getMetadataWithMultipleApiInfoWithDifferentVersionFormat(), true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveDefaultApiDoc(SERVICE_ID); - - assertEquals(API_ID, actualResponse.getApiInfo().getApiId()); - assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl()); - assertEquals(HIGHER_SERVICE_VERSION_V, actualResponse.getApiInfo().getVersion()); - assertEquals(SWAGGER_URL, actualResponse.getApiInfo().getSwaggerUrl()); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveDefaultApiDoc(SERVICE_ID); - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); - assertEquals(responseBody, actualResponse.getApiDocContent()); + assertNotNull(lastApiInfo.get()); + assertEquals(API_ID, lastApiInfo.get().getApiId()); + assertEquals(GATEWAY_URL, lastApiInfo.get().getGatewayUrl()); + assertEquals(HIGHER_SERVICE_VERSION_V, lastApiInfo.get().getVersion()); + assertEquals(SWAGGER_URL, lastApiInfo.get().getSwaggerUrl()); - assertEquals("[api -> api=RoutedService(subServiceId=api-v1, gatewayUrl=api, serviceUrl=/)]", actualResponse.getRoutes().toString()); + assertNotNull(actualApiDoc); + assertEquals(responseBody, actualApiDoc); } @Test void givenNoApiDocs_thenReturnNull() { String responseBody = "api-doc body"; - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getMetadataWithoutApiInfo(), true)); HttpClientMockHelper.mockResponse(response, HttpStatus.SC_OK, responseBody); - ApiDocInfo actualResponse = apiDocRetrievalService.retrieveDefaultApiDoc(SERVICE_ID); + String actualApiDoc = apiDocRetrievalServiceRest.retrieveDefaultApiDoc(SERVICE_ID); - assertNotNull(actualResponse); - assertNotNull(actualResponse.getApiDocContent()); - - assertEquals(responseBody, actualResponse.getApiDocContent()); + assertNotNull(actualApiDoc); + assertEquals(responseBody, actualApiDoc); } } @@ -351,19 +354,19 @@ void givenNoApiDocs_thenReturnNull() { class WhenGetApiVersions { @Test void givenApiVersions_thenReturnThem() { - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getStandardMetadata(), false)); - List actualVersions = apiDocRetrievalService.retrieveApiVersions(SERVICE_ID); + List actualVersions = apiDocRetrievalServiceRest.retrieveApiVersions(SERVICE_ID); assertEquals(Collections.singletonList(SERVICE_VERSION_V), actualVersions); } @Test void givenNoApiVersions_thenThrowException() { - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)).thenReturn(null); + when(eurekaClient.getApplication(SERVICE_ID)).thenReturn(null); Exception exception = assertThrows(ApiVersionNotFoundException.class, () -> - apiDocRetrievalService.retrieveApiVersions(SERVICE_ID) + apiDocRetrievalServiceRest.retrieveApiVersions(SERVICE_ID) ); assertEquals("Could not load instance information for service " + SERVICE_ID + ".", exception.getMessage()); } @@ -373,10 +376,10 @@ void givenNoApiVersions_thenThrowException() { class WhenGetDefaultApiVersion { @Test void givenDefaultApiVersion_thenReturnIt() { - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(getMetadataWithMultipleApiInfo(), false)); - String defaultVersion = apiDocRetrievalService.retrieveDefaultApiVersion(SERVICE_ID); + String defaultVersion = apiDocRetrievalServiceRest.retrieveDefaultApiVersion(SERVICE_ID); assertEquals(SERVICE_VERSION_V, defaultVersion); } @@ -385,26 +388,26 @@ void givenNoDefaultApiVersion_thenReturnHighestVersion() { Map metadata = getMetadataWithMultipleApiInfo(); metadata.remove(API_INFO + ".1." + API_INFO_IS_DEFAULT); // unset default API, so higher version becomes default - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)) + when(eurekaClient.getApplication(SERVICE_ID)) .thenReturn(getStandardInstance(metadata, false)); - String defaultVersion = apiDocRetrievalService.retrieveDefaultApiVersion(SERVICE_ID); + String defaultVersion = apiDocRetrievalServiceRest.retrieveDefaultApiVersion(SERVICE_ID); assertEquals(HIGHER_SERVICE_VERSION_V, defaultVersion); } @Test void givenNoApiInfo_thenThrowException() { - when(instanceRetrievalService.getInstanceInfo(SERVICE_ID)).thenReturn(null); + when(eurekaClient.getApplication(SERVICE_ID)).thenReturn(null); Exception exception = assertThrows(ApiVersionNotFoundException.class, () -> - apiDocRetrievalService.retrieveDefaultApiVersion(SERVICE_ID) + apiDocRetrievalServiceRest.retrieveDefaultApiVersion(SERVICE_ID) ); assertEquals("Could not load instance information for service " + SERVICE_ID + ".", exception.getMessage()); } } - private InstanceInfo getStandardInstance(Map metadata, Boolean isPortSecure) { - return InstanceInfo.Builder.newBuilder() + private Application getStandardInstance(Map metadata, Boolean isPortSecure) { + InstanceInfo instance = InstanceInfo.Builder.newBuilder() .setAppName(SERVICE_ID) .setHostName(SERVICE_HOST) .setPort(SERVICE_PORT) @@ -413,6 +416,7 @@ private InstanceInfo getStandardInstance(Map metadata, Boolean i .setStatus(InstanceInfo.InstanceStatus.UP) .setMetadata(metadata) .build(); + return ServicesBuilder.createApp(SERVICE_ID, instance); } private Map getStandardMetadata() { @@ -501,4 +505,5 @@ private ServiceAddress getProperties() { .hostname(GATEWAY_HOST) .build(); } + } diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocServiceTest.java index 8bb21b1775..da77d75b79 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocServiceTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/TransformApiDocServiceTest.java @@ -13,7 +13,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; import org.zowe.apiml.apicatalog.swagger.api.AbstractApiDocService; import org.zowe.apiml.apicatalog.swagger.api.ApiDocV2Service; import org.zowe.apiml.apicatalog.swagger.api.ApiDocV3Service; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocServiceTest.java index d037a74c7e..a2fe304950 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocServiceTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/AbstractApiDocServiceTest.java @@ -23,7 +23,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; +import org.zowe.apiml.apicatalog.swagger.ApiDocInfo; import org.zowe.apiml.apicatalog.swagger.api.dummy.DummyApiDocService; import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.product.gateway.GatewayClient; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2ServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2ServiceTest.java index 13fe22759c..921cc2ce72 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2ServiceTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV2ServiceTest.java @@ -25,7 +25,7 @@ import org.mockito.quality.Strictness; import org.springframework.core.io.ClassPathResource; import org.springframework.test.util.ReflectionTestUtils; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; +import org.zowe.apiml.apicatalog.swagger.ApiDocInfo; import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.product.constants.CoreService; import org.zowe.apiml.product.gateway.GatewayClient; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3ServiceTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3ServiceTest.java index 26c13ee806..d4eedb312a 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3ServiceTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/ApiDocV3ServiceTest.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.core.io.ClassPathResource; import org.springframework.test.util.ReflectionTestUtils; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; +import org.zowe.apiml.apicatalog.swagger.ApiDocInfo; import org.zowe.apiml.config.ApiInfo; import org.zowe.apiml.product.constants.CoreService; import org.zowe.apiml.product.gateway.GatewayClient; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/dummy/DummyApiDocService.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/dummy/DummyApiDocService.java index a27787bc27..b05de94507 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/dummy/DummyApiDocService.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/swagger/api/dummy/DummyApiDocService.java @@ -10,7 +10,7 @@ package org.zowe.apiml.apicatalog.swagger.api.dummy; -import org.zowe.apiml.apicatalog.services.cached.model.ApiDocInfo; +import org.zowe.apiml.apicatalog.swagger.ApiDocInfo; import org.zowe.apiml.apicatalog.swagger.api.AbstractApiDocService; import org.zowe.apiml.product.gateway.GatewayClient; diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ContainerServiceMockUtil.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ContainerServiceMockUtil.java deleted file mode 100644 index 7757c67dcf..0000000000 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ContainerServiceMockUtil.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.apicatalog.util; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.shared.Application; -import org.zowe.apiml.apicatalog.model.APIContainer; -import org.zowe.apiml.apicatalog.model.APIService; -import org.zowe.apiml.apicatalog.services.cached.CachedServicesService; - -import java.util.*; -import java.util.stream.Stream; - -import static org.mockito.Mockito.when; - -public class ContainerServiceMockUtil { - - public ContainerServiceState createContainersServicesAndInstances() { - ContainerServiceState containerServiceState = new ContainerServiceState(); - containerServiceState.setApplications(new ArrayList<>()); - containerServiceState.setServices(new ArrayList<>()); - containerServiceState.setInstances(new ArrayList<>()); - containerServiceState.setContainers(new ArrayList<>()); - - List services = new ArrayList<>(); - - int index = 1000; - - index = generateInstancesAndServices(containerServiceState, services, "service1", index, 3); - index = generateInstancesAndServices(containerServiceState, services, "service2", index, 1); - - // two containers same services - APIContainer container = new APIContainer("api-one", "API One", "This is API One", new HashSet<>(services)); - APIContainer container1 = new APIContainer("api-two", "API Two", "This is API Two", new HashSet<>(services)); - - // one extra service - index = generateInstancesAndServices(containerServiceState, services, "service3", index, 1); - - APIContainer container2 = new APIContainer("api-three", "API Three", "This is API Three", new HashSet<>(services)); - - // unique service - services.clear(); - - index = generateInstancesAndServices(containerServiceState, services, "service4", index, 1); - - APIContainer container4 = new APIContainer("api-four", "API Four", "This is API Four", new HashSet<>(services)); - - containerServiceState.setContainers(Arrays.asList(container, container1, container2, container4)); - - return containerServiceState; - } - - public void mockServiceRetrievalFromCache(CachedServicesService cachedServicesService, - List applications) { - applications.forEach(application -> - when(cachedServicesService.getService(application.getName())).thenReturn(application)); - } - - public InstanceInfo createInstance(String serviceId, String instanceId, - InstanceInfo.InstanceStatus status, - InstanceInfo.ActionType actionType, - HashMap metadata) { - return new InstanceInfo(instanceId, serviceId.toUpperCase(), null, "192.168.0.1", null, - new InstanceInfo.PortWrapper(true, 9090), null, null, null, null, null, null, null, 0, null, "hostname", - status, null, null, null, null, metadata, null, null, actionType, null); - } - - private int generateInstancesAndServices(ContainerServiceState containerServiceState, - List services, - String serviceId, - int index, - int limit) { - List generatedInstances = Stream.iterate(index, i -> i + 1) - .limit(limit) - .map(mIndex -> getInstance(mIndex, serviceId)) - .toList(); - - addApiService(serviceId, containerServiceState.getServices(), services); - addInstancesToApplications( - generatedInstances, - containerServiceState.getApplications(), - containerServiceState.getInstances(), - serviceId); - - return index + limit; - } - - private void addInstancesToApplications(List instanceCollection, - List applications, - List instances, - String serviceId) { - instances.addAll(instanceCollection); - applications.add(new Application(serviceId, instanceCollection)); - } - - - private APIService addApiService(String serviceId, - List allServices, - List services) { - APIService service = new APIService.Builder(serviceId) - .title(serviceId + "-title") - .description(serviceId + "-desc") - .secured(false) - .baseUrl("base") - .homePageUrl("home") - .basePath("base") - .sso(false) - .apis(Collections.emptyMap()) - .build(); - services.add(service); - allServices.add(service); - return service; - } - - private InstanceInfo getInstance(int index, String serviceId) { - InstanceInfo instance = createInstance( - serviceId, - serviceId + ":" + index, - InstanceInfo.InstanceStatus.UP, - InstanceInfo.ActionType.ADDED, - new HashMap<>()); - return instance; - } - -} diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ServicesBuilder.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ServicesBuilder.java index 5e703c00c0..2da83db08f 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ServicesBuilder.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/util/ServicesBuilder.java @@ -10,35 +10,24 @@ package org.zowe.apiml.apicatalog.util; -import java.util.HashMap; -import java.util.Map; - import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.shared.Application; +import lombok.experimental.UtilityClass; -import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; +import java.util.HashMap; +import java.util.Map; import static org.zowe.apiml.constants.EurekaMetadataDefinition.*; +@UtilityClass public class ServicesBuilder { - private int id = 0; - private CachedProductFamilyService service; - - public InstanceInfo instance1; - public InstanceInfo instance2; - - public ServicesBuilder(CachedProductFamilyService service) { - this.service = service; - instance1 = createInstance("service1", "demoapp"); - instance2 = createInstance("service2", "demoapp2"); - } + private int id = 0; public Application createApp(String serviceId, InstanceInfo...instanceInfos) { Application application = new Application(serviceId); for (InstanceInfo instanceInfo : instanceInfos) { application.addInstance(instanceInfo); - service.saveContainerFromInstance(serviceId, instanceInfo); } return application; } diff --git a/api-catalog-services/src/test/resources/standalone/invalid-apiDoc/apiDocs/service1-instance1_zowe v1.0.0_default.json b/api-catalog-services/src/test/resources/standalone/invalid-apiDoc/apiDocs/service1-instance1_zowe v1.0.0_default.json deleted file mode 100644 index fa4a2e51a2..0000000000 --- a/api-catalog-services/src/test/resources/standalone/invalid-apiDoc/apiDocs/service1-instance1_zowe v1.0.0_default.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "negative": "testing" -} diff --git a/api-catalog-services/src/test/resources/standalone/invalid-apiDocName/apiDocs/service2.json b/api-catalog-services/src/test/resources/standalone/invalid-apiDocName/apiDocs/service2.json deleted file mode 100644 index c46874e76a..0000000000 --- a/api-catalog-services/src/test/resources/standalone/invalid-apiDocName/apiDocs/service2.json +++ /dev/null @@ -1 +0,0 @@ -negative testing diff --git a/api-catalog-services/src/test/resources/standalone/invalid-app/apps/service2.json b/api-catalog-services/src/test/resources/standalone/invalid-app/apps/service2.json deleted file mode 100644 index fa4a2e51a2..0000000000 --- a/api-catalog-services/src/test/resources/standalone/invalid-app/apps/service2.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "negative": "testing" -} diff --git a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance1_zowe v1.0.0_default.json b/api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance1_zowe v1.0.0_default.json deleted file mode 100644 index b130e5443c..0000000000 --- a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance1_zowe v1.0.0_default.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Swagger Petstore - OpenAPI 3.0", - "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.", - "version": "1.0.0" - }, - "externalDocs": { - "description": "Find out more about Swagger", - "url": "http://zowe.org" - }, - "servers": [ - { - "url": "https://petstore3.swagger.io/api/v3" - } - ], - "tags": [ - { - "name": "pet", - "description": "Everything about your Pets", - "externalDocs": { - "description": "Find out more", - "url": "http://swagger.io" - } - } - ], - "paths": { - "/pet/findByStatus": { - "get": { - "tags": [ - "pet" - ], - "summary": "Finds Pets by status", - "description": "Multiple status values can be provided with comma separated strings", - "operationId": "findPetsByStatus", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Status values that need to be considered for filter", - "required": false, - "explode": true, - "schema": { - "type": "string", - "default": "available", - "enum": [ - "available", - "pending", - "sold" - ] - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "Invalid status value" - } - }, - "security": [ - { - "petstore_auth": [ - "write:pets", - "read:pets" - ] - } - ] - } - } - }, - "components": { - "schemas": { - "Pet": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64", - "example": 10 - }, - "name": { - "type": "string", - "example": "doggie" - } - } - } - }, - "requestBodies": { - "Pet": { - "description": "Pet object that needs to be added to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "securitySchemes": { - "petstore_auth": { - "type": "oauth2", - "flows": { - "implicit": { - "authorizationUrl": "https://petstore3.swagger.io/oauth/authorize", - "scopes": { - "write:pets": "modify pets in your account", - "read:pets": "read your pets" - } - } - } - } - } - } -} diff --git a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance2_zowe v1.0.0_default.json b/api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance2_zowe v1.0.0_default.json deleted file mode 100644 index b130e5443c..0000000000 --- a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service1-instance2_zowe v1.0.0_default.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Swagger Petstore - OpenAPI 3.0", - "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.", - "version": "1.0.0" - }, - "externalDocs": { - "description": "Find out more about Swagger", - "url": "http://zowe.org" - }, - "servers": [ - { - "url": "https://petstore3.swagger.io/api/v3" - } - ], - "tags": [ - { - "name": "pet", - "description": "Everything about your Pets", - "externalDocs": { - "description": "Find out more", - "url": "http://swagger.io" - } - } - ], - "paths": { - "/pet/findByStatus": { - "get": { - "tags": [ - "pet" - ], - "summary": "Finds Pets by status", - "description": "Multiple status values can be provided with comma separated strings", - "operationId": "findPetsByStatus", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Status values that need to be considered for filter", - "required": false, - "explode": true, - "schema": { - "type": "string", - "default": "available", - "enum": [ - "available", - "pending", - "sold" - ] - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "Invalid status value" - } - }, - "security": [ - { - "petstore_auth": [ - "write:pets", - "read:pets" - ] - } - ] - } - } - }, - "components": { - "schemas": { - "Pet": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64", - "example": 10 - }, - "name": { - "type": "string", - "example": "doggie" - } - } - } - }, - "requestBodies": { - "Pet": { - "description": "Pet object that needs to be added to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "securitySchemes": { - "petstore_auth": { - "type": "oauth2", - "flows": { - "implicit": { - "authorizationUrl": "https://petstore3.swagger.io/oauth/authorize", - "scopes": { - "write:pets": "modify pets in your account", - "read:pets": "read your pets" - } - } - } - } - } - } -} diff --git a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v1.0.0_default.json b/api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v1.0.0_default.json deleted file mode 100644 index b130e5443c..0000000000 --- a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v1.0.0_default.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Swagger Petstore - OpenAPI 3.0", - "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.", - "version": "1.0.0" - }, - "externalDocs": { - "description": "Find out more about Swagger", - "url": "http://zowe.org" - }, - "servers": [ - { - "url": "https://petstore3.swagger.io/api/v3" - } - ], - "tags": [ - { - "name": "pet", - "description": "Everything about your Pets", - "externalDocs": { - "description": "Find out more", - "url": "http://swagger.io" - } - } - ], - "paths": { - "/pet/findByStatus": { - "get": { - "tags": [ - "pet" - ], - "summary": "Finds Pets by status", - "description": "Multiple status values can be provided with comma separated strings", - "operationId": "findPetsByStatus", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Status values that need to be considered for filter", - "required": false, - "explode": true, - "schema": { - "type": "string", - "default": "available", - "enum": [ - "available", - "pending", - "sold" - ] - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "Invalid status value" - } - }, - "security": [ - { - "petstore_auth": [ - "write:pets", - "read:pets" - ] - } - ] - } - } - }, - "components": { - "schemas": { - "Pet": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64", - "example": 10 - }, - "name": { - "type": "string", - "example": "doggie" - } - } - } - }, - "requestBodies": { - "Pet": { - "description": "Pet object that needs to be added to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "securitySchemes": { - "petstore_auth": { - "type": "oauth2", - "flows": { - "implicit": { - "authorizationUrl": "https://petstore3.swagger.io/oauth/authorize", - "scopes": { - "write:pets": "modify pets in your account", - "read:pets": "read your pets" - } - } - } - } - } - } -} diff --git a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v2.0.0.json b/api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v2.0.0.json deleted file mode 100644 index b130e5443c..0000000000 --- a/api-catalog-services/src/test/resources/standalone/services/apiDocs/service2_zowe v2.0.0.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Swagger Petstore - OpenAPI 3.0", - "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.", - "version": "1.0.0" - }, - "externalDocs": { - "description": "Find out more about Swagger", - "url": "http://zowe.org" - }, - "servers": [ - { - "url": "https://petstore3.swagger.io/api/v3" - } - ], - "tags": [ - { - "name": "pet", - "description": "Everything about your Pets", - "externalDocs": { - "description": "Find out more", - "url": "http://swagger.io" - } - } - ], - "paths": { - "/pet/findByStatus": { - "get": { - "tags": [ - "pet" - ], - "summary": "Finds Pets by status", - "description": "Multiple status values can be provided with comma separated strings", - "operationId": "findPetsByStatus", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Status values that need to be considered for filter", - "required": false, - "explode": true, - "schema": { - "type": "string", - "default": "available", - "enum": [ - "available", - "pending", - "sold" - ] - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "Invalid status value" - } - }, - "security": [ - { - "petstore_auth": [ - "write:pets", - "read:pets" - ] - } - ] - } - } - }, - "components": { - "schemas": { - "Pet": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64", - "example": 10 - }, - "name": { - "type": "string", - "example": "doggie" - } - } - } - }, - "requestBodies": { - "Pet": { - "description": "Pet object that needs to be added to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "securitySchemes": { - "petstore_auth": { - "type": "oauth2", - "flows": { - "implicit": { - "authorizationUrl": "https://petstore3.swagger.io/oauth/authorize", - "scopes": { - "write:pets": "modify pets in your account", - "read:pets": "read your pets" - } - } - } - } - } - } -} diff --git a/api-catalog-services/src/test/resources/standalone/services/apps/service1.json b/api-catalog-services/src/test/resources/standalone/services/apps/service1.json deleted file mode 100644 index afe54d072e..0000000000 --- a/api-catalog-services/src/test/resources/standalone/services/apps/service1.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "application": [ - { - "name": "SERVICE1-INSTANCE1", - "instance": [ - { - "hostName": "zowe.org", - "app": "service1-instance1", - "ipAddr": "1.1.1.1", - "status": "UP", - "port": { - "$": 1000, - "@enabled": "false" - }, - "securePort": { - "$": 1000, - "@enabled": "true" - }, - "vipAddress": "service1", - "metadata": { - "apiml.service.description": "Test mock application 1 / instance 1 - just for testing purposes", - "apiml.catalog.tile.version": "1.0.0", - "apiml.apiInfo.api.version": "1.0.0", - "apiml.apiInfo.api.defaultApi": "false", - "apiml.apiInfo.api.gatewayUrl": "api", - "apiml.apiInfo.api.swaggerUrl": "/standAloneApps/swagger/openapi.json", - "apiml.apiInfo.api.apiId": "zowe", - "apiml.routes.api.serviceUrl": "", - "apiml.apiInfo.api.documentationUrl": "https://www.zowe.org", - "apiml.service.title": "Test mock application 1", - "apiml.catalog.tile.description": "Mock apps from file", - "version": "1.0.0", - "apiml.routes.api.gatewayUrl": "api", - "apiml.routes.ui.gatewayUrl": "ui", - "apiml.catalog.tile.id": "mock1", - "apiml.authentication.scheme": "zosmf", - "apiml.catalog.tile.title": "Mocked Services from file", - "apiml.routes.ui.serviceUrl": "" - }, - "homePageUrl": "https://www.zowe.org:1000" - } - ] - }, - { - "name": "SERVICE1-INSTANCE2", - "instance": [ - { - "hostName": "zowe.org", - "app": "service1-instance2", - "ipAddr": "1.1.1.2", - "status": "UP", - "port": { - "$": 2000, - "@enabled": "false" - }, - "securePort": { - "$": 2000, - "@enabled": "true" - }, - "vipAddress": "service1", - "metadata": { - "apiml.service.description": "Test mock application 1 / instance 2 - just for testing purposes", - "apiml.catalog.tile.version": "1.0.0", - "apiml.apiInfo.api.version": "1.0.0", - "apiml.apiInfo.api.defaultApi": "true", - "apiml.apiInfo.api.gatewayUrl": "api", - "apiml.apiInfo.api.apiId": "zowe", - "apiml.routes.api.serviceUrl": "", - "apiml.apiInfo.api.documentationUrl": "https://www.zowe.org", - "apiml.service.title": "Test mock application 1", - "apiml.catalog.tile.description": "Mock apps from file", - "version": "1.0.0", - "apiml.routes.api.gatewayUrl": "api", - "apiml.routes.ui.gatewayUrl": "ui", - "apiml.catalog.tile.id": "mock1", - "apiml.authentication.scheme": "zosmf", - "apiml.catalog.tile.title": "Mocked Services from file", - "apiml.routes.ui.serviceUrl": "" - }, - "homePageUrl": "https://www.zowe.org:2000" - } - ] - } - ] -} diff --git a/api-catalog-services/src/test/resources/standalone/services/apps/service2.json b/api-catalog-services/src/test/resources/standalone/services/apps/service2.json deleted file mode 100644 index fa19c89212..0000000000 --- a/api-catalog-services/src/test/resources/standalone/services/apps/service2.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "application": [ - { - "name": "SERVICE2", - "instance": [ - { - "hostName": "zowe.org", - "app": "service2", - "ipAddr": "1.1.2.1", - "status": "UP", - "port": { - "$": 3000, - "@enabled": "false" - }, - "securePort": { - "$": 3000, - "@enabled": "true" - }, - "vipAddress": "service2", - "metadata": { - "apiml.service.description": "Test mock application 2 - just for testing purposes", - "apiml.catalog.tile.version": "1.0.0", - "apiml.apiInfo.v1.version": "1.0.0", - "apiml.apiInfo.v1.defaultApi": "true", - "apiml.apiInfo.v1.gatewayUrl": "api", - "apiml.apiInfo.v1.swaggerUrl": "/services/swagger/openapi.json", - "apiml.apiInfo.v1.apiId": "zowe", - "apiml.apiInfo.v1.documentationUrl": "https://www.zowe.org", - "apiml.apiInfo.v2.version": "2.0.0", - "apiml.apiInfo.v2.defaultApi": "false", - "apiml.apiInfo.v2.gatewayUrl": "api", - "apiml.apiInfo.v2.swaggerUrl": "/services/swagger/openapi.json", - "apiml.apiInfo.v2.apiId": "zowe", - "apiml.apiInfo.v2.documentationUrl": "https://www.zowe.org", - "apiml.routes.api.serviceUrl": "", - "apiml.service.title": "Test mock application 2", - "apiml.catalog.tile.description": "Another mock apps from file", - "version": "1.0.0", - "apiml.routes.api.gatewayUrl": "api", - "apiml.routes.ui.gatewayUrl": "ui", - "apiml.catalog.tile.id": "mock2", - "apiml.authentication.scheme": "zosmf", - "apiml.catalog.tile.title": "Another mocked Services from file", - "apiml.routes.ui.serviceUrl": "" - }, - "homePageUrl": "https://www.zowe.org:1000" - } - ] - } - ] -} diff --git a/api-catalog-ui/frontend/mocked-backend/assets/containers.json b/api-catalog-ui/frontend/mocked-backend/assets/containers.json index 03a7352aa8..50fabf3591 100644 --- a/api-catalog-ui/frontend/mocked-backend/assets/containers.json +++ b/api-catalog-ui/frontend/mocked-backend/assets/containers.json @@ -189,7 +189,7 @@ "apiId":"zowe.apiml.discoverableclient", "gatewayUrl":"api/v1", "version":null, - "swaggerUrl":"https://localhost:10012/discoverableclient/v2/api-docs", + "swaggerUrl":"https://localhost:10012/discoverableclient/v3/api-docs", "documentationUrl":null, "codeSnippet":[ diff --git a/common-service-core/src/main/java/org/zowe/apiml/util/EurekaUtils.java b/common-service-core/src/main/java/org/zowe/apiml/util/EurekaUtils.java index 1c687b1313..fc3ba5eb4c 100644 --- a/common-service-core/src/main/java/org/zowe/apiml/util/EurekaUtils.java +++ b/common-service-core/src/main/java/org/zowe/apiml/util/EurekaUtils.java @@ -11,23 +11,30 @@ package org.zowe.apiml.util; import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import com.netflix.discovery.shared.Application; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; +import org.zowe.apiml.constants.EurekaMetadataDefinition; + +import java.util.Optional; + +import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; +import static org.zowe.apiml.product.constants.CoreService.GATEWAY; /** * This util offer basic operation with eureka, like: extraction serviceId from instanceId, construct URL by * InstanceInfo etc. */ -public final class EurekaUtils { - - private EurekaUtils() { - - } +@UtilityClass +public class EurekaUtils { /** * Extract serviceId from instanceId * @param instanceId input, instanceId in format "host:service:random number to unique instanceId" * @return second part, it means serviceId. If it doesn't exist return null; */ - public static final String getServiceIdFromInstanceId(String instanceId) { + public String getServiceIdFromInstanceId(String instanceId) { final int startIndex = instanceId.indexOf(':'); if (startIndex < 0) return null; @@ -42,7 +49,7 @@ public static final String getServiceIdFromInstanceId(String instanceId) { * @param instanceInfo Instance of service, for which we want to get an URL * @return URL to the instance */ - public static final String getUrl(InstanceInfo instanceInfo) { + public String getUrl(InstanceInfo instanceInfo) { if (instanceInfo.getSecurePort() == 0 || !instanceInfo.isPortEnabled(InstanceInfo.PortType.SECURE)) { return "http://" + instanceInfo.getHostName() + ":" + instanceInfo.getPort(); } else { @@ -50,4 +57,37 @@ public static final String getUrl(InstanceInfo instanceInfo) { } } + private Optional getPrimaryInstanceInfo(EurekaClient eurekaClient, String serviceId) { + return Optional.ofNullable(eurekaClient.getApplication(serviceId)) + .map(Application::getInstances) + .map(instances -> instances.stream() + .filter(instance -> EurekaMetadataDefinition.RegistrationType.of(instance.getMetadata()).isPrimary()) + .findFirst() + .get() + ); + } + + private Optional getSecondaryInstanceInfo(EurekaClient eurekaClient, String apimlId) { + return Optional.ofNullable(eurekaClient.getApplication(GATEWAY.getServiceId())) + .map(Application::getInstances) + .map(instances -> instances.stream() + .filter(instance -> EurekaMetadataDefinition.RegistrationType.of(instance.getMetadata()).isAdditional()) + .filter(instance -> StringUtils.equals(apimlId, instance.getMetadata().get(APIML_ID))) + .findFirst() + .get() + ); + } + + /** + * It tries to find service with primary registration, if it does not exist it looks also + * for a gateway with secondary registration and id is used as apimlId + * @param eurekaClient eureka client instance for look up + * @param id serviceId for primary or apimlId for secondary registration + * @return instance or empty Optional object + */ + public Optional getInstanceInfo(EurekaClient eurekaClient, String id) { + return getPrimaryInstanceInfo(eurekaClient, id) + .or(() -> getSecondaryInstanceInfo(eurekaClient, id)); + } + } diff --git a/config/docker/api-defs/staticclient.yml b/config/docker/api-defs/staticclient.yml index 695a81788f..7df25b9083 100644 --- a/config/docker/api-defs/staticclient.yml +++ b/config/docker/api-defs/staticclient.yml @@ -25,7 +25,7 @@ services: apiInfo: - apiId: zowe.apiml.discoverableclient gatewayUrl: api/v1 - swaggerUrl: https://discoverable-client:10012/discoverableclient/v2/api-docs + swaggerUrl: https://discoverable-client:10012/discoverableclient/v3/api-docs customMetadata: apiml: okToRetryOnAllOperations: true diff --git a/config/local/api-defs-http/staticclient.yml b/config/local/api-defs-http/staticclient.yml index 22a5c0ad86..0f87493414 100644 --- a/config/local/api-defs-http/staticclient.yml +++ b/config/local/api-defs-http/staticclient.yml @@ -25,7 +25,7 @@ services: apiInfo: - apiId: zowe.apiml.discoverableclient gatewayUrl: api/v1 - swaggerUrl: http://localhost:10012/discoverableclient/v2/api-docs + swaggerUrl: http://localhost:10012/discoverableclient/v3/api-docs customMetadata: apiml: okToRetryOnAllOperations: true diff --git a/config/local/api-defs/staticclient.yml b/config/local/api-defs/staticclient.yml index 15047cbce0..5f16df1b5f 100644 --- a/config/local/api-defs/staticclient.yml +++ b/config/local/api-defs/staticclient.yml @@ -25,7 +25,7 @@ services: apiInfo: - apiId: zowe.apiml.discoverableclient gatewayUrl: api/v1 - swaggerUrl: https://localhost:10012/discoverableclient/v2/api-docs + swaggerUrl: https://localhost:10012/discoverableclient/v3/api-docs customMetadata: apiml: okToRetryOnAllOperations: true diff --git a/gateway-service/build.gradle b/gateway-service/build.gradle index 57de5f3203..5e2ccbcdb7 100644 --- a/gateway-service/build.gradle +++ b/gateway-service/build.gradle @@ -75,7 +75,9 @@ dependencies { implementation libs.spring.boot.starter.actuator implementation libs.spring.boot.starter.oauth2.client implementation libs.spring.boot.starter.thymeleaf - implementation libs.spring.doc.webflux + implementation(libs.spring.doc.webflux) { + exclude group: "jakarta.xml.bind", module: "jakarta.xml.bind-api" + } implementation libs.netty.reactor.http implementation libs.google.gson implementation libs.jjwt @@ -104,6 +106,7 @@ dependencies { testFixturesImplementation libs.lombok testFixturesImplementation libs.spring.cloud.starter.eureka.client testFixturesImplementation libs.guava + testFixturesImplementation libs.jaxbApi testFixturesAnnotationProcessor libs.lombok compileOnly libs.lombok diff --git a/integration-tests/src/test/java/org/zowe/apiml/functional/apicatalog/ApiCatalogStandaloneTest.java b/integration-tests/src/test/java/org/zowe/apiml/functional/apicatalog/ApiCatalogStandaloneTest.java deleted file mode 100644 index 4cdd3a41bb..0000000000 --- a/integration-tests/src/test/java/org/zowe/apiml/functional/apicatalog/ApiCatalogStandaloneTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.functional.apicatalog; - -import io.restassured.RestAssured; -import io.restassured.config.SSLConfig; -import io.restassured.response.ValidatableResponse; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.zowe.apiml.util.config.ApiCatalogServiceConfiguration; -import org.zowe.apiml.util.config.ConfigReader; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import static io.restassured.RestAssured.given; -import static io.restassured.RestAssured.when; -import static org.apache.http.HttpStatus.SC_OK; -import static org.hamcrest.core.Is.is; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Tag("ApiCatalogStandaloneTest") -public class ApiCatalogStandaloneTest { - - private static final String GET_ALL_CONTAINERS_ENDPOINT = "/apicatalog/containers"; - private static final String GET_API_CATALOG_API_DOC_DEFAULT_ENDPOINT = "/apicatalog/apidoc/service2"; - private static final String GET_API_CATALOG_API_DOC_ENDPOINT = "/apicatalog/apidoc/service2/org.zowe v2.0.0"; - - private final static String USERNAME = ConfigReader.environmentConfiguration().getAuxiliaryUserList().getCredentials("servicesinfo-authorized").get(0).getUser(); - private final static String PASSWORD = ConfigReader.environmentConfiguration().getAuxiliaryUserList().getCredentials("servicesinfo-authorized").get(0).getPassword(); - - private String baseHost; - - @BeforeAll - static void init() throws Exception { - RestAssured.useRelaxedHTTPSValidation(); - } - - @BeforeEach - void setUp() { - ApiCatalogServiceConfiguration configuration = ConfigReader.environmentConfiguration().getApiCatalogStandaloneConfiguration(); - String host = configuration.getHost(); - String scheme = configuration.getScheme(); - int port = configuration.getPort(); - baseHost = scheme + "://" + host + ":" + port; - RestAssured.config = RestAssured.config().sslConfig(SSLConfig.sslConfig()); - RestAssured.useRelaxedHTTPSValidation(); - } - - @Nested - class Containers { - - @Nested - class HasRegisteredServices { - - @Test - void whenGetContainers() throws IOException { - final ValidatableResponse response = when() - .get(baseHost + GET_ALL_CONTAINERS_ENDPOINT) - .then() - .statusCode(is(SC_OK)) - .contentType("application/json"); - - List> list = response.extract().jsonPath().getList("$."); - assertEquals(2, list.size()); - assertEquals("Mocked Services from file", list.get(0).get("title")); - assertEquals("Another mocked Services from file", list.get(1).get("title")); - } - } - - @Nested - class ApiDocIsAvailable { - - @Test - void whenGetApiDocDefaultEndpoint() { - final ValidatableResponse response = when() - .get(baseHost + GET_API_CATALOG_API_DOC_DEFAULT_ENDPOINT) - .then() - .statusCode(is(SC_OK)) - .contentType("application/json"); - assertEquals("Service 2 - v1 (default)", response.extract().jsonPath().get("info.title")); - } - - @Test - void whenGetApiDocv2Endpoint() { - final ValidatableResponse response = when() - .get(baseHost + GET_API_CATALOG_API_DOC_ENDPOINT) - .then() - .statusCode(is(SC_OK)) - .contentType("application/json"); - assertEquals("Service 2 - v2", response.extract().jsonPath().get("info.title")); - } - } - } - - @Nested - class Access { - - @Nested - class AuthenticationIsNotRequired { - - @Test - void givenBasicAuthenticationIsProvided() { - given() - .auth() - .basic(USERNAME, PASSWORD) - .when() - .get(baseHost + GET_ALL_CONTAINERS_ENDPOINT) - .then() - .statusCode(is(SC_OK)) - .contentType("application/json"); - } - } - } -}