Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.knappsack.swagger4springweb.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* An annotation which indicates if this Api should be added to a category
* <p/>
* This annotation is applicable to the controller class
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ApiCategory {
java.lang.String value();

java.lang.String description() default "";
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
package com.knappsack.swagger4springweb.parser;

import com.knappsack.swagger4springweb.util.JavaToScalaUtil;
import com.wordnik.swagger.annotations.*;
import com.wordnik.swagger.converter.ModelConverters;
import com.wordnik.swagger.model.*;
import com.wordnik.swagger.model.Authorization;
import com.wordnik.swagger.model.AuthorizationScope;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import scala.Option;
import static java.lang.String.format;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static java.lang.String.format;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.knappsack.swagger4springweb.util.JavaToScalaUtil;
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.Authorization;
import com.wordnik.swagger.model.AuthorizationScope;
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 scala.Option;

public class ApiOperationParser {

Expand Down Expand Up @@ -51,6 +59,8 @@ public Operation parseDocumentationOperation(Method method) {
final Type type = parameterizedType.getActualTypeArguments()[0];
if (type instanceof ParameterizedType) {
documentationOperation.setResponseClass((Class<?>) ((ParameterizedType) type).getRawType());
} else if (type instanceof WildcardType) {
documentationOperation.setResponseClass(Object.class);
} else {
documentationOperation.setResponseClass((Class<?>) type);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,54 @@
package com.knappsack.swagger4springweb.parser;

import static org.reflections.ReflectionUtils.withAnnotation;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.knappsack.swagger4springweb.annotation.ApiCategory;
import com.knappsack.swagger4springweb.controller.ApiDocumentationController;
import com.knappsack.swagger4springweb.filter.ApiExcludeFilter;
import com.knappsack.swagger4springweb.filter.Filter;
import com.knappsack.swagger4springweb.util.AnnotationUtils;
import com.knappsack.swagger4springweb.util.ApiListingUtil;
import com.knappsack.swagger4springweb.util.JavaToScalaUtil;
import com.knappsack.swagger4springweb.util.ScalaToJavaUtil;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.config.SwaggerConfig;
import com.wordnik.swagger.model.*;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import scala.Option;
import com.wordnik.swagger.model.ApiDescription;
import com.wordnik.swagger.model.ApiInfo;
import com.wordnik.swagger.model.ApiListing;
import com.wordnik.swagger.model.ApiListingReference;
import com.wordnik.swagger.model.Model;
import com.wordnik.swagger.model.Operation;
import com.wordnik.swagger.model.ResourceListing;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

import static org.reflections.ReflectionUtils.withAnnotation;
import scala.Option;
import scala.Some;

public class ApiParserImpl implements ApiParser {

private static final Logger LOGGER = LoggerFactory.getLogger(ApiParserImpl.class);

private static final String swaggerVersion = com.wordnik.swagger.core.SwaggerSpec.version();

private final Map<String, ApiListing> apiListingMap = new HashMap<String, ApiListing>();
private final Map<String, ApiListing> apiListingMap = new TreeMap<>();

private final List<String> controllerPackages;
private final List<String> ignorableAnnotations;
Expand Down Expand Up @@ -70,7 +89,7 @@ public ResourceListing getResourceListing(Map<String, ApiListing> apiListingMap)
List<ApiListingReference> apiListingReferences = new ArrayList<ApiListingReference>();
for (String key : apiListingMap.keySet()) {
ApiListing apiListing = apiListingMap.get(key);
String docPath = "/doc"; //servletPath + "/doc"; //"/api/doc";
String docPath = "/doc"; //servletPath + "/doc"; //"/api/doc";
ApiListingReference apiListingReference = new ApiListingReference(docPath + key, apiListing.description(),
apiListing.position());

Expand Down Expand Up @@ -98,7 +117,7 @@ else if (o1.position() > o2.position())
swaggerConfig.info());
}

public Map<String, ApiListing> createApiListings() {
public Map<String, ApiListing> createApiListings() {
Set<Class<?>> controllerClasses = new HashSet<Class<?>>();
for (String controllerPackage : controllerPackages) {
Reflections reflections = new Reflections(controllerPackage);
Expand All @@ -125,24 +144,43 @@ private Map<String, ApiListing> processControllers(Set<Class<?>> controllerClass
.getAllMethods(controllerClass, withAnnotation(RequestMapping.class));
ApiListing apiListing = processControllerApi(controllerClass);
String description = "";
Api controllerApi = controllerClass.getAnnotation(Api.class);
if (controllerApi != null) {
ApiCategory apiCategory = AnnotationUtils.getAnnotationAnnotation(ApiCategory.class, controllerClass);
if (apiCategory != null) {
description = apiCategory.description();
} else {
Api controllerApi = controllerClass.getAnnotation(Api.class);
if (controllerApi != null) {
description = controllerApi.description();
}
}

if (apiListing.apis() == null) {
apiListing = processMethods(requestMappingMethods, controllerClass, apiListing, description);
}
if (apiCategory != null && apiCategory.value() != null) {
String key = prependSlashIfMissing(apiCategory.value());
ApiListing existingApiListing = apiListingMap.get(key);
if (existingApiListing != null) {
apiListing = ApiListingUtil.combine(existingApiListing, apiListing);
}

addApiListingToMap(apiListing, key);
continue;
}

// controllers without any operations are excluded from the apiListingMap list
if (apiListing.apis() != null && !apiListing.apis().isEmpty()) {
apiListingMap.put(apiListing.resourcePath(), apiListing);
addApiListingToMap(apiListing, apiListing.resourcePath());
}
}

return apiListingMap;
}

private void addApiListingToMap(ApiListing apiListing, String key) {
apiListingMap.put(key, ApiListingUtil.sortApisByPath(apiListing));
}


private ApiListing processControllerApi(Class<?> controllerClass) {
String resourcePath = "";
Api controllerApi = controllerClass.getAnnotation(Api.class);
Expand All @@ -159,22 +197,39 @@ private ApiListing processControllerApi(Class<?> controllerClass) {
resourcePath = controllerClass.getName();
}
}
if (!resourcePath.startsWith("/")) {
resourcePath = "/" + resourcePath;
}
resourcePath = prependSlashIfMissing(resourcePath);

String docRoot = resourcePath;
String docRoot = resourcePath;
if(docRoot.contains(controllerClass.getName())) {
docRoot = docRoot.replace(controllerClass.getName(), "");
}
SpringApiReader reader = new SpringApiReader();
Option<ApiListing> apiListingOption = reader.read(docRoot, controllerClass, swaggerConfig);

ApiListing apiListing = null;
Option<String> descriptionOption = null;
String categoryValue = null;

ApiCategory category = AnnotationUtils.getAnnotationAnnotation(ApiCategory.class, controllerClass);
String description = null;
if (category != null) {
categoryValue = category.value();
description = category.description();
descriptionOption = Some.apply(description);
}

if (apiListingOption.nonEmpty()) {
apiListing = apiListingOption.get();
apiListing = apiListingOption.get();
apiListing = ApiListingUtil.changeDescription(apiListing, description);
}

//Allow for multiple controllers having the same resource path.
if (categoryValue != null) {
ApiListing existingApiListing = apiListingMap.get(categoryValue);
if (existingApiListing != null) {
return existingApiListing;
}
}
ApiListing existingApiListing = apiListingMap.get(resourcePath);
if (existingApiListing != null) {
return existingApiListing;
Expand All @@ -185,7 +240,7 @@ private ApiListing processControllerApi(Class<?> controllerClass) {
}

return new ApiListing(apiVersion, swaggerVersion, basePath, resourcePath, null, null, null, null, null, null,
null, 0);
descriptionOption, 0);
}

private ApiListing processMethods(Collection<Method> methods, Class<?> controllerClass, ApiListing apiListing, String description) {
Expand Down Expand Up @@ -269,4 +324,11 @@ private boolean ignore(Method method) {
return false;
}

private String prependSlashIfMissing(String key) {
if (!key.startsWith("/")) {
key = "/" + key;
}
return key;
}

}
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package com.knappsack.swagger4springweb.util;

import com.google.common.collect.Lists;
import com.knappsack.swagger4springweb.model.AnnotatedParameter;
import com.thoughtworks.paranamer.BytecodeReadingParanamer;
import com.thoughtworks.paranamer.Paranamer;
import com.wordnik.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import org.springframework.web.bind.annotation.RequestMapping;

import com.google.common.collect.Lists;
import com.knappsack.swagger4springweb.model.AnnotatedParameter;
import com.thoughtworks.paranamer.BytecodeReadingParanamer;
import com.thoughtworks.paranamer.Paranamer;
import com.wordnik.swagger.annotations.ApiParam;

public class AnnotationUtils {

/**
Expand Down Expand Up @@ -84,4 +85,23 @@ public static List<AnnotatedParameter> getAnnotatedParameters(Method method) {
}
return annotatedParameters;
}


public static <T extends Annotation> T getAnnotationAnnotation(Class<T> annotationClass, Class<?> controllerClass) {
Annotation[] annotations = controllerClass.getAnnotations();
for (Annotation annotation : annotations) {
T value = getAnnotation(annotation, annotationClass);
if (value != null) {
return value;
}
}
return null;
}

private static <T extends Annotation> T getAnnotation(Annotation ann, Class<T> annotationType) {
if (annotationType.isInstance(ann)) {
return (T) ann;
}
return ann.annotationType().getAnnotation(annotationType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.knappsack.swagger4springweb.util

import com.wordnik.swagger.model.ApiListing

/**
* @author Allar Tammik
*/
object ApiListingUtil {
def combine(main: ApiListing, additional : ApiListing) : ApiListing = main.copy(apis = main.apis ++ additional.apis)
def sortApisByPath(obj: ApiListing) : ApiListing = obj.copy(apis = obj.apis.sortBy(api => api.path))
def changeDescription(obj: ApiListing, desc: String) : ApiListing = obj.copy(description = Some(desc))
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.knappsack.swagger4springweb;

import com.wordnik.swagger.model.ApiInfo;

import java.util.Arrays;
import java.util.List;

import com.wordnik.swagger.model.ApiInfo;

public abstract class AbstractTest {

public static final String BASE_CONTROLLER_PACKAGE = "com.knappsack.swagger4springweb.testController";
public static final String EXCLUDE_LABEL = "exclude";
public static final List<String> END_POINT_PATHS = Arrays.asList("/doc/api/v1/partialExclude", "/doc/api/v1/test", "/doc/api/v1/exclude2", "/doc/com.knappsack.swagger4springweb.testController.NoClassLevelMappingController");
public static final List<String> END_POINT_PATHS = Arrays.asList("/doc/api/v1/partialExclude", "/doc/api/v1/test", "/doc/api/v1/exclude2", "/doc/com.knappsack.swagger4springweb.testController.NoClassLevelMappingController", "/doc/api/v1/nocolor", "/doc/dark", "/doc/light");
public static final ApiInfo API_INFO = new ApiInfo("swagger4spring-web example app", "This is a basic web app for demonstrating swagger4spring-web", "http://localhost:8080/terms", "http://localhost:8080/contact", "MIT", "http://opensource.org/licenses/MIT");
}
Loading