Skip to content

Commit

Permalink
feat: Code snippet configuration (#2509)
Browse files Browse the repository at this point in the history
* Send full ApiInfos to UI

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Fix unit tests

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* WIP - work on mapping code snippets to ApiInfo

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* Add missing class

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* Use object mapper to parse apiinfo

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Deserialize one code block

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Convert map to map of list object in order to avoid replacing of values in nested map

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* Support multiple codesnippet entries

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Improve codesnippet testing

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Suppress unchecked warnings

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Propagate codesnippet config to UI

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Add unit test

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Add ApiInfo integration test

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Remove console.log

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Fix integration test

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Fix code smells

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Fix code smell

Signed-off-by: Carson Cook <carson.cook@ibm.com>

* Remove unused message

Signed-off-by: Carson Cook <carson.cook@ibm.com>

Co-authored-by: Andrea Tabone <andrea.tabone@broadcom.com>
Co-authored-by: Andrea Tabone <39694626+taban03@users.noreply.github.com>
  • Loading branch information
3 people committed Jul 22, 2022
1 parent 11bf491 commit 4d2298e
Show file tree
Hide file tree
Showing 20 changed files with 343 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.zowe.apiml.config.ApiInfo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -63,11 +65,8 @@ public class APIService implements Serializable {
@Schema(description = "The SSO support for all instances")
private boolean ssoAllInstances;

@Schema(description = "The API ID for this service")
private Map<String, String> apiId;

@Schema(description = "The Gateway URLs used within this service")
private Map<String, String> gatewayUrls;
@Schema(description = "The API information for each API ID for this service")
private Map<String, ApiInfo> apis = new HashMap<>(); // NOSONAR

private List<String> instances = new ArrayList<>();

Expand All @@ -77,7 +76,7 @@ private APIService(String serviceId) {
}

public static class Builder {
private APIService apiService;
private final APIService apiService;

public Builder(String serviceId) {
apiService = new APIService(serviceId);
Expand Down Expand Up @@ -129,18 +128,13 @@ public Builder sso(boolean sso) {
return this;
}

public Builder apiId(Map<String, String> apiId) {
apiService.apiId = apiId;
return this;
}

public Builder gatewayUrls(Map<String, String> gatewayUrls) {
apiService.gatewayUrls = gatewayUrls;
public Builder instanceId(String id) {
apiService.instances.add(id);
return this;
}

public Builder instanceId(String id) {
apiService.instances.add(id);
public Builder apis(Map<String, ApiInfo> apis) {
apiService.apis = apis;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;
import static org.zowe.apiml.constants.EurekaMetadataDefinition.*;
Expand All @@ -43,6 +42,8 @@
@Service
public class CachedProductFamilyService {

private static final String DEFAULT_APIINFO_KEY = "default";

@InjectApimlLogger
private final ApimlLogger apimlLog = ApimlLogger.empty();

Expand Down Expand Up @@ -130,7 +131,7 @@ public APIContainer saveContainerFromInstance(String productFamilyId, InstanceIn
} else {
Set<APIService> apiServices = container.getServices();
APIService service = createAPIServiceFromInstance(instanceInfo);

// Verify whether already exists
if (apiServices.contains(service)) {
apiServices.stream()
Expand All @@ -139,7 +140,7 @@ public APIContainer saveContainerFromInstance(String productFamilyId, InstanceIn
if (!existingService.getInstances().contains(instanceInfo.getInstanceId())) {
existingService.getInstances().add(instanceInfo.getInstanceId());
}
}); // If the instance is in list, do nothing otherwise
}); // If the instance is in list, do nothing otherwise
} else {
apiServices.add(service);
}
Expand All @@ -166,7 +167,7 @@ public APIContainer saveContainerFromInstance(String productFamilyId, InstanceIn
* 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
*/
Expand Down Expand Up @@ -219,7 +220,7 @@ public void calculateContainerServiceValues(APIContainer apiContainer) {
boolean isSso = servicesCount > 0;
for (APIService apiService : apiContainer.getServices()) {
if (update(apiService)) {
activeServicesCount ++;
activeServicesCount++;
}
isSso &= apiService.isSsoAllInstances();
}
Expand Down Expand Up @@ -333,22 +334,19 @@ private APIService createAPIServiceFromInstance(InstanceInfo instanceInfo) {

String instanceHomePage = getInstanceHomePageUrl(instanceInfo);
String apiBasePath = getApiBasePath(instanceInfo);
Map<String, String> apiId = new HashMap<>();
Map<String, String> gatewayUrls = new HashMap<>();
Map<String, ApiInfo> apiInfoById = new HashMap<>();

try {
apiId = metadataParser.parseApiInfo(instanceInfo.getMetadata()).stream().filter(apiInfo -> apiInfo.getApiId() != null).collect(
Collectors.toMap(
apiInfo -> (apiInfo.getMajorVersion() < 0) ? "default" : apiInfo.getApiId() + " v" + apiInfo.getVersion(),
ApiInfo::getApiId
)
);

gatewayUrls = metadataParser.parseApiInfo(instanceInfo.getMetadata()).stream().filter(apiInfo -> apiInfo.getApiId() != null).collect(
Collectors.toMap(
apiInfo -> (apiInfo.getMajorVersion() < 0) ? "default" : apiInfo.getApiId() + " v" + apiInfo.getVersion(),
ApiInfo::getGatewayUrl
)
);
List<ApiInfo> apiInfoList = metadataParser.parseApiInfo(instanceInfo.getMetadata());
apiInfoList.stream().filter(apiInfo -> apiInfo.getApiId() != null).forEach(apiInfo -> {
String id = (apiInfo.getMajorVersion() < 0) ? DEFAULT_APIINFO_KEY : apiInfo.getApiId() + " v" + apiInfo.getVersion();
apiInfoById.put(id, apiInfo);
});

if (!apiInfoById.containsKey(DEFAULT_APIINFO_KEY)) {
ApiInfo defaultApiInfo = apiInfoList.stream().filter(ApiInfo::isDefaultApi).findFirst().orElse(null);
apiInfoById.put(DEFAULT_APIINFO_KEY, defaultApiInfo);
}
} catch (Exception ex) {
log.info("createApiServiceFromInstance#incorrectVersions {}", ex.getMessage());
}
Expand All @@ -361,8 +359,7 @@ private APIService createAPIServiceFromInstance(InstanceInfo instanceInfo) {
.homePageUrl(instanceHomePage)
.basePath(apiBasePath)
.sso(isSso(instanceInfo))
.apiId(apiId)
.gatewayUrls(gatewayUrls)
.apis(apiInfoById)
.instanceId(instanceInfo.getInstanceId())
.build();
}
Expand Down
7 changes: 7 additions & 0 deletions api-catalog-services/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ eureka:
version: 1.0.0
gatewayUrl: api/v1
swaggerUrl: https://${apiml.service.hostname}:${apiml.service.port}${apiml.service.contextPath}/v3/api-docs
codeSnippet:
- endpoint: "/greetings"
codeBlock: |
```
System. out. println("Hello, World!");
```
language: java

service:
title: API Catalog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ class WhenAllContainersAreRequested {
@Test
void thenReturnNoContent() {
given(cachedProductFamilyService.getAllContainers()).willReturn(null);

RestAssuredMockMvc.given().
when().
get(pathToContainers).
then().
statusCode(HttpStatus.NO_CONTENT.value());
}
}

@Nested
class WhenSpecificContainerRequested {
@Test
Expand Down Expand Up @@ -103,8 +103,8 @@ 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(cachedApiDocService.getDefaultApiDocForService("service1")).willReturn("service1");
given(cachedApiDocService.getApiVersionsForService("service1")).willReturn(apiVersions);

given(cachedServicesService.getService("service2")).willReturn(service2);
given(cachedApiDocService.getDefaultApiDocForService("service2")).willReturn("service2");
Expand Down Expand Up @@ -193,7 +193,7 @@ private void assertThereIsOneContainer(ResponseEntity<List<APIContainer>> contai
}
}




// =========================================== Helper Methods ===========================================
Expand All @@ -209,7 +209,7 @@ private List<APIContainer> createContainers() {
.homePageUrl("home")
.basePath("base")
.sso(false)
.apiId(Collections.emptyMap())
.apis(Collections.emptyMap())
.build();
services.add(service);

Expand All @@ -221,7 +221,7 @@ private List<APIContainer> createContainers() {
.homePageUrl("home")
.basePath("base")
.sso(false)
.apiId(Collections.emptyMap())
.apis(Collections.emptyMap())
.build();
services.add(service);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void prepareInstances() throws URLTransformationException {
metadata.put(SERVICE_DESCRIPTION, "sDescription");
instance = servicesBuilder.createInstance("service1", InstanceInfo.InstanceStatus.UP, metadata);

Map<String, String> updatedMetadata = new HashMap<String, String>();
Map<String, String> updatedMetadata = new HashMap<>();
updatedMetadata.put(CATALOG_ID, "demoapp");
updatedMetadata.put(CATALOG_TITLE, "Title2");
updatedMetadata.put(CATALOG_DESCRIPTION, "Description2");
Expand All @@ -101,7 +101,7 @@ void prepareInstances() throws URLTransformationException {
@Nested
class GivenInstanceIsNotInCache {
@Test
void createNew() throws URLTransformationException {
void createNew() {
APIContainer originalContainer = underTest.saveContainerFromInstance("demoapp", instance);

List<APIContainer> lsContainer = underTest.getRecentlyUpdatedContainers();
Expand All @@ -112,7 +112,7 @@ void createNew() throws URLTransformationException {
@Nested
class GivenInstanceIsInTheCache {
@Test
void update() throws InterruptedException, URLTransformationException {
void update() throws InterruptedException {
APIContainer originalContainer = underTest.saveContainerFromInstance("demoapp", instance);
Calendar createdTimestamp = originalContainer.getLastUpdatedTimestamp();

Expand Down Expand Up @@ -151,7 +151,7 @@ private void assertThatMetadataAreCorrect(APIContainer result, Map<String, Strin
assertThat(result.getDescription(), is(correct.get(CATALOG_DESCRIPTION)));
assertThat(result.getVersion(), is(correct.get(CATALOG_VERSION)));
}
}
}

@Nested
class WhenRemovingService {
Expand All @@ -170,7 +170,7 @@ void prepareExistingInstance() {
metadata.put(SERVICE_TITLE, "sTitle");
metadata.put(SERVICE_DESCRIPTION, "sDescription");
removedInstance = servicesBuilder.createInstance("service1", InstanceInfo.InstanceStatus.UP, metadata);

underTest.saveContainerFromInstance(removedInstanceFamilyId, removedInstance);
}

Expand Down Expand Up @@ -398,11 +398,11 @@ void groupThem() {
underTest.calculateContainerServiceValues(apiContainer);

APIService apiService = apiContainer.getServices().iterator().next();
assertNotNull(apiService.getApiId());
assertEquals(3, apiService.getApiId().size());
assertEquals("api1", apiService.getApiId().get("api1 v1.0.0"));
assertEquals("api2", apiService.getApiId().get("api2 v2"));
assertEquals("api3", apiService.getApiId().get("default"));
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"));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ private List<APIContainer> createContainers() {
.homePageUrl("home")
.basePath("base")
.sso(false)
.apiId(Collections.emptyMap())
.apis(Collections.emptyMap())
.build();
services.add(service);

Expand All @@ -184,7 +184,7 @@ private List<APIContainer> createContainers() {
.homePageUrl("home")
.basePath("base")
.sso(false)
.apiId(Collections.emptyMap())
.apis(Collections.emptyMap())
.build();
services.add(service);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private APIService addApiService(String serviceId,
.homePageUrl("home")
.basePath("base")
.sso(false)
.apiId(Collections.emptyMap())
.apis(Collections.emptyMap())
.build();
services.add(service);
allServices.add(service);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export default class InstanceInfo extends Component {
render() {
const { selectedService, selectedVersion } = this.props;

const apiId =
selectedService.apiId[selectedVersion || selectedService.defaultApiVersion] ||
selectedService.apiId.default;
const apiInfo =
selectedService.apis[selectedVersion || selectedService.defaultApiVersion] || selectedService.apis.default;
const { apiId } = apiInfo;
return (
<Shield title="Cannot display information about selected instance">
<div className="apiInfo-item">
Expand All @@ -31,11 +31,7 @@ export default class InstanceInfo extends Component {
</Tooltip>
</div>
<div className="apiInfo-item">
<Tooltip
key={selectedService.apiId}
title="API IDs of the APIs that are provided by this service"
placement="bottom"
>
<Tooltip title="API IDs of the APIs that are provided by this service" placement="bottom">
<Typography>
{/* eslint-disable-next-line jsx-a11y/label-has-for */}
<label htmlFor="apiid">API ID:</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const selectedService = {

describe('>>> InstanceInfo component tests', () => {
it('Service with version v1', () => {
selectedService.apiId = {
v1: 'zowe.apiml.gateway',
selectedService.apis = {
v1: { apiId: 'zowe.apiml.gateway' },
};
const selectService = jest.fn();
const instanceInfo = shallow(
Expand All @@ -35,8 +35,8 @@ describe('>>> InstanceInfo component tests', () => {
});

it('No selected version, use defaultApiVersion', () => {
selectedService.apiId = {
v1: 'zowe.apiml.gateway',
selectedService.apis = {
v1: { apiId: 'zowe.apiml.gateway' },
};
selectedService.defaultApiVersion = ['v1'];
const selectService = jest.fn();
Expand All @@ -46,8 +46,8 @@ describe('>>> InstanceInfo component tests', () => {
});

it('No selected version and not set defaultApiVersion use key default', () => {
selectedService.apiId = {
default: 'zowe.apiml.gateway',
selectedService.apis = {
default: { apiId: 'zowe.apiml.gateway' },
};
selectedService.defaultApiVersion = null;
const selectService = jest.fn();
Expand Down
Loading

0 comments on commit 4d2298e

Please sign in to comment.