/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.ext.swagger;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.Method;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.SwaggerTranslator;
import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ApiDeclaration;
import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ResourceListing;
import org.restlet.ext.apispark.internal.introspection.application.ApplicationIntrospector;
import org.restlet.ext.apispark.internal.model.Definition;
import org.restlet.ext.jackson.JacksonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.routing.Router;
/**
* Restlet that generates Swagger documentation in the format defined by the
* swagger-spec project.<br>
* It helps to generate the high level documentation for the whole API (set by
* calling {@link #setApplication(Application)} or
* {@link #setApiInboundRoot(Application)} methods, and the documentation for
* each resource.<br>
* By default it instrospects the chain of Application's routers, filters,
* restlet.<br>
* Use the {@link JaxRsApplicationSwaggerSpecificationRestlet} restlet for
* Jax-RS applications.
*
* <p>
* Usage example:
*
* <pre>
* // this is the current Application
* SwaggerSpecificationRestlet swaggerSpecificationRestlet = new SwaggerSpecificationRestlet(this);
* swaggerSpecificationRestlet.setBasePath("http://myapp.com/api/v1");
* swaggerSpecificationRestlet.attach(baseRouter);
* </pre>
*
* </p>
*
* @author Thierry Boileau
* @see <a href="http://github.com/wordnik/swagger-ui">Swagger UI (github)</a>
* @see <a href="http://petstore.swagger.wordnik.com">Petstore sample
* application of Swagger-UI</a>
* @see <a href="http://helloreverb.com/developers/swagger">Swagger Developper
* page</a>
*/
public class SwaggerSpecificationRestlet extends Restlet {
/** The version of the API. */
private String apiVersion;
/** The Application to describe. */
private Application application;
/** The base path of the API. */
private String basePath;
/** The base reference of the API. */
private Reference baseRef;
/** The RWADef of the API. */
private Definition definition;
/**
* The version of the Swagger specification. Default is
* {@link SwaggerTranslator#SWAGGER_VERSION}
*/
private String swaggerVersion = SwaggerTranslator.SWAGGER_VERSION;
/**
* Default constructor.<br>
*/
public SwaggerSpecificationRestlet() {
}
/**
* Constructor.<br>
*
* @param application
* The application to describe.
*/
public SwaggerSpecificationRestlet(Application application) {
super(application.getContext());
this.application = application;
}
/**
* Constructor.<br>
*
* @param context
* The context.
*/
public SwaggerSpecificationRestlet(Context context) {
super(context);
}
/**
* Defines two routes, one for the high level "Resource listing" (by default
* "/api-docs"), and the other one for the "API declaration". The second
* route is a sub-resource of the first one, defined with the path variable
* "resource" (ie "/api-docs/{resource}").
*
* @param router
* The router on which defining the new route.
*
* @see #attach(org.restlet.routing.Router, String) to attach it with a
* custom path
*/
public void attach(Router router) {
attach(router, "/api-docs");
}
/**
* Defines two routes, one for the high level "Resource listing", and the
* other one for the "API declaration". The second route is a sub-resource
* of the first one, defined with the path variable "resource".
*
* @param router
* The router on which defining the new route.
* @param path
* The root path of the documentation Restlet.
*
* @see #attach(org.restlet.routing.Router) to attach it with the default
* path
*/
public void attach(Router router, String path) {
router.attach(path, this);
router.attach(path + "/{resource}", this);
}
/**
* Returns the Swagger documentation of a given resource, also known as
* "API Declaration" in Swagger vocabulary.
*
* @param category
* The category of the resource to describe.
* @return The representation of the API declaration.
*/
public Representation getApiDeclaration(String category) {
ApiDeclaration apiDeclaration = SwaggerTranslator.getApiDeclaration(
category, getDefinition());
apiDeclaration.setSwaggerVersion(swaggerVersion);
return new JacksonRepresentation<>(apiDeclaration);
}
/**
* Returns the API's version.
*
* @return The API's version.
*/
public String getApiVersion() {
return apiVersion;
}
/**
* Returns the base path of the API.
*
* @return The base path of the API.
*/
public String getBasePath() {
return basePath;
}
/**
* Returns the application's definition.
*
* @return The application's definition.
*/
private synchronized Definition getDefinition() {
if (definition == null) {
synchronized (SwaggerSpecificationRestlet.class) {
definition = ApplicationIntrospector.getDefinition(application,
baseRef, null, false);
// This data seems necessary for Swagger codegen.
if (definition.getVersion() == null) {
definition.setVersion(apiVersion != null ? apiVersion
: "1.0");
}
}
}
return definition;
}
/**
* Returns the representation of the whole resource listing of the
* Application.
*
* @return The representation of the whole resource listing of the
* Application.
*/
public Representation getResourceListing() {
ResourceListing resourcelisting = SwaggerTranslator
.getResourcelisting(getDefinition());
resourcelisting.setSwaggerVersion(swaggerVersion);
return new JacksonRepresentation<>(resourcelisting);
}
/**
* Returns the version of the Swagger specification. Default is
* {@link SwaggerTranslator#SWAGGER_VERSION}
*
* @return The version of the Swagger specification.
*/
public String getSwaggerVersion() {
return swaggerVersion;
}
@Override
public void handle(Request request, Response response) {
super.handle(request, response);
if (Method.GET.equals(request.getMethod())) {
Object resource = request.getAttributes().get("resource");
if (resource instanceof String) {
response.setEntity(getApiDeclaration((String) resource));
} else {
response.setEntity(getResourceListing());
}
} else {
response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
}
}
/**
* Sets the root Restlet for the given application.
*
* @param application
* The application.
*/
public void setApiInboundRoot(Application application) {
this.application = application;
}
/**
* Sets the API's version.
*
* @param apiVersion
* The API version.
*/
public void setApiVersion(String apiVersion) {
this.apiVersion = apiVersion;
}
/**
* Sets the root Restlet for the given application.
*
* @param application
* The application.
*/
public void setApplication(Application application) {
this.application = application;
}
/**
* Sets the base path of the API.
*
* @param basePath
* The base path of the API
*/
public void setBasePath(String basePath) {
this.basePath = basePath;
// Process basepath and check validity
this.baseRef = basePath != null ? new Reference(basePath) : null;
}
/**
* Sets the version of the Swagger specification.
*
* @param swaggerVersion
* The version of the Swagger specification.
*/
public void setSwaggerVersion(String swaggerVersion) {
this.swaggerVersion = swaggerVersion;
}
}