diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 93325c9df2..ea7eecb6fd 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -270,26 +270,27 @@ jobs:
services:
# First group of services represents central apiml instance with central gateway registry
+ api-catalog-services:
+ image: ghcr.io/balhar-jakub/api-catalog-services:${{ github.run_id }}-${{ github.run_number }}
+ volumes:
+ - /api-defs:/api-defs
discovery-service:
image: ghcr.io/balhar-jakub/discovery-service:${{ github.run_id }}-${{ github.run_number }}
- gateway-service:
- image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }}
- env:
- APIML_SERVICE_APIMLID: central-apiml
- APIML_SERVICE_HOSTNAME: gateway-service
zaas-service:
image: ghcr.io/balhar-jakub/zaas-service:${{ github.run_id }}-${{ github.run_number }}
env:
APIML_SECURITY_X509_ENABLED: true
APIML_SECURITY_X509_ACCEPTFORWARDEDCERT: true
APIML_SECURITY_X509_CERTIFICATESURL: https://gateway-service:10010/gateway/certificates
- central-gateway-service:
+ gateway-service:
image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }}
env:
APIML_SERVICE_APIMLID: central-apiml
- APIML_SERVICE_HOSTNAME: central-gateway-service
+ APIML_SERVICE_HOSTNAME: gateway-service
APIML_GATEWAY_REGISTRY_ENABLED: true
APIML_SECURITY_X509_REGISTRY_ALLOWEDUSERS: USER,UNKNOWNUSER
+ mock-services:
+ image: ghcr.io/balhar-jakub/mock-services:${{ github.run_id }}-${{ github.run_number }}
# Second group of services represents domain apiml instance which registers it's gateway in central's discovery service
discovery-service-2:
@@ -299,16 +300,6 @@ jobs:
env:
APIML_SERVICE_HOSTNAME: discovery-service-2
APIML_SERVICE_PORT: 10031
- gateway-service-2:
- image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }}
- env:
- APIML_SERVICE_APIMLID: domain-apiml
- APIML_SERVICE_HOSTNAME: gateway-service-2
- APIML_SERVICE_PORT: 10037
- APIML_SERVICE_DISCOVERYSERVICEURLS: https://discovery-service-2:10031/eureka/
- ZWE_CONFIGS_APIML_SERVICE_ADDITIONALREGISTRATION_0_DISCOVERYSERVICEURLS: https://discovery-service:10011/eureka
- ZWE_CONFIGS_APIML_SERVICE_ADDITIONALREGISTRATION_0_ROUTES_0_GATEWAYURL: /
- ZWE_CONFIGS_APIML_SERVICE_ADDITIONALREGISTRATION_0_ROUTES_0_SERVICEURL: /
zaas-service-2:
image: ghcr.io/balhar-jakub/zaas-service:${{ github.run_id }}-${{ github.run_number }}
env:
@@ -316,11 +307,11 @@ jobs:
APIML_SECURITY_X509_ACCEPTFORWARDEDCERT: true
APIML_SECURITY_X509_CERTIFICATESURL: https://gateway-service:10010/gateway/certificates
APIML_SERVICE_DISCOVERYSERVICEURLS: https://discovery-service-2:10031/eureka/
- central-gateway-service-2:
+ gateway-service-2:
image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }}
env:
APIML_SERVICE_APIMLID: domain-apiml
- APIML_SERVICE_HOSTNAME: central-gateway-service-2
+ APIML_SERVICE_HOSTNAME: gateway-service-2
APIML_GATEWAY_REGISTRY_ENABLED: false
APIML_SECURITY_X509_REGISTRY_ALLOWEDUSERS: USER,UNKNOWNUSER
APIML_SERVICE_DISCOVERYSERVICEURLS: https://discovery-service-2:10031/eureka/
@@ -447,7 +438,6 @@ jobs:
gateway-service:
image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }}
env:
- APIML_SECURITY_X509_ENABLED: true
APIML_SECURITY_X509_ACCEPTFORWARDEDCERT: true
APIML_SECURITY_X509_CERTIFICATESURL: https://gateway-service:10010/gateway/certificates
mock-services:
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/services/cached/CachedProductFamilyService.java
index 03cb15e6dc..edc2993115 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/services/cached/CachedProductFamilyService.java
@@ -384,13 +384,17 @@ APIService createAPIServiceFromInstance(InstanceInfo instanceInfo) {
String title = instanceInfo.getMetadata().get(SERVICE_TITLE);
if (StringUtils.equalsIgnoreCase(GATEWAY.getServiceId(), serviceId)) {
if (RegistrationType.of(instanceInfo.getMetadata()).isAdditional()) {
- // additional registration for GW means domain one, update serviceId with the ApimlId
+ // additional registration for GW means domain one, update serviceId and basePath with the ApimlId
String apimlId = instanceInfo.getMetadata().get(APIML_ID);
if (apimlId != null) {
serviceId = apimlId;
+ apiBasePath = String.join("/", "", serviceId.toLowerCase());
title += " (" + apimlId + ")";
}
}
+ else {
+ apiBasePath = "/";
+ }
}
return new APIService.Builder(StringUtils.lowerCase(serviceId))
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
index 7728dff03e..20839e7376 100644
--- 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
@@ -546,6 +546,7 @@ void givenPrimaryInstance_whenCreateDto_thenDoNotUpdateTitle() {
var dto = createDto(RegistrationType.ADDITIONAL);
assertEquals("title (apimlId)", dto.getTitle());
assertEquals("apimlid", dto.getServiceId());
+ assertEquals("/apimlid", dto.getBasePath());
}
@Test
@@ -553,6 +554,7 @@ 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-ui/frontend/cypress/e2e/detail-page/multiple-gateway-services.cy.js b/api-catalog-ui/frontend/cypress/e2e/detail-page/multiple-gateway-services.cy.js
index 2290286c6b..fe6214ed4b 100644
--- a/api-catalog-ui/frontend/cypress/e2e/detail-page/multiple-gateway-services.cy.js
+++ b/api-catalog-ui/frontend/cypress/e2e/detail-page/multiple-gateway-services.cy.js
@@ -43,7 +43,7 @@ describe('>>> Multi-tenancy deployment test', () => {
'#swaggerContainer > div > div:nth-child(2) > div.scheme-container > section > div:nth-child(1) > div > div > label > select > option'
)
.should('exist')
- .should('contain', `${baseUrl.match(/^https?:\/\/([^/?#]+)(?:[/?#]|$)/i)[1]}/gateway/api/v1`);
+ .should('contain', `${baseUrl.match(/^https?:\/\/([^/?#]+)(?:[/?#]|$)/i)[1]}/apiml2/gateway/api/v1`);
cy.get('.tabs-container').should('not.exist');
cy.get('.serviceTab').should('exist').and('contain', 'API Gateway');
diff --git a/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js b/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js
index 9e01a1309c..7214f781d0 100644
--- a/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js
+++ b/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js
@@ -56,7 +56,12 @@ describe("Swagger rendering", () => {
.get('label')
.should('contain', "API Base Path:");
- const regex = new RegExp(`^\/${service.serviceId}\/api(\/v1)?$`);
+ let regexContent = `^\/${service.serviceId}\/api(\/v1)?$`;
+ if (service.serviceId === 'gateway') {
+ regexContent = '/';
+ }
+ const regex = new RegExp(regexContent);
+
cy.get('@basePath')
.get('#apiBasePath').invoke("text").should(text => {
expect(text).to.match(regex);
diff --git a/api-catalog-ui/frontend/package-lock.json b/api-catalog-ui/frontend/package-lock.json
index c5bbe565da..7af0f3c103 100644
--- a/api-catalog-ui/frontend/package-lock.json
+++ b/api-catalog-ui/frontend/package-lock.json
@@ -36,6 +36,7 @@
"lodash": "4.17.21",
"loglevel": "1.9.2",
"openapi-snippet": "0.14.0",
+ "process": "0.11.10",
"react": "18.3.1",
"react-app-polyfill": "3.0.0",
"react-dom": "18.3.1",
diff --git a/api-catalog-ui/frontend/package.json b/api-catalog-ui/frontend/package.json
index 5c8cda7ba5..8ee9b73005 100644
--- a/api-catalog-ui/frontend/package.json
+++ b/api-catalog-ui/frontend/package.json
@@ -32,6 +32,7 @@
"lodash": "4.17.21",
"loglevel": "1.9.2",
"openapi-snippet": "0.14.0",
+ "process": "0.11.10",
"react": "18.3.1",
"react-app-polyfill": "3.0.0",
"react-dom": "18.3.1",
@@ -136,8 +137,8 @@
"source-map-explorer": "2.5.3",
"start-server-and-test": "2.0.8",
"tmpl": "1.0.5",
- "yaml": "2.6.0",
- "undici": "6.19.8"
+ "undici": "6.19.8",
+ "yaml": "2.6.0"
},
"overrides": {
"nth-check": "2.1.1",
diff --git a/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.jsx b/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.jsx
index 383f0d7806..0fda99ea06 100644
--- a/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.jsx
+++ b/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.jsx
@@ -36,14 +36,19 @@ export default class ServiceTab extends Component {
const { selectedVersion } = this.state;
let basePath = '';
- if (selectedService.basePath) {
- const version = selectedVersion || selectedService.defaultApiVersion;
- let gatewayUrl = '';
- if (selectedService.apis && selectedService.apis[version] && selectedService.apis[version].gatewayUrl) {
- gatewayUrl = selectedService.apis[version].gatewayUrl;
+ if (selectedService?.basePath) {
+ if (selectedService?.instances?.[0]?.includes('gateway')) {
+ // Return the basePath right away, since it's a GW instance (either primary or additional)
+ basePath = selectedService.basePath;
+ } else {
+ const version = selectedVersion || selectedService.defaultApiVersion;
+ let gatewayUrl = '';
+ if (selectedService.apis && selectedService.apis[version] && selectedService.apis[version].gatewayUrl) {
+ gatewayUrl = selectedService.apis[version].gatewayUrl;
+ }
+ // Take the first part of the basePath and then add the gatewayUrl
+ basePath = `/${selectedService.serviceId}/${gatewayUrl}`;
}
- // Take the first part of the basePath and then add the gatewayUrl
- basePath = `/${selectedService.serviceId}/${gatewayUrl}`;
}
return basePath;
}
@@ -321,6 +326,7 @@ ServiceTab.propTypes = {
gatewayUrl: PropTypes.string,
})
),
+ instances: PropTypes.arrayOf(PropTypes.string),
apiVersions: PropTypes.arrayOf(PropTypes.string),
serviceId: PropTypes.string,
status: PropTypes.string,
diff --git a/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.test.jsx b/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.test.jsx
index a22c631bbb..15ba989606 100644
--- a/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.test.jsx
+++ b/api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.test.jsx
@@ -31,6 +31,7 @@ const selectedService = {
defaultApiVersion: ['org.zowe v1'],
ssoAllInstances: true,
apis: { 'org.zowe v1': { gatewayUrl: 'api/v1' } },
+ instances: ["localhost:gateway:10010"]
};
const selectedServiceDown = {
diff --git a/api-catalog-ui/frontend/src/components/Swagger/SwaggerUIApiml.jsx b/api-catalog-ui/frontend/src/components/Swagger/SwaggerUIApiml.jsx
index 8fd836cb4c..603fd2e5f2 100644
--- a/api-catalog-ui/frontend/src/components/Swagger/SwaggerUIApiml.jsx
+++ b/api-catalog-ui/frontend/src/components/Swagger/SwaggerUIApiml.jsx
@@ -14,23 +14,30 @@ import InstanceInfo from '../ServiceTab/InstanceInfo';
import getBaseUrl from '../../helpers/urls';
import { CustomizedSnippedGenerator } from '../../utils/generateSnippets';
import { AdvancedFilterPlugin } from '../../utils/filterApis';
+import PropTypes from "prop-types";
-function transformSwaggerToCurrentHost(swagger) {
+function transformSwaggerToCurrentHost(swagger, selectedService) {
swagger.host = window.location.host;
- if (swagger.servers !== null && swagger.servers !== undefined) {
+ if (swagger.servers?.length) {
swagger.servers.forEach((server) => {
const location = `${window.location.protocol}//${window.location.host}`;
try {
const swaggerUrl = new URL(server.url);
- server.url = location + swaggerUrl.pathname;
+ if (swaggerUrl?.pathname?.includes('gateway')) {
+ const basePath = selectedService?.basePath === '/' ? '' : selectedService?.basePath || '';
+
+ server.url = location + basePath + swaggerUrl.pathname;
+ }
+ else {
+ server.url = location + swaggerUrl.pathname;
+ }
} catch (e) {
// not a proper url, assume it is an endpoint
server.url = location + server;
}
});
}
-
return swagger;
}
@@ -130,11 +137,9 @@ export default class SwaggerUIApiml extends Component {
// If no version selected use the default apiDoc
if (
(selectedVersion === null || selectedVersion === undefined) &&
- selectedService.apiDoc !== null &&
- selectedService.apiDoc !== undefined &&
- selectedService.apiDoc.length !== 0
+ selectedService?.apiDoc?.length
) {
- const swagger = transformSwaggerToCurrentHost(JSON.parse(selectedService.apiDoc));
+ const swagger = transformSwaggerToCurrentHost(JSON.parse(selectedService.apiDoc), selectedService);
this.setState({
swaggerReady: true,
@@ -148,9 +153,9 @@ export default class SwaggerUIApiml extends Component {
},
});
}
- if (selectedVersion !== null && selectedVersion !== undefined) {
+ if (selectedVersion && selectedService) {
const basePath = `${selectedService.serviceId}/${selectedVersion}`;
- const url = `${getBaseUrl()}${process.env.REACT_APP_APIDOC_UPDATE}/${basePath}`;
+ const url = `${getBaseUrl()}${process?.env.REACT_APP_APIDOC_UPDATE}/${basePath}`;
this.setState({
swaggerReady: true,
swaggerProps: {
@@ -161,7 +166,7 @@ export default class SwaggerUIApiml extends Component {
plugins: [this.customPlugins, AdvancedFilterPlugin, CustomizedSnippedGenerator(codeSnippets)],
responseInterceptor: (res) => {
// response.text field is used to render the swagger
- const swagger = transformSwaggerToCurrentHost(JSON.parse(res.text));
+ const swagger = transformSwaggerToCurrentHost(JSON.parse(res.text), selectedService);
res.text = JSON.stringify(swagger);
return res;
},
@@ -204,6 +209,13 @@ export default class SwaggerUIApiml extends Component {
}
}
+SwaggerUIApiml.propTypes = {
+ selectedService: PropTypes.shape({
+ apiDoc: PropTypes.string,
+ }).isRequired,
+ url: PropTypes.string,
+};
+
SwaggerUIApiml.defaultProps = {
url: `${getBaseUrl()}/apidoc`,
};
diff --git a/api-catalog-ui/frontend/src/components/Swagger/SwaggerUI.test.jsx b/api-catalog-ui/frontend/src/components/Swagger/SwaggerUIApiml.test.jsx
similarity index 89%
rename from api-catalog-ui/frontend/src/components/Swagger/SwaggerUI.test.jsx
rename to api-catalog-ui/frontend/src/components/Swagger/SwaggerUIApiml.test.jsx
index 874199559f..f0557c609b 100644
--- a/api-catalog-ui/frontend/src/components/Swagger/SwaggerUI.test.jsx
+++ b/api-catalog-ui/frontend/src/components/Swagger/SwaggerUIApiml.test.jsx
@@ -104,7 +104,8 @@ describe('>>> Swagger component tests', () => {
const container = document.createElement('div');
document.body.appendChild(container);
- await act(async () => createRoot(container).render(