Skip to content

Commit 5a28d27

Browse files
committed
feat(*): add ability to generate separate public api
1 parent 7756298 commit 5a28d27

File tree

15 files changed

+537
-6
lines changed

15 files changed

+537
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ There are several other aspects you can optionally configure. Here is the full
7676
<skip>false</skip>
7777
<format>YAML</format>
7878
<filename>openapi-2.0</filename>
79+
<separatePublicApi>false</separatePublicApi>
7980
<oauth2 />
8081
</configuration>
8182
</execution>

restdocs-spec-generator/src/main/java/com/berkleytechnologyservices/restdocs/spec/generator/openapi_v2/OpenApi20SpecificationGenerator.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,24 @@ public class OpenApi20SpecificationGenerator implements SpecificationGenerator {
2929

3030
private static final Map<SpecificationFormat, JsonProcessingFunction> FORMAT_GENERATORS = createFormatGeneratorsMap();
3131

32+
private final OpenApi20Generator generator;
33+
34+
public OpenApi20SpecificationGenerator() {
35+
this(OpenApi20Generator.INSTANCE);
36+
}
37+
38+
public OpenApi20SpecificationGenerator(OpenApi20Generator generator) {
39+
this.generator = generator;
40+
}
41+
3242
@Override
3343
public Specification getSpecification() {
3444
return Specification.OPENAPI_V2;
3545
}
3646

3747
@Override
3848
public String generate(ApiDetails details, List<ResourceModel> models) throws SpecificationGeneratorException {
39-
Swagger spec = OpenApi20Generator.INSTANCE.generate(
49+
Swagger spec = generator.generate(
4050
models,
4151
details.getBasePath(),
4252
details.getHost(),

restdocs-spec-maven-plugin/src/it/json-format/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
<goal>generate</goal>
4848
</goals>
4949
<configuration>
50-
<snippetsDirectory>${project.basedir}/snippets</snippetsDirectory>
5150
<filename>sample-api-spec</filename>
5251
<format>JSON</format>
5352
<oauth2>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.berkleytechnologyservices.restdocs.it</groupId>
7+
<artifactId>json-format-it</artifactId>
8+
<version>1.0-SNAPSHOT</version>
9+
10+
<description>Verify using json as the specification format.</description>
11+
12+
<properties>
13+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14+
</properties>
15+
16+
<build>
17+
<plugins>
18+
<plugin>
19+
<artifactId>maven-resources-plugin</artifactId>
20+
<version>3.1.0</version>
21+
<executions>
22+
<execution>
23+
<id>copy-resources</id>
24+
<phase>validate</phase>
25+
<goals>
26+
<goal>copy-resources</goal>
27+
</goals>
28+
<configuration>
29+
<outputDirectory>${project.basedir}/target/generated-snippets</outputDirectory>
30+
<resources>
31+
<resource>
32+
<directory>snippets</directory>
33+
<filtering>false</filtering>
34+
</resource>
35+
</resources>
36+
</configuration>
37+
</execution>
38+
</executions>
39+
</plugin>
40+
<plugin>
41+
<groupId>@project.groupId@</groupId>
42+
<artifactId>@project.artifactId@</artifactId>
43+
<version>@project.version@</version>
44+
<executions>
45+
<execution>
46+
<goals>
47+
<goal>generate</goal>
48+
</goals>
49+
<configuration>
50+
<separatePublicApi>true</separatePublicApi>
51+
<oauth2>
52+
<tokenUrl>https://example.org/uaa/token</tokenUrl>
53+
<authorizationUrl>https://example.org/uaa/authorize</authorizationUrl>
54+
<flows>
55+
<flow>accessCode</flow>
56+
<flow>password</flow>
57+
</flows>
58+
<scopes>
59+
<scope>
60+
<name>write:cart</name>
61+
<description>Write cart to the database.</description>
62+
</scope>
63+
<scope>
64+
<name>read:cart</name>
65+
<description>Read cart from the database.</description>
66+
</scope>
67+
</scopes>
68+
</oauth2>
69+
</configuration>
70+
</execution>
71+
</executions>
72+
</plugin>
73+
</plugins>
74+
</build>
75+
</project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"operationId" : "cart-add-product",
3+
"summary" : "Add products to a cart",
4+
"description" : "Add products to a cart",
5+
"privateResource" : false,
6+
"deprecated" : false,
7+
"request" : {
8+
"path" : "/carts/{id}/products",
9+
"method" : "POST",
10+
"contentType" : "text/uri-list",
11+
"headers" : [ ],
12+
"pathParameters" : [ ],
13+
"requestParameters" : [ ],
14+
"requestFields" : [ ],
15+
"example" : "http://localhost/products/1",
16+
"securityRequirements" : null
17+
},
18+
"response" : {
19+
"status" : 200,
20+
"contentType" : "application/hal+json",
21+
"headers" : [ ],
22+
"responseFields" : [ ],
23+
"example" : "{\r\n \"total\" : 49.99,\r\n \"products\" : [ {\r\n \"quantity\" : 1,\r\n \"product\" : {\r\n \"name\" : \"Fancy pants\",\r\n \"price\" : 49.99\r\n },\r\n \"_links\" : {\r\n \"product\" : {\r\n \"href\" : \"http://localhost:8080/products/1\"\r\n }\r\n }\r\n } ],\r\n \"_links\" : {\r\n \"self\" : {\r\n \"href\" : \"http://localhost:8080/carts/1\"\r\n },\r\n \"order\" : {\r\n \"href\" : \"http://localhost:8080/carts/1/order\"\r\n }\r\n }\r\n}"
24+
}
25+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{
2+
"operationId" : "cart-get",
3+
"summary" : "Get a cart by id",
4+
"description" : "Get a cart by id",
5+
"privateResource" : false,
6+
"deprecated" : false,
7+
"request" : {
8+
"path" : "/carts/{id}",
9+
"method" : "GET",
10+
"contentType" : null,
11+
"headers" : [ ],
12+
"pathParameters" : [ {
13+
"name" : "id",
14+
"description" : "the cart id",
15+
"ignored" : false,
16+
"type" : "STRING",
17+
"optional" : false
18+
} ],
19+
"requestParameters" : [ ],
20+
"requestFields" : [ ],
21+
"example" : null,
22+
"securityRequirements" : {
23+
"type": "OAUTH2",
24+
"requiredScopes": [
25+
"read:cart"
26+
]
27+
}
28+
},
29+
"response" : {
30+
"status" : 200,
31+
"contentType" : "application/hal+json",
32+
"headers" : [ ],
33+
"responseFields" : [ {
34+
"description" : "Total amount of the cart.",
35+
"ignored" : false,
36+
"path" : "total",
37+
"type" : "NUMBER",
38+
"optional" : false
39+
}, {
40+
"description" : "The product line item of the cart.",
41+
"ignored" : false,
42+
"path" : "products",
43+
"type" : "ARRAY",
44+
"optional" : false
45+
}, {
46+
"description" : "Link to the product.",
47+
"ignored" : false,
48+
"path" : "products[]._links.product",
49+
"type" : "OBJECT",
50+
"optional" : false
51+
}, {
52+
"description" : "The quantity of the line item.",
53+
"ignored" : false,
54+
"path" : "products[].quantity",
55+
"type" : "NUMBER",
56+
"optional" : false
57+
}, {
58+
"description" : "The product the line item relates to.",
59+
"ignored" : false,
60+
"path" : "products[].product",
61+
"type" : "OBJECT",
62+
"optional" : false
63+
}, {
64+
"description" : "Links section.",
65+
"ignored" : false,
66+
"path" : "_links",
67+
"type" : "OBJECT",
68+
"optional" : false
69+
} ],
70+
"example" : "{\r\n \"total\" : 49.99,\r\n \"products\" : [ {\r\n \"quantity\" : 1,\r\n \"product\" : {\r\n \"name\" : \"Fancy pants\",\r\n \"price\" : 49.99\r\n },\r\n \"_links\" : {\r\n \"product\" : {\r\n \"href\" : \"http://localhost:8080/products/2\"\r\n }\r\n }\r\n } ],\r\n \"_links\" : {\r\n \"self\" : {\r\n \"href\" : \"http://localhost:8080/carts/2\"\r\n },\r\n \"order\" : {\r\n \"href\" : \"http://localhost:8080/carts/2/order\"\r\n }\r\n }\r\n}"
71+
}
72+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"operationId" : "cart-order",
3+
"summary" : "Order a cart",
4+
"description" : "Order a cart",
5+
"privateResource" : true,
6+
"deprecated" : false,
7+
"request" : {
8+
"path" : "/carts/{id}/order",
9+
"method" : "POST",
10+
"contentType" : null,
11+
"headers" : [ ],
12+
"pathParameters" : [ ],
13+
"requestParameters" : [ ],
14+
"requestFields" : [ ],
15+
"example" : null,
16+
"securityRequirements" : null
17+
},
18+
"response" : {
19+
"status" : 200,
20+
"contentType" : "application/hal+json",
21+
"headers" : [ ],
22+
"responseFields" : [ ],
23+
"example" : "{\r\n \"total\" : 49.99,\r\n \"products\" : [ {\r\n \"quantity\" : 1,\r\n \"product\" : {\r\n \"name\" : \"Fancy pants\",\r\n \"price\" : 49.99\r\n },\r\n \"_links\" : {\r\n \"product\" : {\r\n \"href\" : \"http://localhost:8080/products/3\"\r\n }\r\n }\r\n } ],\r\n \"_links\" : {\r\n \"self\" : {\r\n \"href\" : \"http://localhost:8080/carts/3\"\r\n }\r\n }\r\n}"
24+
}
25+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"operationId" : "carts-create",
3+
"summary" : "Create a cart",
4+
"description" : "Create a cart",
5+
"privateResource" : true,
6+
"deprecated" : false,
7+
"request" : {
8+
"path" : "/carts",
9+
"method" : "POST",
10+
"contentType" : null,
11+
"headers" : [ ],
12+
"pathParameters" : [ ],
13+
"requestParameters" : [ ],
14+
"requestFields" : [ ],
15+
"example" : null,
16+
"securityRequirements" : {
17+
"type": "OAUTH2",
18+
"requiredScopes": [
19+
"write:cart"
20+
]
21+
}
22+
},
23+
"response" : {
24+
"status" : 201,
25+
"contentType" : "application/hal+json",
26+
"headers" : [ ],
27+
"responseFields" : [ ],
28+
"example" : "{\r\n \"total\" : 0,\r\n \"products\" : [ ],\r\n \"_links\" : {\r\n \"self\" : {\r\n \"href\" : \"http://localhost:8080/carts/4\"\r\n },\r\n \"order\" : {\r\n \"href\" : \"http://localhost:8080/carts/4/order\"\r\n }\r\n }\r\n}"
29+
}
30+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import org.codehaus.plexus.util.FileUtils
2+
3+
File privateApiFile = new File(basedir as String, "target/restdocs-spec/openapi-2.0.yml")
4+
File publicApiFile = new File(basedir as String, "target/restdocs-spec/openapi-2.0-public.yml")
5+
6+
String privateApiContents = FileUtils.fileRead(privateApiFile)
7+
String publicApiContents = FileUtils.fileRead(publicApiFile)
8+
9+
assert privateApiFile.exists() : 'Private spec file was not found at the expected location'
10+
assert publicApiFile.exists() : 'Public spec file was not found at the expected location'
11+
12+
assert privateApiContents.contains('Add products to a cart') : 'Private spec file is missing "Add products to a cart"'
13+
assert privateApiContents.contains('Get a cart by id') : 'Private spec file is missing "Get a cart by id"'
14+
assert privateApiContents.contains('Order a cart') : 'Private spec file is missing "Order a cart"'
15+
assert privateApiContents.contains('Create a cart') : 'Private spec file is missing "Create a cart"'
16+
17+
assert publicApiContents.contains('Add products to a cart') : 'Public spec file is missing "Add products to a cart"'
18+
assert publicApiContents.contains('Get a cart by id') : 'Public spec file is missing "Get a cart by id"'
19+
assert !publicApiContents.contains('Order a cart') : 'Public spec file should not contain "Order a cart"'
20+
assert !publicApiContents.contains('Create a cart') : 'Public spec file should not contain "Create a cart"'
21+

restdocs-spec-maven-plugin/src/main/java/com/berkleytechnologyservices/restdocs/mojo/GenerateMojo.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.ArrayList;
2222
import java.util.Collections;
2323
import java.util.List;
24+
import java.util.stream.Collectors;
2425

2526
/**
2627
* This mojo generates an api specification using snippet files.
@@ -98,6 +99,9 @@ public class GenerateMojo extends AbstractMojo {
9899
@Parameter(property = "filename")
99100
private String filename;
100101

102+
@Parameter(defaultValue = "false", property = "separatePublicApi", required = true)
103+
private boolean separatePublicApi;
104+
101105
@Parameter
102106
private List<SpecificationOptions> specifications = Collections.emptyList();
103107

@@ -164,7 +168,15 @@ private void validateOptions(SpecificationOptions options) throws MojoExecutionE
164168
private void generateSpecifications(List<SpecificationOptions> allSpecificationOptions) throws MojoExecutionException {
165169
List<ResourceModel> snippets = snippetReader.getModels(snippetsDirectory);
166170
for (SpecificationOptions options : allSpecificationOptions) {
167-
writeSpecificationToFile(options, generateSpecification(options, snippets));
171+
writeSpecificationToFile(options.getFilenameWithExtension(), generateSpecification(options, snippets));
172+
173+
if (separatePublicApi) {
174+
List<ResourceModel> publicResources = snippets.stream()
175+
.filter(resource -> !resource.getPrivateResource())
176+
.collect(Collectors.toList());
177+
178+
writeSpecificationToFile(options.getPublicFilenameWithExtension(), generateSpecification(options, publicResources));
179+
}
168180
}
169181
}
170182

@@ -176,8 +188,8 @@ private String generateSpecification(SpecificationOptions options, List<Resource
176188
}
177189
}
178190

179-
private void writeSpecificationToFile(SpecificationOptions options, String outputString) throws MojoExecutionException {
180-
Path filePath = new File(outputDirectory, options.getFullFilename()).toPath();
191+
private void writeSpecificationToFile(String filename, String outputString) throws MojoExecutionException {
192+
Path filePath = new File(outputDirectory, filename).toPath();
181193
try {
182194
Files.write(filePath, outputString.getBytes());
183195
} catch (IOException e) {

0 commit comments

Comments
 (0)