Skip to content

Commit 75d5467

Browse files
committed
Add old behavior
Signed-off-by: xil <fridalu66@gmail.com>
1 parent adf4bc5 commit 75d5467

File tree

6 files changed

+993
-87
lines changed

6 files changed

+993
-87
lines changed

DEVELOPER_GUIDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ npm config set registry https://registry.npmjs.org/
234234
3. **Generate Protobuf**
235235

236236
```bash
237-
export OPENAPI_GENERATOR_VERSION=7.19.0
238-
npx @openapitools/openapi-generator-cli generate \
237+
export OPENAPI_GENERATOR_VERSION=7.19.0
238+
npx @openapitools/openapi-generator-cli generate \
239239
-c tools/proto-convert/src/config/protobuf-generator-config.yaml
240240
```
241241

tools/proto-convert/src/SchemaModifier.ts

Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import logger from "./utils/logger";
77

88
const DEFAULT_MAP_KEY = 'field' // default key for simplified additionalProperties
99
const DEFAULT_MAP_VALUE = 'value' // default value for simplified additionalProperties
10+
const QUERY_CONTAINER_SCHEMA_NAME = 'QueryContainer' // schema name that requires special inline handling.
1011

1112
export class SchemaModifier {
1213
root: OpenAPIV3.Document;
@@ -37,8 +38,8 @@ export class SchemaModifier {
3738
});
3839
const visit = new Set();
3940
traverse(this.root, {
40-
onSchemaProperty: (schema) => {
41-
this.simplifySingleMapSchema(schema, visit);
41+
onSchemaProperty: (schema, propertyName, parentSchemaName) => {
42+
this.simplifySingleMapSchema(schema, visit, parentSchemaName);
4243
this.handleAdditionalPropertiesUndefined(schema)
4344

4445
},
@@ -279,7 +280,6 @@ export class SchemaModifier {
279280

280281
/**
281282
* Extracts type name from $ref or title
282-
* Note: Schema names have already been sanitized by Sanitizer, so '___' prefixes are already removed
283283
**/
284284
private getTypeName(schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject): string | null {
285285
if ('$ref' in schema) {
@@ -293,58 +293,115 @@ export class SchemaModifier {
293293
}
294294

295295
/**
296-
* Transforms SchemaObject that single-key maps (`minProperties = 1` and `maxProperties = 1`) into a new wrapper schema.
297-
*
296+
* Transforms SchemaObject that single-key maps (`minProperties = 1` and `maxProperties = 1`) into a $ref to a new map schema.
298297
*
299-
* Example:
300-
* Input:
298+
* Input (items of DecayFunction):
301299
* {
302300
* type: "object",
301+
* propertyNames: { title: "field", type: "string" },
303302
* additionalProperties: {
304-
* $ref: "#/components/schemas/SortOrder"
303+
* title: "placement",
304+
* $ref: "#/components/schemas/DecayPlacement"
305305
* },
306306
* minProperties: 1,
307307
* maxProperties: 1,
308308
* }
309309
*
310310
* Output:
311+
* - Replaces with: { $ref: "#/components/schemas/DecayPlacementSingleMap" }
312+
* - Creates new schema DecayPlacementSingleMap:
311313
* {
312314
* type: "object",
313-
* title: "SortOrderMap",
314315
* properties: {
315316
* field: { type: "string" },
316-
* sort_order: { $ref: "#/components/schemas/SortOrder" }
317+
* decay_placement: {
318+
* title: "placement",
319+
* $ref: "#/components/schemas/DecayPlacement"
320+
* }
317321
* },
318-
* required: ["field", "sort_order"]
322+
* required: ["field", "decay_placement"]
319323
* }
320324
*
321325
**/
322-
simplifySingleMapSchema(schema: OpenAPIV3.SchemaObject, visit: Set<any>): void {
326+
simplifySingleMapSchema(schema: OpenAPIV3.SchemaObject, visit: Set<any>, parentSchemaName?: string): void {
323327
if (schema.type === 'object' && typeof schema.additionalProperties === 'object' &&
324328
!Array.isArray(schema.additionalProperties) && schema.minProperties === 1 && schema.maxProperties === 1){
325329

330+
// Check if this is a QueryContainer property
331+
// If so, use the old inline behavior to avoid breaking QueryContainer structure
332+
// TODO: Remove this special case in next major release and use SingleMap wrapper for all schemas
333+
if (parentSchemaName === QUERY_CONTAINER_SCHEMA_NAME) {
334+
logger.info(`Using inline modification for ${QUERY_CONTAINER_SCHEMA_NAME} property (legacy behavior)`+JSON.stringify(schema));
335+
// Old behavior: inline modification
336+
const reconstructAdditionalPropertySchema = this.reconstructAdditionalPropertySchema(schema.additionalProperties, visit);
337+
Object.assign(schema, reconstructAdditionalPropertySchema);
338+
339+
delete schema.additionalProperties;
340+
delete schema.minProperties;
341+
delete schema.maxProperties;
342+
delete schema.type;
343+
if ('propertyNames' in schema) {
344+
delete schema.propertyNames;
345+
}
346+
return;
347+
}
348+
349+
// New behavior: create intermediate SingleMap schema
326350
const valueSchema = schema.additionalProperties;
327-
const typeName = this.getTypeName(valueSchema) || 'Value'; // Use 'Value' as fallback
328-
329-
// Create new wrapper schema
330-
// Avoid collision with the reserved 'field' key property name
331-
const rawPropertyName = toSnakeCase(typeName);
332-
const valuePropertyName = rawPropertyName === 'field' ? `${rawPropertyName}_value` : rawPropertyName;
333-
const wrapperTitle = schema.title || `${typeName}Map`;
334-
335-
schema.title = wrapperTitle;
336-
schema.properties = {
337-
field: { type: 'string' as const },
338-
[valuePropertyName]: valueSchema
351+
const typeName = this.getTypeName(valueSchema) || 'Value';
352+
353+
// Extract field property name and schema from propertyNames
354+
let fieldPropertyName = 'field';
355+
let fieldPropertySchema: any = { type: 'string' as const };
356+
357+
if ((schema as any).propertyNames) {
358+
const propertyNames = (schema as any).propertyNames;
359+
360+
// If propertyNames has a title, use it as the property name
361+
if (propertyNames.title && typeof propertyNames.title === 'string') {
362+
fieldPropertyName = propertyNames.title;
363+
364+
fieldPropertySchema = { ...propertyNames };
365+
delete fieldPropertySchema.title;
366+
} else {
367+
// Use propertyNames as-is for the field schema
368+
fieldPropertySchema = propertyNames;
369+
}
370+
}
371+
372+
// Create new map schema name
373+
const mapSchemaName = `${typeName}SingleMap`;
374+
375+
// Create the new map schema
376+
const newMapSchema: OpenAPIV3.SchemaObject = {
377+
type: 'object',
378+
properties: {
379+
[fieldPropertyName]: fieldPropertySchema,
380+
[toSnakeCase(typeName)]: valueSchema
381+
},
382+
required: [fieldPropertyName, toSnakeCase(typeName)]
339383
};
340-
schema.required = ['field', valuePropertyName];
341384

385+
// Add to components.schemas if not already exists
386+
if (!this.root.components) {
387+
this.root.components = {};
388+
}
389+
if (!this.root.components.schemas) {
390+
this.root.components.schemas = {};
391+
}
392+
if (!this.root.components.schemas[mapSchemaName]) {
393+
this.root.components.schemas[mapSchemaName] = newMapSchema;
394+
}
395+
396+
// Replace current schema with $ref to the new map schema
397+
// Only delete the properties we don't need, keep vendor extensions (x-*)
398+
delete schema.type;
342399
delete schema.additionalProperties;
343400
delete schema.minProperties;
344401
delete schema.maxProperties;
345-
if ('propertyNames' in schema) {
346-
delete schema.propertyNames;
347-
}
402+
delete (schema as any).propertyNames;
403+
404+
(schema as any).$ref = `#/components/schemas/${mapSchemaName}`;
348405
}
349406
}
350407

tools/proto-convert/src/config/spec-filter.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ excluded_schemas:
1818
- CombinedFieldsQuery
1919
- DistanceFeatureQuery
2020
- GeoPolygonQuery
21+
- GeoShapeQuery
2122
- HasChildQuery
2223
- HasParentQuery
2324
- MoreLikeThisQuery

tools/proto-convert/src/utils/OpenApiTraverser.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export type SchemaVisitorSet = {
55
onResponseSchema?: (schema: OpenAPIV3.SchemaObject, name: string) => void;
66
onRequestSchema?: (schema: OpenAPIV3.SchemaObject, name: string) => void;
77
onParameter?: (param: OpenAPIV3.ParameterObject, name: string) => void;
8-
onSchemaProperty?: (schema: OpenAPIV3.SchemaObject, propertyName: string) => void;
8+
onSchemaProperty?: (schema: OpenAPIV3.SchemaObject, propertyName: string, parentSchemaName?: string) => void;
99
};
1010

1111
/**
@@ -24,7 +24,7 @@ export function traverse(
2424
const schema = components.schemas[schemaName];
2525
visitors.onSchema?.(schema, schemaName);
2626
if (!('$ref' in schema)) {
27-
traverseSchema(schema, visitors);
27+
traverseSchema(schema, visitors, schemaName);
2828
}
2929
}
3030
}
@@ -79,7 +79,8 @@ export function traverse(
7979
*/
8080
export function traverseSchema(
8181
schema: OpenAPIV3.SchemaObject,
82-
visitors: SchemaVisitorSet
82+
visitors: SchemaVisitorSet,
83+
parentSchemaName?: string
8384
): void {
8485
if (!schema) return;
8586

@@ -88,21 +89,23 @@ export function traverseSchema(
8889
for (const propName in schema.properties) {
8990
const propSchema = schema.properties[propName];
9091
if (!('$ref' in propSchema)) {
91-
visitors.onSchemaProperty?.(propSchema, propName);
92-
traverseSchema(propSchema, visitors); // recurse into nested properties
92+
visitors.onSchemaProperty?.(propSchema, propName, parentSchemaName);
93+
traverseSchema(propSchema, visitors, parentSchemaName); // recurse into nested properties
9394
}
9495
}
9596
}
9697

9798
// Visit items (for array types)
9899
if (schema.type === 'array' && schema.items && typeof schema.items === 'object' && !('$ref' in schema.items)) {
99-
traverseSchema(schema.items as OpenAPIV3.SchemaObject, visitors);
100+
const itemsSchema = schema.items as OpenAPIV3.SchemaObject;
101+
visitors.onSchemaProperty?.(itemsSchema, 'items', parentSchemaName);
102+
traverseSchema(itemsSchema, visitors, parentSchemaName);
100103
}
101104

102105
// Visit additionalProperties
103106
if (schema.additionalProperties && typeof schema.additionalProperties === 'object' && !('$ref' in schema.additionalProperties)
104107
) {
105-
traverseSchema(schema.additionalProperties as OpenAPIV3.SchemaObject, visitors);
108+
traverseSchema(schema.additionalProperties as OpenAPIV3.SchemaObject, visitors, parentSchemaName);
106109
}
107110

108111
// Visit composed schemas
@@ -113,7 +116,7 @@ export function traverseSchema(
113116
for (const sub of subschemas) {
114117
if (!('$ref' in sub)) {
115118
visitors.onSchema?.(sub, `${key}`);
116-
traverseSchema(sub, visitors);
119+
traverseSchema(sub, visitors, parentSchemaName);
117120
}
118121
}
119122
}

0 commit comments

Comments
 (0)