Skip to content

Commit 6400aa3

Browse files
fix: Display multiple APIs with multiple different versions. (#1800)
* Some work on the topic Signed-off-by: Jakub Balhar <jakub@balhar.net> * Show properly multiple APIs with multiple Versions Signed-off-by: Jakub Balhar <jakub@balhar.net> * Fix the processing of Service Tab Signed-off-by: Jakub Balhar <jakub@balhar.net> * Properly return the highest version available as default. Signed-off-by: Jakub Balhar <jakub@balhar.net> * Keep one default Reflect Andrej's comments Signed-off-by: Jakub Balhar <jakub@balhar.net> * Fix other issues Signed-off-by: Jakub Balhar <jakub@balhar.net> * Retrieve the first API doc when invalid version provided. Signed-off-by: Jakub Balhar <jakub@balhar.net> * Fix style issue Signed-off-by: Jakub Balhar <jakub@balhar.net> * Improve coverage Signed-off-by: Jakub Balhar <jakub@balhar.net> * Merge conflicts Signed-off-by: jandadav <janda.david@gmail.com> Co-authored-by: jandadav <janda.david@gmail.com>
1 parent 39f73f6 commit 6400aa3

File tree

18 files changed

+213
-53
lines changed

18 files changed

+213
-53
lines changed

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocController.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.springframework.web.bind.annotation.PathVariable;
1919
import org.springframework.web.bind.annotation.RequestMapping;
2020
import org.springframework.web.bind.annotation.RestController;
21+
import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException;
2122

2223
/**
2324
* Main API for handling requests from the API Catalog UI, routed through the gateway
@@ -67,7 +68,11 @@ public ResponseEntity<String> getApiDocInfo(
6768
@PathVariable(value = "serviceId") String serviceId,
6869
@ApiParam(name = "apiVersion", value = "The major version of the API documentation (v1, v2, etc.)", required = true, example = "v1")
6970
@PathVariable(value = "apiVersion") String apiVersion) {
70-
return this.apiServiceStatusService.getServiceCachedApiDocInfo(serviceId, apiVersion);
71+
try {
72+
return this.apiServiceStatusService.getServiceCachedApiDocInfo(serviceId, apiVersion);
73+
} catch (ApiDocNotFoundException e) {
74+
return this.apiServiceStatusService.getServiceCachedApiDocInfo(serviceId, null);
75+
}
7176
}
7277

7378
@GetMapping(value = "/{serviceId}/{apiVersion1}/{apiVersion2}", produces = MediaType.TEXT_HTML_VALUE)

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/model/APIService.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,16 @@ public class APIService implements Serializable {
6565
@ApiModelProperty(notes = "The API ID for this service")
6666
private Map<String, String> apiId;
6767

68+
@ApiModelProperty(notes = "The Gateway URLs used within this service")
69+
private Map<String, String> gatewayUrls;
70+
6871
private APIService(String serviceId) {
6972
this.serviceId = serviceId;
7073
this.status = "UP";
7174
}
7275
public static class Builder {
7376
private APIService apiService;
74-
77+
7578
public Builder(String serviceId) {
7679
apiService = new APIService(serviceId);
7780
apiService.status = "UP";
@@ -127,6 +130,11 @@ public Builder apiId(Map<String, String> apiId) {
127130
return this;
128131
}
129132

133+
public Builder gatewayUrls(Map<String, String> gatewayUrls) {
134+
apiService.gatewayUrls = gatewayUrls;
135+
return this;
136+
}
137+
130138
public APIService build() {
131139
return apiService;
132140
}

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyService.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,21 @@ private APIService createAPIServiceFromInstance(InstanceInfo instanceInfo) {
328328
String instanceHomePage = getInstanceHomePageUrl(instanceInfo);
329329
String apiBasePath = getApiBasePath(instanceInfo);
330330
Map<String, String> apiId = new HashMap<>();
331+
Map<String, String> gatewayUrls = new HashMap<>();
331332
try {
332333
apiId = metadataParser.parseApiInfo(instanceInfo.getMetadata()).stream().filter(apiInfo -> apiInfo.getApiId() != null).collect(
333334
Collectors.toMap(
334-
apiInfo -> (apiInfo.getMajorVersion() < 0) ? "default" : "v" + apiInfo.getMajorVersion(),
335+
apiInfo -> (apiInfo.getMajorVersion() < 0) ? "default" : apiInfo.getApiId() + " v" + apiInfo.getVersion(),
335336
ApiInfo::getApiId
336337
)
337338
);
339+
340+
gatewayUrls = metadataParser.parseApiInfo(instanceInfo.getMetadata()).stream().filter(apiInfo -> apiInfo.getApiId() != null).collect(
341+
Collectors.toMap(
342+
apiInfo -> (apiInfo.getMajorVersion() < 0) ? "default" : apiInfo.getApiId() + " v" + apiInfo.getVersion(),
343+
ApiInfo::getGatewayUrl
344+
)
345+
);
338346
} catch (Exception ex) {
339347
log.info("createApiServiceFromInstance#incorrectVersions {}", ex.getMessage());
340348
}
@@ -348,6 +356,7 @@ private APIService createAPIServiceFromInstance(InstanceInfo instanceInfo) {
348356
.basePath(apiBasePath)
349357
.sso(isSso(instanceInfo))
350358
.apiId(apiId)
359+
.gatewayUrls(gatewayUrls)
351360
.build();
352361
}
353362

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIDocRetrievalService.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.ArrayList;
3333
import java.util.Collections;
3434
import java.util.List;
35+
import java.util.Optional;
3536

3637
/**
3738
* Retrieves the API documentation for a registered service
@@ -67,11 +68,7 @@ public List<String> retrieveApiVersions(@NonNull String serviceId) {
6768
List<ApiInfo> apiInfoList = metadataParser.parseApiInfo(instanceInfo.getMetadata());
6869
List<String> apiVersions = new ArrayList<>();
6970
for (ApiInfo apiInfo : apiInfoList) {
70-
int majorVersion = getMajorVersion(apiInfo);
71-
if (majorVersion >= 0) {
72-
// -1 indicates major version not found
73-
apiVersions.add("v" + majorVersion);
74-
}
71+
apiVersions.add(apiInfo.getApiId() + " v" + apiInfo.getVersion());
7572
}
7673
return apiVersions;
7774
}
@@ -97,7 +94,11 @@ public String retrieveDefaultApiVersion(@NonNull String serviceId) {
9794
List<ApiInfo> apiInfoList = metadataParser.parseApiInfo(instanceInfo.getMetadata());
9895
ApiInfo defaultApiInfo = getDefaultApiInfo(apiInfoList);
9996

100-
return "v" + getMajorVersion(defaultApiInfo);
97+
if (defaultApiInfo == null) {
98+
return "";
99+
}
100+
101+
return defaultApiInfo.getApiId() + " v" + defaultApiInfo.getVersion();
101102
}
102103

103104
/**
@@ -314,12 +315,25 @@ private ApiInfo findApi(List<ApiInfo> apiInfos, String apiVersion) {
314315
return null;
315316
}
316317

317-
return apiInfos.stream()
318+
if (apiVersion == null) {
319+
return apiInfos.get(0);
320+
}
321+
322+
String[] api = apiVersion.split(" ");
323+
String apiId = api.length > 0 ? api[0] : "";
324+
String version = api.length > 1 ? api[1].replace("v", "") : "";
325+
326+
Optional<ApiInfo> result = apiInfos.stream()
318327
.filter(
319-
f -> f.getGatewayUrl().equals(apiVersion == null ? "api" : "api/" + apiVersion)
328+
f -> apiId.equals(f.getApiId()) && (version == null || version.equals(f.getVersion()))
320329
)
321-
.findFirst()
322-
.orElse(apiInfos.get(0));
330+
.findFirst();
331+
332+
if (!result.isPresent()) {
333+
throw new ApiDocNotFoundException("There is no api doc for combination of apiId and version. " + apiId + " " + version);
334+
} else {
335+
return result.get();
336+
}
323337
}
324338

325339
private InstanceInfo getInstanceInfo(String serviceId) {

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/APIServiceStatusService.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.http.MediaType;
2929
import org.springframework.http.ResponseEntity;
3030
import org.springframework.stereotype.Service;
31+
import org.zowe.apiml.apicatalog.services.status.model.ApiDocNotFoundException;
3132

3233
import java.util.ArrayList;
3334
import java.util.Collections;
@@ -96,16 +97,16 @@ public ResponseEntity<String> getServiceCachedApiDocInfo(@NonNull String service
9697
* @return response containing HTML document detailing changes between api doc versions
9798
*/
9899
public ResponseEntity<String> getApiDiffInfo(@NonNull String serviceId, String apiVersion1, String apiVersion2) {
99-
String doc1 = cachedApiDocService.getApiDocForService(serviceId, apiVersion1);
100-
String doc2 = cachedApiDocService.getApiDocForService(serviceId, apiVersion2);
101100
try {
101+
String doc1 = cachedApiDocService.getApiDocForService(serviceId, apiVersion1);
102+
String doc2 = cachedApiDocService.getApiDocForService(serviceId, apiVersion2);
102103
ChangedOpenApi diff = openApiCompareProducer.fromContents(doc1, doc2);
103104
HtmlRender render = new HtmlRender();
104105
String result = render.render(diff);
105106
//Remove external stylesheet
106107
result = result.replace("<link rel=\"stylesheet\" href=\"http://deepoove.com/swagger-diff/stylesheets/demo.css\">", "");
107108
return new ResponseEntity<>(result, createHeaders(), HttpStatus.OK);
108-
} catch (NullPointerException e) {
109+
} catch (NullPointerException | ApiDocNotFoundException e) {
109110
throw new ApiDiffNotAvailableException(String.format("No Diff available for %s and versions %s and %s", serviceId, apiVersion1, apiVersion2));
110111
}
111112
}

api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class CatalogApiDocControllerApiDocNotFoundTest {
3838
private MockMvc mockMvc;
3939

4040
@Test
41-
void getApiDocAnfFailThenThrowApiDocNotFoundException() throws Exception {
41+
void getApiDocAndFailThenThrowApiDocNotFoundException() throws Exception {
4242
this.mockMvc.perform(get("/apidoc/service2/v1"))
4343
.andExpect(status().isInternalServerError())
4444
.andExpect(jsonPath("$.messages[?(@.messageNumber == 'ZWEAC103E')].messageContent",

api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public CatalogApiDocController catalogApiDocController() {
3030
when(apiServiceStatusService.getServiceCachedApiDocInfo("service2", "v1"))
3131
.thenThrow(new ApiDocNotFoundException("Really bad stuff happened"));
3232

33+
when(apiServiceStatusService.getServiceCachedApiDocInfo("service2", null))
34+
.thenThrow(new ApiDocNotFoundException("Really bad stuff happened"));
35+
3336
verify(apiServiceStatusService, never()).getServiceCachedApiDocInfo("service2", "v1");
3437

3538
return new CatalogApiDocController(apiServiceStatusService);
@@ -41,7 +44,8 @@ public MessageService messageService() {
4144
}
4245

4346
@Bean
44-
public CatalogApiDocControllerExceptionHandler catalogApiDocControllerExceptionHandler() {
45-
return new CatalogApiDocControllerExceptionHandler(messageService());
47+
public CatalogApiDocControllerExceptionHandler catalogApiDocControllerExceptionHandler(MessageService messageService) {
48+
return new CatalogApiDocControllerExceptionHandler(messageService);
4649
}
50+
4751
}

api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/cached/CachedProductFamilyServiceTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,8 @@ void testGivenMultipleApiIds_whenCalculateContainerServiceValues_thenGroupThem()
458458
APIService apiService = apiContainer.getServices().iterator().next();
459459
assertNotNull(apiService.getApiId());
460460
assertEquals(3, apiService.getApiId().size());
461-
assertEquals("api1", apiService.getApiId().get("v1"));
462-
assertEquals("api2", apiService.getApiId().get("v2"));
461+
assertEquals("api1", apiService.getApiId().get("api1 v1.0.0"));
462+
assertEquals("api2", apiService.getApiId().get("api2 v2"));
463463
assertEquals("api3", apiService.getApiId().get("default"));
464464
}
465465

api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/services/status/LocalApiDocServiceTest.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ class LocalApiDocServiceTest {
4747
private static final int SERVICE_PORT = 8080;
4848
private static final String SERVICE_VERSION = "1.0.0";
4949
private static final String HIGHER_SERVICE_VERSION = "2.0.0";
50-
private static final String SERVICE_VERSION_V = "v1";
51-
private static final String HIGHER_SERVICE_VERSION_V = "v2";
50+
private static final String SERVICE_VERSION_V = "test.app v1.0.0";
51+
private static final String HIGHER_SERVICE_VERSION_V = "test.app v2.0.0";
5252
private static final String GATEWAY_SCHEME = "http";
5353
private static final String GATEWAY_HOST = "gateway:10000";
5454
private static final String GATEWAY_URL = "api/v1";
@@ -83,7 +83,7 @@ void testRetrievalOfAPIDoc() {
8383
when(restTemplate.exchange(SWAGGER_URL, HttpMethod.GET, getObjectHttpEntity(), String.class))
8484
.thenReturn(expectedResponse);
8585

86-
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION);
86+
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V);
8787

8888
assertEquals(API_ID, actualResponse.getApiInfo().getApiId());
8989
assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl());
@@ -109,7 +109,7 @@ void givenZosmfId_whenDocIsRequested_ValidDocIsProduced() {
109109
when(restTemplate.exchange(SWAGGER_URL, HttpMethod.GET, getObjectHttpEntity(), String.class))
110110
.thenReturn(expectedResponse);
111111

112-
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(ZOSMF_ID, SERVICE_VERSION);
112+
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(ZOSMF_ID, SERVICE_VERSION_V);
113113

114114
assertEquals(API_ID, actualResponse.getApiInfo().getApiId());
115115
assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl());
@@ -134,14 +134,14 @@ void givenZosmfId_whenIncorrectResponseFromServer_thenReturnDefaultDoc() {
134134
when(restTemplate.exchange(SWAGGER_URL, HttpMethod.GET, getObjectHttpEntity(), String.class))
135135
.thenReturn(expectedResponse);
136136

137-
ApiDocInfo result = apiDocRetrievalService.retrieveApiDoc(ZOSMF_ID, SERVICE_VERSION);
137+
ApiDocInfo result = apiDocRetrievalService.retrieveApiDoc(ZOSMF_ID, SERVICE_VERSION_V);
138138
assertThat(result.getApiDocContent(), is(notNullValue()));
139139
}
140140

141141
@Test
142142
void testFailedRetrievalOfAPIDocWhenServiceNotFound() {
143143
Exception exception = assertThrows(ApiDocNotFoundException.class, () -> {
144-
apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION);
144+
apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V);
145145
});
146146
assertEquals("Could not load instance information for service " + SERVICE_ID + ".", exception.getMessage());
147147
}
@@ -158,7 +158,7 @@ void testFailedRetrievalOfAPIDocWhenServerError() {
158158
.thenReturn(expectedResponse);
159159

160160
Exception exception = assertThrows(ApiDocNotFoundException.class, () -> {
161-
apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION);
161+
apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V);
162162
});
163163
assertEquals("No API Documentation was retrieved due to " + SERVICE_ID + " server error: '" + responseBody + "'.", exception.getMessage());
164164
}
@@ -175,7 +175,7 @@ void testFailedRetrievalOfAPIDocWhenMetadataNotDefined() {
175175
.thenReturn(expectedResponse);
176176

177177
Exception exception = assertThrows(ApiDocNotFoundException.class, () -> {
178-
apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION);
178+
apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V);
179179
});
180180
assertEquals("No API Documentation defined for service " + SERVICE_ID + ".", exception.getMessage());
181181
}
@@ -220,7 +220,7 @@ void shouldGenerateSubstituteSwaggerIfSwaggerUrlNull() {
220220
when(restTemplate.exchange(SWAGGER_URL, HttpMethod.GET, getObjectHttpEntity(), String.class))
221221
.thenReturn(expectedResponse);
222222

223-
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION);
223+
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V);
224224

