diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java index 5424e916b..0f26649c6 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java @@ -6,7 +6,7 @@ @Mojo(name = "doc", threadSafe = true) @Slf4j -public class GenerateDocMojo extends GenerateMojo { +public class GenerateDocMojo extends GenerateFromDirectoryDocMojo { @Override public void execute() throws MojoExecutionException { diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateFromDirectoryDocMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateFromDirectoryDocMojo.java new file mode 100644 index 000000000..e2f2aa957 --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateFromDirectoryDocMojo.java @@ -0,0 +1,51 @@ +package com.backbase.oss.boat; + +import lombok.extern.slf4j.Slf4j; +import org.apache.maven.plugin.MojoExecutionException; + +import java.io.File; + +/** + * Allows generate::Doc to accept inputSpec as a directory + * Output docs will be placed in separate folders for each spec + */ +@Slf4j +public class GenerateFromDirectoryDocMojo extends GenerateMojo { + + @Override + public void execute() throws MojoExecutionException { + + File inputSpecFile = new File(inputSpec); + + if (inputSpecFile.isDirectory()){ + log.info("inputSpec is being read as a directory"); + + File[] inputSpecs; + File outPutDirectory = output; + + inputSpecs = inputSpecFile.listFiles(pathname -> pathname.getName().endsWith(".yaml")); + + if (inputSpecs == null || inputSpecs.length == 0) { + throw new MojoExecutionException("No OpenAPI specs found in: " + inputSpec); + } + + for(File f : inputSpecs){ + inputSpec = f.getPath(); + output = new File(outPutDirectory.getPath(),f.getName().substring(0,f.getName().lastIndexOf(".")).concat("-docs")); + + if(!output.exists()){ + output.mkdir(); + } + + log.info(" Generating docs for spec {} in directory", f.getName()); + super.execute(); + } + + }else { + + log.info("inputSpec being read as a single file"); + super.execute(); + + } + } +} diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java index daed3e43e..4aa024d51 100644 --- a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java @@ -85,6 +85,36 @@ void testBoatDocs() throws MojoExecutionException { assertThat(output.list()).containsExactlyInAnyOrder("index.html", ".openapi-generator-ignore", ".openapi-generator"); } + @Test + void testBoatDocsWithDirectory() throws MojoExecutionException { + + String spec = System.getProperty("spec", getClass().getResource("/boat-doc-oas-examples").getFile()); + + log.info("Generating docs for: {}", spec); + + GenerateDocMojo mojo = new GenerateDocMojo(); + File input = new File(spec); + File output = new File("target/boat-docs-directory"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + + mojo.getLog(); + mojo.buildContext = defaultBuildContext; + mojo.project = new MavenProject(); + mojo.inputSpec = input.getAbsolutePath(); + mojo.output = output; + mojo.skip = false; + mojo.skipIfSpecIsUnchanged = false; + mojo.bundleSpecs = true; + mojo.dereferenceComponents = true; + mojo.execute(); + assertThat(output.list()).containsExactlyInAnyOrder("link-docs", "petstore-docs", "petstore-new-non-breaking-docs", "upto-docs"); + } + @Test void testBundledBoatDocs() throws MojoExecutionException, MojoFailureException { @@ -270,80 +300,5 @@ void testWebClient() throws MojoExecutionException { } - @Test - void testJavaClient() throws MojoExecutionException, MavenInvocationException { - GenerateMojo mojo = new GenerateMojo(); - - String spec = System.getProperty("spec", getClass().getResource("/oas-examples/petstore.yaml").getFile()); - - File input = new File(spec); - File output = new File("target/javaclient"); - if (output.exists()) { - output.delete(); - } - output.mkdirs(); - - DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); - defaultBuildContext.enableLogging(new ConsoleLogger()); - mojo.generatorName = "java"; - mojo.library = "native"; - mojo.buildContext = defaultBuildContext; - mojo.project = new MavenProject(); - mojo.inputSpec = input.getAbsolutePath(); - mojo.output = output; - mojo.skip = false; - mojo.skipIfSpecIsUnchanged = false; - mojo.skipOverwrite = false; - mojo.generateAliasAsModel = false; - mojo.execute(); - - InvocationRequest invocationRequest = new DefaultInvocationRequest(); - invocationRequest.setPomFile(new File(output, "pom.xml")); - invocationRequest.setGoals(Arrays.asList("compile")); - invocationRequest.setBatchMode(true); - - Invoker invoker = new DefaultInvoker(); - InvocationResult invocationResult = invoker.execute(invocationRequest); - assertNull(invocationResult.getExecutionException()); - - } - - @Test - void testReactiveJavaClient() throws MojoExecutionException, MavenInvocationException { - GenerateMojo mojo = new GenerateMojo(); - - String spec = System.getProperty("spec", getClass().getResource("/oas-examples/petstore.yaml").getFile()); - - File input = new File(spec); - File output = new File("target/webclient"); - if (output.exists()) { - output.delete(); - } - output.mkdirs(); - - DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); - defaultBuildContext.enableLogging(new ConsoleLogger()); - mojo.generatorName = "java"; - mojo.library = "webclient"; - mojo.buildContext = defaultBuildContext; - mojo.project = new MavenProject(); - mojo.inputSpec = input.getAbsolutePath(); - mojo.output = output; - mojo.skip = false; - mojo.skipIfSpecIsUnchanged = false; - mojo.skipOverwrite = false; - mojo.generateAliasAsModel = false; - mojo.execute(); - - InvocationRequest invocationRequest = new DefaultInvocationRequest(); - invocationRequest.setPomFile(new File(output, "pom.xml")); - invocationRequest.setGoals(Arrays.asList("compile")); - invocationRequest.setBatchMode(true); - - Invoker invoker = new DefaultInvoker(); - InvocationResult invocationResult = invoker.execute(invocationRequest); - assertNull(invocationResult.getExecutionException()); - - } } diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/link.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/link.yaml new file mode 100644 index 000000000..d1c8cd22d --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/link.yaml @@ -0,0 +1,203 @@ +openapi: "3.0.0" +info: + title: Link Example + version: 1.0.0 +paths: + /2.0/users/{username}: + get: + operationId: getUserByName + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + '200': + description: The User + content: + application/json: + schema: + $ref: '#/components/schemas/user' + links: + userRepositories: + $ref: '#/components/links/UserRepositories' + /2.0/repositories/{username}: + get: + operationId: getRepositoriesByOwner + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + '200': + description: repositories owned by the supplied user + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/repository' + links: + userRepository: + $ref: '#/components/links/UserRepository' + /2.0/repositories/{username}/{slug}: + get: + operationId: getRepository + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + responses: + '200': + description: The repository + content: + application/json: + schema: + $ref: '#/components/schemas/repository' + links: + repositoryPullRequests: + $ref: '#/components/links/RepositoryPullRequests' + /2.0/repositories/{username}/{slug}/pullrequests: + get: + operationId: getPullRequestsByRepository + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: state + in: query + schema: + type: string + enum: + - open + - merged + - declined + responses: + '200': + description: an array of pull request objects + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/pullrequest' + /2.0/repositories/{username}/{slug}/pullrequests/{pid}: + get: + operationId: getPullRequestsById + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: pid + in: path + required: true + schema: + type: string + responses: + '200': + description: a pull request object + content: + application/json: + schema: + $ref: '#/components/schemas/pullrequest' + links: + pullRequestMerge: + $ref: '#/components/links/PullRequestMerge' + /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge: + post: + operationId: mergePullRequest + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: pid + in: path + required: true + schema: + type: string + responses: + '204': + description: the PR was successfully merged +components: + links: + UserRepositories: + # returns array of '#/components/schemas/repository' + operationId: getRepositoriesByOwner + parameters: + username: $response.body#/username + UserRepository: + # returns '#/components/schemas/repository' + operationId: getRepository + parameters: + username: $response.body#/owner/username + slug: $response.body#/slug + RepositoryPullRequests: + # returns '#/components/schemas/pullrequest' + operationId: getPullRequestsByRepository + parameters: + username: $response.body#/owner/username + slug: $response.body#/slug + PullRequestMerge: + # executes /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge + operationId: mergePullRequest + parameters: + username: $response.body#/author/username + slug: $response.body#/repository/slug + pid: $response.body#/id + schemas: + user: + type: object + properties: + username: + type: string + uuid: + type: string + repository: + type: object + properties: + slug: + type: string + owner: + $ref: '#/components/schemas/user' + pullrequest: + type: object + properties: + id: + type: integer + title: + type: string + repository: + $ref: '#/components/schemas/repository' + author: + $ref: '#/components/schemas/user' \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore-new-non-breaking.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore-new-non-breaking.yaml new file mode 100644 index 000000000..9f7ec3683 --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore-new-non-breaking.yaml @@ -0,0 +1,113 @@ +openapi: "3.0.0" +info: + version: 1.1.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + new-property: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore.yaml new file mode 100644 index 000000000..137eaff93 --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore.yaml @@ -0,0 +1,138 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + text/csv: + schema: + type: string + 500: + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + description: InternalServerError + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /no-schema: + get: + summary: No Schema + operationId: getNoSchema + tags: + - pets + responses: + 200: + description: A response that does not specify a schema + content: + application/json: {} +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + InternalServerError: + type: object + required: + - message + properties: + message: + type: string \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/upto.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/upto.yaml new file mode 100644 index 000000000..a822e0c5a --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/upto.yaml @@ -0,0 +1,210 @@ +openapi: "3.0.0" +servers: + - url: '{scheme}://developer.uspto.gov/ds-api' + variables: + scheme: + description: 'The Data Set API is accessible via https and http' + enum: + - 'https' + - 'http' + default: 'https' +info: + description: >- + The Data Set API (DSAPI) allows the public users to discover and search + USPTO exported data sets. This is a generic API that allows USPTO users to + make any CSV based data files searchable through API. With the help of GET + call, it returns the list of data fields that are searchable. With the help + of POST call, data can be fetched based on the filters on the field names. + Please note that POST call is used to search the actual data. The reason for + the POST call is that it allows users to specify any complex search criteria + without worry about the GET size limitations as well as encoding of the + input parameters. + version: 1.0.0 + title: USPTO Data Set API + contact: + name: Open Data Portal + url: 'https://developer.uspto.gov' + email: developer@uspto.gov +tags: + - name: metadata + description: Find out about the data sets + - name: search + description: Search a data set +paths: + /: + get: + tags: + - metadata + operationId: list-data-sets + summary: List available data sets + responses: + '200': + description: Returns a list of data sets + content: + application/json: + schema: + $ref: '#/components/schemas/dataSetList' + example: + { + "total": 2, + "apis": [ + { + "apiKey": "oa_citations", + "apiVersionNumber": "v1", + "apiUrl": "https://developer.uspto.gov/ds-api/oa_citations/v1/fields", + "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/oa_citations.json" + }, + { + "apiKey": "cancer_moonshot", + "apiVersionNumber": "v1", + "apiUrl": "https://developer.uspto.gov/ds-api/cancer_moonshot/v1/fields", + "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/cancer_moonshot.json" + } + ] + } + /{dataset}/{version}/fields: + get: + tags: + - metadata + summary: >- + Provides the general information about the API and the list of fields + that can be used to query the dataset. + description: >- + This GET API returns the list of all the searchable field names that are + in the oa_citations. Please see the 'fields' attribute which returns an + array of field names. Each field or a combination of fields can be + searched using the syntax options shown below. + operationId: list-searchable-fields + parameters: + - name: dataset + in: path + description: 'Name of the dataset.' + required: true + example: "oa_citations" + schema: + type: string + - name: version + in: path + description: Version of the dataset. + required: true + example: "v1" + schema: + type: string + responses: + '200': + description: >- + The dataset API for the given version is found and it is accessible + to consume. + content: + application/json: + schema: + type: string + '404': + description: >- + The combination of dataset name and version is not found in the + system or it is not published yet to be consumed by public. + content: + application/json: + schema: + type: string + /{dataset}/{version}/records: + post: + tags: + - search + summary: >- + Provides search capability for the data set with the given search + criteria. + description: >- + This API is based on Solr/Lucene Search. The data is indexed using + SOLR. This GET API returns the list of all the searchable field names + that are in the Solr Index. Please see the 'fields' attribute which + returns an array of field names. Each field or a combination of fields + can be searched using the Solr/Lucene Syntax. Please refer + https://lucene.apache.org/core/3_6_2/queryparsersyntax.html#Overview for + the query syntax. List of field names that are searchable can be + determined using above GET api. + operationId: perform-search + parameters: + - name: version + in: path + description: Version of the dataset. + required: true + schema: + type: string + default: v1 + - name: dataset + in: path + description: 'Name of the dataset. In this case, the default value is oa_citations' + required: true + schema: + type: string + default: oa_citations + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + type: object + additionalProperties: + type: object + '404': + description: No matching record found for the given criteria. + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + criteria: + description: >- + Uses Lucene Query Syntax in the format of + propertyName:value, propertyName:[num1 TO num2] and date + range format: propertyName:[yyyyMMdd TO yyyyMMdd]. In the + response please see the 'docs' element which has the list of + record objects. Each record structure would consist of all + the fields and their corresponding values. + type: string + default: '*:*' + start: + description: Starting record number. Default value is 0. + type: integer + default: 0 + rows: + description: >- + Specify number of rows to be returned. If you run the search + with default values, in the response you will see 'numFound' + attribute which will tell the number of records available in + the dataset. + type: integer + default: 100 + required: + - criteria +components: + schemas: + dataSetList: + type: object + properties: + total: + type: integer + apis: + type: array + items: + type: object + properties: + apiKey: + type: string + description: To be used as a dataset parameter value + apiVersionNumber: + type: string + description: To be used as a version parameter value + apiUrl: + type: string + format: uriref + description: "The URL describing the dataset's fields" + apiDocumentationUrl: + type: string + format: uriref + description: A URL to the API console for each API \ No newline at end of file