/* * Copyright 2015 herd contributors * * 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 org.finra.herd.swaggergen; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.swagger.models.Info; import io.swagger.models.Scheme; import io.swagger.models.SecurityRequirement; import io.swagger.models.Swagger; import io.swagger.models.auth.ApiKeyAuthDefinition; import io.swagger.models.auth.BasicAuthDefinition; import io.swagger.models.auth.OAuth2Definition; import io.swagger.models.auth.SecuritySchemeDefinition; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.springframework.util.CollectionUtils; /** * Goal which generates a Swagger YAML documentation file. */ @Mojo(name = "herd-swaggergen", defaultPhase = LifecyclePhase.GENERATE_RESOURCES) public class SwaggerGenMojo extends AbstractMojo { /** * Location where the generated output YAML file will go. */ @org.apache.maven.plugins.annotations.Parameter(property = "outputDir", required = true, defaultValue = "${project.build.directory}/generated-sources/herd-swaggergen") private File outputDirectory; /** * The output file name. */ @org.apache.maven.plugins.annotations.Parameter(property = "outputFilename", required = true, defaultValue = "swagger.yaml") private String outputFilename; /** * The REST Java package location. */ @org.apache.maven.plugins.annotations.Parameter(property = "restJavaPackage", required = true) private String restJavaPackage; /** * The Model Java package location. */ @org.apache.maven.plugins.annotations.Parameter(property = "modelJavaPackage", required = true) private String modelJavaPackage; /** * The Error Information model class name. */ @org.apache.maven.plugins.annotations.Parameter(property = "modelErrorClassName") private String modelErrorClassName; /** * The REST base path. The default assumes the controller's class' names are in the form {TAGNAME}RestController (e.g. SampleRestController). "tag" is the * pattern that is read for the value to use. */ @org.apache.maven.plugins.annotations.Parameter(property = "tagPattern", required = true, defaultValue = "(?<tag>.+?)RestController") private String tagPatternParameter; /** * The application title. */ @org.apache.maven.plugins.annotations.Parameter(property = "title", required = true, defaultValue = "Application") private String title; /** * The application version. */ @org.apache.maven.plugins.annotations.Parameter(property = "version", required = true, defaultValue = "${project.version}") private String version; /** * The REST base path. */ @org.apache.maven.plugins.annotations.Parameter(property = "basePath", required = true, defaultValue = "/rest") private String basePath; /** * The list of schemes. * * @see Scheme */ @org.apache.maven.plugins.annotations.Parameter(property = "schemes") private List<String> schemeParameters; /** * The name of the XSD file which defines the models. */ @org.apache.maven.plugins.annotations.Parameter(property = "xsdName", required = false) private String xsdName; /** * The authorization type. Values come from SecuritySchemeDefinition types (i.e. "basic", "oauth2", and "apiKey"). If nothing is specified, no authorization * will be added. */ @org.apache.maven.plugins.annotations.Parameter(property = "authType") private String authType; // A list of the Swagger security scheme definitions. Each one has a "type" to identify it if necessary. private static final List<SecuritySchemeDefinition> SECURITY_SCHEME_DEFINITIONS = new ArrayList<>(Arrays.asList(new BasicAuthDefinition(), new OAuth2Definition(), new ApiKeyAuthDefinition())); /** * The main execution method for this Mojo. * * @throws MojoExecutionException if any errors were encountered. */ @Override public void execute() throws MojoExecutionException { // Create the output directory if it doesn't already exist. getLog().debug("Creating output directory \"" + outputDirectory + "\"."); java.nio.file.Path outputDirectoryPath = Paths.get(outputDirectory.toURI()); if (!Files.exists(outputDirectoryPath)) { try { Files.createDirectories(outputDirectoryPath); } catch (IOException e) { throw new MojoExecutionException("Unable to create directory for output path \"" + outputDirectoryPath + "\".", e); } } // Get a new Swagger metadata object. Swagger swagger = getSwagger(); // Find all the model classes. // Note: this needs to be done before we process the REST controllers below because it finds the modelErrorClass. ModelClassFinder modelClassFinder = new ModelClassFinder(getLog(), modelJavaPackage, modelErrorClassName); // Find and process all the REST controllers and add them to the Swagger metadata. RestControllerProcessor restControllerProcessor = new RestControllerProcessor(getLog(), swagger, restJavaPackage, tagPatternParameter, modelClassFinder.getModelErrorClass()); XsdParser xsdParser = null; if (xsdName != null) { xsdParser = new XsdParser(xsdName); } // Generate the definitions into Swagger based on the model classes collected. new DefinitionGenerator(getLog(), swagger, restControllerProcessor.getExampleClassNames(), modelClassFinder.getModelClasses(), xsdParser); // Write to Swagger information to a YAML file. createYamlFile(swagger); } /** * Gets a new Swagger metadata. * * @return the Swagger metadata. * @throws MojoExecutionException if any problems were encountered. */ private Swagger getSwagger() throws MojoExecutionException { getLog().debug("Creating Swagger Metadata"); // Set up initial Swagger metadata. Swagger swagger = new Swagger(); swagger.setInfo(new Info().title(title).version(version)); swagger.setBasePath(basePath); // Set the schemes. if (!CollectionUtils.isEmpty(schemeParameters)) { List<Scheme> schemes = new ArrayList<>(); for (String schemeParameter : schemeParameters) { Scheme scheme = Scheme.forValue(schemeParameter); if (scheme == null) { throw new MojoExecutionException("Invalid scheme specified: " + schemeParameter); } schemes.add(scheme); } swagger.setSchemes(schemes); } // Add authorization support if specified. if (authType != null) { // Find the definition for the user specified type. SecuritySchemeDefinition securitySchemeDefinition = null; for (SecuritySchemeDefinition possibleDefinition : SECURITY_SCHEME_DEFINITIONS) { if (possibleDefinition.getType().equalsIgnoreCase(authType)) { securitySchemeDefinition = possibleDefinition; break; } } // If we found a match, set it on the swagger object. if (securitySchemeDefinition != null) { // Come up with an authentication name for easy identification (e.g. basicAuthentication, etc.). String securityName = securitySchemeDefinition.getType() + "Authentication"; // Add the security definition. swagger.addSecurityDefinition(securityName, securitySchemeDefinition); // Add the security for everything based on the name of the definition. SecurityRequirement securityRequirement = new SecurityRequirement(); securityRequirement.requirement(securityName); swagger.addSecurity(securityRequirement); } } // Use default paths and definitions. swagger.setPaths(new TreeMap<>()); swagger.setDefinitions(new TreeMap<>()); return swagger; } /** * Creates the YAML file in the output location based on the Swagger metadata. * * @param swagger the Swagger metadata. * * @throws MojoExecutionException if any error was encountered while writing the YAML information to the file. */ private void createYamlFile(Swagger swagger) throws MojoExecutionException { String yamlOutputLocation = outputDirectory + "/" + outputFilename; try { getLog().debug("Creating output YAML file \"" + yamlOutputLocation + "\""); ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); objectMapper.setPropertyNamingStrategy(new SwaggerNamingStrategy()); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.writeValue(new File(yamlOutputLocation), swagger); } catch (IOException e) { throw new MojoExecutionException("Error creating output YAML file \"" + yamlOutputLocation + "\". Reason: " + e.getMessage(), e); } } }