225225
assertEquals(API_ID, actualResponse.getApiInfo().getApiId());
226226
assertEquals(GATEWAY_URL, actualResponse.getApiInfo().getGatewayUrl());
@@ -245,7 +245,7 @@ void shouldCreateApiDocUrlFromRouting() {
245245
when(restTemplate.exchange(SWAGGER_URL, HttpMethod.GET, getObjectHttpEntity(), String.class))
246246
.thenReturn(expectedResponse);
247247

248-
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION);
248+
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V);
249249

250250
assertNotNull(actualResponse);
251251
assertNotNull(actualResponse.getApiDocContent());
@@ -264,7 +264,7 @@ void shouldCreateApiDocUrlFromRoutingAndUseHttp() {
264264
when(restTemplate.exchange("http://service:8080/service/api-doc", HttpMethod.GET, getObjectHttpEntity(), String.class))
265265
.thenReturn(expectedResponse);
266266

267-
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION);
267+
ApiDocInfo actualResponse = apiDocRetrievalService.retrieveApiDoc(SERVICE_ID, SERVICE_VERSION_V);
268268

269269
assertNotNull(actualResponse);
270270
assertNotNull(actualResponse.getApiDocContent());
@@ -375,7 +375,7 @@ void givenApiVersions_whenRetrieveVersions_thenReturnThem() {
375375
.thenReturn(getStandardInstance(getStandardMetadata(), false));
376376

377377
List<String> actualVersions = apiDocRetrievalService.retrieveApiVersions(SERVICE_ID);
378-
assertEquals(Collections.singletonList("v1"), actualVersions);
378+
assertEquals(Collections.singletonList(SERVICE_VERSION_V), actualVersions);
379379
}
380380

