├── .gitignore ├── Jenkinsfile ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── roamsys │ └── swagger │ ├── SwaggerAPIAuthorizationHandler.java │ ├── SwaggerAPIConfig.java │ ├── SwaggerAPICustomRequestHandler.java │ ├── SwaggerAPIDefaultAuthorizationHandler.java │ ├── SwaggerAPIListener.java │ ├── SwaggerAPIModel.java │ ├── SwaggerAPIServlet.java │ ├── annotations │ ├── SwaggerApi.java │ ├── SwaggerModel.java │ └── SwaggerParameter.java │ ├── data │ ├── ContentType.java │ ├── SwaggerAPIContext.java │ ├── SwaggerAPIModelData.java │ ├── SwaggerAPIParameterData.java │ └── SwaggerExceptionHandler.java │ └── documentation │ ├── AbstractApiSpecPart.java │ ├── ApiSpecBuilder.java │ ├── InfoApiSpec.java │ ├── OperationApiSpec.java │ ├── ParameterApiSpec.java │ ├── ResponseApiSpec.java │ ├── SwaggerApiSpec.java │ └── TagApiSpec.java └── test ├── java └── com │ └── roamsys │ └── swagger │ ├── TestsAPI.java │ ├── data │ └── SwaggerAPIModelDataTest.java │ └── documentation │ └── ApiSpecBuilderTest.java └── resources └── swagger.json /.gitignore: -------------------------------------------------------------------------------- 1 | nb-configuration.xml 2 | nbactions*.xml 3 | target/ 4 | .settings 5 | .classpath 6 | .project -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | buildAndDeploy([ 2 | git: 'https://github.com/ROAMSYS/swaggerapi' 3 | ]) 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Roamsys S.A. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swagger Java Client 2 | 3 | * [Configuration](#configuration) 4 | * [Register the Swagger API components](#register-the-swagger-api-components) 5 | * [Start Up listener](#start-up-listener) 6 | * [Servlet for API calls](#servlet-for-api-calls) 7 | * [Example API declaration](#example-api-declaration) 8 | * [API class annotation](#api-class-annotation) 9 | * [API method annotations](#api-method-annotations) 10 | * [Method parameters](#method-parameters) 11 | * [License](#license) 12 | 13 | ## Configuration 14 | 15 | Create a class that extends the **SwaggerAPIListener** class. Here you can do all your configuration using the instance of SwaggerAPIConfig. Use the **registerModel** method to add your API services. 16 | ````java 17 | public class SwaggerListener extends SwaggerAPIListener { 18 | 19 | @Override 20 | public void initialize(final SwaggerAPIConfig config) { 21 | config.allowCrossOriginAccess(); 22 | config.setAPIVersion("0.1.0"); 23 | config.setBasePath("http://example.roamsys.lan:8080/api/"); 24 | config.setSwaggerVersion("1.1"); 25 | config.setDefaultContentType(SwaggerAPIConfig.CONTENT_TYPE_JSON_UTF8); 26 | config.registerModel(new TestModel()); 27 | } 28 | } 29 | ```` 30 | ## Register the Swagger API components 31 | 32 | ### Start Up listener 33 | Register the start up listener class you've created in your **web.xml**. This will initiate the Swagger API services at the start up of your application. 34 | ````xml 35 | 36 | ... 37 | 38 | ... 39 | com.roamsys.example.gwt.server.swagger.SwaggerListener 40 | 41 | 42 | ```` 43 | 44 | ### Servlet for API calls 45 | Edit your **web.xml** and add the Swagger API servlet. Thats all. 46 | ````xml 47 | 48 | ... 49 | 50 | SwaggerAPI 51 | com.roamsys.swagger.SwaggerAPIServlet 52 | 53 | 54 | SwaggerAPI 55 | /api/* 56 | 57 | 58 | ```` 59 | 60 | ## Example API declaration 61 | ````java 62 | @SwaggerModel (path = "/metadata") 63 | public class MetadataAPI implements SwaggerAPIModel { 64 | 65 | @SwaggerApi ( 66 | notes = "Returns a list of all documents", 67 | method = HTTPMethod.GET, 68 | path = "/all", 69 | summary = "Get document list") 70 | public void all(final SwaggerAPIContext context) throws IOException { 71 | context.getResponse().getWriter().println("[ { \"name\" : \"document 1\", \"hash\" : \"abc\"}, { \"name\": \"another document\", \"hash\" : \"rrr\"} ]"); 72 | } 73 | 74 | @SwaggerApi ( 75 | notes = "Returns detailed information a specific document", 76 | method = HTTPMethod.GET, 77 | path = "/details/{hash}", 78 | summary = "Get document details") 79 | public void allForTypeAndFormat(final SwaggerAPIContext context, 80 | @SwaggerParameter ( 81 | name = "hash", 82 | description = "The document hash", 83 | required = true, 84 | paramType = ParamType.PATH, 85 | dataType = DataType.STRING 86 | ) final String hash) { 87 | if (hash.equals("abc")) { 88 | context.getResponse().getWriter().println("{ \"name\" : \"document 1\", \"hash\" : \"abc\", , \"size\" : 1232, , \"extension\" : \"odt\"}"); 89 | } else if (hash.equals("rrr")) { 90 | context.getResponse().getWriter().println("{ \"name\" : \"another document\", \"hash\" : \"rrr\", , \"size\" : 3532, , \"extension\" : \"zip\"}"); 91 | } 92 | } 93 | } 94 | ```` 95 | ### API class annotation 96 | 97 | Each class defining an API has to be annotated with @SwaggerModel(path = "pathToAPIs"). All APIs defined in the class will have that path as prefix. 98 | The following example defines two APIs. Both APIs will be available thru the base path of your application followed by */metadata*. 99 | 100 | ### API method annotations 101 | 102 | To make an method available as public API annotate it with @SwaggerApi and provide the usual Swagger specifications: 103 | 104 | * **notes** - A description for the API 105 | * **method** - The HTTP method GET, PUT, POST, DELETE 106 | * **path** - The URL pattern containing placeholders for parameters 107 | * **summary** - A short description or name for the API 108 | 109 | ### Method parameters 110 | 111 | The parameters used in the URL must annotated with the @SwaggerParameter annotation, which uses the following properties: 112 | 113 | * **name** - The name of the parameter, should be the same as the method argument 114 | * **description** - A short description for the parameter 115 | * **required** - Set this to *true* for mandatory parameters, optional parameters should be placed at the end of the URL, if paramType is PATH 116 | * **paramType** - The type/kind of the parameter 117 | * *PATH* - For parameters placed in a REST-full URL seperated by slashes 118 | * *QUERY* - For parameters in a query string append to the URL 119 | * *BODY* - For parameters in a the body (the data) of a PUT or POST request 120 | * *HEADER* - For parameters in the request header 121 | * *FORM* - For parameters in a request body encoded with *multipart/form-data* 122 | * **dataType** - The data type of the parameter 123 | * *STRING* 124 | * *INTEGER* 125 | * *LONG* 126 | * *BOOLEAN* 127 | * *DATE* 128 | * *DATETIME* 129 | 130 | ## Documentation support 131 | 132 | The support for Swagger UI has been changed since Release 5: 133 | * Former versions support the Swagger UI version 1.x (aka resource.json). 134 | * The current version now supports Swagger UI version 2.0 which is also known as *OpenAPI* framework. 135 | 136 | The following URL may be used for getting the OpenAPI specification based on the annotation information defined on class and method level: 137 | `https:////swagger.json` 138 | 139 | The API key has to be specified as URL parameter 'api_key' or as header field 'X-Api-Key'. 140 | 141 | ## License 142 | 143 | The MIT License (MIT) 144 | 145 | Copyright (c) 2014 Roamsys S.A. 146 | 147 | Permission is hereby granted, free of charge, to any person obtaining a copy 148 | of this software and associated documentation files (the "Software"), to deal 149 | in the Software without restriction, including without limitation the rights 150 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 151 | copies of the Software, and to permit persons to whom the Software is 152 | furnished to do so, subject to the following conditions: 153 | 154 | The above copyright notice and this permission notice shall be included in all 155 | copies or substantial portions of the Software. 156 | 157 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 158 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 159 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 160 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 161 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 162 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 163 | SOFTWARE. 164 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 4.0.0 7 | com.roamsys.opensource 8 | swaggerapi 9 | jar 10 | 11.0.0-SNAPSHOT 11 | 12 | ROAMSYS S.A. 13 | http://www.roamsys.com 14 | 15 | SwaggerAPI 16 | Swagger API support for server with REST APIs written in Java 17 | 18 | 19 | jakarta.servlet 20 | jakarta.servlet-api 21 | 6.1.0 22 | provided 23 | 24 | 25 | org.apache.commons 26 | commons-lang3 27 | 3.17.0 28 | 29 | 30 | commons-io 31 | commons-io 32 | 2.18.0 33 | 34 | 35 | com.google.code.gson 36 | gson 37 | 2.12.1 38 | 39 | 40 | junit 41 | junit 42 | 4.13.2 43 | test 44 | 45 | 46 | 47 | UTF-8 48 | 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-compiler-plugin 54 | 3.14.0 55 | 56 | 17 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.wagon 63 | wagon-ssh 64 | 3.3.3 65 | 66 | 67 | 68 | 69 | 70 | Roamsys Repository 71 | roamsys-repository 72 | scp://37.157.154.101/home/mvnuser/repository 73 | 74 | 75 | 76 | 77 | roamsys-repository 78 | Roamsys Repository 79 | scp://37.157.154.101/home/mvnuser/repository 80 | 81 | 82 | 83 | 84 | Jörg Adams 85 | adams@roamsys-next.com 86 | ROAMSYS S.A. 87 | http://www.roamsys.com 88 | 89 | 90 | Johanna Heib 91 | heib@roamsys-next.com 92 | ROAMSYS S.A. 93 | http://www.roamsys.com 94 | 95 | 96 | Norbert Becker 97 | becker@roamsys-next.com 98 | ROAMSYS S.A. 99 | http://www.roamsys.com 100 | 101 | 102 | 103 | scm:git:git@github.com:ROAMSYS/swaggerapi.git 104 | scm:git:git@github.com:ROAMSYS/swaggerapi.git 105 | git@github.com:ROAMSYS/swaggerapi.git 106 | 107 | 108 | 109 | MIT License 110 | http://www.opensource.org/licenses/mit-license.php 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/SwaggerAPIAuthorizationHandler.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | import java.io.IOException; 4 | import jakarta.servlet.ServletException; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletResponse; 7 | 8 | /** 9 | * Interface for authentication handler. 10 | * 11 | * @author mbartel 12 | */ 13 | public interface SwaggerAPIAuthorizationHandler { 14 | 15 | /** 16 | * Handles a Swagger API authentication request. 17 | * 18 | * @param request the request 19 | * @param response the response 20 | * @return true, if the request is authorized 21 | * @throws ServletException 22 | * @throws IOException 23 | */ 24 | boolean isRequestAuthorized(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException; 25 | 26 | /** 27 | * Default implementation for resolving API key from request. 28 | * The following strategy is used: 29 | *
    30 | *
  1. Check for URL parameter api_key
  2. 31 | *
  3. Check for header field X-Api-Key
  4. 32 | *
33 | * 34 | * @param request the request 35 | * @return the resolved API key or null 36 | */ 37 | default String resolveApiKey(final HttpServletRequest request) { 38 | final String requestAPIKeyParameter = request.getParameter("api_key"); 39 | if (requestAPIKeyParameter == null) { 40 | return request.getHeader("x-api-key"); 41 | } else { 42 | return requestAPIKeyParameter; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/SwaggerAPIConfig.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | import com.roamsys.swagger.annotations.SwaggerApi; 4 | import com.roamsys.swagger.annotations.SwaggerModel; 5 | import com.roamsys.swagger.annotations.SwaggerParameter; 6 | import com.roamsys.swagger.data.SwaggerAPIModelData; 7 | import com.roamsys.swagger.data.SwaggerAPIParameterData; 8 | import com.roamsys.swagger.data.SwaggerExceptionHandler; 9 | import com.roamsys.swagger.documentation.ApiSpecBuilder; 10 | import com.roamsys.swagger.documentation.SwaggerApiSpec; 11 | import java.lang.annotation.Annotation; 12 | import java.lang.reflect.Method; 13 | import java.net.URL; 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.stream.Collectors; 20 | import jakarta.servlet.ServletContext; 21 | 22 | /** 23 | * The swagger configuration. 24 | * 25 | * @author johanna 26 | */ 27 | public class SwaggerAPIConfig { 28 | 29 | /** 30 | * Content type for JavaScript 31 | */ 32 | public final static String CONTENT_TYPE_JAVASCRIPT = "text/javascript"; 33 | 34 | /** 35 | * The swagger API servlet context attribute name 36 | */ 37 | public final static String SERVLET_ATTRIBUTE_NAME = "SwaggerAPIModel"; 38 | 39 | /** 40 | * The map that holds all swagger API data with their path as identifier 41 | */ 42 | private final Map> swaggerAPIs = new HashMap<>(); 43 | 44 | /** 45 | * The builder for collecting API spec information 46 | */ 47 | private final ApiSpecBuilder apiSpecBuilder; 48 | 49 | /** 50 | * Defines if cross origin access is allowed 51 | */ 52 | private boolean allowOriginAccess = false; 53 | 54 | /** 55 | * The default content type for the HTTP response 56 | */ 57 | private String defaultContentType; 58 | 59 | /** 60 | * The exception handler 61 | */ 62 | private SwaggerExceptionHandler exceptionHandler = SwaggerExceptionHandler.DEFAULT; 63 | 64 | /** 65 | * The handler used to authenticate the API calls 66 | */ 67 | private SwaggerAPIAuthorizationHandler authorizationHandler; 68 | 69 | /** 70 | * The handler that is executed before each request after the authentication 71 | */ 72 | private SwaggerAPICustomRequestHandler preRequestHandler; 73 | 74 | /** 75 | * The handler that is executed after each request 76 | */ 77 | private SwaggerAPICustomRequestHandler postRequestHandler; 78 | 79 | /** 80 | * The context of the servlet session 81 | */ 82 | private final ServletContext servletContext; 83 | 84 | /** 85 | * Constructor to initialize values 86 | * @param servletContext the servlet context 87 | */ 88 | public SwaggerAPIConfig(final ServletContext servletContext) { 89 | this.servletContext = servletContext; 90 | apiSpecBuilder = new ApiSpecBuilder(); 91 | } 92 | 93 | /** 94 | * Registers a new API model 95 | * 96 | * @param model new Swagger API model 97 | */ 98 | public void registerModel(final SwaggerAPIModel model) { 99 | // check if current model is annotated with {@link SwaggerModel} 100 | final SwaggerModel modelAnnotation = model.getClass().getAnnotation(SwaggerModel.class); 101 | if (modelAnnotation != null) { 102 | final String modelPath = modelAnnotation.path() + "." + modelAnnotation.format(); 103 | 104 | for (final Method method : model.getClass().getMethods()) { 105 | if (method.isAnnotationPresent(SwaggerApi.class)) { 106 | 107 | // fetch Swagger annotations for the method and it's parameters and prepare the data structures for them 108 | method.setAccessible(true); 109 | final SwaggerApi annotation = method.getAnnotation(SwaggerApi.class); 110 | final Annotation[][] annotations = method.getParameterAnnotations(); 111 | final List paramAnnotations = new ArrayList<>(annotations.length); 112 | 113 | // collect the parameter annotations 114 | for (final Annotation[] param : annotations) { 115 | for (final Annotation currentParamAnnotation : param) { 116 | if (currentParamAnnotation.annotationType().equals(SwaggerParameter.class)) { 117 | final SwaggerParameter paramAnnotaion = (SwaggerParameter) currentParamAnnotation; 118 | paramAnnotations.add(paramAnnotaion); 119 | } 120 | } 121 | } 122 | 123 | // add the data structure with the collected information to the list of APIs for the current base path 124 | final String path = modelPath + annotation.path(); 125 | final List parameters = paramAnnotations.stream().map(a -> new SwaggerAPIParameterData(a.name(), a.paramType(), a.dataType())).collect(Collectors.toList()); 126 | swaggerAPIs.computeIfAbsent(modelPath, p -> new ArrayList<>()).add(new SwaggerAPIModelData(model, method, annotation.method(), path, parameters)); 127 | 128 | // add the API operation to spec 129 | apiSpecBuilder.addOperation(modelAnnotation, annotation, paramAnnotations); 130 | } 131 | } 132 | } else { 133 | throw new IllegalArgumentException(SwaggerAPIModel.class.getSimpleName() + " annotation must be present on model class"); 134 | } 135 | } 136 | 137 | /** 138 | * Get swagger API list for current base path 139 | * 140 | * @param path the base path to the API model 141 | * @return list of swagger API data that belong to the base path 142 | */ 143 | public List getAPIsFor(final String path) { 144 | return swaggerAPIs.get(path); 145 | } 146 | 147 | /** 148 | * Get the OpenAPI spec for the configured models. 149 | * 150 | * @return the API spec 151 | */ 152 | public SwaggerApiSpec getApiSpec() { 153 | return apiSpecBuilder.getApiSpec(); 154 | } 155 | 156 | /** 157 | * Sets the URL serving the API. This field is important for completing the OpenAPI specification. 158 | * Declarations on the server providing the APIs themselves, it is not a requirement. 159 | * 160 | * @param apiUrl The API URL 161 | */ 162 | public void setURL(final URL apiUrl) { 163 | apiSpecBuilder.setSchemes(Collections.singletonList(apiUrl.getProtocol())); 164 | if (apiUrl.getPort() != -1) { 165 | apiSpecBuilder.setHost(String.format("%s:%d", apiUrl.getHost(), apiUrl.getPort())); 166 | } else { 167 | apiSpecBuilder.setHost(apiUrl.getHost()); 168 | } 169 | apiSpecBuilder.setBasePath(apiUrl.getPath()); 170 | } 171 | 172 | /** 173 | * Provides the version of the application API. 174 | * 175 | * @param apiVersion the API version as string 176 | */ 177 | public void setAPIVersion(final String apiVersion) { 178 | apiSpecBuilder.setVersion(apiVersion); 179 | } 180 | 181 | /** 182 | * Provides title metadata about the API. The title can be used by the clients if needed, and can be presented in the 183 | * Swagger-UI for convenience. 184 | * 185 | * @param info the swagger metadata info as string 186 | */ 187 | public void setTitle(final String title) { 188 | apiSpecBuilder.setTitle(title); 189 | } 190 | 191 | /** 192 | * Provides description metadata about the API. This field is important for completing the OpenAPI specification. 193 | * 194 | * @param info the swagger metadata info as string 195 | */ 196 | public void setDescription(final String description) { 197 | apiSpecBuilder.setDescription(description); 198 | } 199 | 200 | /** 201 | * Returns the context of the Swagger API servlet 202 | * @return the servlet context 203 | */ 204 | public ServletContext getServletContext() { 205 | return servletContext; 206 | } 207 | 208 | /** 209 | * Returns if current path belongs to an API model definition 210 | * 211 | * @param path the current API path 212 | * @return true if current path defines an API model 213 | */ 214 | public boolean isAPIModelPath(final String path) { 215 | return swaggerAPIs.containsKey(path); 216 | } 217 | 218 | /** 219 | * Allows cross origin access to the API. Is not allowed by default. 220 | */ 221 | public void allowCrossOriginAccess() { 222 | allowOriginAccess = true; 223 | } 224 | 225 | /** 226 | * Returns if cross origin access to the API is allowed. 227 | * 228 | * @return true if cross origin access is enabled 229 | */ 230 | public boolean isCrossOriginAccessAllowed() { 231 | return allowOriginAccess; 232 | } 233 | 234 | /** 235 | * Sets the default content type for the HTTP response 236 | * 237 | * @param defaultContentType 238 | */ 239 | public void setDefaultContentType(final String defaultContentType) { 240 | this.defaultContentType = defaultContentType; 241 | } 242 | 243 | /** 244 | * Returns default content type for the HTTP response 245 | * 246 | * @return default content type for the HTTP response 247 | */ 248 | public String getDefaultContentType() { 249 | return defaultContentType; 250 | } 251 | 252 | /** 253 | * Returns the exception handler for handling the exceptions thrown while method invocations 254 | * @return the exception handler or null if none is set 255 | */ 256 | public SwaggerExceptionHandler getExceptionHandler() { 257 | return exceptionHandler; 258 | } 259 | 260 | /** 261 | * Sets an exception handler for handling exceptions thrown while method invocations 262 | * @param exceptionHandler the exception handler 263 | */ 264 | public void setExceptionHandler(final SwaggerExceptionHandler exceptionHandler) { 265 | this.exceptionHandler = exceptionHandler; 266 | } 267 | 268 | /** 269 | * Sets the authorization handler to authenticate the API calls 270 | * @param authorizationHandler the authorization handler (set to null to disable authorization) 271 | */ 272 | public void setAuthorizationHandler(final SwaggerAPIAuthorizationHandler authorizationHandler) { 273 | this.authorizationHandler = authorizationHandler; 274 | } 275 | 276 | /** 277 | * Returns the authentication handler, which is called before every time before a request is handled 278 | * @return the authentication handler 279 | */ 280 | public SwaggerAPIAuthorizationHandler getAuthorizationHandler() { 281 | return authorizationHandler; 282 | } 283 | 284 | /** 285 | * Returns the pre-request handler, that is executed before each API request (after the authentication) 286 | * @return the pre request handler 287 | */ 288 | public SwaggerAPICustomRequestHandler getPreRequestHandler() { 289 | return preRequestHandler; 290 | } 291 | 292 | /** 293 | * Sets the pre-request handler, that is executed before each API request (after the authentication) 294 | * @param preRequestHandler the pre request hander (set to null to disable) 295 | */ 296 | public void setPreRequestHandler(final SwaggerAPICustomRequestHandler preRequestHandler) { 297 | this.preRequestHandler = preRequestHandler; 298 | } 299 | 300 | /** 301 | * Returns the post-request handler, that is executed after each API request 302 | * @return the post request handler 303 | */ 304 | public SwaggerAPICustomRequestHandler getPostRequestHandler() { 305 | return postRequestHandler; 306 | } 307 | 308 | /** 309 | * Sets the post-request handler, that is executed after each API request 310 | * @param postRequestHandler the post request hander (set to null to disable) 311 | */ 312 | public void setPostRequestHandler(final SwaggerAPICustomRequestHandler postRequestHandler) { 313 | this.postRequestHandler = postRequestHandler; 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/SwaggerAPICustomRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | import java.io.IOException; 4 | import jakarta.servlet.ServletException; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletResponse; 7 | 8 | /** 9 | * Interface for pre- and post-request handlers 10 | * @author mbartel 11 | */ 12 | public interface SwaggerAPICustomRequestHandler { 13 | 14 | /** 15 | * Handles a Swagger API request 16 | * @param request the request 17 | * @param response the response 18 | * @throws ServletException 19 | * @throws IOException 20 | */ 21 | public void handle(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/SwaggerAPIDefaultAuthorizationHandler.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | import java.io.IOException; 4 | import jakarta.servlet.ServletException; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletResponse; 7 | 8 | /** 9 | * Check for a valid API key 10 | * @author mbartel 11 | */ 12 | public class SwaggerAPIDefaultAuthorizationHandler implements SwaggerAPIAuthorizationHandler { 13 | 14 | /** 15 | * The API key 16 | */ 17 | private final String apiKey; 18 | 19 | /** 20 | * Creates a new authentication handler that only checks the API key 21 | * @param apiKey the API key 22 | */ 23 | public SwaggerAPIDefaultAuthorizationHandler(final String apiKey) { 24 | this.apiKey = apiKey; 25 | } 26 | 27 | @Override 28 | public boolean isRequestAuthorized(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 29 | final String requestAPIKey = resolveApiKey(request); 30 | return requestAPIKey != null && requestAPIKey.equals(apiKey); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/SwaggerAPIListener.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | import jakarta.servlet.ServletContext; 4 | import jakarta.servlet.ServletContextEvent; 5 | import jakarta.servlet.ServletContextListener; 6 | 7 | /** 8 | * The swagger API listener to be executed on startup 9 | * 10 | * @author johanna 11 | */ 12 | public abstract class SwaggerAPIListener implements ServletContextListener { 13 | 14 | @Override 15 | public void contextInitialized(final ServletContextEvent sce) { 16 | final ServletContext servletContext = sce.getServletContext(); 17 | 18 | final SwaggerAPIConfig model = new SwaggerAPIConfig(servletContext); 19 | servletContext.setAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME, model); 20 | 21 | System.out.print("Initializing Swagger API components ... "); 22 | initialize(model); 23 | System.out.println("done. => " + model.getApiSpec().toString()); 24 | } 25 | 26 | @Override 27 | public void contextDestroyed(final ServletContextEvent sce) { 28 | sce.getServletContext().removeAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME); 29 | } 30 | 31 | /** 32 | * Initializes the swagger API startup definitions 33 | * 34 | * @param model the swagger API model 35 | */ 36 | public abstract void initialize(final SwaggerAPIConfig model); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/SwaggerAPIModel.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | /** 4 | * A marker interface for swagger API models 5 | * 6 | * @author johanna 7 | */ 8 | public interface SwaggerAPIModel { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/SwaggerAPIServlet.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | import com.google.gson.GsonBuilder; 4 | import com.roamsys.swagger.annotations.SwaggerApi.HTTPMethod; 5 | import com.roamsys.swagger.annotations.SwaggerParameter.DataType; 6 | import com.roamsys.swagger.data.ContentType; 7 | import com.roamsys.swagger.data.SwaggerAPIContext; 8 | import com.roamsys.swagger.data.SwaggerAPIModelData; 9 | import com.roamsys.swagger.data.SwaggerAPIParameterData; 10 | import com.roamsys.swagger.data.SwaggerExceptionHandler; 11 | import java.io.IOException; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.text.ParseException; 14 | import java.text.SimpleDateFormat; 15 | import java.util.List; 16 | import java.util.regex.Matcher; 17 | import jakarta.servlet.ServletException; 18 | import jakarta.servlet.http.HttpServlet; 19 | import jakarta.servlet.http.HttpServletRequest; 20 | import jakarta.servlet.http.HttpServletResponse; 21 | import org.apache.commons.lang3.StringUtils; 22 | 23 | /** 24 | * The Swagger API servlet. 25 | * 26 | * @author johanna 27 | */ 28 | public class SwaggerAPIServlet extends HttpServlet { 29 | 30 | private static final long serialVersionUID = 1L; 31 | 32 | /** 33 | * Date format for API calls 34 | */ 35 | private static final String DATE_FORMAT = "yyyy-MM-dd"; 36 | 37 | /** 38 | * Date-Time format for API calls 39 | */ 40 | private static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX"; 41 | 42 | /** 43 | * Processes requests for both HTTP GET and POST methods. 44 | * 45 | * @param request servlet request 46 | * @param response servlet response 47 | * @param method the HTTP method call type 48 | * @throws ServletException if a servlet-specific error occurs 49 | * @throws IOException if an I/O error occurs 50 | */ 51 | protected void processRequest(final HttpServletRequest request, final HttpServletResponse response, final HTTPMethod method) throws ServletException, IOException { 52 | final SwaggerAPIConfig config = (SwaggerAPIConfig) request.getSession().getServletContext().getAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME); 53 | 54 | // register exception handler for API 55 | final String exceptionHandlerClass = getServletConfig().getInitParameter("exceptionHandler"); 56 | SwaggerExceptionHandler exceptionHandler = config.getExceptionHandler(); 57 | if (!StringUtils.isEmpty(exceptionHandlerClass)) { 58 | try { 59 | final Class clazz = Class.forName(exceptionHandlerClass); 60 | exceptionHandler = (SwaggerExceptionHandler) clazz.getConstructor().newInstance(); 61 | config.setExceptionHandler(exceptionHandler); 62 | } catch (final Exception ex) { 63 | // log error with default exception handler 64 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Could not instantiate Swagger exception handler [" + exceptionHandlerClass + "]. Default exception handler will be used.", ex); 65 | } 66 | } 67 | 68 | // get the URL decoded path to be called 69 | final String path = request.getPathInfo(); 70 | if (path == null) { 71 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid empty path", null); 72 | return; 73 | } 74 | 75 | // enables cross-origin-access, if allowed in config 76 | if (config.isCrossOriginAccessAllowed()) { 77 | response.addHeader("Access-Control-Allow-Origin", "*"); 78 | response.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, UPDATE, OPTIONS"); 79 | response.addHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, X-Api-Key"); 80 | } 81 | 82 | // sets the default content type, if defined in config 83 | if (config.getDefaultContentType() != null) { 84 | response.setContentType(config.getDefaultContentType()); 85 | } 86 | 87 | // try to authenticate the API call 88 | if (config.getAuthorizationHandler() != null && !config.getAuthorizationHandler().isRequestAuthorized(request, response)) { 89 | exceptionHandler.handleException(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid authorization key", null); 90 | return; 91 | } 92 | 93 | // execute pre-request handler 94 | if (config.getPreRequestHandler() != null) { 95 | config.getPreRequestHandler().handle(request, response); 96 | } 97 | 98 | // OpenAPI swagger.json request e.g. from Swagger UI 99 | if (path.equals("/swagger.json")) { 100 | response.setStatus(HttpServletResponse.SC_OK); 101 | response.setContentType(ContentType.JSON_UTF8); 102 | final GsonBuilder gsonBuilder = new GsonBuilder(); 103 | gsonBuilder.excludeFieldsWithoutExposeAnnotation(); 104 | gsonBuilder.create().toJson(config.getApiSpec(), response.getWriter()); 105 | } else { 106 | // API method calls 107 | final int basePathEndPos = path.indexOf("/", 1); 108 | if (basePathEndPos == -1) { 109 | exceptionHandler.handleException(response, HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Invalid base URL", null); 110 | return; 111 | } 112 | 113 | // initialize for maybe no match 114 | boolean matchFound = false; 115 | 116 | final String basePath = path.substring(0, path.indexOf("/", 1)); 117 | if (config.isAPIModelPath(basePath)) { 118 | for (final SwaggerAPIModelData api : config.getAPIsFor(basePath)) { 119 | // Find matching API model and method 120 | if (api.getHTTPMethod() == method) { 121 | final Matcher pathParamMatcher = api.matchPath(path); 122 | if (pathParamMatcher.matches()) { 123 | matchFound = true; 124 | response.setStatus(HttpServletResponse.SC_OK); 125 | 126 | // Set up variables for parameter collection 127 | final List paramsData = api.getParameters(); 128 | final int parameterCount = paramsData.size(); 129 | final Object[] arguments = new Object[parameterCount + 1]; 130 | arguments[0] = new SwaggerAPIContext(this, request, response, exceptionHandler); 131 | 132 | // Collect parameters 133 | int getParamIndex = 0; 134 | for (int i = 1; i <= parameterCount; i++) { 135 | final SwaggerAPIParameterData paramData = paramsData.get(i - 1); 136 | 137 | // Fetch and convert argument value 138 | try { 139 | switch (paramData.getParamType()) { 140 | case PATH: 141 | if (pathParamMatcher.groupCount() >= getParamIndex + 1) { 142 | arguments[i] = convertParamToArgument(paramData.getDataType(), pathParamMatcher.group(1 + getParamIndex++)); 143 | } 144 | break; 145 | 146 | case QUERY: 147 | arguments[i] = convertParamToArgument(paramData.getDataType(), request.getParameter(paramData.getName())); 148 | break; 149 | 150 | case FORM: 151 | case BODY: 152 | arguments[i] = request.getInputStream(); 153 | break; 154 | 155 | case HEADER: 156 | arguments[i] = convertParamToArgument(paramData.getDataType(), request.getHeader(paramData.getName())); 157 | break; 158 | 159 | default: 160 | throw new IllegalArgumentException("Handling for parameter type \"" + paramData.getParamType().name() + "\" not yet implemented."); 161 | } 162 | } catch (final ParseException ex) { 163 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid value for parameter " + paramData.getName(), ex); 164 | return; 165 | } 166 | } 167 | 168 | // Try to invoke method 169 | try { 170 | api.getMethod().invoke(api.getAPIModelClass(), arguments); 171 | } catch (final IllegalAccessException ex) { 172 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Called method is not accessable", ex); 173 | } catch (final IllegalArgumentException ex) { 174 | exceptionHandler.handleException(response, HttpServletResponse.SC_NOT_ACCEPTABLE, "Illegal parameters for called method. See server error log for details.", ex); 175 | } catch (final InvocationTargetException ex) { 176 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Error calling method. See server error log for details.", ex.getTargetException()); 177 | } catch (final Throwable ex) { 178 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Internal server error for called method. See server error log for details.", ex); 179 | } 180 | break; 181 | } 182 | } 183 | } 184 | } 185 | if (!matchFound) { 186 | exceptionHandler.handleException(response, HttpServletResponse.SC_NOT_IMPLEMENTED, "Called method does not exist", null); 187 | } 188 | } 189 | 190 | // execute post-request handler 191 | if (config.getPostRequestHandler() != null) { 192 | config.getPostRequestHandler().handle(request, response); 193 | } 194 | 195 | response.flushBuffer(); 196 | } 197 | 198 | /** 199 | * Convert the swagger API parameter to a method argument class type depending on the swagger API data type. 200 | * 201 | * @param dataType the swagger API parameter data type 202 | * @param paramValue the value of the parameter as string 203 | * @return the argument 204 | */ 205 | private Object convertParamToArgument(final DataType dataType, final String paramValue) throws ParseException, IOException { 206 | if (StringUtils.isEmpty(paramValue)) { 207 | return null; 208 | } else { 209 | switch (dataType) { 210 | case STRING: 211 | return paramValue; 212 | case INTEGER: 213 | return Integer.parseInt(paramValue); 214 | case LONG: 215 | return Long.parseLong(paramValue); 216 | case BOOLEAN: 217 | return Boolean.valueOf(paramValue); 218 | case DATE: 219 | return new SimpleDateFormat(DATE_FORMAT).parse(paramValue); 220 | case DATETIME: 221 | return new SimpleDateFormat(DATE_TIME_FORMAT).parse(paramValue); 222 | default: 223 | throw new IllegalArgumentException("Handling for data type \"" + dataType.name() + "\" not yet implemented."); 224 | } 225 | } 226 | } 227 | 228 | @Override 229 | protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 230 | processRequest(request, response, HTTPMethod.GET); 231 | } 232 | 233 | @Override 234 | protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 235 | processRequest(request, response, HTTPMethod.POST); 236 | } 237 | 238 | @Override 239 | protected void doDelete(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 240 | processRequest(request, response, HTTPMethod.DELETE); 241 | } 242 | 243 | @Override 244 | protected void doPut(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 245 | processRequest(request, response, HTTPMethod.PUT); 246 | } 247 | 248 | @Override 249 | protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 250 | final SwaggerAPIConfig config = (SwaggerAPIConfig) request.getSession().getServletContext().getAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME); 251 | 252 | if (config.isCrossOriginAccessAllowed()) { 253 | response.addHeader("Access-Control-Allow-Origin", "*"); 254 | response.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, UPDATE, OPTIONS"); 255 | response.addHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, X-Api-Key"); 256 | } 257 | 258 | if (config.getDefaultContentType() != null) { 259 | response.setContentType(config.getDefaultContentType()); 260 | } 261 | 262 | response.setStatus(HttpServletResponse.SC_OK); 263 | response.flushBuffer(); 264 | } 265 | 266 | @Override 267 | protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 268 | try { 269 | super.service(request, response); 270 | } finally { 271 | // because Swagger REST calls are stateless the session should be invalidated immediately 272 | request.getSession().invalidate(); 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/annotations/SwaggerApi.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * The swagger API annotation 10 | * 11 | * @author johanna 12 | */ 13 | @Target ({ElementType.METHOD}) 14 | @Retention (RetentionPolicy.RUNTIME) 15 | public @interface SwaggerApi { 16 | 17 | /** 18 | * Enum constants for HTTP methods 19 | */ 20 | public enum HTTPMethod { 21 | 22 | GET("get"), 23 | POST("post"), 24 | PUT("put"), 25 | DELETE("delete"); 26 | 27 | private final String name; 28 | 29 | private HTTPMethod(final String name) { 30 | this.name = name; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return name; 36 | } 37 | 38 | } 39 | 40 | /** 41 | * The HTTP method of the current service 42 | * 43 | * @return HTTP 44 | */ 45 | HTTPMethod method() default HTTPMethod.GET; 46 | 47 | /** 48 | * The URL pattern with placeholder for parameters 49 | * 50 | * @return URL as string 51 | */ 52 | String path(); 53 | 54 | /** 55 | * The summary of the current service 56 | * 57 | * @return summary as string 58 | */ 59 | String summary(); 60 | 61 | /** 62 | * A verbose explanation of the operation behavior. 63 | * 64 | * @return implementation notes as string 65 | */ 66 | String notes(); 67 | 68 | /** 69 | * The description for the implementation notes 70 | * 71 | * @return description as string 72 | */ 73 | String description() default ""; 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/annotations/SwaggerModel.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * The swagger API model 11 | * 12 | * @author johanna 13 | */ 14 | @Target ({ElementType.TYPE}) 15 | @Retention (RetentionPolicy.RUNTIME) 16 | @Inherited 17 | public @interface SwaggerModel { 18 | 19 | /** 20 | * The base URL pattern to the model 21 | * 22 | * @return URL as string 23 | */ 24 | String path(); 25 | 26 | /** 27 | * The description of the model 28 | * 29 | * @return model description as string 30 | */ 31 | String description() default ""; 32 | 33 | /** 34 | * The result format of the model. 35 | * 36 | * @return result format 37 | */ 38 | String format() default "json"; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/annotations/SwaggerParameter.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * The swagger API parameter annotation 10 | * 11 | * @author johanna 12 | */ 13 | @Target ({ElementType.PARAMETER}) 14 | @Retention (RetentionPolicy.RUNTIME) 15 | public @interface SwaggerParameter { 16 | 17 | /** 18 | * Enum constants for parameter types 19 | */ 20 | public enum ParamType { 21 | 22 | PATH("path"), 23 | QUERY("query"), 24 | BODY("body"), 25 | HEADER("header"), 26 | FORM("form"); 27 | 28 | private final String name; 29 | 30 | private ParamType(final String name) { 31 | this.name = name; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return name; 37 | } 38 | } 39 | 40 | /** 41 | * Enum constants for parameter date types 42 | */ 43 | public enum DataType { 44 | STRING("string"), 45 | INTEGER("integer"), 46 | LONG("long"), 47 | BOOLEAN("boolean"), 48 | DATE("date"), 49 | DATETIME("dateTime"); 50 | 51 | private final String name; 52 | 53 | private DataType(final String name) { 54 | this.name = name; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return name; 60 | } 61 | } 62 | 63 | /** 64 | * The parameter name as it appears in the URL pattern. 65 | * 66 | * @return name 67 | */ 68 | String name(); 69 | 70 | /** 71 | * Defines if parameter is mandatory 72 | * 73 | * @return boolean if parameter is required 74 | */ 75 | boolean required(); 76 | 77 | /** 78 | * Another way to allow multiple values for a "query" parameter. If used, the query parameter may accept comma-separated values. 79 | * 80 | * @return true if multiple allowed 81 | */ 82 | boolean allowMultiple() default false; 83 | 84 | /** 85 | * The parameter description. 86 | * 87 | * @return description as string 88 | */ 89 | String description(); 90 | 91 | /** 92 | * The type of the parameter (that is, the location of the parameter in the request). 93 | * 94 | * @return parameter type (default is PATH) 95 | */ 96 | ParamType paramType() default ParamType.PATH; 97 | 98 | /** 99 | * The data type of the parameter 100 | * @return the data type (default is STRING) 101 | */ 102 | DataType dataType() default DataType.STRING; 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/data/ContentType.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.data; 2 | 3 | /** 4 | * List of MIME content type constants 5 | * @author mbartel 6 | */ 7 | public class ContentType { 8 | /** 9 | * The standard content type for JSON UTF-8 encoded responses 10 | */ 11 | final public static String JSON_UTF8 = "application/json;charset=UTF-8"; 12 | /** 13 | * Content type for plain text UTF-8 encoded 14 | */ 15 | final public static String TEXT = "text/plain"; 16 | /** 17 | * Content type for RSS feeds 18 | */ 19 | final public static String RSS = "application/rss+xml"; 20 | /** 21 | * Content type for XML content 22 | */ 23 | final public static String XML = "application/xml"; 24 | /** 25 | * Content type for PDFs 26 | */ 27 | final public static String PDF = "application/pdf"; 28 | /** 29 | * Content type for ZIP archives 30 | */ 31 | final public static String ZIP = "application/zip"; 32 | /** 33 | * Content type for comma separated values 34 | */ 35 | final public static String CSV = "text/csv"; 36 | /** 37 | * Content type for HTML content 38 | */ 39 | final public static String HTML = "text/html"; 40 | /** 41 | * Content type for cascading style sheets 42 | */ 43 | final public static String CSS = "text/css"; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/data/SwaggerAPIContext.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.data; 2 | 3 | import jakarta.servlet.http.HttpServlet; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | 7 | /** 8 | * Context for an swagger API call. 9 | * 10 | * @author mbartel 11 | */ 12 | public class SwaggerAPIContext { 13 | 14 | /** 15 | * The Swagger API request 16 | */ 17 | private final HttpServletRequest request; 18 | /** 19 | * The Swagger API response 20 | */ 21 | private final HttpServletResponse response; 22 | /** 23 | * The Swagger API servlet 24 | */ 25 | private final HttpServlet servlet; 26 | /** 27 | * The Swagger API exception handler 28 | */ 29 | private final SwaggerExceptionHandler exceptionHandler; 30 | 31 | /** 32 | * Create a new Swagger API request context. 33 | * 34 | * @param servlet the servlet 35 | * @param request the request 36 | * @param response the response 37 | */ 38 | public SwaggerAPIContext(final HttpServlet servlet, final HttpServletRequest request, final HttpServletResponse response, final SwaggerExceptionHandler exceptionHandler) { 39 | this.servlet = servlet; 40 | this.request = request; 41 | this.response = response; 42 | this.exceptionHandler = exceptionHandler; 43 | } 44 | 45 | /** 46 | * The HTTP request. 47 | * 48 | * @return the HTTP request 49 | */ 50 | public HttpServletRequest getRequest() { 51 | return request; 52 | } 53 | 54 | /** 55 | * The HTTP response. 56 | * 57 | * @return the HTTP response 58 | */ 59 | public HttpServletResponse getResponse() { 60 | return response; 61 | } 62 | 63 | /** 64 | * The HTTP servlet. 65 | * 66 | * @return the servlet 67 | */ 68 | public HttpServlet getServlet() { 69 | return servlet; 70 | } 71 | 72 | /** 73 | * Gets the exception handler. 74 | * 75 | * @return the exception handler 76 | */ 77 | public SwaggerExceptionHandler getExceptionHandler() { 78 | return exceptionHandler; 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/data/SwaggerAPIModelData.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.data; 2 | 3 | import com.roamsys.swagger.SwaggerAPIModel; 4 | import com.roamsys.swagger.annotations.SwaggerApi.HTTPMethod; 5 | import java.lang.reflect.Method; 6 | import java.util.List; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * Holds the data for a swagger API 12 | * 13 | * @author johanna 14 | */ 15 | public class SwaggerAPIModelData { 16 | 17 | /** 18 | * Instance of the swagger API model 19 | */ 20 | private final SwaggerAPIModel modelClass; 21 | /** 22 | * The reflection method 23 | */ 24 | private final Method method; 25 | /** 26 | * The path pattern 27 | */ 28 | private final Pattern pathPattern; 29 | 30 | /** 31 | * HTTP method type 32 | */ 33 | private final HTTPMethod httpMethod; 34 | 35 | /** 36 | * Parameter data 37 | */ 38 | private final List parameters; 39 | 40 | /** 41 | * The standard parameter pattern 42 | */ 43 | private static final String PATTERN = "\\{[0-9a-zA-Z]+\\}"; 44 | 45 | /** 46 | * Creates a new wrapper for swagger API 47 | * 48 | * @param modelClass the class of the API model 49 | * @param method the method 50 | * @param httpMethod the HTTP method 51 | * @param path the entire path of the API method 52 | * @param parameters the detailed data for the method parameters 53 | */ 54 | public SwaggerAPIModelData(final SwaggerAPIModel modelClass, final Method method, final HTTPMethod httpMethod, final String path, final List parameters) { 55 | this.modelClass = modelClass; 56 | this.method = method; 57 | this.httpMethod = httpMethod; 58 | this.pathPattern = Pattern.compile(path.replaceAll(PATTERN, "(\\[^/\\]+)")); 59 | this.parameters = parameters; 60 | } 61 | 62 | /** 63 | * Returns the matcher for the given path with the current swagger API path 64 | * 65 | * @param path full swagger API path as string 66 | * @return matcher 67 | */ 68 | public Matcher matchPath(final String path) { 69 | return this.pathPattern.matcher(path); 70 | } 71 | 72 | /** 73 | * Returns the HTTP method of current swagger API 74 | * 75 | * @return HTTP method 76 | */ 77 | public HTTPMethod getHTTPMethod() { 78 | return httpMethod; 79 | } 80 | 81 | /** 82 | * Returns the reflection method of current swagger API 83 | * 84 | * @return reflection method 85 | */ 86 | public Method getMethod() { 87 | return method; 88 | } 89 | 90 | /** 91 | * Returns the class of the parent swagger API model 92 | * 93 | * @return instance of swagger API model 94 | */ 95 | public SwaggerAPIModel getAPIModelClass() { 96 | return modelClass; 97 | } 98 | 99 | /** 100 | * Returns detailed data about parameters 101 | * 102 | * @return parameter details 103 | */ 104 | public List getParameters() { 105 | return parameters; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/data/SwaggerAPIParameterData.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.data; 2 | 3 | import com.roamsys.swagger.annotations.SwaggerParameter.DataType; 4 | import com.roamsys.swagger.annotations.SwaggerParameter.ParamType; 5 | 6 | /** 7 | * Data class for method parameters 8 | * @author mbartel 9 | */ 10 | public class SwaggerAPIParameterData { 11 | 12 | /** 13 | * The data type of the parameter 14 | */ 15 | private final DataType dataType; 16 | 17 | /** 18 | * The HTTP parameter type of the parameter 19 | */ 20 | private final ParamType paramType; 21 | 22 | /** 23 | * The parameters name 24 | */ 25 | private final String name; 26 | 27 | /** 28 | * Creates a new data object for method parameter data 29 | * @param name the name of the parameter 30 | * @param paramType the type of parameter 31 | * @param dataType the data type of the parameter 32 | */ 33 | public SwaggerAPIParameterData(final String name, final ParamType paramType, final DataType dataType) { 34 | this.name = name; 35 | this.paramType = paramType; 36 | this.dataType = dataType; 37 | } 38 | 39 | /** 40 | * Returns the data type of the parameter 41 | * @return the data type 42 | */ 43 | public DataType getDataType() { 44 | return dataType; 45 | } 46 | 47 | /** 48 | * Returns the type of parameter 49 | * @return the parameter type 50 | */ 51 | public ParamType getParamType() { 52 | return paramType; 53 | } 54 | 55 | /** 56 | * Returns the name of the parameter 57 | * @return the parameters name 58 | */ 59 | public String getName() { 60 | return name; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/data/SwaggerExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.data; 2 | 3 | import com.google.gson.GsonBuilder; 4 | import java.io.IOException; 5 | import java.io.PrintWriter; 6 | import java.util.Map; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * Handle exceptions thrown while invoking the Swagger API method. 11 | * 12 | * @author mbartel 13 | */ 14 | public interface SwaggerExceptionHandler { 15 | 16 | /** 17 | * Default implementation that uses {@link System#err}, sets the status and returns error JSON. 18 | */ 19 | public static final SwaggerExceptionHandler DEFAULT = new SwaggerExceptionHandler() { 20 | 21 | @Override 22 | public void handleException(final HttpServletResponse response, final int code, final String message, final Throwable ex) { 23 | System.err.println("Error: " + message + ", status code: " + code); 24 | if (ex != null) { 25 | ex.printStackTrace(System.err); 26 | } 27 | response.setStatus(code); 28 | // write error JSON 29 | try { 30 | final PrintWriter writer = response.getWriter(); 31 | response.setContentType(ContentType.JSON_UTF8); 32 | new GsonBuilder().create().toJson(Map.of("code", code, "reason", message), writer); 33 | writer.flush(); 34 | } catch (final IOException nested) { 35 | nested.printStackTrace(System.err); 36 | } 37 | } 38 | }; 39 | 40 | 41 | /** 42 | * Handles an exception. 43 | * 44 | * @param response the response 45 | * @param code the HTTP response code 46 | * @param message the exception message 47 | * @param ex the exception 48 | */ 49 | public void handleException(final HttpServletResponse response, final int code, final String message, final Throwable ex); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/AbstractApiSpecPart.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | /** 6 | * Base class of an OpenAPI specification block. 7 | * Every block should have at least a description property. 8 | * 9 | * @author nbecker 10 | */ 11 | public class AbstractApiSpecPart { 12 | 13 | @Expose 14 | String description; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/ApiSpecBuilder.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.roamsys.swagger.annotations.SwaggerApi; 4 | import com.roamsys.swagger.annotations.SwaggerModel; 5 | import com.roamsys.swagger.annotations.SwaggerParameter; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | 13 | /** 14 | * Builder for creating OpenAPI Swagger documentation/specification based on metadata/code annotations. 15 | * 16 | * @author nbecker 17 | */ 18 | public class ApiSpecBuilder { 19 | 20 | /** 21 | * The currently used OpenAPI version: swagger: "2.0" 22 | */ 23 | private static final String SWAGGER_VERSION = "2.0"; 24 | 25 | // defaults for several structures that are declared static in context of this framework 26 | private static final String DEFAULT_TITLE = "Swagger API"; 27 | private static final List DEFAULT_SCHEMES = Collections.singletonList("https"); 28 | private static final Map> DEFAULT_SECURITY_DEFINITION = Collections.singletonMap("api_key", Map.of("type", "apiKey", "name", "x-api-key", "in", "header")); 29 | private static final List>> DEFAULT_SECURITY = Collections.singletonList(Collections.singletonMap("api_key", Collections.emptyList())); 30 | private static final Map DEFAULT_OBJECT_SCHEMA = Collections.singletonMap("type", "object"); 31 | 32 | private final SwaggerApiSpec apiSpec; 33 | 34 | /** 35 | * Constructor. 36 | */ 37 | public ApiSpecBuilder() { 38 | apiSpec = new SwaggerApiSpec(); 39 | apiSpec.swaggerVersion = SWAGGER_VERSION; 40 | apiSpec.info = new InfoApiSpec(); 41 | apiSpec.info.title = DEFAULT_TITLE; 42 | apiSpec.basePath = "/"; 43 | apiSpec.schemes = DEFAULT_SCHEMES; 44 | apiSpec.securityDefinitions = DEFAULT_SECURITY_DEFINITION; 45 | apiSpec.paths = new TreeMap<>(); 46 | } 47 | 48 | /** 49 | * @return the created specification 50 | */ 51 | public SwaggerApiSpec getApiSpec() { 52 | return apiSpec; 53 | } 54 | 55 | /** 56 | * Sets the host to be shown and also used for generating test URLs. 57 | * 58 | * @param host the host 59 | * @return this pointer 60 | */ 61 | public ApiSpecBuilder setHost(final String host) { 62 | apiSpec.host = host; 63 | return this; 64 | } 65 | 66 | /** 67 | * Sets the base path to be shown and also used for generating test URLs. 68 | * 69 | * @param host the host 70 | * @return this pointer 71 | */ 72 | public ApiSpecBuilder setBasePath(final String basePath) { 73 | apiSpec.basePath = basePath; 74 | return this; 75 | } 76 | 77 | /** 78 | * Sets the schemes to be shown and also used for generating test URLs. 79 | * 80 | * @param schemes the schemes 81 | * @return this pointer 82 | */ 83 | public ApiSpecBuilder setSchemes(final List schemes) { 84 | apiSpec.schemes = schemes; 85 | return this; 86 | } 87 | 88 | /** 89 | * Sets the version to be shown in info block. 90 | * 91 | * @param version the version 92 | * @return this pointer 93 | */ 94 | public ApiSpecBuilder setVersion(final String version) { 95 | apiSpec.info.version = version; 96 | return this; 97 | } 98 | 99 | /** 100 | * Sets the title to be shown in info block. 101 | * 102 | * @param version the title 103 | * @return this pointer 104 | */ 105 | public ApiSpecBuilder setTitle(final String title) { 106 | apiSpec.info.title = title; 107 | return this; 108 | } 109 | 110 | /** 111 | * Sets the description to be shown in info block. 112 | * 113 | * @param description the version 114 | * @return this pointer 115 | */ 116 | public ApiSpecBuilder setDescription(final String description) { 117 | apiSpec.info.description = description; 118 | return this; 119 | } 120 | 121 | /** 122 | * Adds an operation by generating and using the related path. Multiple operations may be added for same path but with different HTTP method. 123 | * 124 | * @param modelAnnotation the model annotation 125 | * @param apiAnnotation the API call annotation 126 | * @param parameterAnnotations the list of parameter annotations of the API call 127 | * @return this pointer 128 | */ 129 | public ApiSpecBuilder addOperation(final SwaggerModel modelAnnotation, final SwaggerApi apiAnnotation, final List parameterAnnotations) { 130 | final String modelName = cleanupPath(modelAnnotation.path()); 131 | final String path = String.format("%s.%s%s", modelAnnotation.path(), modelAnnotation.format(), apiAnnotation.path()); 132 | // add operation using given path and method 133 | apiSpec.paths.computeIfAbsent(path, p -> new HashMap<>()).put(apiAnnotation.method().toString(), createOperatorSpec(modelName, modelAnnotation, apiAnnotation, parameterAnnotations)); 134 | // add a tag for providing description 135 | if (!modelAnnotation.description().isEmpty()) { 136 | if (apiSpec.tags == null) { 137 | apiSpec.tags = new ArrayList<>(); 138 | } 139 | // tag maybe created by former call for another call for same model 140 | if (apiSpec.tags.stream().noneMatch(t -> modelName.equalsIgnoreCase(t.name))) { 141 | final TagApiSpec tagSpec = new TagApiSpec(); 142 | tagSpec.name = modelName; 143 | tagSpec.description = modelAnnotation.description(); 144 | apiSpec.tags.add(tagSpec); 145 | } 146 | } 147 | return this; 148 | } 149 | 150 | /** 151 | * Helper for creating operator specification block. 152 | */ 153 | private OperationApiSpec createOperatorSpec(final String modelName, final SwaggerModel modelAnnotation, final SwaggerApi apiAnnotation, final List parameterAnnotations) { 154 | final OperationApiSpec operationSpec = new OperationApiSpec(); 155 | operationSpec.description = apiAnnotation.description().isEmpty() ? apiAnnotation.notes() : String.format("\n%s\n*Notes: %s*", apiAnnotation.description(), apiAnnotation.notes()); 156 | operationSpec.operationId = String.format("%s-%s-%s", apiAnnotation.method().toString(), modelName, cleanupPath(apiAnnotation.path())); 157 | operationSpec.tags = Collections.singletonList(modelName); 158 | operationSpec.produces = Collections.singletonList("application/" + modelAnnotation.format()); 159 | operationSpec.summary = apiAnnotation.summary(); 160 | // add all parameters 161 | operationSpec.parameters = new ArrayList<>(); 162 | for (final SwaggerParameter parameterAnnotation : parameterAnnotations) { 163 | operationSpec.parameters.add(createParamterSpec(parameterAnnotation)); 164 | } 165 | operationSpec.responses = new HashMap<>(); 166 | operationSpec.responses.put(403, createResponseSpec("authentication failed")); 167 | operationSpec.security = DEFAULT_SECURITY; 168 | return operationSpec; 169 | } 170 | 171 | /** 172 | * Helper for creating parameter specification block. 173 | */ 174 | private ParameterApiSpec createParamterSpec(final SwaggerParameter parameterAnnotation) { 175 | final ParameterApiSpec parameterSpec = new ParameterApiSpec(); 176 | parameterSpec.name = parameterAnnotation.name(); 177 | parameterSpec.required = parameterAnnotation.required(); 178 | parameterSpec.description = parameterAnnotation.description(); 179 | parameterSpec.in = parameterAnnotation.paramType().toString(); 180 | if (parameterAnnotation.paramType() == SwaggerParameter.ParamType.BODY) { 181 | parameterSpec.schema = DEFAULT_OBJECT_SCHEMA; 182 | } else { 183 | switch (parameterAnnotation.dataType()) { 184 | case DATE: 185 | case DATETIME: 186 | // Date is not a known type in OpenAPI/JSON 187 | parameterSpec.type = SwaggerParameter.DataType.STRING.toString(); 188 | parameterSpec.format = parameterAnnotation.dataType() == SwaggerParameter.DataType.DATETIME ? "date-time" : "date"; 189 | break; 190 | default: 191 | parameterSpec.type = parameterAnnotation.dataType().toString(); 192 | break; 193 | } 194 | } 195 | return parameterSpec; 196 | } 197 | 198 | /** 199 | * Helper for creating response specification block. 200 | */ 201 | private ResponseApiSpec createResponseSpec(final String description) { 202 | final ResponseApiSpec responseSpec = new ResponseApiSpec(); 203 | responseSpec.description = description; 204 | return responseSpec; 205 | } 206 | 207 | /** 208 | * Helper for removing slashes and parentheses from path. 209 | */ 210 | private String cleanupPath(final String path) { 211 | return path.replaceAll("\\/|\\{|\\}", ""); 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/InfoApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | /** 6 | * OpenAPI info block specification. 7 | * 8 | * @author nbecker 9 | */ 10 | public class InfoApiSpec extends AbstractApiSpecPart { 11 | 12 | @Expose 13 | String version, title; 14 | 15 | @Override 16 | public String toString() { 17 | return String.format("%s (Version %s)", title, version); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/OperationApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | /** 8 | * OpenAPI operation block specification. 9 | * 10 | * @author nbecker 11 | */ 12 | public class OperationApiSpec extends AbstractApiSpecPart { 13 | 14 | @Expose 15 | String summary, operationId; 16 | 17 | @Expose 18 | List tags; 19 | 20 | @Expose 21 | List produces; 22 | 23 | @Expose 24 | List parameters; 25 | 26 | @Expose 27 | Map responses; 28 | 29 | @Expose 30 | List>> security; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/ParameterApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import java.util.Map; 5 | 6 | /** 7 | * OpenAPI parameter block specification. 8 | * 9 | * @author nbecker 10 | */ 11 | public class ParameterApiSpec extends AbstractApiSpecPart { 12 | 13 | @Expose 14 | String name, in, format; 15 | 16 | @Expose 17 | boolean required; 18 | 19 | @Expose 20 | String type; 21 | 22 | @Expose 23 | Map schema; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/ResponseApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | /** 4 | * OpenAPI response block specification. 5 | * 6 | * @author nbecker 7 | */ 8 | public class ResponseApiSpec extends AbstractApiSpecPart { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/SwaggerApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * OpenAPI 2.0 Swagger specification. 10 | * 11 | * @author nbecker 12 | */ 13 | public class SwaggerApiSpec { 14 | 15 | @Expose 16 | @SerializedName("swagger") 17 | String swaggerVersion; 18 | 19 | @Expose 20 | InfoApiSpec info; 21 | 22 | @Expose 23 | String host, basePath; 24 | 25 | @Expose 26 | List schemes; 27 | 28 | @Expose 29 | Map> securityDefinitions; 30 | 31 | @Expose 32 | List tags; 33 | 34 | @Expose 35 | Map> paths; 36 | 37 | @Override 38 | public String toString() { 39 | return String.format("Info: %s, Paths: %s", info, paths.keySet()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/roamsys/swagger/documentation/TagApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | /** 6 | * OpenAPI tag block specification. 7 | * 8 | * @author nbecker 9 | */ 10 | public class TagApiSpec extends AbstractApiSpecPart { 11 | 12 | @Expose 13 | String name; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/roamsys/swagger/TestsAPI.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger; 2 | 3 | import com.roamsys.swagger.annotations.SwaggerApi; 4 | import com.roamsys.swagger.annotations.SwaggerApi.HTTPMethod; 5 | import com.roamsys.swagger.annotations.SwaggerModel; 6 | import com.roamsys.swagger.annotations.SwaggerParameter; 7 | import com.roamsys.swagger.annotations.SwaggerParameter.DataType; 8 | import com.roamsys.swagger.annotations.SwaggerParameter.ParamType; 9 | import com.roamsys.swagger.data.SwaggerAPIContext; 10 | 11 | @SwaggerModel(path = "/testAPI") 12 | public class TestsAPI implements SwaggerAPIModel { 13 | public static final String PATH_TEST = "/test"; 14 | public static final String PATH_TEST_ORGANISATION = "/test/{organisationidList}"; 15 | public static final String PATH_TEST_ORGANISATION_BEFORE_DATE = "/test/{organisationidList}/before/{date}"; 16 | public static final String PATH_TEST_ORGANISATION_AFTER_DATE = "/test/{organisationidList}/after/{date}"; 17 | public static final String PATH_TEST_ORGANISATION_TADIG = "/test/{organisationidList}/{tadigList}"; 18 | 19 | @SwaggerApi(method = SwaggerApi.HTTPMethod.GET, path = PATH_TEST_ORGANISATION, summary = "", notes = "Test org method description") 20 | public void test_Org(final SwaggerAPIContext context, 21 | @SwaggerParameter(name = "organisationidList", description = "", required = true, paramType = ParamType.PATH, dataType = DataType.STRING) final String organisationidList) { 22 | //Do nothing 23 | } 24 | 25 | @SwaggerApi(method = SwaggerApi.HTTPMethod.GET, path = PATH_TEST, summary = "", notes = "Test method description") 26 | public void test(final SwaggerAPIContext context) { 27 | //Do nothing 28 | } 29 | 30 | @SwaggerApi(notes = "", method = HTTPMethod.GET, path = PATH_TEST_ORGANISATION_TADIG, summary = "") 31 | public void test_Org_Tadig(final SwaggerAPIContext context, 32 | @SwaggerParameter(name = "organisationidList", description = "", required = true, paramType = ParamType.PATH, dataType = DataType.STRING) final String organisationidList, 33 | @SwaggerParameter(name = "tadigList", description = "", required = true, paramType = ParamType.PATH, dataType = DataType.STRING) final String tadigList) { 34 | //Do nothing 35 | } 36 | 37 | @SwaggerApi(notes = "", method = HTTPMethod.GET, path = PATH_TEST_ORGANISATION_AFTER_DATE, summary = "") 38 | public void test_Org_After_Date(final SwaggerAPIContext context, 39 | @SwaggerParameter(name = "organisationidList", description = "", required = true, paramType = ParamType.PATH, dataType = DataType.STRING) final String organisationidList, 40 | @SwaggerParameter(name = "date", description = "", required = true, paramType = ParamType.PATH, dataType = DataType.STRING) final String date) { 41 | //Do nothing 42 | } 43 | 44 | @SwaggerApi(notes = "", method = HTTPMethod.GET, path = PATH_TEST_ORGANISATION_BEFORE_DATE, summary = "") 45 | public void test_Org_Before_Date(final SwaggerAPIContext context, 46 | @SwaggerParameter(name = "organisationidList", description = "", required = true, paramType = ParamType.PATH, dataType = DataType.STRING) final String organisationidList, 47 | @SwaggerParameter(name = "date", description = "", required = true, paramType = ParamType.PATH, dataType = DataType.STRING) final String date) { 48 | //Do nothing 49 | } 50 | } -------------------------------------------------------------------------------- /src/test/java/com/roamsys/swagger/data/SwaggerAPIModelDataTest.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.data; 2 | 3 | import com.roamsys.swagger.SwaggerAPIConfig; 4 | import com.roamsys.swagger.TestsAPI; 5 | import com.roamsys.swagger.annotations.SwaggerApi; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.junit.runners.Parameterized; 12 | import org.junit.runners.Parameterized.Parameters; 13 | 14 | /** 15 | * Tests for {@link SwaggerAPIModelData} and {@link SwaggerAPIConfig}. 16 | * 17 | * @author nbecker 18 | */ 19 | @RunWith(Parameterized.class) 20 | public class SwaggerAPIModelDataTest { 21 | 22 | private final String path; 23 | private final String expectedPattern; 24 | 25 | @Parameters 26 | public static List data() { 27 | return Arrays.asList(new Object[][] { 28 | { "/testAPI.json/test", TestsAPI.PATH_TEST }, 29 | { "/testAPI.json/test/123456789012345678901234", TestsAPI.PATH_TEST_ORGANISATION }, 30 | { "/testAPI.json/test/123456789012345678901234/ROAM0", TestsAPI.PATH_TEST_ORGANISATION_TADIG }, 31 | { "/testAPI.json/test/123456789012345678901234/before/2007-08-31T16:47+00:00", TestsAPI.PATH_TEST_ORGANISATION_BEFORE_DATE }, 32 | { "/testAPI.json/test/123456789012345678901234/after/2007-08-31T16:47+00:00", TestsAPI.PATH_TEST_ORGANISATION_AFTER_DATE } }); 33 | } 34 | 35 | public SwaggerAPIModelDataTest(final String path, final String expectedPattern) { 36 | this.path = path; 37 | this.expectedPattern = expectedPattern; 38 | } 39 | 40 | @Test 41 | public void testMatchPath() { 42 | final SwaggerAPIConfig config = new SwaggerAPIConfig(null); 43 | config.registerModel(new TestsAPI()); 44 | final String basePath = path.substring(0, path.indexOf("/", 1)); 45 | for (final SwaggerAPIModelData api : config.getAPIsFor(basePath)) { 46 | final boolean isMatch = api.matchPath(path).matches(); 47 | final String currentPattern = api.getMethod().getAnnotation(SwaggerApi.class).path(); 48 | Assert.assertEquals(expectedPattern.equals(currentPattern), isMatch); 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/com/roamsys/swagger/documentation/ApiSpecBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.roamsys.swagger.documentation; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonElement; 6 | import com.roamsys.swagger.TestsAPI; 7 | import com.roamsys.swagger.annotations.SwaggerApi; 8 | import com.roamsys.swagger.annotations.SwaggerModel; 9 | import com.roamsys.swagger.data.SwaggerAPIContext; 10 | import java.lang.reflect.Method; 11 | import java.util.Collections; 12 | import org.apache.commons.io.IOUtils; 13 | import org.junit.Assert; 14 | import org.junit.Test; 15 | 16 | /** 17 | * Test for {@link ApiSpecBuilder}. 18 | * 19 | * @author nbecker 20 | */ 21 | public class ApiSpecBuilderTest { 22 | 23 | @Test 24 | public void testApiSpec() throws Exception { 25 | final ApiSpecBuilder apiSpecBuilder = new ApiSpecBuilder(); 26 | final Method testMethod = TestsAPI.class.getMethod("test", SwaggerAPIContext.class); 27 | apiSpecBuilder.addOperation(TestsAPI.class.getAnnotation(SwaggerModel.class), testMethod.getAnnotation(SwaggerApi.class), Collections.emptyList()); 28 | final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); 29 | final JsonElement specJson = gson.toJsonTree(apiSpecBuilder.getApiSpec()); 30 | Assert.assertEquals(specJson, gson.fromJson(IOUtils.toString(getClass().getClassLoader().getResourceAsStream("swagger.json"), "UTF-8"), JsonElement.class)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/resources/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "Swagger API" 5 | }, 6 | "basePath": "/", 7 | "schemes": [ 8 | "https" 9 | ], 10 | "securityDefinitions": { 11 | "api_key": { 12 | "type": "apiKey", 13 | "name": "x-api-key", 14 | "in": "header" 15 | } 16 | }, 17 | "paths": { 18 | "/testAPI.json/test": { 19 | "get": { 20 | "summary": "", 21 | "operationId": "get-testAPI-test", 22 | "tags": [ 23 | "testAPI" 24 | ], 25 | "produces": [ 26 | "application/json" 27 | ], 28 | "parameters": [], 29 | "responses": { 30 | "403": { 31 | "description": "authentication failed" 32 | } 33 | }, 34 | "security": [ 35 | { 36 | "api_key": [] 37 | } 38 | ], 39 | "description": "Test method description" 40 | } 41 | } 42 | } 43 | } --------------------------------------------------------------------------------