/**
* Copyright 2013 SmartBear Software, Inc.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.smartbear.swagger4j.impl;
import com.smartbear.swagger4j.Api;
import com.smartbear.swagger4j.ApiDeclaration;
import com.smartbear.swagger4j.Authorizations;
import com.smartbear.swagger4j.DataType;
import com.smartbear.swagger4j.Info;
import com.smartbear.swagger4j.Model;
import com.smartbear.swagger4j.Operation;
import com.smartbear.swagger4j.Parameter;
import com.smartbear.swagger4j.PrimitiveType;
import com.smartbear.swagger4j.Property;
import com.smartbear.swagger4j.ResourceListing;
import com.smartbear.swagger4j.SwaggerFormat;
import com.smartbear.swagger4j.SwaggerReader;
import com.smartbear.swagger4j.SwaggerSource;
import com.smartbear.swagger4j.SwaggerVersion;
import com.smartbear.swagger4j.URISwaggerSource;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Default SwaggerReader implementation
*
* @see SwaggerReader
*/
public class SwaggerReaderImpl implements SwaggerReader {
private final static Logger logger = Logger.getLogger(SwaggerReaderImpl.class.getName());
public ResourceListing readResourceListing(URI uri) throws IOException {
assert uri != null;
return readResourceListing(new URISwaggerSource(uri));
}
public ResourceListing readResourceListing(SwaggerSource source) throws IOException {
assert source != null;
SwaggerParser parser = SwaggerParser.newParser(source.readResourceListing(), source.getFormat());
String versionString = parser.getString(Constants.SWAGGER_VERSION);
if (versionString == null) {
versionString = parser.getString(Constants.SWAGGER_V2_VERSION);
}
SwaggerVersion swaggerVersion = SwaggerVersion.fromIdentifier(versionString);
Constants constants = Constants.get(swaggerVersion);
// basePath was mandatory in V1.1
String basePath = parser.getString(constants.BASE_PATH);
ResourceListingImpl resourceListing = new ResourceListingImpl(swaggerVersion);
resourceListing.setBasePath(basePath);
String apiVersion = parser.getString(constants.API_VERSION);
if (apiVersion != null) {
resourceListing.setApiVersion(apiVersion);
}
resourceListing.setSwaggerVersion(swaggerVersion);
for (SwaggerParser node : parser.getChildren(constants.APIS)) {
String path = node.getString(constants.PATH);
Reader reader = source.readApiDeclaration(basePath, path);
ApiDeclaration apiDeclaration = readApiDeclaration(reader, source.getFormat());
ResourceListing.ResourceListingApi api = resourceListing.addApi(apiDeclaration, path);
api.setDescription(node.getString(constants.DESCRIPTION));
}
SwaggerParser child = parser.getChild(constants.INFO);
if (child != null) {
readResourceListingInfo(constants, resourceListing, child);
}
child = parser.getChild(constants.AUTHORIZATIONS);
if (child != null) {
readAuthorizations(resourceListing, child);
}
return resourceListing;
}
private void readAuthorizations(ResourceListingImpl resourceListing, SwaggerParser child) {
String[] names = child.getChildNames();
if (names != null) {
for (String name : names) {
SwaggerParser auth = child.getChild(name);
String type = auth.getString(Constants.AUTHORIZATION_TYPE);
if (type.equals(Constants.OAUTH_2_TYPE)) {
readOAuth2Authorization(resourceListing, name, auth);
} else if (type.equals(Constants.API_KEY_TYPE)) {
Authorizations.ApiKeyAuthorization apiKey = (Authorizations.ApiKeyAuthorization) resourceListing.getAuthorizations().addAuthorization(name, Authorizations.AuthorizationType.API_KEY);
apiKey.setKeyName(auth.getString(Constants.API_KEY_KEY_NAME));
apiKey.setPassAs(auth.getString(Constants.API_KEY_PASS_AS));
} else if (type.equals(Constants.BASIC_AUTH_TYPE)) {
resourceListing.getAuthorizations().addAuthorization(name, Authorizations.AuthorizationType.BASIC);
}
}
}
}
private void readOAuth2Authorization(ResourceListingImpl resourceListing, String name, SwaggerParser auth) {
Authorizations.OAuth2Authorization oauth = (Authorizations.OAuth2Authorization) resourceListing.getAuthorizations().addAuthorization(name, Authorizations.AuthorizationType.OAUTH2);
List<SwaggerParser> scopes = auth.getChildren(Constants.OAUTH2_SCOPES);
if (scopes != null && !scopes.isEmpty()) {
for (SwaggerParser p : scopes) {
oauth.addScope(p.getString(Constants.OAUTH2_SCOPE), p.getString(Constants.OAUTH2_SCOPE_DESCRIPTION));
}
}
SwaggerParser grants = auth.getChild(Constants.OAUTH2_GRANT_TYPES);
if (grants != null) {
SwaggerParser implicitGrant = grants.getChild(Constants.OAUTH2_IMPLICIT_GRANT);
if (implicitGrant != null) {
Authorizations.OAuth2Authorization.ImplicitGrant ig = oauth.getImplicitGrant();
if (implicitGrant.getChild(Constants.OAUTH2_IMPLICIT_LOGIN_ENDPOINT) != null) {
ig.setLoginEndpoint(implicitGrant.getChild(Constants.OAUTH2_IMPLICIT_LOGIN_ENDPOINT).getString(Constants.OAUTH2_IMPLICIT_LOGIN_ENDPOINT_URL));
}
ig.setTokenName(implicitGrant.getString(Constants.OAUTH2_IMPLICIT_TOKEN_NAME));
}
SwaggerParser ac = grants.getChild(Constants.OAUTH2_AUTHORIZATION_CODE_GRANT);
if (ac != null) {
Authorizations.OAuth2Authorization.AuthorizationCodeGrant acg = oauth.getAuthorizationCodeGrant();
SwaggerParser tre = ac.getChild(Constants.OAUTH2_AUTHORIZATION_GRANT_TOKEN_REQUEST_ENDPOINT);
if (tre != null) {
acg.setTokenRequestEndpoint(tre.getString(Constants.OAUTH2_AUTHORIZATION_CODE_TOKEN_REQUEST_ENDPOINT_URL));
acg.setClientIdName(tre.getString(Constants.OAUTH2_AUTHORIZATION_CODE_TOKEN_REQUEST_ENDPOINT_CLIENT_ID_NAME));
acg.setClientSecretName(tre.getString(Constants.OAUTH2_AUTHORIZATION_CODE_TOKEN_REQUEST_ENDPOINT_CLIENT_SECRET_NAME));
}
SwaggerParser te = ac.getChild(Constants.OAUTH2_AUTHORIZATION_CODE_TOKEN_ENDPOINT);
if (te != null) {
acg.setTokenEndpoint(te.getString(Constants.OAUTH2_AUTHORIZATION_CODE_TOKEN_ENDPOINT_URL));
acg.setTokenName(te.getString(Constants.OAUTH2_AUTHORIZATION_CODE_TOKEN_ENDPOINT_TOKEN_NAME));
}
}
}
}
private void readResourceListingInfo(Constants constants, ResourceListingImpl resourceListing, SwaggerParser child) {
Info info = resourceListing.getInfo();
info.setContact(child.getString(constants.INFO_CONTACT));
info.setDescription(child.getString(constants.INFO_DESCRIPTION));
info.setLicense(child.getString(constants.INFO_LICENSE));
info.setLicenseUrl(child.getString(constants.INFO_LICENSE_URL));
info.setTermsOfServiceUrl(child.getString(constants.INFO_TERMSOFSERVICEURL));
info.setTitle(child.getString(constants.INFO_TITLE));
}
public ApiDeclaration readApiDeclaration(URI uri) throws IOException {
assert uri != null : "uri can not be null";
SwaggerFormat format = URISwaggerSource.extractFormat(uri);
Reader reader = new InputStreamReader(uri.toURL().openStream());
return readApiDeclaration(reader, format);
}
public ApiDeclaration readApiDeclaration(Reader reader, SwaggerFormat format) throws IOException {
SwaggerParser parser = SwaggerParser.newParser(reader, format);
SwaggerVersion swaggerVersion = SwaggerVersion.fromIdentifier(parser.getString(Constants.SWAGGER_VERSION));
Constants constants = Constants.get(swaggerVersion);
String basePath = parser.getString(constants.BASE_PATH);
String resourcePath = parser.getString(constants.RESOURCE_PATH);
ApiDeclaration apiDeclaration = new ApiDeclarationImpl(basePath, resourcePath);
apiDeclaration.setSwaggerVersion(swaggerVersion);
String apiVersion = parser.getString(constants.API_VERSION);
if (apiVersion != null) {
apiDeclaration.setApiVersion(apiVersion);
}
for (String produces : parser.getArray(constants.PRODUCES)) {
apiDeclaration.addProduces(produces);
}
for (String consumes : parser.getArray(constants.CONSUMES)) {
apiDeclaration.addConsumes(consumes);
}
for (SwaggerParser apiNode : parser.getChildren(constants.APIS)) {
String apiPath = apiNode.getString(constants.PATH);
if (apiDeclaration.getApi(apiPath) != null) {
logger.log(Level.INFO, "Skipping duplicate API at path [" + apiPath +
"] in ApiDeclaration at [" + basePath + resourcePath + "]");
} else {
Api api = apiDeclaration.addApi(apiPath);
api.setDescription(apiNode.getString(constants.DESCRIPTION));
for (SwaggerParser opNode : apiNode.getChildren(constants.OPERATIONS)) {
String nickName = opNode.getString(constants.NICKNAME);
if (nickName == null) {
logger.log(Level.INFO, "Missing nickname in operation - using method instead");
nickName = getOperationMethod(constants, opNode);
}
if (api.getOperation(nickName) != null) {
logger.log(Level.INFO, "Skipping duplicate Operation with nickName [" +
nickName + "] in API at path [" + apiPath +
"] in ApiDeclaration at [" + basePath + resourcePath + "]");
} else {
readOperation(constants, api, opNode, nickName);
}
}
}
}
if (swaggerVersion == SwaggerVersion.V1_2) {
SwaggerParser modelsNode = parser.getChild(constants.MODELS);
if (modelsNode != null) {
for (String modelKey : modelsNode.getChildNames()) {
SwaggerParser node = modelsNode.getChild(modelKey);
apiDeclaration.addModel(readModel(constants, node));
}
}
} else {
logger.log(Level.INFO, "skipping models parsing, not supported for version {0}", swaggerVersion.getIdentifier());
}
return apiDeclaration;
}
private void readOperation(Constants constants, Api api, SwaggerParser opNode, String nickName) {
String method = getOperationMethod(constants, opNode);
Operation operation = api.addOperation(nickName,
Operation.Method.valueOf(method.toUpperCase()));
operation.setSummary(opNode.getString(constants.SUMMARY));
operation.setNotes(opNode.getString(constants.NOTES));
operation.setResponseClass(opNode.getString(constants.OPERATION_TYPE));
for (SwaggerParser parameterNode : opNode.getChildren(constants.PARAMETERS)) {
try {
Parameter parameter = operation.addParameter(parameterNode.getString(constants.NAME),
Parameter.ParamType.valueOf(parameterNode.getString(constants.PARAM_TYPE)));
parameter.setAllowMultiple(parameterNode.getBoolean(constants.ALLOW_MULTIPLE));
parameter.setDescription(parameterNode.getString(constants.DESCRIPTION));
parameter.setRequired(parameterNode.getBoolean(constants.REQUIRED));
parameter.setType(parameterNode.getString(constants.TYPE));
} catch (Exception e) {
Swagger4jExceptionHandler.get().onException(e);
}
}
for (SwaggerParser responseMessage : opNode.getChildren(constants.RESPONSE_MESSAGES)) {
operation.addResponseMessage(
responseMessage.getInteger(constants.CODE), responseMessage.getString(constants.MESSAGE)
).setResponseModel(responseMessage.getString(constants.RESPONSE_MODEL));
}
for (String produces : opNode.getArray(constants.PRODUCES)) {
operation.addProduces(produces);
}
for (String consumes : opNode.getArray(constants.CONSUMES)) {
operation.addConsumes(consumes);
}
}
private String getOperationMethod(Constants constants, SwaggerParser opNode) {
String method = opNode.getString(constants.METHOD);
if (method == null) {
method = opNode.getString(constants.HTTP_METHOD);
}
return method;
}
private Model readModel(Constants constants, SwaggerParser modelNode) {
String id = modelNode.getString(constants.ID);
String modelDescription = modelNode.getString(constants.DESCRIPTION);
ModelImpl model = new ModelImpl(id, modelDescription);
List<String> requiredProperties = modelNode.getArray(constants.REQUIRED);
model.setRequiredProperties(requiredProperties);
SwaggerParser propertiesNode = modelNode.getChild(constants.PROPERTIES);
List<Property> properties = new ArrayList<Property>();
for (String propertyName : propertiesNode.getChildNames()) {
SwaggerParser propertyNode = propertiesNode.getChild(propertyName);
DataType dataType = readDataType(constants, propertyNode);
String propertyDescription = propertyNode.getString(constants.DESCRIPTION);
boolean required = requiredProperties.contains(propertyName);
PropertyImpl property = new PropertyImpl(propertyName, dataType, propertyDescription, required);
if (dataType.isPrimitive() && dataType.isArray() == false) {
PrimitiveType primitiveType = (PrimitiveType) dataType;
property.setDefaultValue(
readDefaultValue(constants, propertyNode, primitiveType));
if (primitiveType.isString()) {
property.setEnumValues(propertyNode.getArray(constants.ENUM));
}
if (primitiveType.isNumber()) {
property.setMaximum(propertyNode.getNumber(constants.MAXIMUM));
property.setMinimum(propertyNode.getNumber(constants.MINIMUM));
}
}
properties.add(property);
}
model.setProperties(properties);
return model;
}
private DataType readDataType(Constants constants, SwaggerParser node) {
String type = node.getString(constants.TYPE);
if (type == null) {
return new RefDataType(node.getString(constants.$REF));
}
if ("array".equals(type)) {
return readArrayDataType(constants, node.getChild(constants.ITEMS));
}
// TODO: handle complex data
try {
return PrimitiveType.get(type, node.getString(constants.FORMAT));
} catch (IllegalArgumentException ex) {
// not a primitive so a model's id
return new NamedDataType(type);
}
}
private DataType readArrayDataType(Constants constants, SwaggerParser itemsNode) {
String itemsType = itemsNode.getString(constants.TYPE);
if (itemsType == null) {
return new RefArrayType(itemsNode.getString(constants.$REF));
}
try {
return new PrimitiveArrayType(PrimitiveType.get(
itemsType,
itemsNode.getString(constants.FORMAT)));
} catch (IllegalArgumentException ex) {
// not a primitive so a model's id
return new NamedDataArrayType(itemsType);
}
}
private Object readDefaultValue(Constants constants, SwaggerParser node, PrimitiveType dataType) {
Utils.SwaggerDataParser dataParser = new Utils.SwaggerDataParser(node);
switch (dataType) {
case INTEGER:
return dataParser.getInteger(constants.DEFAULT_VALUE);
case LONG:
return dataParser.getLong(constants.DEFAULT_VALUE);
case FLOAT:
return dataParser.getFloat(constants.DEFAULT_VALUE);
case DOUBLE:
return dataParser.getDouble(constants.DEFAULT_VALUE);
case BOOLEAN:
return dataParser.getBoolean(constants.DEFAULT_VALUE);
case BYTE: // FALL THROUGH
case DATE: // FALL THROUGH
case DATE_TIME: // FALL THROUGH
case NUMBER:
case STRING:
return dataParser.getString(constants.DEFAULT_VALUE);
default:
throw new AssertionError();
}
}
}