/** * 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.apispark.internal.conversion.swagger.v1_2; 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.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import org.restlet.data.ChallengeScheme; import org.restlet.data.Status; import org.restlet.engine.util.StringUtils; import org.restlet.ext.apispark.internal.conversion.TranslationException; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ApiDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ApiInfo; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.AuthorizationsDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.BasicAuthorizationDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ItemsDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ModelDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.OAuth2AuthorizationDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ResourceDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ResourceListing; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ResourceListingApi; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ResourceOperationDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ResourceOperationParameterDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.ResponseMessageDeclaration; import org.restlet.ext.apispark.internal.conversion.swagger.v1_2.model.TypePropertyDeclaration; import org.restlet.ext.apispark.internal.introspection.util.Types; import org.restlet.ext.apispark.internal.model.Contact; import org.restlet.ext.apispark.internal.model.Contract; import org.restlet.ext.apispark.internal.model.Definition; import org.restlet.ext.apispark.internal.model.Endpoint; import org.restlet.ext.apispark.internal.model.License; import org.restlet.ext.apispark.internal.model.Operation; import org.restlet.ext.apispark.internal.model.PathVariable; import org.restlet.ext.apispark.internal.model.PayLoad; import org.restlet.ext.apispark.internal.model.Property; import org.restlet.ext.apispark.internal.model.QueryParameter; import org.restlet.ext.apispark.internal.model.Representation; import org.restlet.ext.apispark.internal.model.Resource; import org.restlet.ext.apispark.internal.model.Response; import org.restlet.ext.apispark.internal.model.Section; import org.restlet.ext.apispark.internal.reflect.ReflectUtils; /** * Tool library for converting Restlet Web API Definition to and from Swagger * documentation. * * @author Cyprien Quilici */ public abstract class SwaggerTranslator { /** Internal logger. */ protected static Logger LOGGER = Logger.getLogger(SwaggerTranslator.class .getName()); /** Supported version of Swagger. */ public static final String SWAGGER_VERSION = "1.2"; /** * Fills Swagger ResourceOperationDeclaration's type from Restlet Web API * definition's Operation * * @param operation * The Restlet Web API definition's Operation * @param rod * The Swagger Swagger ResourceOperationDeclaration * @param contract * The Restlet Web API definition's Contract * @param usedModels * The models specified by this API declaration */ private static void fillApiDeclarationInRepresentation(Operation operation, ResourceOperationDeclaration rod, Contract contract, Collection<String> usedModels) { // Get in representation ResourceOperationParameterDeclaration ropd; PayLoad inRepr = operation.getInputPayLoad(); if (inRepr != null) { Representation representation = contract.getRepresentation(inRepr .getType()); ropd = new ResourceOperationParameterDeclaration(); ropd.setParamType("body"); ropd.setName("body"); ropd.setRequired(true); ropd.setType(inRepr.getType()); if (representation != null) { usedModels.add(inRepr.getType()); } rod.getParameters().add(ropd); } } /** * Fills Swagger API declaration main attributes from Restlet Web API * definition * * @param definition * The Restlet Web API definition * @param apiDeclaration * The Swagger 1.2 API declaration * @param sectionName * The name of the current section */ private static void fillApiDeclarationMainAttributes(Definition definition, ApiDeclaration apiDeclaration, String sectionName) { apiDeclaration.setApiVersion(definition.getVersion()); // No way to specify multiple endpoints in Swagger so we take the first // one Endpoint endpoint; if (!definition.getEndpoints().isEmpty()) { endpoint = definition.getEndpoints().get(0); apiDeclaration.setBasePath(endpoint.computeUrl()); } else { endpoint = new Endpoint("http://example.com"); } // Authentication // TODO deal with API key authentication AuthorizationsDeclaration authorizations = new AuthorizationsDeclaration(); if (ChallengeScheme.HTTP_BASIC.getName().equals( (endpoint.getAuthenticationProtocol()))) { authorizations.setBasicAuth(new BasicAuthorizationDeclaration()); apiDeclaration.setAuthorizations(authorizations); } else if (ChallengeScheme.HTTP_OAUTH.getName().equals( (endpoint.getAuthenticationProtocol())) || ChallengeScheme.HTTP_OAUTH_BEARER.getName().equals( (endpoint.getAuthenticationProtocol())) || ChallengeScheme.HTTP_OAUTH_MAC.getName().equals( (endpoint.getAuthenticationProtocol()))) { authorizations.setOauth2(new OAuth2AuthorizationDeclaration()); } apiDeclaration.setInfo(new ApiInfo()); apiDeclaration.setSwaggerVersion(SWAGGER_VERSION); apiDeclaration.setResourcePath("/" + sectionName); } /** * Fills Swagger ResourceDeclaration's ResourceOperationDeclaration from * Restlet Web API definition's Resource * * @param resource * The Restlet Web API definition's Resource * @param contract * The Restlet Web API definition's Contract * @param usedModels * The models specified by this API declaration * @param rd * The Swagger Swagger ResourceDeclaration */ private static void fillApiDeclarationOperations(Resource resource, Contract contract, Collection<String> usedModels, ResourceDeclaration rd) { // Get operations for (Operation operation : resource.getOperations()) { ResourceOperationDeclaration rod = new ResourceOperationDeclaration(); rod.setMethod(operation.getMethod()); rod.setSummary(operation.getDescription()); rod.setNickname(operation.getName()); rod.setProduces(operation.getProduces()); rod.setConsumes(operation.getConsumes()); // fill the resource operation parameters fillApiDeclarationPathVariables(resource, rod); fillApiDeclarationQueryParameters(operation, rod); // fill the resource operation in representation fillApiDeclarationInRepresentation(operation, rod, contract, usedModels); // fill the resource operation out representation fillApiDeclarationOutRepresentation(operation, rod, usedModels); // fill the resource operation erorr response models fillApiDeclarationResponses(operation, usedModels, rod); rd.getOperations().add(rod); } } /** * Fills Swagger ResourceOperationDeclaration's returned type from Restlet * Web API definition's Operation * * @param operation * The Restlet Web API definition's Operation * @param rod * The Swagger Swagger ResourceOperationDeclaration * @param usedModels * The models specified by this API declaration */ private static void fillApiDeclarationOutRepresentation( Operation operation, ResourceOperationDeclaration rod, Collection<String> usedModels) { // Get out representation PayLoad outRepr = null; for (Response response : operation.getResponses()) { if (Status.isSuccess(response.getCode())) { outRepr = response.getOutputPayLoad(); } } if (outRepr != null && outRepr.getType() != null) { if (outRepr.isArray()) { rod.setType("array"); if (Types.isPrimitiveType(outRepr.getType())) { SwaggerTypeFormat swaggerTypeFormat = SwaggerTypes .toSwaggerType(outRepr.getType()); rod.getItems().setType(swaggerTypeFormat.getType()); rod.getItems().setFormat(swaggerTypeFormat.getFormat()); } else { rod.getItems().setRef(outRepr.getType()); } } else { rod.setType(outRepr.getType()); } usedModels.add(outRepr.getType()); } else { rod.setType("void"); } } /** * Fills Swagger ResourceOperationDeclaration's * ResourceOperationParameterDeclaration from Restlet Web API definition's * Resource * * @param resource * The Restlet Web API definition's Resource * @param rod * The Swagger Swagger ResourceOperationDeclaration */ private static void fillApiDeclarationPathVariables(Resource resource, ResourceOperationDeclaration rod) { // Get path variables ResourceOperationParameterDeclaration ropd; for (PathVariable pv : resource.getPathVariables()) { ropd = new ResourceOperationParameterDeclaration(); ropd.setParamType("path"); SwaggerTypeFormat swaggerTypeFormat = SwaggerTypes.toSwaggerType(pv .getType()); ropd.setType(swaggerTypeFormat.getType()); ropd.setFormat(swaggerTypeFormat.getFormat()); ropd.setRequired(true); ropd.setName(pv.getName()); ropd.setAllowMultiple(false); ropd.setDescription(pv.getDescription()); rod.getParameters().add(ropd); } } /** * Fills Swagger ResourceOperationDeclaration's * ResourceOperationParameterDeclaration from Restlet Web API definition's * Operation * * @param operation * The Restlet Web API definition's Operation * @param rod * The Swagger Swagger ResourceOperationDeclaration */ private static void fillApiDeclarationQueryParameters(Operation operation, ResourceOperationDeclaration rod) { // Get query parameters ResourceOperationParameterDeclaration ropd; for (QueryParameter qp : operation.getQueryParameters()) { ropd = new ResourceOperationParameterDeclaration(); ropd.setParamType("query"); SwaggerTypeFormat swaggerTypeFormat = SwaggerTypes.toSwaggerType(qp .getType()); ropd.setType(swaggerTypeFormat.getType()); ropd.setFormat(swaggerTypeFormat.getFormat()); ropd.setName(qp.getName()); ropd.setAllowMultiple(true); ropd.setDescription(qp.getDescription()); ropd.setEnum_(qp.getEnumeration()); ropd.setDefaultValue(qp.getDefaultValue()); rod.getParameters().add(ropd); } } /** * Fills Swagger ApiDeclaration's ModelDeclarations from Restlet Web API * definition * * @param definition * The Restlet Web API definition * @param apiDeclaration * The Swagger API declaration * @param usedModels * The models specified by this API declaration */ private static void fillApiDeclarationRepresentations( Definition definition, ApiDeclaration apiDeclaration, Collection<String> usedModels) { Contract contract = definition.getContract(); apiDeclaration.setModels(new TreeMap<String, ModelDeclaration>()); List<String> usedModelsList = new ArrayList<>(usedModels); for (int i = 0; i < usedModelsList.size(); i++) { String model = usedModelsList.get(i); Representation repr = contract.getRepresentation(model); if (repr == null || Types.isPrimitiveType(model)) { continue; } ModelDeclaration md = new ModelDeclaration(); fillModel(apiDeclaration, usedModelsList, model, repr.getDescription(), repr.getProperties(), md); } } private static void fillModel(ApiDeclaration apiDeclaration, List<String> usedModelsList, String model, String description, List<Property> properties, ModelDeclaration md) { md.setId(model); md.setDescription(description); for (Property prop : properties) { String type = prop.getType(); boolean composite = Types.isCompositeType(type); if (composite) { type = model + StringUtils.firstUpper(prop.getName()); } if (prop.getMinOccurs() > 0) { md.getRequired().add(prop.getName()); } if (!Types.isPrimitiveType(type) && !usedModelsList.contains(type)) { usedModelsList.add(type); } TypePropertyDeclaration tpd = new TypePropertyDeclaration(); tpd.setDescription(prop.getDescription()); tpd.setEnum_(prop.getEnumeration()); if (prop.getMaxOccurs() > 1 || prop.getMaxOccurs() == -1) { tpd.setType("array"); tpd.setItems(new ItemsDeclaration()); if (Types.isPrimitiveType(type)) { SwaggerTypeFormat swaggerTypeFormat = SwaggerTypes .toSwaggerType(type); tpd.getItems().setType(swaggerTypeFormat.getType()); tpd.getItems().setFormat(swaggerTypeFormat.getFormat()); } else { tpd.getItems().setRef(type); if (composite) { ModelDeclaration m = new ModelDeclaration(); fillModel(apiDeclaration, usedModelsList, type, null, prop.getProperties(), m); } } } else { if (Types.isPrimitiveType(type)) { SwaggerTypeFormat swaggerTypeFormat = SwaggerTypes .toSwaggerType(type); tpd.setType(swaggerTypeFormat.getType()); tpd.setFormat(swaggerTypeFormat.getFormat()); } else { tpd.setRef(type); if (composite) { ModelDeclaration m = new ModelDeclaration(); fillModel(apiDeclaration, usedModelsList, type, null, prop.getProperties(), m); } } } tpd.setMaximum(prop.getMax()); tpd.setMinimum(prop.getMin()); tpd.setUniqueItems(prop.isUniqueItems()); md.getProperties().put(prop.getName(), tpd); } apiDeclaration.getModels().put(md.getId(), md); } /** * Fills Swagger ApiDeclaration's ResourceDeclarations from Restlet Web API * definition * * @param definition * The Restlet Web API definition * @param apiDeclaration * The Swagger API declaration * @param sectionName * The name of the current section * @return The models specified by this API declaration */ private static Collection<String> fillApiDeclarationResources( Definition definition, ApiDeclaration apiDeclaration, String sectionName) { Set<String> usedModels = new HashSet<>(); Contract contract = definition.getContract(); // Get the resources corresponding to the sectionName List<Resource> resources = new ArrayList<>(); boolean allResources = contract.getSections().isEmpty(); for (Resource resource : contract.getResources()) { if (allResources) { resources.add(resource); } else { if (resource.getSections().contains(sectionName)) { resources.add(resource); } } } // Get resources for (Resource resource : resources) { // Discriminate the resources of one category if (allResources && !resource.getResourcePath() .startsWith("/" + sectionName)) { continue; } ResourceDeclaration rd = new ResourceDeclaration(); rd.setPath(resource.getResourcePath()); rd.setDescription(resource.getDescription()); // fill resource declaration fillApiDeclarationOperations(resource, contract, usedModels, rd); apiDeclaration.getApis().add(rd); } // Sort the API declarations according to their path. Collections.sort(apiDeclaration.getApis(), new Comparator<ResourceDeclaration>() { @Override public int compare(ResourceDeclaration o1, ResourceDeclaration o2) { return o1.getPath().compareTo(o2.getPath()); } }); return usedModels; } /** * Fills Swagger ResourceOperationDeclaration's error responses from Restlet * Web API definition's Operation * * @param operation * The Restlet Web API definition's Operation * @param usedModels * The models specified by this API declaration * @param rod * The Swagger Swagger ResourceOperationDeclaration */ private static void fillApiDeclarationResponses(Operation operation, Collection<String> usedModels, ResourceOperationDeclaration rod) { // Get response messages for (Response response : operation.getResponses()) { if (Status.isSuccess(response.getCode())) { continue; } ResponseMessageDeclaration rmd = new ResponseMessageDeclaration(); rmd.setCode(response.getCode()); rmd.setMessage(response.getMessage()); if (response.getOutputPayLoad() != null) { rmd.setResponseModel(response.getOutputPayLoad().getType()); usedModels.add(response.getOutputPayLoad().getType()); } rod.getResponseMessages().add(rmd); } } /** * Fills Restlet Web API definition's Contract from Swagger 1.2 definition * * @param contract * The Restlet Web API definition's Contract * @param listing * The Swagger ResourceListing * @param apiDeclarations * The Swagger ApiDeclaration */ private static void fillContract(Contract contract, ResourceListing listing, Map<String, ApiDeclaration> apiDeclarations) { // Resource listing Resource resource; List<String> declaredTypes = new ArrayList<>(); for (Entry<String, ApiDeclaration> entry : apiDeclarations.entrySet()) { ApiDeclaration apiDeclaration = entry.getValue(); Section section = new Section(); if (entry.getKey().startsWith("/")) { section.setName(entry.getKey().substring(1)); } else { section.setName(entry.getKey()); } section.setDescription(listing.getApi(entry.getKey()) .getDescription()); contract.getSections().add(section); for (ResourceDeclaration api : apiDeclaration.getApis()) { resource = new Resource(); resource.setResourcePath(api.getPath()); List<String> declaredPathVariables = new ArrayList<>(); fillOperations(resource, apiDeclaration, api, contract, section, declaredPathVariables, declaredTypes); resource.getSections().add(section.getName()); contract.getResources().add(resource); LOGGER.log(Level.FINE, "Resource " + api.getPath() + " added."); } } } /** * Fills Restlet Web API definition's Contract from Swagger 1.2 API * declaration * * @param contract * The Restlet Web API definition's Contract * @param apiDeclaration * The Swagger ApiDeclaration */ private static void fillContract(Contract contract, ApiDeclaration apiDeclaration) { // Resource listing Resource resource; List<String> declaredTypes = new ArrayList<>(); Section section = new Section(); if (apiDeclaration.getResourcePath().startsWith("/")) { section.setName(apiDeclaration.getResourcePath().substring(1)); } else { section.setName(apiDeclaration.getResourcePath()); } contract.getSections().add(section); for (ResourceDeclaration api : apiDeclaration.getApis()) { resource = new Resource(); resource.setResourcePath(api.getPath()); List<String> declaredPathVariables = new ArrayList<>(); fillOperations(resource, apiDeclaration, api, contract, section, declaredPathVariables, declaredTypes); resource.getSections().add(section.getName()); contract.getResources().add(resource); LOGGER.log(Level.FINE, "Resource " + api.getPath() + " added."); } } /** * Fills Restlet Web API definition's main attributes from Swagger 1.2 * definition * * @param definition * The Restlet Web API definition * @param listing * The Swagger 1.2 resource listing * @param basePath * The basePath of the described Web API */ private static void fillMainAttributes(Definition definition, ResourceListing listing, String basePath) { definition.setVersion(listing.getApiVersion()); Contact contact = new Contact(); contact.setEmail(listing.getInfo().getContact()); definition.setContact(contact); License license = new License(); license.setUrl(listing.getInfo().getLicenseUrl()); definition.setLicense(license); Contract contract = new Contract(); contract.setName(listing.getInfo().getTitle()); LOGGER.log(Level.FINE, "Contract " + contract.getName() + " added."); contract.setDescription(listing.getInfo().getDescription()); definition.setContract(contract); if (definition.getEndpoints().isEmpty()) { // TODO verify how to deal with API key auth + oauth Endpoint endpoint = new Endpoint(basePath); definition.getEndpoints().add(endpoint); if (listing.getAuthorizations().getBasicAuth() != null) { endpoint.setAuthenticationProtocol(ChallengeScheme.HTTP_BASIC .getName()); } else if (listing.getAuthorizations().getOauth2() != null) { endpoint.setAuthenticationProtocol(ChallengeScheme.HTTP_OAUTH .getName()); } else if (listing.getAuthorizations().getApiKey() != null) { endpoint.setAuthenticationProtocol(ChallengeScheme.CUSTOM .getName()); } } } /** * Fills Restlet Web API definition's Operations from Swagger ApiDeclaration * * @param resource * The Restlet Web API definition's Resource * @param apiDeclaration * The Swagger ApiDeclaration * @param api * The Swagger ResourceDeclaration * @param contract * The Restlet Web API definition's Contract * @param section * The Restlet Web API definition's current Section * @param declaredPathVariables * The list of all declared path variables for the Resource * @param declaredTypes * The list of all declared types for the Contract */ private static void fillOperations(Resource resource, ApiDeclaration apiDeclaration, ResourceDeclaration api, Contract contract, Section section, List<String> declaredPathVariables, List<String> declaredTypes) { List<String> apiProduces = apiDeclaration.getProduces(); List<String> apiConsumes = apiDeclaration.getConsumes(); Map<String, List<String>> subtypes = new HashMap<>(); Representation representation; // Operations listing Operation operation; for (ResourceOperationDeclaration swaggerOperation : api .getOperations()) { String methodName = swaggerOperation.getMethod(); operation = new Operation(); operation.setMethod(swaggerOperation.getMethod()); operation.setName(swaggerOperation.getNickname()); operation.setDescription(swaggerOperation.getSummary()); // fill produced and consumed variants fillVariants(operation, swaggerOperation, apiProduces, apiConsumes); // Extract success response message Response success = new Response(); success.setCode(Status.SUCCESS_OK.getCode()); success.setDescription("Success"); success.setMessage(Status.SUCCESS_OK.getDescription()); success.setName("Success"); // fill output payload fillOutPayLoad(success, swaggerOperation); operation.getResponses().add(success); // fill parameters fillParameters(resource, operation, swaggerOperation, declaredPathVariables); // fill responses fillResponseMessages(operation, swaggerOperation); resource.getOperations().add(operation); LOGGER.log(Level.FINE, "Method " + methodName + " added."); // fill representations fillRepresentations(contract, section, apiDeclaration, subtypes, declaredTypes); // Deal with subtyping for (Entry<String, List<String>> subtypesPair : subtypes.entrySet()) { List<String> subtypesOf = subtypesPair.getValue(); for (String subtypeOf : subtypesOf) { representation = contract.getRepresentation(subtypeOf); representation.setExtendedType(subtypesPair.getKey()); } } } } /** * Fills Restlet Web API definition's operation output payload from Swagger * ResourceOperationDeclaration * * @param success * The Restlet Web API definition's operation success Response * @param swaggerOperation * The Swagger ResourceOperationDeclaration */ private static void fillOutPayLoad(Response success, ResourceOperationDeclaration swaggerOperation) { // Set response's entity PayLoad rwadOutRepr = new PayLoad(); if ("array".equals(swaggerOperation.getType())) { LOGGER.log(Level.FINER, "Operation: " + swaggerOperation.getNickname() + " returns an array"); rwadOutRepr.setArray(true); if (swaggerOperation.getItems().getType() != null) { rwadOutRepr.setType(swaggerOperation.getItems().getType()); } else { rwadOutRepr.setType(swaggerOperation.getItems().getRef()); } } else { LOGGER.log(Level.FINER, "Operation: " + swaggerOperation.getNickname() + " returns a single Representation"); rwadOutRepr.setArray(false); if (swaggerOperation.getType() != null) { rwadOutRepr.setType(swaggerOperation.getType()); } else { rwadOutRepr.setType(swaggerOperation.getRef()); } } success.setOutputPayLoad(rwadOutRepr); } /** * Fills Restlet Web API definition's operation parameter from Swagger * ResourceOperationDeclaration * * @param resource * The Restlet Web API definition's Resource to which the * operation is attached * @param operation * The Restlet Web API definition's Operation * @param swaggerOperation * The Swagger ResourceOperationDeclaration * @param declaredPathVariables * The list of declared pathVariable on the resource */ private static void fillParameters(Resource resource, Operation operation, ResourceOperationDeclaration swaggerOperation, List<String> declaredPathVariables) { // Loop over Swagger parameters. for (ResourceOperationParameterDeclaration param : swaggerOperation .getParameters()) { if ("path".equals(param.getParamType())) { if (!declaredPathVariables.contains(param.getName())) { declaredPathVariables.add(param.getName()); PathVariable pathVariable = toPathVariable(param); resource.getPathVariables().add(pathVariable); } } else if ("body".equals(param.getParamType())) { if (operation.getInputPayLoad() == null) { PayLoad rwadInRepr = toEntity(param); operation.setInputPayLoad(rwadInRepr); } } else if ("query".equals(param.getParamType())) { QueryParameter rwadQueryParam = toQueryParameter(param); operation.getQueryParameters().add(rwadQueryParam); } } } /** * Fills Restlet Web API definition's Representations from Swagger * ApiDeclaration * * @param contract * The Restlet Web API definition's Contract * @param section * The Restlet Web API definition's current Section * @param apiDeclaration * The Swagger ApiDeclaration * @param subtypes * The list of this Representation's subtypes * @param declaredTypes * The list of all declared types for the Contract */ private static void fillRepresentations(Contract contract, Section section, ApiDeclaration apiDeclaration, Map<String, List<String>> subtypes, List<String> declaredTypes) { // Add representations Representation representation; for (Entry<String, ModelDeclaration> modelEntry : apiDeclaration .getModels().entrySet()) { ModelDeclaration model = modelEntry.getValue(); if (model.getSubTypes() != null && !model.getSubTypes().isEmpty()) { subtypes.put(model.getId(), model.getSubTypes()); } if (!declaredTypes.contains(modelEntry.getKey())) { declaredTypes.add(modelEntry.getKey()); representation = toRepresentation(model, modelEntry.getKey()); representation.getSections().add(section.getName()); contract.getRepresentations().add(representation); LOGGER.log(Level.FINE, "Representation " + modelEntry.getKey() + " added."); } } } /** * Fills Swagger resource listing main attributes from Restlet Web API * definition * * @param definition * The Restlet Web API definition * @param listing * The Swagger 1.2 resource listing */ private static void fillResourceListingApis(Definition definition, ResourceListing listing) { Contract contract = definition.getContract(); boolean allResources = contract.getSections().isEmpty(); // Resources List<String> addedApis = new ArrayList<>(); if (definition.getContract() != null && contract.getResources() != null) { listing.setApis(new ArrayList<ResourceListingApi>()); for (Resource resource : contract.getResources()) { if (allResources) { ResourceListingApi rd = new ResourceListingApi(); rd.setDescription(resource.getDescription()); rd.setPath(ReflectUtils.getFirstSegment(resource .getResourcePath())); if (!addedApis.contains(rd.getPath())) { addedApis.add(rd.getPath()); listing.getApis().add(rd); } } else { for (String sectionName : resource.getSections()) { Section section = contract.getSection(sectionName); ResourceListingApi rd = new ResourceListingApi(); rd.setDescription(section.getDescription()); rd.setPath("/" + sectionName); if (!addedApis.contains(rd.getPath())) { addedApis.add(rd.getPath()); listing.getApis().add(rd); } } } } } Collections.sort(listing.getApis(), new Comparator<ResourceListingApi>() { @Override public int compare(ResourceListingApi o1, ResourceListingApi o2) { return o1.getPath().compareTo(o2.getPath()); } }); } /** * Fills Swagger resource listing main attributes from Restlet Web API * definition * * @param definition * The Restlet Web API definition * @param listing * The Swagger 1.2 resource listing */ private static void fillResourceListingMainAttributes( Definition definition, ResourceListing listing) { // common properties listing.setApiVersion(definition.getVersion()); // result.setBasePath(definition.getEndpoint()); listing.setInfo(new ApiInfo()); listing.setSwaggerVersion(SWAGGER_VERSION); if (definition.getContact() != null) { listing.getInfo().setContact(definition.getContact().getEmail()); } if (definition.getLicense() != null) { listing.getInfo().setLicenseUrl(definition.getLicense().getUrl()); } if (definition.getContract() != null) { listing.getInfo().setTitle(definition.getContract().getName()); listing.getInfo().setDescription( definition.getContract().getDescription()); } if (!definition.getEndpoints().isEmpty()) { String authenticationProtocol = definition.getEndpoints().get(0) .getAuthenticationProtocol(); if (authenticationProtocol != null) { AuthorizationsDeclaration authorizations = new AuthorizationsDeclaration(); // TODO add other authentication protocols if (ChallengeScheme.HTTP_BASIC.getName().equals( authenticationProtocol)) { authorizations .setBasicAuth(new BasicAuthorizationDeclaration()); listing.setAuthorizations(authorizations); } else if (ChallengeScheme.HTTP_OAUTH.getName().equals( authenticationProtocol) || ChallengeScheme.HTTP_OAUTH_BEARER.getName().equals( authenticationProtocol) || ChallengeScheme.HTTP_OAUTH_MAC.getName().equals( authenticationProtocol)) { authorizations .setOauth2(new OAuth2AuthorizationDeclaration()); } } } } /** * Fills Restlet Web API definition's operation Responses from Swagger * ResourceOperationDeclaration * * @param operation * The Restlet Web API definition's Operation * @param swaggerOperation * The Swagger ResourceOperationDeclaration */ private static void fillResponseMessages(Operation operation, ResourceOperationDeclaration swaggerOperation) { // Set error response messages if (swaggerOperation.getResponseMessages() != null) { for (ResponseMessageDeclaration swagResponse : swaggerOperation .getResponseMessages()) { Response response = new Response(); PayLoad outputPayLoad = new PayLoad(); outputPayLoad.setType(swagResponse.getResponseModel()); response.setOutputPayLoad(outputPayLoad); response.setName("Error " + swagResponse.getCode()); response.setCode(swagResponse.getCode()); response.setMessage(swagResponse.getMessage()); operation.getResponses().add(response); } } } /** * Fills Restlet Web API definition's variants from Swagger 1.2 definition * * @param operation * The Restlet Web API definition's Operation * @param swaggerOperation * The Swagger ResourceOperationDeclaration * @param apiProduces * The list of media types produced by the operation * @param apiConsumes * The list of media types consumed by the operation */ private static void fillVariants(Operation operation, ResourceOperationDeclaration swaggerOperation, List<String> apiProduces, List<String> apiConsumes) { // Set variants for (String produced : apiProduces.isEmpty() ? swaggerOperation .getProduces() : apiProduces) { operation.getProduces().add(produced); } for (String consumed : apiConsumes.isEmpty() ? swaggerOperation .getConsumes() : apiConsumes) { operation.getConsumes().add(consumed); } } /** * Retrieves the Swagger API declaration corresponding to a category of the * given Restlet Web API Definition * * @param sectionName * The category of the API declaration * @param definition * The Restlet Web API Definition * @return The Swagger API definition of the given category */ public static ApiDeclaration getApiDeclaration(String sectionName, Definition definition) { ApiDeclaration result = new ApiDeclaration(); // fill API declaration main attributes fillApiDeclarationMainAttributes(definition, result, sectionName); // fill API declaration resources Collection<String> usedModels = fillApiDeclarationResources(definition, result, sectionName); // fill API declaration representations fillApiDeclarationRepresentations(definition, result, usedModels); return result; } /** * Translates a Restlet Web API Definition to a Swagger resource listing. * * @param definition * The Restlet Web API Definition. * @return The corresponding resource listing */ public static ResourceListing getResourcelisting(Definition definition) { ResourceListing result = new ResourceListing(); // fill resource listing main attributes fillResourceListingMainAttributes(definition, result); // fill resource listing API list fillResourceListingApis(definition, result); return result; } /** * Converts a Swagger parameter to an instance of * {@link org.restlet.ext.apispark.internal.model.PayLoad}. * * @param parameter * The Swagger parameter. * @return An instance of * {@link org.restlet.ext.apispark.internal.model.PayLoad}. */ private static PayLoad toEntity( ResourceOperationParameterDeclaration parameter) { PayLoad result = new PayLoad(); if ("array".equals(parameter.getType())) { result.setArray(true); if (parameter.getItems() != null && parameter.getItems().getType() != null) { result.setType(parameter.getItems().getType()); } else if (parameter.getItems() != null) { result.setType(parameter.getItems().getRef()); } } else { result.setArray(false); result.setType(parameter.getType()); } return result; } /** * Converts a Swagger parameter to an instance of * {@link org.restlet.ext.apispark.internal.model.PathVariable}. * * @param parameter * The Swagger parameter. * @return An instance of * {@link org.restlet.ext.apispark.internal.model.PathVariable}. */ private static PathVariable toPathVariable( ResourceOperationParameterDeclaration parameter) { PathVariable result = new PathVariable(); result.setName(parameter.getName()); result.setDescription(parameter.getDescription()); result.setType(SwaggerTypes.toDefinitionType(new SwaggerTypeFormat( parameter.getType(), parameter.getFormat()))); return result; } /** * Converts a Swagger parameter to an instance of * {@link org.restlet.ext.apispark.internal.model.QueryParameter}. * * @param parameter * The Swagger parameter. * @return An instance of * {@link org.restlet.ext.apispark.internal.model.QueryParameter}. */ private static QueryParameter toQueryParameter( ResourceOperationParameterDeclaration parameter) { QueryParameter result = new QueryParameter(); result.setName(parameter.getName()); result.setDescription(parameter.getDescription()); result.setRequired(parameter.isRequired()); result.setAllowMultiple(parameter.isAllowMultiple()); result.setDefaultValue(parameter.getDefaultValue()); if (parameter.getEnum_() != null && !parameter.getEnum_().isEmpty()) { result.setEnumeration(new ArrayList<String>()); for (String value : parameter.getEnum_()) { result.getEnumeration().add(value); } } return result; } /** * Converts a Swagger model to an instance of * {@link org.restlet.ext.apispark.internal.model.Representation}. * * @param model * The Swagger model. * @param name * The name of the representation. * @return An instance of * {@link org.restlet.ext.apispark.internal.model.Representation}. */ private static Representation toRepresentation(ModelDeclaration model, String name) { Representation result = new Representation(); result.setName(name); result.setDescription(model.getDescription()); // Set properties for (Entry<String, TypePropertyDeclaration> swagProperties : model .getProperties().entrySet()) { TypePropertyDeclaration swagProperty = swagProperties.getValue(); Property property = new Property(); property.setName(swagProperties.getKey()); // Set property's type boolean isArray = "array".equals(swagProperty.getType()); if (isArray) { property.setType(swagProperty.getItems().getType() != null ? swagProperty .getItems().getType() : swagProperty.getItems() .getRef()); } else if (swagProperty.getType() != null) { property.setType(swagProperty.getType()); } else if (swagProperty.getRef() != null) { property.setType(swagProperty.getRef()); } if (model.getRequired() != null) { property.setMinOccurs(model.getRequired().contains( swagProperties.getKey()) ? 1 : 0); } else { property.setMinOccurs(0); } property.setMaxOccurs(isArray ? -1 : 1); property.setDescription(swagProperty.getDescription()); property.setMin(swagProperty.getMinimum()); property.setMax(swagProperty.getMaximum()); property.setUniqueItems(swagProperty.isUniqueItems()); result.getProperties().add(property); LOGGER.log(Level.FINE, "Property " + property.getName() + " added."); } return result; } /** * Translates a Swagger documentation to a Restlet definition. * * @param listing * The Swagger resource listing. * @param apiDeclarations * The list of Swagger API declarations. * @return The Restlet definition. * @throws org.restlet.ext.apispark.internal.conversion.TranslationException */ public static Definition translate(ResourceListing listing, Map<String, ApiDeclaration> apiDeclarations) throws TranslationException { validate(listing, apiDeclarations); try { Definition definition = new Definition(); // fill main attributes of the Restlet Web API definition String basePath = apiDeclarations.get( (listing.getApis().get(0).getPath())).getBasePath(); fillMainAttributes(definition, listing, basePath); fillContract(definition.getContract(), listing, apiDeclarations); LOGGER.log(Level.FINE, "Definition successfully retrieved from Swagger definition"); return definition; } catch (Exception e) { throw new TranslationException( "compliance", "Impossible to read your API definition, check your Swagger specs compliance", e); } } /** * Translates a Swagger API declaration to a Restlet Web API definition. * * @param apiDeclaration * The Swagger API declaration * @return the Restlet Web API definition * @throws TranslationException */ public static Definition translate(ApiDeclaration apiDeclaration) throws TranslationException { try { Definition definition = new Definition(); definition.setContract(new Contract()); definition.getEndpoints().add( new Endpoint(apiDeclaration.getBasePath())); fillContract(definition.getContract(), apiDeclaration); LOGGER.log(Level.FINE, "Definition successfully retrieved from Swagger definition"); return definition; } catch (Exception e) { throw new TranslationException( "compliance", "Impossible to read your API definition, check your Swagger specs compliance", e); } } /** * Indicates if the given resource listing and list of API declarations * match. * * @param resourceListing * The Swagger resource listing. * @param apiDeclarations * The list of Swagger API declarations. * @throws org.restlet.ext.apispark.internal.conversion.TranslationException */ private static void validate(ResourceListing resourceListing, Map<String, ApiDeclaration> apiDeclarations) throws TranslationException { int rlSize = resourceListing.getApis().size(); int adSize = apiDeclarations.size(); if (rlSize < adSize) { throw new TranslationException("file", "One of your API declarations is not mapped in your resource listing"); } else if (rlSize > adSize) { throw new TranslationException("file", "Some API declarations are missing"); } } /** * Private constructor to ensure that the class acts as a true utility class * i.e. it isn't instantiable and extensible. */ private SwaggerTranslator() { } }