From 27dd93dbf020e981820b0ea92fc6ae406da93806 Mon Sep 17 00:00:00 2001 From: Andrey Antonov Date: Wed, 19 Feb 2014 18:13:42 +0400 Subject: [PATCH] Bugs fixing and minor improvements ApiOperationParser 1. A bug when we have @ApiOperation without "method" specified. Then value is taken from httpMethod (without trimming). As a result we get "POST ", which is not recognized by Swagger UI. 2. A bug when we have @ApiOperation without "produces" or "consumes" specifed. Then code populates this arrays with "" value and then in Swagger UI we see "Produces: ['']" 3. ApiError renamed to ApiResponse in code 4. Validation logic partially moved to DocumentationOperation (as well as toScalaOperation()) ApiModelParser, ApiParserImpl 5. A bug with missing models. Some of the models were ignored by parsing and Swagger UI had no ability to show them to user. Models parsing logic was moved to ApiUtils class so that both of classes mentioned could use it. In future, probably, it is better to merge this "model parsing logic" and remove baseModelPackage property, because it seems that this property is not necessary. 6. Work with Generics was improved. Code now correctly parses generic return types: List -> List[SomeClass] ContainerClass -> ContainerClass[InnerClass] My Fork of Swagger UI renders such response types correctly https://github.com/wordnik/swagger-ui/pull/401 Elements with only one generic would be processed. --- .../parser/ApiModelParser.java | 45 +-- .../parser/ApiOperationParser.java | 280 ++++++++++-------- .../parser/ApiParserImpl.java | 110 +++---- .../swagger4springweb/util/ApiUtils.java | 49 ++- 4 files changed, 241 insertions(+), 243 deletions(-) diff --git a/src/main/java/com/knappsack/swagger4springweb/parser/ApiModelParser.java b/src/main/java/com/knappsack/swagger4springweb/parser/ApiModelParser.java index 2e87fe1..fdc27be 100644 --- a/src/main/java/com/knappsack/swagger4springweb/parser/ApiModelParser.java +++ b/src/main/java/com/knappsack/swagger4springweb/parser/ApiModelParser.java @@ -1,55 +1,24 @@ package com.knappsack.swagger4springweb.parser; -import com.knappsack.swagger4springweb.model.AnnotatedParameter; -import com.knappsack.swagger4springweb.util.AnnotationUtils; -import com.wordnik.swagger.converter.SwaggerSchemaConverter; +import com.knappsack.swagger4springweb.util.ApiUtils; import com.wordnik.swagger.model.Model; import org.springframework.web.bind.annotation.ResponseBody; -import scala.Option; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.HashMap; -import java.util.List; import java.util.Map; public class ApiModelParser { public Map getResponseBodyModels(Method method) { - Map documentationSchemaMap = new HashMap(); - if(method.getAnnotation(ResponseBody.class) != null) { - Class returnType = method.getReturnType(); - SwaggerSchemaConverter parser = new SwaggerSchemaConverter(); - String schemaName; - if(returnType.isArray()) { - //TODO - possibly reinvestigate what we should do in the case of an array - //parser = new ApiModelParser(returnType.getComponentType()); - schemaName = returnType.getComponentType().getSimpleName(); - } else { - schemaName = returnType.getSimpleName(); - } - Option model = parser.read(returnType); - if(model.nonEmpty()) { - documentationSchemaMap.put(schemaName, model.get()); - } - } - - return documentationSchemaMap; - } - - public Map getParameterModels(Method method) { - - Map documentationSchemaMap = new HashMap(); + Map models = new HashMap(); + if (method.getAnnotation(ResponseBody.class) != null) { + Type type = method.getGenericReturnType(); - List annotatedParameters = AnnotationUtils.getAnnotatedParameters(method); - for (AnnotatedParameter annotatedParameter : annotatedParameters) { - Class parameterType = annotatedParameter.getParameterType(); - SwaggerSchemaConverter parser = new SwaggerSchemaConverter(); - Option model = parser.read(parameterType); - if(model.nonEmpty()) { - documentationSchemaMap.put(parameterType.getSimpleName(), model.get()); - } + ApiUtils.addModels(type, models); } - return documentationSchemaMap; + return models; } } diff --git a/src/main/java/com/knappsack/swagger4springweb/parser/ApiOperationParser.java b/src/main/java/com/knappsack/swagger4springweb/parser/ApiOperationParser.java index 9cc6411..c1c5c25 100644 --- a/src/main/java/com/knappsack/swagger4springweb/parser/ApiOperationParser.java +++ b/src/main/java/com/knappsack/swagger4springweb/parser/ApiOperationParser.java @@ -4,25 +4,33 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import com.wordnik.swagger.converter.ModelConverters; +import com.wordnik.swagger.model.Model; import com.wordnik.swagger.model.Operation; import com.wordnik.swagger.model.Parameter; import com.wordnik.swagger.model.ResponseMessage; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import scala.Option; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static java.lang.String.format; + public class ApiOperationParser { private String resourcePath; private List ignorableAnnotations; private boolean ignoreUnusedPathVariables; - public ApiOperationParser(String resourcePath, List ignorableAnnotations, boolean ignoreUnusedPathVariables) { + public ApiOperationParser(String resourcePath, List ignorableAnnotations, + boolean ignoreUnusedPathVariables) { this.ignorableAnnotations = ignorableAnnotations; this.ignoreUnusedPathVariables = ignoreUnusedPathVariables; this.resourcePath = resourcePath; @@ -31,108 +39,106 @@ public ApiOperationParser(String resourcePath, List ignorableAnnotations public Operation getDocumentationOperation(Method method) { DocumentationOperation documentationOperation = new DocumentationOperation(); - documentationOperation.setName(method.getName()); documentationOperation.setNickname(method.getName());// method name - documentationOperation.setResponseTypeInternal(method.getReturnType().getName()); - String responseClass; - Class returnType = method.getReturnType(); - if(returnType.isArray()) { - responseClass = returnType.getComponentType().getSimpleName(); - } else { - responseClass = method.getReturnType().getSimpleName(); + + Type returnType = method.getGenericReturnType(); + if (returnType instanceof ParameterizedType) { + final ParameterizedType parameterizedType = (ParameterizedType) returnType; + + if (parameterizedType.getActualTypeArguments().length == 1) { + final Type type = parameterizedType.getActualTypeArguments()[0]; + if (type instanceof ParameterizedType) { + documentationOperation.setResponseClass((Class) ((ParameterizedType) type).getRawType()); + } else { + documentationOperation.setResponseClass((Class) type); + } + documentationOperation + .setResponseContainer(((Class) parameterizedType.getRawType())); + } else { + // TODO what to do here? + // not supporting generic containing other generic + } } - documentationOperation.setResponseClass(responseClass); - String httpMethod = ""; - RequestMapping methodRequestMapping = method - .getAnnotation(RequestMapping.class); - if (httpMethod.isEmpty()) { - for (RequestMethod requestMethod : methodRequestMapping.method()) { - httpMethod += requestMethod.name() + " "; + if (StringUtils.isEmpty(documentationOperation.getResponseClass())) { + Class clazz = method.getReturnType(); + if (clazz.isArray()) { + documentationOperation.setResponseClass(clazz.getComponentType()); + } else { + documentationOperation.setResponseClass(clazz); } } - documentationOperation.getConsumes().addAll(Arrays.asList(methodRequestMapping.consumes())); + + String httpMethod = ""; + RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class); + for (RequestMethod requestMethod : methodRequestMapping.method()) { + httpMethod += requestMethod.name() + " "; + } documentationOperation.setHttpMethod(httpMethod.trim()); - documentationOperation.getProduces().addAll(Arrays.asList(methodRequestMapping.produces())); + documentationOperation.addConsumes(Arrays.asList(methodRequestMapping.consumes())); + documentationOperation.addProduces(Arrays.asList(methodRequestMapping.produces())); // get ApiOperation information ApiOperation apiOperation = method.getAnnotation(ApiOperation.class); if (apiOperation != null) { - if (!apiOperation.httpMethod().isEmpty()) { - httpMethod = apiOperation.httpMethod(); - } - if (!(apiOperation.response() == null)) { - documentationOperation.setResponseClass(apiOperation.response().getName()); - } + documentationOperation.setHttpMethod(apiOperation.httpMethod()); + documentationOperation.setResponseClass(apiOperation.response()); + documentationOperation.setResponseContainer(apiOperation.responseContainer()); + documentationOperation.addProduces(apiOperation.produces()); + documentationOperation.addConsumes(apiOperation.consumes()); documentationOperation.setSummary(apiOperation.value()); documentationOperation.setNotes(apiOperation.notes()); documentationOperation.setPosition(apiOperation.position()); - documentationOperation.getProduces().add(apiOperation.produces()); - documentationOperation.getConsumes().add(apiOperation.consumes()); - documentationOperation.getProtocols().add(apiOperation.protocols()); - documentationOperation.getAuthorizations().add(apiOperation.authorizations()); - documentationOperation.setHttpMethod(httpMethod); + documentationOperation.addProtocols(apiOperation.protocols()); + documentationOperation.addAuthorizations(apiOperation.authorizations()); } - ApiResponse apiError = method.getAnnotation(ApiResponse.class); - if (apiError != null) { - addError(documentationOperation, apiError); + ApiResponse apiResponse = method.getAnnotation(ApiResponse.class); + if (apiResponse != null) { + addResponse(documentationOperation, apiResponse); } - ApiResponses apiErrors = method.getAnnotation(ApiResponses.class); - if (apiErrors != null) { - ApiResponse[] errors = apiErrors.value(); - for (ApiResponse error : errors) { - addError(documentationOperation, error); + ApiResponses apiResponses = method.getAnnotation(ApiResponses.class); + if (apiResponses != null) { + ApiResponse[] responses = apiResponses.value(); + for (ApiResponse response : responses) { + addResponse(documentationOperation, response); } } ApiParameterParser apiParameterParser = new ApiParameterParser(ignorableAnnotations); - List documentationParameters = apiParameterParser - .getApiParameters(method); + List documentationParameters = apiParameterParser.getApiParameters(method); documentationOperation.setParameters(documentationParameters); addUnusedPathVariables(documentationOperation, methodRequestMapping.value()); - - return new Operation(documentationOperation.getHttpMethod(), - documentationOperation.getSummary(), - documentationOperation.getNotes(), - documentationOperation.getResponseClass(), - documentationOperation.getNickname(), - documentationOperation.getPosition(), - JavaToScalaUtil.toScalaList(documentationOperation.getProduces()), - JavaToScalaUtil.toScalaList(documentationOperation.getConsumes()), - JavaToScalaUtil.toScalaList(documentationOperation.getProtocols()), - JavaToScalaUtil.toScalaList(documentationOperation.getAuthorizations()), - JavaToScalaUtil.toScalaList(documentationOperation.getParameters()) , - JavaToScalaUtil.toScalaList(documentationOperation.getResponseMessages()), - null); + return documentationOperation.toScalaOperation(); } - private void addError(DocumentationOperation documentationOperation, ApiResponse apiError) { - Option responseOption = Option.apply(apiError.response().getName()); - ResponseMessage responseMessage = new ResponseMessage(apiError.code(), apiError.message(),responseOption); - documentationOperation.getResponseMessages().add(responseMessage); + private void addResponse(DocumentationOperation documentationOperation, ApiResponse apiResponse) { + Option responseOption = Option.apply(apiResponse.response().getName()); + ResponseMessage responseMessage = new ResponseMessage(apiResponse.code(), apiResponse.message(), + responseOption); + documentationOperation.addResponseMessage(responseMessage); } private void addUnusedPathVariables(DocumentationOperation documentationOperation, String[] methodPath) { - if(ignoreUnusedPathVariables){ - return; + if (ignoreUnusedPathVariables) { + return; } - for(Parameter documentationParameter : new ApiPathParser().getPathParameters(resourcePath, methodPath)){ - if(!isParameterPresented(documentationOperation, documentationParameter.name())){ - documentationOperation.getParameters().add(documentationParameter); + for (Parameter documentationParameter : new ApiPathParser().getPathParameters(resourcePath, methodPath)) { + if (!isParameterPresented(documentationOperation, documentationParameter.name())) { + documentationOperation.addParameter(documentationParameter); } } } - private boolean isParameterPresented(DocumentationOperation documentationOperation, String parameter){ - if(parameter == null || documentationOperation.getParameters() == null){ + private boolean isParameterPresented(DocumentationOperation documentationOperation, String parameter) { + if (documentationOperation.getParameters().isEmpty()) { return false; } - for(Parameter documentationParameter : documentationOperation.getParameters()){ - if(parameter.equals(documentationParameter.name())){ + for (Parameter documentationParameter : documentationOperation.getParameters()) { + if (parameter.equals(documentationParameter.name())) { return true; } } @@ -141,9 +147,8 @@ private boolean isParameterPresented(DocumentationOperation documentationOperati //This class is used as a temporary solution to create a Swagger Operation object, since the Operation is immutable class DocumentationOperation { - private String name; + private String nickname; - private String responseTypeInternal; private String responseClass; private String summary; private String notes; @@ -156,116 +161,129 @@ class DocumentationOperation { private List protocols = new ArrayList(); private List authorizations = new ArrayList(); - String getName() { - return name; - } - - void setName(String name) { - this.name = name; - } - - String getNickname() { - return nickname; + Operation toScalaOperation() { + return new Operation(httpMethod, + summary, + notes, + responseClass, + nickname, + position, + JavaToScalaUtil.toScalaList(produces), + JavaToScalaUtil.toScalaList(consumes), + JavaToScalaUtil.toScalaList(protocols), + JavaToScalaUtil.toScalaList(authorizations), + JavaToScalaUtil.toScalaList(parameters), + JavaToScalaUtil.toScalaList(responseMessages), + null); } void setNickname(String nickname) { this.nickname = nickname; } - String getResponseTypeInternal() { - return responseTypeInternal; - } - - void setResponseTypeInternal(String responseTypeInternal) { - this.responseTypeInternal = responseTypeInternal; - } - - String getResponseClass() { - return responseClass; - } - - void setResponseClass(String responseClass) { - this.responseClass = responseClass; - } + void setResponseClass(Class responseClass) { + if (responseClass == null || responseClass == Void.class) { + return; + } - String getSummary() { - return summary; + Option model = ModelConverters.read(responseClass); + if (model.nonEmpty()) { + this.responseClass = model.get().name(); + } else { + this.responseClass = responseClass.getSimpleName(); + } } void setSummary(String summary) { this.summary = summary; } - String getNotes() { - return notes; - } - void setNotes(String notes) { this.notes = notes; } - String getHttpMethod() { - return httpMethod; - } - void setHttpMethod(String httpMethod) { + if (StringUtils.isEmpty(httpMethod)) { + return; + } this.httpMethod = httpMethod; } - List getParameters() { - return parameters; - } - void setParameters(List parameters) { this.parameters = parameters; } - List getResponseMessages() { - return responseMessages; + void setPosition(int position) { + this.position = position; } - void setResponseMessages(List responseMessages) { - this.responseMessages = responseMessages; + void addConsumes(final List consumes) { + this.consumes.addAll(consumes); } - int getPosition() { - return position; + void addProduces(final List produces) { + this.produces.addAll(produces); } - void setPosition(int position) { - this.position = position; + public void addResponseMessage(final ResponseMessage responseMessage) { + this.responseMessages.add(responseMessage); + } + + public List getParameters() { + return parameters; } - List getProduces() { - return produces; + public void addParameter(final Parameter parameter) { + this.parameters.add(parameter); } - void setProduces(List produces) { - this.produces = produces; + public void addAuthorizations(final String authorizations) { + if (StringUtils.isEmpty(authorizations)) { + return; + } + this.authorizations.add(authorizations); } - List getConsumes() { - return consumes; + void addProtocols(final String protocols) { + if (StringUtils.isEmpty(protocols)) { + return; + } + this.protocols.add(protocols); } - void setConsumes(List consumes) { - this.consumes = consumes; + public void addProduces(final String produces) { + if (StringUtils.isEmpty(produces)) { + return; + } + this.produces.add(produces); } - List getProtocols() { - return protocols; + public void addConsumes(final String consumes) { + if (StringUtils.isEmpty(consumes)) { + return; + } + this.consumes.add(consumes); } - void setProtocols(List protocols) { - this.protocols = protocols; + public void setResponseContainer(final String container) { + if (StringUtils.isEmpty(container)) { + return; + } + this.responseClass = format("%s[%s]", container, responseClass); } - List getAuthorizations() { - return authorizations; + public void setResponseContainer(final Class type) { + Option model = ModelConverters.read(type); + if (model.nonEmpty()) { + setResponseContainer(model.get().name()); + } else { + setResponseContainer(type.getSimpleName()); + } } - void setAuthorizations(List authorizations) { - this.authorizations = authorizations; + public String getResponseClass() { + return responseClass; } + } } diff --git a/src/main/java/com/knappsack/swagger4springweb/parser/ApiParserImpl.java b/src/main/java/com/knappsack/swagger4springweb/parser/ApiParserImpl.java index f07d663..640ab01 100644 --- a/src/main/java/com/knappsack/swagger4springweb/parser/ApiParserImpl.java +++ b/src/main/java/com/knappsack/swagger4springweb/parser/ApiParserImpl.java @@ -3,6 +3,7 @@ import com.knappsack.swagger4springweb.annotation.ApiExclude; import com.knappsack.swagger4springweb.controller.ApiDocumentationController; import com.knappsack.swagger4springweb.util.AnnotationUtils; +import com.knappsack.swagger4springweb.util.ApiUtils; import com.knappsack.swagger4springweb.util.JavaToScalaUtil; import com.knappsack.swagger4springweb.util.ScalaToJavaUtil; import com.wordnik.swagger.annotations.Api; @@ -25,6 +26,7 @@ import static org.reflections.ReflectionUtils.withAnnotation; public class ApiParserImpl implements ApiParser { + private static final String swaggerVersion = com.wordnik.swagger.core.SwaggerSpec.version(); private List controllerPackages = new ArrayList(); @@ -39,7 +41,8 @@ public class ApiParserImpl implements ApiParser { private final Map apiListingMap = new HashMap(); - public ApiParserImpl(ApiInfo apiInfo, List baseControllerPackage, List baseModelPackage, String basePath, String servletPath, + public ApiParserImpl(ApiInfo apiInfo, List baseControllerPackage, List baseModelPackage, + String basePath, String servletPath, String apiVersion, List ignorableAnnotations, boolean ignoreUnusedPathVariables) { this.controllerPackages = baseControllerPackage; this.modelPackages = baseModelPackage; @@ -51,7 +54,7 @@ public ApiParserImpl(ApiInfo apiInfo, List baseControllerPackage, List apiListingMap) if (!key.startsWith("/")) { docPath = docPath + "/"; } - ApiListingReference apiListingReference = new ApiListingReference(docPath + key, apiListing.description(), count); + ApiListingReference apiListingReference = new ApiListingReference(docPath + key, apiListing.description(), + count); apiListingReferences.add(apiListingReference); count++; } - return new ResourceListing(apiVersion, swaggerVersion, JavaToScalaUtil.toScalaList(apiListingReferences), null, swaggerConfig.info()); + return new ResourceListing(apiVersion, swaggerVersion, JavaToScalaUtil.toScalaList(apiListingReferences), null, + swaggerConfig.info()); } public Map createApiListings() { @@ -100,14 +105,15 @@ private Map processControllers(Set> controllerClass continue; } - Set requestMappingMethods = Reflections.getAllMethods(controllerClass, withAnnotation(RequestMapping.class)); + Set requestMappingMethods = Reflections + .getAllMethods(controllerClass, withAnnotation(RequestMapping.class)); ApiListing apiListing = processControllerApi(controllerClass); String description = ""; Api controllerApi = controllerClass.getAnnotation(Api.class); if (controllerApi != null) { description = controllerApi.description(); } else { - if(apiListing.apis() == null) { + if (apiListing.apis() == null) { apiListing = processMethods(requestMappingMethods, apiListing, description); //Loop over operations 'methods' //processMethods(requestMappingMethods, apiListing, description); @@ -136,7 +142,8 @@ private ApiListing processControllerApi(Class controllerClass) { if (controllerApi == null || resourcePath.isEmpty()) { RequestMapping controllerRequestMapping = controllerClass.getAnnotation(RequestMapping.class); - if (controllerRequestMapping != null && controllerRequestMapping.value() != null && controllerRequestMapping.value().length > 0) { + if (controllerRequestMapping != null && controllerRequestMapping.value() != null && + controllerRequestMapping.value().length > 0) { resourcePath = controllerRequestMapping.value()[0]; } else { resourcePath = controllerClass.getName(); @@ -146,26 +153,27 @@ private ApiListing processControllerApi(Class controllerClass) { SpringApiReader reader = new SpringApiReader(); Option apiListingOption = reader.read(resourcePath, controllerClass, swaggerConfig); ApiListing apiListing = null; - if(apiListingOption.nonEmpty()) { - apiListing = reader.read(resourcePath, controllerClass, swaggerConfig).get(); + if (apiListingOption.nonEmpty()) { + apiListing = apiListingOption.get(); } //Allow for multiple controllers having the same resource path. ApiListing existingApiListing = apiListingMap.get(resourcePath); - if (existingApiListing != null){ - return existingApiListing; + if (existingApiListing != null) { + return existingApiListing; } - if(apiListing != null) { + if (apiListing != null) { return apiListing; } - return new ApiListing(apiVersion, swaggerVersion, basePath, resourcePath, null, null, null, null, null, null, null, 0); + return new ApiListing(apiVersion, swaggerVersion, basePath, resourcePath, null, null, null, null, null, null, + null, 0); } private ApiListing processMethods(Collection methods, ApiListing apiListing, String description) { Map endPointMap = new HashMap(); - + populateApiDescriptionMapForApiListing(apiListing, endPointMap); for (Method method : methods) { @@ -175,10 +183,10 @@ private ApiListing processMethods(Collection methods, ApiListing apiList String requestMappingValue = AnnotationUtils.getMethodRequestMappingValue(method); ApiDescriptionParser documentationEndPointParser = new ApiDescriptionParser(); - ApiDescription apiDescription = documentationEndPointParser.getApiDescription(method, description, apiListing.resourcePath()); + ApiDescription apiDescription = documentationEndPointParser + .getApiDescription(method, description, apiListing.resourcePath()); if (!endPointMap.containsKey(requestMappingValue)) { endPointMap.put(requestMappingValue, apiDescription); -// documentation.apis().add(documentationEndPoint); } } @@ -190,13 +198,13 @@ private ApiListing processMethods(Collection methods, ApiListing apiList String value = AnnotationUtils.getMethodRequestMappingValue(method); List operations = operationMap.get(value); - if(operations == null) { + if (operations == null) { operations = new ArrayList(); operationMap.put(value, operations); } -// ApiDescription documentationEndPoint = endPointMap.get(value); - ApiOperationParser apiOperationParser = new ApiOperationParser(apiListing.resourcePath(), ignorableAnnotations, ignoreUnusedPathVariables); + ApiOperationParser apiOperationParser = new ApiOperationParser(apiListing.resourcePath(), + ignorableAnnotations, ignoreUnusedPathVariables); Operation operation = apiOperationParser.getDocumentationOperation(method); operations.add(operation); } @@ -204,43 +212,34 @@ private ApiListing processMethods(Collection methods, ApiListing apiList List newApiDescriptions = new ArrayList(); for (String key : endPointMap.keySet()) { ApiDescription apiDescription = endPointMap.get(key); - ApiDescription newApiDescription = new ApiDescription(apiDescription.path(), apiDescription.description(), JavaToScalaUtil.toScalaList(operationMap.get(key))); + ApiDescription newApiDescription = new ApiDescription(apiDescription.path(), apiDescription.description(), + JavaToScalaUtil.toScalaList(operationMap.get(key))); newApiDescriptions.add(newApiDescription); } - Map modelMap = new HashMap(); for (Method method : methods) { ApiModelParser apiModelParser = new ApiModelParser(); - modelMap.putAll(apiModelParser.getResponseBodyModels(method)); -// for (String key : documentationSchemaMap.keySet()) { -// documentation.models().add(key, documentationSchemaMap.get(key)); -// } -// -// Map> parameterDocumentationSchemaMap = apiModelParser.getParameterDocumentationSchema(method); -// for (String key : parameterDocumentationSchemaMap.keySet()) { -// documentation.models().add(key, parameterDocumentationSchemaMap.get(key)); -// } + apiListingModels.putAll(apiModelParser.getResponseBodyModels(method)); } -// Map apiListingModels = createApiListingModels(); + Option> modelOptions = Option + .apply(JavaToScalaUtil.toScalaImmutableMap(apiListingModels)); -// Option.>empty(); -// ScalaToJavaUtil.toScalaImmutableMap(documentationSchemaMap); - Option> modelOptions = Option.apply(JavaToScalaUtil.toScalaImmutableMap(apiListingModels)); - - return new ApiListing(apiListing.apiVersion(), apiListing.swaggerVersion(), apiListing.basePath(), apiListing.resourcePath(), - apiListing.produces(), apiListing.consumes(), apiListing.protocols(), apiListing.authorizations(), JavaToScalaUtil.toScalaList(newApiDescriptions), modelOptions, + return new ApiListing(apiListing.apiVersion(), apiListing.swaggerVersion(), apiListing.basePath(), + apiListing.resourcePath(), apiListing.produces(), apiListing.consumes(), apiListing.protocols(), + apiListing.authorizations(), JavaToScalaUtil.toScalaList(newApiDescriptions), modelOptions, apiListing.description(), apiListing.position()); } - private void populateApiDescriptionMapForApiListing(ApiListing apiListing, Map apiDescriptionMap){ - if (apiListing.apis() != null){ + private void populateApiDescriptionMapForApiListing(ApiListing apiListing, + Map apiDescriptionMap) { + if (apiListing.apis() != null) { - List apiDescriptions = ScalaToJavaUtil.toJavaList(apiListing.apis()); - for (ApiDescription apiDescription : apiDescriptions){ - apiDescriptionMap.put(apiDescription.path(), apiDescription); - } - } + List apiDescriptions = ScalaToJavaUtil.toJavaList(apiListing.apis()); + for (ApiDescription apiDescription : apiDescriptions) { + apiDescriptionMap.put(apiDescription.path(), apiDescription); + } + } } private void createApiListingModels() { @@ -253,28 +252,7 @@ private void createApiListingModels() { .setScanners(new SubTypesScanner(false), new ResourcesScanner())); Set> allModelClasses = reflections.getSubTypesOf(Object.class); for (Class clazz : allModelClasses) { - Model model = null; -// ApiModelParser parser; - String schemaName; - if (clazz.isArray()) { -// parser = new ApiModelParser(clazz.getComponentType()); - schemaName = clazz.getComponentType().getSimpleName(); - Option modelOption = ModelConverters.read(clazz.getComponentType()); - if(modelOption.nonEmpty()) { - model = modelOption.get(); - } - } else { -// parser = new ApiModelParser(clazz); - schemaName = clazz.getSimpleName(); - Option modelOption = ModelConverters.read(clazz); - if(modelOption.nonEmpty()) { - model = modelOption.get(); - } - } - if(model != null) { - modelMap.put(schemaName, model); - } -// documentation.addModel(schemaName, parser.parse().toDocumentationSchema()); + ApiUtils.addModels(clazz, modelMap); } } apiListingModels = modelMap; diff --git a/src/main/java/com/knappsack/swagger4springweb/util/ApiUtils.java b/src/main/java/com/knappsack/swagger4springweb/util/ApiUtils.java index d3b504c..6a9c4b4 100644 --- a/src/main/java/com/knappsack/swagger4springweb/util/ApiUtils.java +++ b/src/main/java/com/knappsack/swagger4springweb/util/ApiUtils.java @@ -1,10 +1,15 @@ package com.knappsack.swagger4springweb.util; +import com.wordnik.swagger.converter.ModelConverters; +import com.wordnik.swagger.model.Model; import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.multipart.MultipartFile; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.Collection; import java.util.Date; +import java.util.Map; /** * Author: andrey.antonov @@ -14,7 +19,7 @@ public class ApiUtils { public static String getSwaggerTypeFor(Class parameterType) { Class type = parameterType; - if(parameterType.isArray()) { + if (parameterType.isArray()) { type = type.getComponentType(); } // swagger types are @@ -30,21 +35,21 @@ public static String getSwaggerTypeFor(Class parameterType) { return "string"; } else if (Boolean.class.isAssignableFrom(type)) { return "boolean"; - } else if(Byte.class.isAssignableFrom(type)) { + } else if (Byte.class.isAssignableFrom(type)) { return "byte"; - } else if(Long.class.isAssignableFrom(type)) { + } else if (Long.class.isAssignableFrom(type)) { return "int64"; - } else if(Integer.class.isAssignableFrom(type)) { + } else if (Integer.class.isAssignableFrom(type)) { return "int32"; - } else if(Float.class.isAssignableFrom(type)) { + } else if (Float.class.isAssignableFrom(type)) { return "float"; - } else if(MultipartFile.class.isAssignableFrom(type)) { + } else if (MultipartFile.class.isAssignableFrom(type)) { return "file"; } else if (Number.class.isAssignableFrom(type)) { return "double"; - } else if(Double.class.isAssignableFrom(type)) { + } else if (Double.class.isAssignableFrom(type)) { return "double"; - } else if(Date.class.isAssignableFrom(type)) { + } else if (Date.class.isAssignableFrom(type)) { return "date"; } // others @@ -58,4 +63,32 @@ public static boolean isSet(String value) { public static boolean isAllowMultiple(Class parameterType) { return parameterType != null && (parameterType.isArray() || Collection.class.isAssignableFrom(parameterType)); } + + static boolean isIgnorableModel(String name) { + return name.equalsIgnoreCase("map") || name.equalsIgnoreCase("list") || name.equalsIgnoreCase("string"); + } + + public static void addModels(final Class clazz, final Map models) { + scala.collection.immutable.List modelOption = ModelConverters.readAll(clazz); + scala.collection.Iterator iter = modelOption.iterator(); + while (iter.hasNext()) { + Model model = iter.next(); + if (!isIgnorableModel(model.name())) { + models.put(model.name(), model); + } + } + } + + public static void addModels(final Type type, final Map models) { + if (type instanceof ParameterizedType) { + // Adding both part of generic type + final ParameterizedType parameterizedType = (ParameterizedType) type; + addModels(parameterizedType.getRawType(), models); + for (Type t : parameterizedType.getActualTypeArguments()) { + addModels(t, models); + } + } else if (type instanceof Class) { + addModels((Class) type, models); + } + } }