11import type { OpenAPIV3 } from "openapi-types" ;
22import { traverse } from './utils/OpenApiTraverser' ;
33import isEqual from 'lodash.isequal' ;
4- import { compressMultipleUnderscores , isPrimitiveType , resolveObj , isReferenceObject , isEmptyObjectSchema } from './utils/helper' ;
4+ import { compressMultipleUnderscores , isPrimitiveType , resolveObj , isReferenceObject , isEmptyObjectSchema , is_simple_ref } from './utils/helper' ;
55import Logger from "./utils/logger" ;
66
77
@@ -33,7 +33,6 @@ export class SchemaModifier {
3333 this . handleOneOfConst ( schema , schemaName )
3434 this . collapseOrMergeOneOfArray ( schema )
3535 this . collapseOneOfObjectPropContainsTitleSchema ( schema )
36- this . removeArrayOfMapWrapper ( schema )
3736 } ,
3837 } ) ;
3938 const visit = new Set ( ) ;
@@ -47,6 +46,7 @@ export class SchemaModifier {
4746 if ( ! schema || isReferenceObject ( schema ) ) return ;
4847 this . simplifySingleMapSchema ( schema , visit )
4948 this . handleAdditionalPropertiesUndefined ( schema )
49+ this . markOneOfExtensions ( schema ) ;
5050 } ,
5151 } ) ;
5252 return this . root
@@ -475,4 +475,85 @@ export class SchemaModifier {
475475 }
476476 }
477477 }
478+
479+ /**
480+ * Marks schemas and properties with oneOf extensions.
481+ * Adds x-oneof-property to properties when schema has minProperties=1 and maxProperties=1.
482+ * Adds x-oneof-schema to schemas that have the min/max pattern AND to parent schemas
483+ * that contain nested schemas with the pattern.
484+ **/
485+ markOneOfExtensions ( schema : OpenAPIV3 . SchemaObject ) : void {
486+ const hasDirectPattern = schema . minProperties === 1 && schema . maxProperties === 1 ;
487+ const hasNestedPattern = this . hasNestedOneOfPattern ( schema ) ;
488+
489+ if ( ! hasDirectPattern && ! hasNestedPattern ) {
490+ return ;
491+ }
492+
493+ if ( hasDirectPattern ) {
494+ if ( schema . properties ) {
495+ for ( const propName in schema . properties ) {
496+ const prop = schema . properties [ propName ] as any ;
497+ if ( prop && typeof prop === 'object' ) {
498+ prop [ 'x-oneof-property' ] = true ;
499+
500+ if ( '$ref' in prop ) {
501+ this . markReferencedSchemaAsOneof ( prop . $ref ) ;
502+ }
503+ }
504+ }
505+ }
506+ ( schema as any ) [ 'x-oneof-schema' ] = true ;
507+ this . logger . info ( `Added x-oneof-property to properties and marked schema with x-oneof-schema` ) ;
508+ } else if ( hasNestedPattern ) {
509+ ( schema as any ) [ 'x-oneof-schema' ] = true ;
510+ this . logger . info ( `Marked parent schema with x-oneof-schema (contains nested oneOf pattern)` ) ;
511+ }
512+ }
513+
514+ /**
515+ * Recursively marks $ref schemas that are part of a oneOf, but stops when reaching a schema with actual content.
516+ **/
517+ private markReferencedSchemaAsOneof ( ref : string , visited : Set < string > = new Set ( ) ) : void {
518+ if ( visited . has ( ref ) ) {
519+ return ;
520+ }
521+ visited . add ( ref ) ;
522+
523+ const schemaName = ref . split ( '/' ) . pop ( ) ;
524+ if ( ! schemaName || ! this . root . components ?. schemas ) {
525+ return ;
526+ }
527+
528+ const schema = this . root . components . schemas [ schemaName ] ;
529+ if ( ! schema ) {
530+ return ;
531+ }
532+
533+ if ( is_simple_ref ( schema ) ) {
534+ ( schema as any ) [ 'x-oneof-property' ] = true ;
535+ this . markReferencedSchemaAsOneof ( ( schema as any ) . $ref , visited ) ;
536+ }
537+ }
538+
539+ /**
540+ * Checks if schema has nested items (in allOf/anyOf/oneOf) with oneOf pattern.
541+ **/
542+ private hasNestedOneOfPattern ( schema : OpenAPIV3 . SchemaObject ) : boolean {
543+ const composedKeys = [ 'allOf' , 'anyOf' , 'oneOf' ] as const ;
544+ for ( const key of composedKeys ) {
545+ const items = schema [ key ] ;
546+ if ( Array . isArray ( items ) ) {
547+ for ( const item of items ) {
548+ if ( item && typeof item === 'object' && ! ( '$ref' in item ) ) {
549+ const itemSchema = item as any ;
550+ if ( itemSchema . minProperties === 1 && itemSchema . maxProperties === 1 ) {
551+ return true ;
552+ }
553+ }
554+ }
555+ }
556+ }
557+ return false ;
558+ }
478559}
0 commit comments