diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/api/SwaggerVerb.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/api/SwaggerVerb.java index efdca8f7f6..f8fb5d8862 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/api/SwaggerVerb.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/api/SwaggerVerb.java @@ -26,9 +26,8 @@ public class SwaggerVerb { private String verb; private String description; private String responseStatus; - private String responseType; private Map responseProperties; - private Object responseExample; + private boolean array; public String getVerb() { return verb; @@ -54,14 +53,6 @@ public void setResponseStatus(String responseStatus) { this.responseStatus = responseStatus; } - public String getResponseType() { - return responseType; - } - - public void setResponseType(String responseType) { - this.responseType = responseType; - } - public Map getResponseProperties() { return responseProperties; } @@ -70,11 +61,11 @@ public void setResponseProperties(Map responseProperties) { this.responseProperties = responseProperties; } - public Object getResponseExample() { - return responseExample; + public boolean isArray() { + return array; } - public void setResponseExample(Object responseExample) { - this.responseExample = responseExample; + public void setArray(boolean array) { + this.array = array; } } diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/ApiServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/ApiServiceImpl.java index 39731e6ea8..8531089738 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/ApiServiceImpl.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/ApiServiceImpl.java @@ -63,7 +63,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; -import org.springframework.util.CollectionUtils; import javax.xml.bind.DatatypeConverter; import java.io.FileInputStream; @@ -71,7 +70,6 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import static io.gravitee.management.model.EventType.PUBLISH_API; @@ -262,10 +260,10 @@ private ApiEntity create(final NewApiEntity newApiEntity, final String userId, header.put("value", "application/json"); configuration.put("headers", singletonList(header)); try { - if (swaggerVerb.getResponseType() != null || swaggerVerb.getResponseExample() != null) { - final Object mockContent = swaggerVerb.getResponseExample() == null ? - generateMockContent(swaggerVerb.getResponseType(), swaggerVerb.getResponseProperties()) : swaggerVerb.getResponseExample(); - configuration.put("content", objectMapper.writeValueAsString(mockContent)); + final Map responseProperties = swaggerVerb.getResponseProperties(); + if (responseProperties != null) { + configuration.put("content", objectMapper.writeValueAsString(swaggerVerb.isArray()? + singletonList(responseProperties): responseProperties)); } policy.setConfiguration(objectMapper.writeValueAsString(configuration)); } catch (final JsonProcessingException e) { @@ -321,37 +319,6 @@ private String addGraviteeUrl(String apiContextPath, String payload) { return swaggerService.replaceServerList(payload, graviteeUrls); } - private Object generateMockContent(final String responseType, final Map responseProperties) { - final Random random = new Random(); - switch (responseType) { - case "string": - return "Mocked " + (responseProperties == null ? "response" : responseProperties.getOrDefault("key", "response")); - case "boolean": - return random.nextBoolean(); - case "integer": - return random.nextInt(1000); - case "number": - return random.nextDouble(); - case "array": - return responseProperties == null ? emptyList() : singletonList(generateMockContent("object", responseProperties)); - case "object": - if (responseProperties == null) { - return emptyMap(); - } - final Map mock = new HashMap<>(responseProperties.size()); - responseProperties.forEach((k, v) -> { - if (v instanceof Map) { - mock.put(k, generateMockContent("object", (Map) v)); - } else { - mock.put(k, generateMockContent((String) v, singletonMap("key", k))); - } - }); - return mock; - default: - return emptyMap(); - } - } - private ApiEntity create0(UpdateApiEntity api, String userId) throws ApiAlreadyExistsException { try { LOGGER.debug("Create {} for user {}", api, userId); diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/SwaggerServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/SwaggerServiceImpl.java index 43211de39a..3bf6b1e059 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/SwaggerServiceImpl.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/SwaggerServiceImpl.java @@ -37,6 +37,7 @@ 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.examples.Example; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.ObjectSchema; @@ -67,7 +68,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static java.util.Collections.emptyMap; +import static java.util.Collections.*; import static java.util.stream.Collectors.toCollection; import static java.util.stream.Collectors.toMap; @@ -328,7 +329,7 @@ private NewSwaggerApiEntity mapSwagger12ToNewApi(final Swagger swagger, final bo if (responseSchema != null) { if (responseSchema instanceof ArrayModel) { final ArrayModel arrayModel = (ArrayModel) responseSchema; - swaggerVerb.setResponseType(arrayModel.getType()); + swaggerVerb.setArray(true); if (arrayModel.getItems() instanceof RefProperty) { final String simpleRef = ((RefProperty) arrayModel.getItems()).getSimpleRef(); swaggerVerb.setResponseProperties(getResponseFromSimpleRef(swagger, simpleRef)); @@ -336,18 +337,15 @@ private NewSwaggerApiEntity mapSwagger12ToNewApi(final Swagger swagger, final bo swaggerVerb.setResponseProperties(getResponseProperties(swagger, ((ObjectProperty) arrayModel.getItems()).getProperties())); } } else if (responseSchema instanceof RefModel) { - swaggerVerb.setResponseType("object"); final String simpleRef = ((RefModel) responseSchema).getSimpleRef(); swaggerVerb.setResponseProperties(getResponseFromSimpleRef(swagger, simpleRef)); } else if (responseSchema instanceof ModelImpl) { final ModelImpl model = (ModelImpl) responseSchema; - swaggerVerb.setResponseType(model.getType()); + swaggerVerb.setArray("array".equals(model.getType())); if ("object".equals(model.getType())) { if (model.getAdditionalProperties() != null) { swaggerVerb.setResponseProperties(Collections.singletonMap("additionalProperty", model.getAdditionalProperties().getType())); } - } else { - swaggerVerb.setResponseType(model.getType()); } } } @@ -490,9 +488,10 @@ private SwaggerVerb getSwaggerVerb(final SwaggerParseResult swagger, final Opera final io.swagger.v3.oas.models.media.MediaType mediaType = responseEntry.getValue().getContent().entrySet().iterator().next().getValue(); if (mediaType.getExample() != null) { - swaggerVerb.setResponseExample(mapper.convertValue(mediaType.getExample(), Map.class)); + swaggerVerb.setResponseProperties(mapper.convertValue(mediaType.getExample(), Map.class)); } else if (mediaType.getExamples() != null) { - swaggerVerb.setResponseExample(mediaType.getExamples().entrySet().iterator().next().getValue().getValue()); + final Entry next = mediaType.getExamples().entrySet().iterator().next(); + swaggerVerb.setResponseProperties(singletonMap(next.getKey(), next.getValue().getValue())); } else { final Schema responseSchema = mediaType.getSchema(); if (responseSchema != null) { @@ -511,26 +510,44 @@ private SwaggerVerb getSwaggerVerb(final SwaggerParseResult swagger, final Opera private void processResponseSchema(SwaggerParseResult swagger, SwaggerVerb swaggerVerb, String type, Schema responseSchema) { if (responseSchema.getProperties() == null) { - swaggerVerb.setResponseType(type); + swaggerVerb.setArray("array".equals(type)); if (responseSchema.getAdditionalProperties() != null) { swaggerVerb.setResponseProperties(Collections.singletonMap("additionalProperty", ((ObjectSchema) responseSchema.getAdditionalProperties()).getType())); } else if (responseSchema.get$ref() != null) { if (!"array".equals(type)) { - final String typeByRef = getTypeByRef(swagger, responseSchema.get$ref()); - if (typeByRef != null) { - swaggerVerb.setResponseType(typeByRef); - } + swaggerVerb.setArray(isRefArray(swagger, responseSchema.get$ref())); } swaggerVerb.setResponseProperties(getResponseFromSimpleRef(swagger, responseSchema.get$ref())); + } else { + swaggerVerb.setResponseProperties(singletonMap(responseSchema.getType(), getResponsePropertiesFromType(responseSchema.getType()))); } } else { - swaggerVerb.setResponseExample(getResponseExample(responseSchema.getProperties())); + swaggerVerb.setResponseProperties(getResponseExample(responseSchema.getProperties())); } } - private String getTypeByRef(SwaggerParseResult swagger, String ref) { + private boolean isRefArray(SwaggerParseResult swagger, final String ref) { final String simpleRef = ref.substring(ref.lastIndexOf('/') + 1); - return swagger.getOpenAPI().getComponents().getSchemas().get(simpleRef).getType(); + final Schema schema = swagger.getOpenAPI().getComponents().getSchemas().get(simpleRef); + return schema instanceof ArraySchema; + } + + private Object getResponsePropertiesFromType(final String responseType) { + final Random random = new Random(); + switch (responseType) { + case "string": + return "Mocked string"; + case "boolean": + return random.nextBoolean(); + case "integer": + return random.nextInt(1000); + case "number": + return random.nextDouble(); + case "array": + return singletonList(getResponsePropertiesFromType("string")); + default: + return emptyMap(); + } } private Map getResponseFromSimpleRef(SwaggerParseResult swagger, String ref) { @@ -538,7 +555,12 @@ private Map getResponseFromSimpleRef(SwaggerParseResult swagger, final Schema schema = swagger.getOpenAPI().getComponents().getSchemas().get(simpleRef); if (schema instanceof ArraySchema) { - return getResponseFromSimpleRef(swagger, ((ArraySchema) schema).getItems().get$ref()); + final Schema items = ((ArraySchema) schema).getItems(); + if (items.get$ref() != null) { + return getResponseFromSimpleRef(swagger, items.get$ref()); + } else { + return singletonMap(items.getType(), singletonList(items.getExample())); + } } else if (schema instanceof ComposedSchema) { final Map response = new HashMap<>(); ((ComposedSchema) schema).getAllOf().forEach(composedSchema -> { @@ -560,10 +582,32 @@ private Map getResponseFromSimpleRef(SwaggerParseResult swagger, private Map getResponseProperties(final SwaggerParseResult swagger, final Map properties) { return properties.entrySet().stream().collect(toMap(Map.Entry::getKey, e -> { - if (e.getValue().getType() != null) { - return e.getValue().getType(); + final String type = e.getValue().getType(); + if (type != null) { + final Object example = e.getValue().getExample(); + if (example == null) { + final List enums = e.getValue().getEnum(); + if (enums == null) { + if (type.equals("object")) { + return getResponseProperties(swagger, e.getValue().getProperties()); + } else if (type.equals("array")) { + return singletonList(((ArraySchema) e.getValue()).getItems().getExample()); + } + return getResponsePropertiesFromType(type); + } else { + return enums.get(0); + } + } else { + return example; + } } else { - return getResponseFromSimpleRef(swagger, e.getValue().get$ref()); + final String simpleRef = e.getValue().get$ref().substring(e.getValue().get$ref().lastIndexOf('/') + 1); + final Schema schema = swagger.getOpenAPI().getComponents().getSchemas().get(simpleRef); + if (schema instanceof ArraySchema) { + return singletonList(((ArraySchema) schema).getItems().getExample()); + } else { + return getResponseFromSimpleRef(swagger, e.getValue().get$ref()); + } } })); } diff --git a/gravitee-management-api-service/src/test/java/io/gravitee/management/service/SwaggerService_PrepareTest.java b/gravitee-management-api-service/src/test/java/io/gravitee/management/service/SwaggerService_PrepareTest.java index b5f7a8bfef..873ae562e9 100644 --- a/gravitee-management-api-service/src/test/java/io/gravitee/management/service/SwaggerService_PrepareTest.java +++ b/gravitee-management-api-service/src/test/java/io/gravitee/management/service/SwaggerService_PrepareTest.java @@ -159,9 +159,8 @@ public void shouldPrepareAPIFromSwaggerV3WithExamples() throws IOException { final SwaggerVerb listVersionsv2 = verbs.iterator().next(); assertEquals("List API versions", listVersionsv2.getDescription()); assertEquals("200", listVersionsv2.getResponseStatus()); - assertNull(listVersionsv2.getResponseType()); assertEquals("GET", listVersionsv2.getVerb()); - assertNotNull(listVersionsv2.getResponseExample()); + assertNotNull(listVersionsv2.getResponseProperties()); } @Test @@ -179,10 +178,9 @@ public void shouldPrepareAPIFromSwaggerV3WithSimpleTypedExamples() throws IOExce final SwaggerVerb postStreams = verbs.iterator().next(); assertEquals("subscribes a client to receive out-of-band data", postStreams.getDescription()); assertEquals("201", postStreams.getResponseStatus()); - assertNull(postStreams.getResponseType()); assertEquals("POST", postStreams.getVerb()); - assertNotNull(postStreams.getResponseExample()); - assertEquals("2531329f-fb09-4ef7-887e-84e648214436", ((Map) postStreams.getResponseExample()).get("subscriptionId")); + assertNotNull(postStreams.getResponseProperties()); + assertEquals("2531329f-fb09-4ef7-887e-84e648214436", (postStreams.getResponseProperties()).get("subscriptionId")); } @Test @@ -209,8 +207,6 @@ public void shouldPrepareAPIFromSwaggerV3WithLinks() throws IOException { assertEquals("getUserByName", getUserByName.getDescription()); assertEquals("GET", getUserByName.getVerb()); assertEquals("200", getUserByName.getResponseStatus()); - assertEquals("object", getUserByName.getResponseType()); - assertNull(getUserByName.getResponseExample()); assertNotNull(getUserByName.getResponseProperties()); assertEquals("string", getUserByName.getResponseProperties().get("username")); assertEquals("string", getUserByName.getResponseProperties().get("uuid")); @@ -222,9 +218,8 @@ public void shouldPrepareAPIFromSwaggerV3WithLinks() throws IOException { assertEquals("getRepositoriesByOwner", getRepositoriesByOwner.getDescription()); assertEquals("GET", getRepositoriesByOwner.getVerb()); assertEquals("200", getRepositoriesByOwner.getResponseStatus()); - assertEquals("array", getRepositoriesByOwner.getResponseType()); - assertNull(getRepositoriesByOwner.getResponseExample()); assertNotNull(getRepositoriesByOwner.getResponseProperties()); + assertTrue(getRepositoriesByOwner.isArray()); assertEquals("string", getRepositoriesByOwner.getResponseProperties().get("slug")); assertEquals("string", ((Map) getRepositoriesByOwner.getResponseProperties().get("owner")).get("username")); assertEquals("string", ((Map) getRepositoriesByOwner.getResponseProperties().get("owner")).get("uuid")); @@ -250,9 +245,8 @@ public void shouldPrepareAPIFromSwaggerV3WithPetstore() throws IOException { assertEquals("List all pets", findPets.getDescription()); assertEquals("GET", findPets.getVerb()); assertEquals("200", findPets.getResponseStatus()); - assertEquals("array", findPets.getResponseType()); - assertNull(findPets.getResponseExample()); assertNotNull(findPets.getResponseProperties()); + assertTrue(findPets.isArray()); assertEquals(3, findPets.getResponseProperties().size()); assertEquals("string", findPets.getResponseProperties().get("name")); assertEquals("string", findPets.getResponseProperties().get("tag")); @@ -279,9 +273,8 @@ public void shouldPrepareAPIFromSwaggerV3WithPetstoreExpanded() throws IOExcepti assertEquals("findPets", findPets.getDescription()); assertEquals("GET", findPets.getVerb()); assertEquals("200", findPets.getResponseStatus()); - assertEquals("array", findPets.getResponseType()); - assertNull(findPets.getResponseExample()); assertNotNull(findPets.getResponseProperties()); + assertTrue(findPets.isArray()); assertEquals(3, findPets.getResponseProperties().size()); assertEquals("string", findPets.getResponseProperties().get("name")); assertEquals("string", findPets.getResponseProperties().get("tag")); @@ -294,8 +287,6 @@ public void shouldPrepareAPIFromSwaggerV3WithPetstoreExpanded() throws IOExcepti assertEquals("find pet by id", findPetsId.getDescription()); assertEquals("GET", findPetsId.getVerb()); assertEquals("200", findPetsId.getResponseStatus()); - assertEquals("object", findPetsId.getResponseType()); - assertNull(findPetsId.getResponseExample()); assertNotNull(findPetsId.getResponseProperties()); assertEquals(3, findPetsId.getResponseProperties().size()); assertEquals("string", findPetsId.getResponseProperties().get("name")); @@ -327,10 +318,8 @@ public void shouldPrepareAPIFromSwaggerV3WithExample() throws IOException { assertEquals("List available data sets", getMetadata.getDescription()); assertEquals("GET", getMetadata.getVerb()); assertEquals("200", getMetadata.getResponseStatus()); - assertNull(getMetadata.getResponseType()); - assertNull(getMetadata.getResponseProperties()); - assertNotNull(getMetadata.getResponseExample()); - final Map responseExample = (Map) getMetadata.getResponseExample(); + assertNotNull(getMetadata.getResponseProperties()); + final Map responseExample = getMetadata.getResponseProperties(); assertEquals(2, responseExample.size()); assertEquals(2, responseExample.get("total")); final List> apis = (List) responseExample.get("apis"); @@ -344,8 +333,46 @@ public void shouldPrepareAPIFromSwaggerV3WithExample() throws IOException { assertEquals("Provides the general information about the API and the list of fields that can be used to query the dataset.", getFields.getDescription()); assertEquals("GET", getFields.getVerb()); assertEquals("200", getFields.getResponseStatus()); - assertEquals("string", getFields.getResponseType()); - assertNull(getFields.getResponseProperties()); + assertNotNull(getFields.getResponseProperties()); + assertEquals("Mocked string", getFields.getResponseProperties().get("string")); + } + + @Test + public void shouldPrepareAPIFromSwaggerV3WithEnumExample() throws IOException { + final NewSwaggerApiEntity api = prepareInline("io/gravitee/management/service/mock/enum-example.yml", true); + assertEquals("v1", api.getVersion()); + assertEquals("Gravitee Import Mock Example", api.getName()); + assertEquals("graviteeimportmockexample", api.getContextPath()); + assertEquals(1, api.getEndpoint().size()); + assertTrue(api.getEndpoint().contains("/")); + assertEquals(1, api.getPaths().size()); + final SwaggerPath swaggerPath = api.getPaths().get(0); + assertEquals("/", swaggerPath.getPath()); + + final List swaggerVerbs = swaggerPath.getVerbs(); + assertNotNull(swaggerVerbs); + assertEquals(1, swaggerVerbs.size()); + final SwaggerVerb getRoot = swaggerVerbs.iterator().next(); + assertEquals("getRoot", getRoot.getDescription()); + assertEquals("GET", getRoot.getVerb()); + assertEquals("200", getRoot.getResponseStatus()); + assertNotNull(getRoot.getResponseProperties()); + assertFalse(getRoot.isArray()); + final Map responseExample = getRoot.getResponseProperties(); + assertEquals(10, responseExample.size()); + assertEquals("string", responseExample.get("optionalValue")); + assertEquals("string", responseExample.get("stringValue")); + assertEquals("exampleValue", responseExample.get("stringExampleValue")); + assertEquals("value1", responseExample.get("enumValue")); + assertEquals(123, responseExample.get("integerExampleValue")); + assertEquals("string", ((Map) responseExample.get("inlinedObjectValue")).get("nestedOptionalValue")); + assertEquals("string", ((Map) responseExample.get("inlinedObjectValue")).get("nestedStringValue")); + assertEquals("nestedExampleValue", ((Map) responseExample.get("inlinedObjectValue")).get("nestedStringExampleValue")); + assertEquals("string", ((Map) responseExample.get("objectValue")).get("nestedOptionalValue")); + assertEquals("string", ((Map) responseExample.get("objectValue")).get("nestedStringValue")); + assertEquals("nestedExampleValue", ((Map) responseExample.get("objectValue")).get("nestedStringExampleValue")); + assertEquals("itemValue", ((List) responseExample.get("inlinedArrayValue")).get(0)); + assertEquals("itemValue", ((List) responseExample.get("arrayValue")).get(0)); } @Test diff --git a/gravitee-management-api-service/src/test/resources/io/gravitee/management/service/mock/enum-example.yml b/gravitee-management-api-service/src/test/resources/io/gravitee/management/service/mock/enum-example.yml new file mode 100644 index 0000000000..75274cb41d --- /dev/null +++ b/gravitee-management-api-service/src/test/resources/io/gravitee/management/service/mock/enum-example.yml @@ -0,0 +1,83 @@ +openapi: '3.0.0' +info: + title: Gravitee Import Mock Example + version: v1 +paths: + /: + get: + operationId: getRoot + responses: + '200': + description: ok + content: + application/json: + schema: + $ref: '#/components/schemas/ExampleResource' +components: + schemas: + ExampleResource: + type: object + required: + - stringValue + - stringExampleValue + - enumValue + - integerValue + - integerExampleValue + - objectValue + - arrayValue + properties: + optionalValue: + type: string + stringValue: + type: string + stringExampleValue: + type: string + example: 'exampleValue' + enumValue: + type: string + enum: [ 'value1', 'value2' ] + integerValue: + type: integer + integerExampleValue: + type: integer + example: 123 + inlinedObjectValue: + type: object + required: + - nestedStringValue + - nestedStringExampleValue + properties: + nestedOptionalValue: + type: string + nestedStringValue: + type: string + nestedStringExampleValue: + type: string + example: 'nestedExampleValue' + objectValue: + $ref: '#/components/schemas/Object' + inlinedArrayValue: + type: array + items: + type: string + example: 'itemValue' + arrayValue: + $ref: '#/components/schemas/Array' + Object: + type: object + required: + - nestedStringValue + - nestedStringExampleValue + properties: + nestedOptionalValue: + type: string + nestedStringValue: + type: string + nestedStringExampleValue: + type: string + example: 'nestedExampleValue' + Array: + type: array + items: + type: string + example: 'itemValue'