Skip to content

Commit 155b200

Browse files
committed
Preprocessing - handle unamed additionalproperties
Signed-off-by: xil <fridalu66@gmail.com>
1 parent 016aeb9 commit 155b200

2 files changed

Lines changed: 99 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
44

55
## [Unreleased]
66
### Added
7-
- Preprocessing: change prefix from underscore_ to x_ and require map key ([#216](https://github.com/opensearch-project/opensearch-protobufs/pull/216))
7+
- Preprocessing: Change prefix from underscore_ to x_ and require map key ([#216](https://github.com/opensearch-project/opensearch-protobufs/pull/216))
88
- Add Cardinality and Missing aggregations. ([#245](https://github.com/opensearch-project/opensearch-protobufs/pull/245))
99
- Add terms aggregation protos ([#268](https://github.com/opensearch-project/opensearch-protobufs/pull/268))
10+
- Preprocessing: Handle unnamed additionalProperties.([#272](https://github.com/opensearch-project/opensearch-protobufs/pull/272))
1011

1112
### Changed
1213
- Update preprocessing for x-protobuf-excluded ([#266](https://github.com/opensearch-project/opensearch-protobufs/pull/266))

tools/proto-convert/src/SchemaModifier.ts

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ export class SchemaModifier {
1919
traverse(this.root, {
2020
onSchemaProperty: (schema) => {
2121
this.deduplicateEnumValue(schema)
22+
this.convertAdditionalPropertiesToProperty(schema, true)
2223
this.handleAdditionalPropertiesUndefined(schema)
2324
this.convertNullTypeToNullValue(schema)
2425
this.collapseOrMergeOneOfArray(schema)
2526
},
2627
onSchema: (schema, schemaName) => {
2728
if (!schema || isReferenceObject(schema)) return;
2829
this.deduplicateEnumValue(schema)
30+
this.convertAdditionalPropertiesToProperty(schema, false)
2931
this.handleAdditionalPropertiesUndefined(schema)
3032
this.convertNullTypeToNullValue(schema)
3133
this.handleOneOfConst(schema, schemaName)
@@ -36,11 +38,14 @@ export class SchemaModifier {
3638
const visit = new Set();
3739
traverse(this.root, {
3840
onSchemaProperty: (schema) => {
39-
this.simplifySingleMapSchema(schema, visit)
41+
this.simplifySingleMapSchema(schema, visit);
42+
this.handleAdditionalPropertiesUndefined(schema)
43+
4044
},
4145
onSchema: (schema) => {
4246
if (!schema || isReferenceObject(schema)) return;
4347
this.simplifySingleMapSchema(schema, visit)
48+
this.handleAdditionalPropertiesUndefined(schema)
4449
},
4550
});
4651
return this.root
@@ -70,7 +75,6 @@ export class SchemaModifier {
7075
break;
7176
}
7277
}
73-
7478
// if found string+const, collect all values
7579
if (hasStringWithConst) {
7680
for (const item of schema.oneOf) {
@@ -84,7 +88,6 @@ export class SchemaModifier {
8488
}
8589
}
8690
}
87-
8891
// Convert to enum
8992
delete schema.oneOf;
9093
schema.type = 'string';
@@ -343,4 +346,95 @@ export class SchemaModifier {
343346
(schema as any).type = 'NullValue';
344347
}
345348
}
349+
350+
/**
351+
* Converts additionalProperties with a title into a named property.
352+
*
353+
* @param schema - The schema to process
354+
* @param isNestedProperty - If true, skip conversion (nested properties already have context)
355+
*
356+
* Example:
357+
* Input:
358+
* {
359+
* type: "object",
360+
* properties: { distance: { type: "string" } },
361+
* propertyNames: { title: "field", type: "string" },
362+
* additionalProperties: {
363+
* title: "location",
364+
* $ref: "#/components/schemas/GeoLocation"
365+
* },
366+
* minProperties: 2
367+
* }
368+
*
369+
* Output:
370+
* {
371+
* type: "object",
372+
* properties: {
373+
* distance: { type: "string" },
374+
* location: {
375+
* type: "object",
376+
* additionalProperties: {
377+
* $ref: "#/components/schemas/GeoLocation"
378+
* }
379+
* }
380+
* },
381+
* minProperties: 2
382+
* }
383+
**/
384+
convertAdditionalPropertiesToProperty(schema: OpenAPIV3.SchemaObject, isNestedProperty: boolean = false): void {
385+
if (!schema.additionalProperties || typeof schema.additionalProperties !== 'object') {
386+
return;
387+
}
388+
389+
const additionalProps = schema.additionalProperties as any;
390+
391+
if (isNestedProperty) {
392+
return;
393+
}
394+
395+
if (!additionalProps.title || typeof additionalProps.title !== 'string') {
396+
return;
397+
}
398+
399+
const propertyName = additionalProps.title;
400+
401+
if (!schema.properties) {
402+
schema.properties = {};
403+
}
404+
405+
if (schema.properties[propertyName]) {
406+
this.logger.warn(`Property '${propertyName}' already exists in schema, skipping additionalProperties conversion`);
407+
return;
408+
}
409+
410+
const innerAdditionalProps: any = {};
411+
for (const key in additionalProps) {
412+
if (key !== 'title') {
413+
innerAdditionalProps[key] = additionalProps[key];
414+
}
415+
}
416+
const hasSchemaDefinition = Boolean(
417+
innerAdditionalProps.type ||
418+
innerAdditionalProps.$ref ||
419+
innerAdditionalProps.properties ||
420+
innerAdditionalProps.enum ||
421+
innerAdditionalProps.items ||
422+
innerAdditionalProps.allOf ||
423+
innerAdditionalProps.anyOf ||
424+
innerAdditionalProps.oneOf
425+
);
426+
427+
schema.properties[propertyName] = {
428+
type: 'object',
429+
additionalProperties: hasSchemaDefinition ? innerAdditionalProps : true
430+
};
431+
432+
delete schema.additionalProperties;
433+
434+
if ('propertyNames' in schema) {
435+
delete schema.propertyNames;
436+
}
437+
438+
this.logger.info(`Converted additionalProperties to named property '${propertyName}' with type: object`);
439+
}
346440
}

0 commit comments

Comments
 (0)