Skip to content

Commit b528e4a

Browse files
authored
[protobuf-schema] Support oneOf and AnyOf (#20798)
* handle primitive oneof handle anyof * Add oneof and anyof test in petstore
1 parent 74e49ac commit b528e4a

File tree

18 files changed

+465
-2
lines changed

18 files changed

+465
-2
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,18 @@
4040
import java.util.regex.Pattern;
4141
import com.google.common.base.CaseFormat;
4242

43-
import static org.openapitools.codegen.utils.StringUtils.camelize;
44-
import static org.openapitools.codegen.utils.StringUtils.underscore;
43+
import static org.openapitools.codegen.utils.StringUtils.*;
4544

4645
public class ProtobufSchemaCodegen extends DefaultCodegen implements CodegenConfig {
4746

4847
private static final String IMPORT = "import";
4948

5049
private static final String IMPORTS = "imports";
5150

51+
private static final String ARRAY_SUFFIX = "Array";
52+
53+
private static final String MAP_SUFFIX = "Map";
54+
5255
public static final String NUMBERED_FIELD_NUMBER_LIST = "numberedFieldNumberList";
5356

5457
public static final String START_ENUMS_WITH_UNSPECIFIED = "startEnumsWithUnspecified";
@@ -295,6 +298,30 @@ public void addEnumIndexes(List<Map<String, Object>> enumVars) {
295298
}
296299
}
297300

301+
public List<CodegenProperty> processOneOfAnyOfItems(List<CodegenProperty> composedSchemasProperty) {
302+
for(CodegenProperty cd: composedSchemasProperty) {
303+
if (cd.getTitle() != null) {
304+
cd.name = cd.getTitle();
305+
cd.baseName = cd.getTitle();
306+
} else{
307+
cd.name = getNameFromDataType(cd);
308+
cd.baseName = getNameFromDataType(cd);
309+
}
310+
}
311+
return composedSchemasProperty;
312+
}
313+
314+
public String getNameFromDataType(CodegenProperty property) {
315+
if (Boolean.TRUE.equals(property.getIsArray())){
316+
return underscore(property.mostInnerItems.dataType + ARRAY_SUFFIX);
317+
} else if (Boolean.TRUE.equals(property.getIsMap())) {
318+
return underscore(property.mostInnerItems.dataType + MAP_SUFFIX);
319+
} else {
320+
return underscore(property.dataType);
321+
}
322+
}
323+
324+
298325
@Override
299326
public ModelsMap postProcessModels(ModelsMap objs) {
300327
objs = postProcessModelsEnum(objs);
@@ -312,6 +339,11 @@ public ModelsMap postProcessModels(ModelsMap objs) {
312339
}
313340
}
314341

342+
if(cm.oneOf != null && !cm.oneOf.isEmpty()){
343+
cm.vars = processOneOfAnyOfItems(cm.getComposedSchemas().getOneOf());
344+
} else if (cm.anyOf != null && !cm.anyOf.isEmpty()) {
345+
cm.vars = processOneOfAnyOfItems(cm.getComposedSchemas().getAnyOf());
346+
}
315347
int index = 1;
316348
for (CodegenProperty var : cm.vars) {
317349
// add x-protobuf-type: repeated if it's an array

modules/openapi-generator/src/main/resources/protobuf-schema/model.mustache

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ import public "{{{modelPackage}}}/{{{import}}}.proto";
1313
{{#model}}
1414
{{#isEnum}}{{>enum}}{{/isEnum}}{{^isEnum}}message {{classname}} {
1515
16+
{{#oneOf}}
17+
{{#-first}}
18+
oneof {{classVarName}} {
19+
{{#vars}}
20+
{{#description}}
21+
// {{{.}}}
22+
{{/description}}
23+
{{#vendorExtensions.x-protobuf-type}}{{{.}}} {{/vendorExtensions.x-protobuf-type}}{{{vendorExtensions.x-protobuf-data-type}}} {{{name}}} = {{vendorExtensions.x-protobuf-index}}{{#vendorExtensions.x-protobuf-packed}} [packed=true]{{/vendorExtensions.x-protobuf-packed}};
24+
{{/vars}}
25+
}
26+
{{/-first}}
27+
{{/oneOf}}
28+
{{^oneOf}}
1629
{{#vars}}
1730
{{#description}}
1831
// {{{.}}}
@@ -33,6 +46,7 @@ import public "{{{modelPackage}}}/{{{import}}}.proto";
3346
{{/isEnum}}
3447

3548
{{/vars}}
49+
{{/oneOf}}
3650
}
3751
{{/isEnum}}
3852
{{/model}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/protobuf/ProtobufSchemaCodegenTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,54 @@ private void assertFileEquals(Path generatedFilePath, Path expectedFilePath) thr
8282
assertEquals(generatedFile, expectedFile);
8383
}
8484

85+
@Test
86+
public void testCodeGenWithPrimitiveOneOf() throws IOException {
87+
// set line break to \n across all platforms
88+
System.setProperty("line.separator", "\n");
89+
90+
File output = Files.createTempDirectory("test").toFile();
91+
92+
final CodegenConfigurator configurator = new CodegenConfigurator()
93+
.setGeneratorName("protobuf-schema")
94+
.setInputSpec("src/test/resources/3_0/oneOf.yaml")
95+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
96+
97+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
98+
DefaultGenerator generator = new DefaultGenerator();
99+
List<File> files = generator.opts(clientOptInput).generate();
100+
101+
TestUtils.ensureContainsFile(files, output, "models/fruit.proto");
102+
Path path = Paths.get(output + "/models/fruit.proto");
103+
104+
assertFileEquals(path, Paths.get("src/test/resources/3_0/protobuf-schema/fruitOneOf.proto"));
105+
106+
output.deleteOnExit();
107+
}
108+
109+
@Test
110+
public void testCodeGenWithPrimitiveAnyOf() throws IOException {
111+
// set line break to \n across all platforms
112+
System.setProperty("line.separator", "\n");
113+
114+
File output = Files.createTempDirectory("test").toFile();
115+
116+
final CodegenConfigurator configurator = new CodegenConfigurator()
117+
.setGeneratorName("protobuf-schema")
118+
.setInputSpec("src/test/resources/3_0/anyOf.yaml")
119+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
120+
121+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
122+
DefaultGenerator generator = new DefaultGenerator();
123+
List<File> files = generator.opts(clientOptInput).generate();
124+
125+
TestUtils.ensureContainsFile(files, output, "models/fruit.proto");
126+
Path path = Paths.get(output + "/models/fruit.proto");
127+
128+
assertFileEquals(path, Paths.get("src/test/resources/3_0/protobuf-schema/fruitAnyOf.proto"));
129+
130+
output.deleteOnExit();
131+
}
132+
85133
@Test(description = "convert a model with dollar signs")
86134
public void modelTest() {
87135
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/dollar-in-names-pull14359.yaml");
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
fruity
3+
4+
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
5+
6+
The version of the OpenAPI document: 0.0.1
7+
8+
Generated by OpenAPI Generator: https://openapi-generator.tech
9+
*/
10+
11+
syntax = "proto3";
12+
13+
package openapitools;
14+
15+
import public "models/apple.proto";
16+
import public "models/banana.proto";
17+
18+
message Fruit {
19+
20+
21+
Apple apple = 93029210;
22+
23+
Banana banana = 322613405;
24+
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
fruity
3+
4+
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
5+
6+
The version of the OpenAPI document: 0.0.1
7+
8+
Generated by OpenAPI Generator: https://openapi-generator.tech
9+
*/
10+
11+
syntax = "proto3";
12+
13+
package openapitools;
14+
15+
import public "models/apple.proto";
16+
import public "models/banana.proto";
17+
18+
message Fruit {
19+
20+
oneof fruit {
21+
Apple apple = 93029210;
22+
23+
Banana banana = 322613405;
24+
25+
}
26+
27+
}

modules/openapi-generator/src/test/resources/3_0/protobuf/petstore.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ tags:
1818
- name: user
1919
description: Operations about user
2020
paths:
21+
/pets:
22+
get:
23+
requestBody:
24+
content:
25+
application/json:
26+
schema:
27+
oneOf:
28+
- $ref: "#/components/schemas/Cat"
29+
- $ref: "#/components/schemas/Dog"
30+
responses:
31+
"200":
32+
description: Updated
33+
post:
34+
requestBody:
35+
content:
36+
application/json:
37+
schema:
38+
anyOf:
39+
- $ref: "#/components/schemas/Cat"
40+
- $ref: "#/components/schemas/Dog"
41+
responses:
42+
"200":
43+
description: Updated
2144
/pet:
2245
post:
2346
tags:
@@ -604,6 +627,21 @@ components:
604627
name: api_key
605628
in: header
606629
schemas:
630+
Dog:
631+
type: object
632+
properties:
633+
bark:
634+
type: boolean
635+
breed:
636+
type: string
637+
enum: [ Dingo, Husky, Retriever, Shepherd ]
638+
Cat:
639+
type: object
640+
properties:
641+
hunts:
642+
type: boolean
643+
age:
644+
type: integer
607645
Order:
608646
title: Pet Order
609647
description: An order for a pets from the pet store
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
README.md
22
models/api_response.proto
3+
models/cat.proto
34
models/category.proto
5+
models/dog.proto
46
models/order.proto
57
models/other_test.proto
68
models/pet.proto
9+
models/pets_get_request.proto
10+
models/pets_post_request.proto
711
models/tag.proto
812
models/user.proto
13+
services/default_service.proto
914
services/pet_service.proto
1015
services/store_service.proto
1116
services/user_service.proto
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
OpenAPI Petstore
3+
4+
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
5+
6+
The version of the OpenAPI document: 1.0.0
7+
8+
Generated by OpenAPI Generator: https://openapi-generator.tech
9+
*/
10+
11+
syntax = "proto3";
12+
13+
package petstore;
14+
15+
16+
message Cat {
17+
18+
bool hunts = 1;
19+
20+
int32 age = 2;
21+
22+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
OpenAPI Petstore
3+
4+
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
5+
6+
The version of the OpenAPI document: 1.0.0
7+
8+
Generated by OpenAPI Generator: https://openapi-generator.tech
9+
*/
10+
11+
syntax = "proto3";
12+
13+
package petstore;
14+
15+
16+
message Dog {
17+
18+
bool bark = 1;
19+
20+
enum Breed {
21+
BREED_UNSPECIFIED = 0;
22+
BREED_DINGO = 1;
23+
BREED_HUSKY = 2;
24+
BREED_RETRIEVER = 3;
25+
BREED_SHEPHERD = 4;
26+
}
27+
28+
Breed breed = 2;
29+
30+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
OpenAPI Petstore
3+
4+
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
5+
6+
The version of the OpenAPI document: 1.0.0
7+
8+
Generated by OpenAPI Generator: https://openapi-generator.tech
9+
*/
10+
11+
syntax = "proto3";
12+
13+
package petstore;
14+
15+
import public "models/cat.proto";
16+
import public "models/dog.proto";
17+
18+
message PetsGetRequest {
19+
20+
oneof pets_get_request {
21+
Cat cat = 1;
22+
Dog dog = 2;
23+
}
24+
}

0 commit comments

Comments
 (0)