Skip to content

Commit b4378a6

Browse files
authored
[Protobuf Schema] Map Field Handling in Composed Schemas (#21002)
* Address map under composed schema Add explanation to code block * add comment to explain the code block
1 parent 1136872 commit b4378a6

File tree

6 files changed

+117
-36
lines changed

6 files changed

+117
-36
lines changed

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

+37-3
Original file line numberDiff line numberDiff line change
@@ -406,15 +406,15 @@ private void processNestedSchemas(Schema schema, Set<Schema> visitedSchemas) {
406406
if (ModelUtils.isMapSchema(schema) && ModelUtils.getAdditionalProperties(schema) != null) {
407407
Schema mapValueSchema = ModelUtils.getAdditionalProperties(schema);
408408
mapValueSchema = ModelUtils.getReferencedSchema(openAPI, mapValueSchema);
409-
if (ModelUtils.isArraySchema(mapValueSchema) || ModelUtils.isMapSchema(mapValueSchema)) {
409+
if (ModelUtils.isArraySchema(mapValueSchema) || (ModelUtils.isMapSchema(mapValueSchema) && !ModelUtils.isModel(mapValueSchema))) {
410410
Schema innerSchema = generateNestedSchema(mapValueSchema, visitedSchemas);
411411
schema.setAdditionalProperties(innerSchema);
412412

413413
}
414414
} else if (ModelUtils.isArraySchema(schema) && ModelUtils.getSchemaItems(schema) != null) {
415415
Schema arrayItemSchema = ModelUtils.getSchemaItems(schema);
416416
arrayItemSchema = ModelUtils.getReferencedSchema(openAPI, arrayItemSchema);
417-
if (ModelUtils.isMapSchema(arrayItemSchema) || ModelUtils.isArraySchema(arrayItemSchema)) {
417+
if ((ModelUtils.isMapSchema(arrayItemSchema) && !ModelUtils.isModel(arrayItemSchema)) || ModelUtils.isArraySchema(arrayItemSchema)) {
418418
Schema innerSchema = generateNestedSchema(arrayItemSchema, visitedSchemas);
419419
schema.setItems(innerSchema);
420420
}
@@ -427,7 +427,7 @@ private void processNestedSchemas(Schema schema, Set<Schema> visitedSchemas) {
427427
Schema innerSchema = generateNestedSchema(oneOfSchema, visitedSchemas);
428428
innerSchema.setTitle(oneOf.getTitle());
429429
newOneOfs.add(innerSchema);
430-
} else if (ModelUtils.isMapSchema(oneOfSchema)) {
430+
} else if (ModelUtils.isMapSchema(oneOfSchema) && !ModelUtils.isModel(oneOfSchema)) {
431431
Schema innerSchema = generateNestedSchema(oneOfSchema, visitedSchemas);
432432
innerSchema.setTitle(oneOf.getTitle());
433433
newOneOfs.add(innerSchema);
@@ -1061,4 +1061,38 @@ public GeneratorLanguage generatorLanguage() {
10611061
return GeneratorLanguage.PROTOBUF;
10621062
}
10631063

1064+
1065+
/**
1066+
* Handles additionalProperties defined in composed schemas (e.g., allOf) by injecting into the model's properties.
1067+
* Example:
1068+
* components:
1069+
* schemas:
1070+
* Dog:
1071+
* allOf:
1072+
* - $ref: '#/components/schemas/DogBase'
1073+
* - type: object
1074+
* additionalProperties:
1075+
* title: pet
1076+
* $ref: '#/components/schemas/Pet'
1077+
* In this case, the second allOf that defines a map with string keys and Pet values will be part of model's property.
1078+
*/
1079+
@Override
1080+
protected void addProperties(Map<String, Schema> properties, List<String> required, Schema schema, Set<Schema> visitedSchemas){
1081+
super.addProperties(properties, required, schema, visitedSchemas);
1082+
if(schema.getAdditionalProperties() != null) {
1083+
String addtionalPropertiesName = "default_map";
1084+
if(schema.getTitle() != null) {
1085+
addtionalPropertiesName = schema.getTitle();
1086+
} else {
1087+
Schema additionalProperties = ModelUtils.getAdditionalProperties(schema);
1088+
if (additionalProperties.getTitle() != null) {
1089+
addtionalPropertiesName = additionalProperties.getTitle();
1090+
} else if (additionalProperties.get$ref() != null) {
1091+
String ref = ModelUtils.getSimpleRef(additionalProperties.get$ref());
1092+
addtionalPropertiesName = toVarName(toModelName(ref));
1093+
}
1094+
}
1095+
properties.put(addtionalPropertiesName, schema);
1096+
}
1097+
}
10641098
}

Diff for: modules/openapi-generator/src/test/resources/3_0/protobuf/petstore-complex.yaml

+37-33
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,22 @@ externalDocs:
3434
components:
3535
schemas:
3636
Dog:
37-
type: object
38-
properties:
39-
bark:
40-
type: boolean
41-
breed:
42-
type: string
43-
enum: [ Dingo, Husky, Retriever, Shepherd ]
37+
allOf:
38+
- type: object
39+
properties:
40+
bark:
41+
type: boolean
42+
breed:
43+
type: string
44+
enum: [Dingo, Husky, Retriever, Shepherd]
45+
- type: object
46+
propertyNames:
47+
title: field
48+
type: string
49+
additionalProperties:
50+
title: pet
51+
$ref: '#/components/schemas/Pet'
52+
minProperties: 1
4453
Cat:
4554
type: object
4655
properties:
@@ -100,31 +109,26 @@ components:
100109
format: int64
101110
category:
102111
$ref: '#/components/schemas/Category'
103-
name:
104-
type: string
105-
photoUrls:
106-
type: array
107-
xml:
108-
name: photoUrl
109-
wrapped: true
110-
items:
112+
name:
111113
type: string
112-
tags:
113-
type: array
114-
xml:
115-
name: tag
116-
wrapped: true
117-
items:
114+
photoUrls:
118115
type: array
119-
additionalProperties:
120-
$ref: '#/components/schemas/Tag'
121-
status:
122-
type: string
123-
description: pet status in the store
124-
deprecated: true
125-
enum:
126-
- available
127-
- pending
128-
- sold
129-
xml:
130-
name: Pet
116+
xml:
117+
name: photoUrl
118+
wrapped: true
119+
items:
120+
type: string
121+
tags:
122+
type: array
123+
items:
124+
type: object
125+
additionalProperties:
126+
$ref: '#/components/schemas/Tag'
127+
status:
128+
type: string
129+
description: pet status in the store
130+
deprecated: true
131+
enum:
132+
- available
133+
- pending
134+
- sold

Diff for: samples/config/petstore/protobuf-schema-config-complex/.openapi-generator/FILES

+1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ models/pet.proto
66
models/string_array.proto
77
models/string_map.proto
88
models/tag.proto
9+
models/tag_map.proto
910
models/tag_name.proto
1011
services/default_service.proto

Diff for: samples/config/petstore/protobuf-schema-config-complex/models/dog.proto

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ syntax = "proto3";
1212

1313
package petstore;
1414

15+
import public "models/pet.proto";
1516

1617
message Dog {
1718

@@ -27,5 +28,7 @@ message Dog {
2728

2829
Breed breed = 2;
2930

31+
map<string, Pet> pet = 3;
32+
3033
}
3134

Diff for: samples/config/petstore/protobuf-schema-config-complex/models/pet.proto

+17
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,29 @@ syntax = "proto3";
1313
package petstore;
1414

1515
import public "models/category.proto";
16+
import public "models/tag_map.proto";
1617

1718
message Pet {
1819

1920
int64 id = 1;
2021

2122
Category category = 2;
2223

24+
string name = 3;
25+
26+
repeated string photo_urls = 4 [json_name="photoUrls"];
27+
28+
repeated TagMap tags = 5;
29+
30+
// pet status in the store
31+
enum Status {
32+
STATUS_UNSPECIFIED = 0;
33+
STATUS_AVAILABLE = 1;
34+
STATUS_PENDING = 2;
35+
STATUS_SOLD = 3;
36+
}
37+
38+
Status status = 6;
39+
2340
}
2441

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+
import public "models/tag.proto";
16+
17+
message TagMap {
18+
19+
map<string, Tag> tag_map = 1;
20+
21+
}
22+

0 commit comments

Comments
 (0)