381381
@Test
@@ -394,7 +394,7 @@ void givenDefaultApiVersion_whenRetrieveDefaultVersion_thenReturnIt() {
394394
.thenReturn(getStandardInstance(getMetadataWithMultipleApiInfo(), false));
395395

396396
String defaultVersion = apiDocRetrievalService.retrieveDefaultApiVersion(SERVICE_ID);
397-
assertEquals("v1", defaultVersion);
397+
assertEquals(SERVICE_VERSION_V, defaultVersion);
398398
}
399399

400400
@Test
@@ -406,7 +406,7 @@ void givenNoDefaultApiVersion_whenRetrieveDefaultVersion_thenReturnHighestVersio
406406
.thenReturn(getStandardInstance(metadata, false));
407407

408408
String defaultVersion = apiDocRetrievalService.retrieveDefaultApiVersion(SERVICE_ID);
409-
assertEquals("v2", defaultVersion);
409+
assertEquals(HIGHER_SERVICE_VERSION_V, defaultVersion);
410410
}
411411

412412
@Test

api-catalog-ui/frontend/src/components/ServiceTab/ServiceTab.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ export default class ServiceTab extends Component {
2121
let basePath = '';
2222
if (selectedService.basePath) {
2323
const version = selectedVersion || selectedService.defaultApiVersion;
24-
basePath = selectedService.basePath.replace('{api-version}', version);
24+
let gatewayUrl = '';
25+
if (selectedService.gatewayUrls && selectedService.gatewayUrls[version]) {
26+
gatewayUrl = selectedService.gatewayUrls[version];
27+
}
28+
// Take the first part of the basePath and then add the gatewayUrl
29+
basePath = `/${selectedService.serviceId}/${gatewayUrl}`;
2530
}
2631
return basePath;
2732
}

0 commit comments

Comments
 (0)