fork of hey-api/openapi-ts because I need some additional things

Merge pull request #3385 from SipanP/fix/discriminator-non-string-type-support

fix(shared): support non-string discriminator property types

authored by

Lubos and committed by
GitHub
26744e1f bb53a982

+792 -26
+5
.changeset/wicked-rings-march.md
··· 1 + --- 2 + "@hey-api/shared": patch 3 + --- 4 + 5 + Support non-string discriminator property types (boolean, integer, number)
+7
packages/openapi-ts-tests/main/test/3.0.x.test.ts
··· 219 219 }, 220 220 { 221 221 config: createConfig({ 222 + input: 'discriminator-non-string.yaml', 223 + output: 'discriminator-non-string', 224 + }), 225 + description: 'handles non-string discriminator property types', 226 + }, 227 + { 228 + config: createConfig({ 222 229 input: 'enum-escape.json', 223 230 output: 'enum-escape', 224 231 }),
+7
packages/openapi-ts-tests/main/test/3.1.x.test.ts
··· 238 238 }, 239 239 { 240 240 config: createConfig({ 241 + input: 'discriminator-non-string.yaml', 242 + output: 'discriminator-non-string', 243 + }), 244 + description: 'handles non-string discriminator property types', 245 + }, 246 + { 247 + config: createConfig({ 241 248 input: 'discriminator-one-of-read-write.yaml', 242 249 output: 'discriminator-one-of-read-write', 243 250 }),
+3
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/discriminator-non-string/index.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type { AutoConfig, BooleanAnyOf, BooleanOneOf, ClientOptions, CustomConfig, IntegerAllOfBase, IntegerAllOfChildA, IntegerAllOfChildB, IntegerOneOf, NumberOneOf, TypeOne, TypeTwo, VersionAlpha, VersionBeta } from './types.gen';
+73
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/discriminator-non-string/types.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type ClientOptions = { 4 + baseUrl: `${string}://${string}` | (string & {}); 5 + }; 6 + 7 + export type BooleanOneOf = ({ 8 + use_custom: false; 9 + } & AutoConfig) | ({ 10 + use_custom: true; 11 + } & CustomConfig); 12 + 13 + export type AutoConfig = { 14 + use_custom: boolean; 15 + auto_setting: string; 16 + }; 17 + 18 + export type CustomConfig = { 19 + use_custom: boolean; 20 + custom_value: number; 21 + }; 22 + 23 + export type BooleanAnyOf = ({ 24 + use_custom?: false; 25 + } & AutoConfig) | ({ 26 + use_custom?: true; 27 + } & CustomConfig); 28 + 29 + export type IntegerOneOf = ({ 30 + type_id: 1; 31 + } & TypeOne) | ({ 32 + type_id: 2; 33 + } & TypeTwo); 34 + 35 + export type TypeOne = { 36 + type_id: number; 37 + one_data: string; 38 + }; 39 + 40 + export type TypeTwo = { 41 + type_id: number; 42 + two_data: string; 43 + }; 44 + 45 + export type NumberOneOf = ({ 46 + version: 1; 47 + } & VersionAlpha) | ({ 48 + version: 2.5; 49 + } & VersionBeta); 50 + 51 + export type VersionAlpha = { 52 + version: number; 53 + alpha_field: string; 54 + }; 55 + 56 + export type VersionBeta = { 57 + version: number; 58 + beta_field: string; 59 + }; 60 + 61 + export type IntegerAllOfBase = { 62 + kind: number; 63 + }; 64 + 65 + export type IntegerAllOfChildA = Omit<IntegerAllOfBase, 'kind'> & { 66 + child_a_field: string; 67 + kind: 1; 68 + }; 69 + 70 + export type IntegerAllOfChildB = Omit<IntegerAllOfBase, 'kind'> & { 71 + child_b_field: string; 72 + kind: 2; 73 + };
+3
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/discriminator-non-string/index.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type { AutoConfig, BooleanAnyOf, BooleanOneOf, ClientOptions, CustomConfig, IntegerAllOfBase, IntegerAllOfChildA, IntegerAllOfChildB, IntegerOneOf, NullableIntegerOneOf, NullableVariantX, NullableVariantY, NumberOneOf, TypeOne, TypeTwo, VersionAlpha, VersionBeta } from './types.gen';
+89
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/discriminator-non-string/types.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type ClientOptions = { 4 + baseUrl: `${string}://${string}` | (string & {}); 5 + }; 6 + 7 + export type BooleanOneOf = ({ 8 + use_custom: false; 9 + } & AutoConfig) | ({ 10 + use_custom: true; 11 + } & CustomConfig); 12 + 13 + export type AutoConfig = { 14 + use_custom: false; 15 + auto_setting: string; 16 + }; 17 + 18 + export type CustomConfig = { 19 + use_custom: true; 20 + custom_value: number; 21 + }; 22 + 23 + export type BooleanAnyOf = ({ 24 + use_custom?: false; 25 + } & AutoConfig) | ({ 26 + use_custom?: true; 27 + } & CustomConfig); 28 + 29 + export type IntegerOneOf = ({ 30 + type_id: 1; 31 + } & TypeOne) | ({ 32 + type_id: 2; 33 + } & TypeTwo); 34 + 35 + export type TypeOne = { 36 + type_id: 1; 37 + one_data: string; 38 + }; 39 + 40 + export type TypeTwo = { 41 + type_id: 2; 42 + two_data: string; 43 + }; 44 + 45 + export type NumberOneOf = ({ 46 + version: 1; 47 + } & VersionAlpha) | ({ 48 + version: 2.5; 49 + } & VersionBeta); 50 + 51 + export type VersionAlpha = { 52 + version: 1; 53 + alpha_field: string; 54 + }; 55 + 56 + export type VersionBeta = { 57 + version: 2.5; 58 + beta_field: string; 59 + }; 60 + 61 + export type IntegerAllOfBase = { 62 + kind: number; 63 + }; 64 + 65 + export type IntegerAllOfChildA = Omit<IntegerAllOfBase, 'kind'> & { 66 + child_a_field: string; 67 + kind: 1; 68 + }; 69 + 70 + export type IntegerAllOfChildB = Omit<IntegerAllOfBase, 'kind'> & { 71 + child_b_field: string; 72 + kind: 2; 73 + }; 74 + 75 + export type NullableIntegerOneOf = ({ 76 + tag: 10; 77 + } & NullableVariantX) | ({ 78 + tag: 20; 79 + } & NullableVariantY); 80 + 81 + export type NullableVariantX = { 82 + tag: 10 | null; 83 + x_data: string; 84 + }; 85 + 86 + export type NullableVariantY = { 87 + tag: 20 | null; 88 + y_data: string; 89 + };
+85 -13
packages/shared/src/openApi/3.0.x/parser/schema.ts
··· 6 6 SchemaType, 7 7 SchemaWithRequired, 8 8 } from '../../../openApi/shared/types/schema'; 9 - import { discriminatorValues } from '../../../openApi/shared/utils/discriminator'; 9 + import { 10 + convertDiscriminatorValue, 11 + type DiscriminatorPropertyType, 12 + discriminatorValues, 13 + } from '../../../openApi/shared/utils/discriminator'; 10 14 import { isTopLevelComponent, refToName } from '../../../utils/ref'; 11 15 import type { ReferenceObject, SchemaObject } from '../types/spec'; 12 16 ··· 25 29 } 26 30 27 31 return; 32 + }; 33 + 34 + /** 35 + * Finds the type of a discriminator property by looking it up in the provided schemas. 36 + * Searches through properties and allOf chains to find the property definition. 37 + */ 38 + const findDiscriminatorPropertyType = ({ 39 + context, 40 + propertyName, 41 + schemas, 42 + }: { 43 + context: Context; 44 + propertyName: string; 45 + schemas: ReadonlyArray<SchemaObject | ReferenceObject>; 46 + }): DiscriminatorPropertyType => { 47 + for (const schema of schemas) { 48 + const resolved = '$ref' in schema ? context.resolveRef<SchemaObject>(schema.$ref) : schema; 49 + 50 + // Check direct properties 51 + const property = resolved.properties?.[propertyName]; 52 + if (property) { 53 + const resolvedProperty = 54 + '$ref' in property ? context.resolveRef<SchemaObject>(property.$ref) : property; 55 + if ( 56 + resolvedProperty.type === 'boolean' || 57 + resolvedProperty.type === 'integer' || 58 + resolvedProperty.type === 'number' 59 + ) { 60 + return resolvedProperty.type; 61 + } 62 + } 63 + 64 + // Check allOf chains 65 + if (resolved.allOf) { 66 + const foundType = findDiscriminatorPropertyType({ 67 + context, 68 + propertyName, 69 + schemas: resolved.allOf, 70 + }); 71 + if (foundType !== 'string') { 72 + return foundType; 73 + } 74 + } 75 + } 76 + 77 + return 'string'; 28 78 }; 29 79 30 80 /** ··· 482 532 // Use allValues if we found children, otherwise use the original values 483 533 const finalValues = allValues.length > 0 ? allValues : values; 484 534 485 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = finalValues.map((value) => ({ 486 - const: value, 487 - type: 'string', 488 - })); 535 + // Detect the actual type of the discriminator property 536 + const propertyType = findDiscriminatorPropertyType({ 537 + context, 538 + propertyName: discriminator.propertyName, 539 + schemas: compositionSchemas, 540 + }); 541 + 542 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = finalValues.map((value) => 543 + convertDiscriminatorValue(value, propertyType), 544 + ); 489 545 490 546 const discriminatorProperty: IR.SchemaObject = 491 547 valueSchemas.length > 1 ··· 674 730 675 731 const compositionSchemas = schema.anyOf; 676 732 733 + const discriminatorPropertyType = schema.discriminator 734 + ? findDiscriminatorPropertyType({ 735 + context, 736 + propertyName: schema.discriminator.propertyName, 737 + schemas: compositionSchemas, 738 + }) 739 + : undefined; 740 + 677 741 for (const compositionSchema of compositionSchemas) { 678 742 let irCompositionSchema = schemaToIrSchema({ 679 743 context, ··· 684 748 // `$ref` should be defined with discriminators 685 749 if (schema.discriminator && irCompositionSchema.$ref != null) { 686 750 const values = discriminatorValues(irCompositionSchema.$ref, schema.discriminator.mapping); 687 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => ({ 688 - const: value, 689 - type: 'string', 690 - })); 751 + 752 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => 753 + convertDiscriminatorValue(value, discriminatorPropertyType!), 754 + ); 691 755 const irDiscriminatorSchema: IR.SchemaObject = { 692 756 properties: { 693 757 [schema.discriminator.propertyName]: ··· 834 898 835 899 const compositionSchemas = schema.oneOf; 836 900 901 + const discriminatorPropertyType = schema.discriminator 902 + ? findDiscriminatorPropertyType({ 903 + context, 904 + propertyName: schema.discriminator.propertyName, 905 + schemas: compositionSchemas, 906 + }) 907 + : undefined; 908 + 837 909 for (const compositionSchema of compositionSchemas) { 838 910 let irCompositionSchema = schemaToIrSchema({ 839 911 context, ··· 844 916 // `$ref` should be defined with discriminators 845 917 if (schema.discriminator && irCompositionSchema.$ref != null) { 846 918 const values = discriminatorValues(irCompositionSchema.$ref, schema.discriminator.mapping); 847 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => ({ 848 - const: value, 849 - type: 'string', 850 - })); 919 + 920 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => 921 + convertDiscriminatorValue(value, discriminatorPropertyType!), 922 + ); 851 923 const irDiscriminatorSchema: IR.SchemaObject = { 852 924 properties: { 853 925 [schema.discriminator.propertyName]:
+93 -13
packages/shared/src/openApi/3.1.x/parser/schema.ts
··· 6 6 SchemaType, 7 7 SchemaWithRequired, 8 8 } from '../../../openApi/shared/types/schema'; 9 - import { discriminatorValues } from '../../../openApi/shared/utils/discriminator'; 9 + import { 10 + convertDiscriminatorValue, 11 + type DiscriminatorPropertyType, 12 + discriminatorValues, 13 + } from '../../../openApi/shared/utils/discriminator'; 10 14 import { isTopLevelComponent, refToName } from '../../../utils/ref'; 11 15 import type { SchemaObject } from '../types/spec'; 12 16 ··· 29 33 } 30 34 31 35 return []; 36 + }; 37 + 38 + /** 39 + * Finds the type of a discriminator property by looking it up in the provided schemas. 40 + * Searches through properties and allOf chains to find the property definition. 41 + */ 42 + const findDiscriminatorPropertyType = ({ 43 + context, 44 + propertyName, 45 + schemas, 46 + }: { 47 + context: Context; 48 + propertyName: string; 49 + schemas: ReadonlyArray<SchemaObject>; 50 + }): DiscriminatorPropertyType => { 51 + for (const schema of schemas) { 52 + const resolved = schema.$ref ? context.resolveRef<SchemaObject>(schema.$ref) : schema; 53 + 54 + // Check direct properties 55 + const property = resolved.properties?.[propertyName]; 56 + if (property === true) { 57 + continue; 58 + } 59 + if (property) { 60 + const resolvedProperty = property.$ref 61 + ? context.resolveRef<SchemaObject>(property.$ref) 62 + : property; 63 + // Handle both single type and array of types (3.1.x supports type arrays) 64 + const propertyTypes = Array.isArray(resolvedProperty.type) 65 + ? resolvedProperty.type 66 + : resolvedProperty.type 67 + ? [resolvedProperty.type] 68 + : []; 69 + for (const propType of propertyTypes) { 70 + if (propType === 'boolean' || propType === 'integer' || propType === 'number') { 71 + return propType; 72 + } 73 + } 74 + } 75 + 76 + // Check allOf chains 77 + if (resolved.allOf) { 78 + const foundType = findDiscriminatorPropertyType({ 79 + context, 80 + propertyName, 81 + schemas: resolved.allOf, 82 + }); 83 + if (foundType !== 'string') { 84 + return foundType; 85 + } 86 + } 87 + } 88 + 89 + return 'string'; 32 90 }; 33 91 34 92 /** ··· 564 622 // Use allValues if we found children, otherwise use the original values 565 623 const finalValues = allValues.length > 0 ? allValues : values; 566 624 567 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = finalValues.map((value) => ({ 568 - const: value, 569 - type: 'string', 570 - })); 625 + // Detect the actual type of the discriminator property 626 + const propertyType = findDiscriminatorPropertyType({ 627 + context, 628 + propertyName: discriminator.propertyName, 629 + schemas: compositionSchemas, 630 + }); 631 + 632 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = finalValues.map((value) => 633 + convertDiscriminatorValue(value, propertyType), 634 + ); 571 635 572 636 const discriminatorProperty: IR.SchemaObject = 573 637 valueSchemas.length > 1 ··· 743 807 744 808 const compositionSchemas = schema.anyOf; 745 809 810 + const discriminatorPropertyType = schema.discriminator 811 + ? findDiscriminatorPropertyType({ 812 + context, 813 + propertyName: schema.discriminator.propertyName, 814 + schemas: compositionSchemas, 815 + }) 816 + : undefined; 817 + 746 818 for (const compositionSchema of compositionSchemas) { 747 819 let irCompositionSchema = schemaToIrSchema({ 748 820 context, ··· 753 825 // `$ref` should be defined with discriminators 754 826 if (schema.discriminator && irCompositionSchema.$ref != null) { 755 827 const values = discriminatorValues(irCompositionSchema.$ref, schema.discriminator.mapping); 756 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => ({ 757 - const: value, 758 - type: 'string', 759 - })); 828 + 829 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => 830 + convertDiscriminatorValue(value, discriminatorPropertyType!), 831 + ); 760 832 const irDiscriminatorSchema: IR.SchemaObject = { 761 833 properties: { 762 834 [schema.discriminator.propertyName]: ··· 894 966 895 967 const compositionSchemas = schema.oneOf; 896 968 969 + const discriminatorPropertyType = schema.discriminator 970 + ? findDiscriminatorPropertyType({ 971 + context, 972 + propertyName: schema.discriminator.propertyName, 973 + schemas: compositionSchemas, 974 + }) 975 + : undefined; 976 + 897 977 for (const compositionSchema of compositionSchemas) { 898 978 let irCompositionSchema = schemaToIrSchema({ 899 979 context, ··· 904 984 // `$ref` should be defined with discriminators 905 985 if (schema.discriminator && irCompositionSchema.$ref != null) { 906 986 const values = discriminatorValues(irCompositionSchema.$ref, schema.discriminator.mapping); 907 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => ({ 908 - const: value, 909 - type: 'string', 910 - })); 987 + 988 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => 989 + convertDiscriminatorValue(value, discriminatorPropertyType!), 990 + ); 911 991 const irDiscriminatorSchema: IR.SchemaObject = { 912 992 properties: { 913 993 [schema.discriminator.propertyName]:
+78
packages/shared/src/openApi/shared/utils/discriminator.ts
··· 1 + import type { IR } from '../../../ir/types'; 1 2 import { refToName } from '../../../utils/ref'; 3 + 4 + /** 5 + * Supported types for discriminator properties. 6 + */ 7 + export type DiscriminatorPropertyType = 'boolean' | 'integer' | 'number' | 'string'; 8 + 9 + /** 10 + * Converts a string discriminator mapping value to the appropriate type based on 11 + * the actual property type in the schema. 12 + * 13 + * OpenAPI discriminator mappings always use string keys, but the actual discriminator 14 + * property may be a boolean, number, or integer. This function converts the string 15 + * key to the correct runtime value and IR type. 16 + */ 17 + export const convertDiscriminatorValue = ( 18 + value: string, 19 + propertyType: DiscriminatorPropertyType, 20 + ): { const: IR.SchemaObject['const']; type: IR.SchemaObject['type'] } => { 21 + switch (propertyType) { 22 + case 'boolean': { 23 + const lowerValue = value.toLowerCase(); 24 + if (lowerValue !== 'true' && lowerValue !== 'false') { 25 + console.warn( 26 + '🚨', 27 + `non-boolean discriminator mapping value "${value}" for boolean property, falling back to string`, 28 + ); 29 + return { 30 + const: value, 31 + type: 'string', 32 + }; 33 + } 34 + return { 35 + const: lowerValue === 'true', 36 + type: 'boolean', 37 + }; 38 + } 39 + case 'integer': { 40 + const parsed = parseInt(value, 10); 41 + if (Number.isNaN(parsed)) { 42 + console.warn( 43 + '🚨', 44 + `non-numeric discriminator mapping value "${value}" for integer property, falling back to string`, 45 + ); 46 + return { 47 + const: value, 48 + type: 'string', 49 + }; 50 + } 51 + return { 52 + const: parsed, 53 + type: 'integer', 54 + }; 55 + } 56 + case 'number': { 57 + const parsed = parseFloat(value); 58 + if (Number.isNaN(parsed)) { 59 + console.warn( 60 + '🚨', 61 + `non-numeric discriminator mapping value "${value}" for number property, falling back to string`, 62 + ); 63 + return { 64 + const: value, 65 + type: 'string', 66 + }; 67 + } 68 + return { 69 + const: parsed, 70 + type: 'number', 71 + }; 72 + } 73 + default: 74 + return { 75 + const: value, 76 + type: 'string', 77 + }; 78 + } 79 + }; 2 80 3 81 export const discriminatorValues = ( 4 82 $ref: string,
+155
specs/3.0.x/discriminator-non-string.yaml
··· 1 + openapi: 3.0.3 2 + info: 3 + title: Non-string discriminator test 4 + version: 1 5 + components: 6 + schemas: 7 + # --- Boolean discriminator (oneOf) --- 8 + BooleanOneOf: 9 + oneOf: 10 + - $ref: '#/components/schemas/AutoConfig' 11 + - $ref: '#/components/schemas/CustomConfig' 12 + discriminator: 13 + propertyName: use_custom 14 + mapping: 15 + 'False': '#/components/schemas/AutoConfig' 16 + 'True': '#/components/schemas/CustomConfig' 17 + 18 + AutoConfig: 19 + type: object 20 + required: 21 + - use_custom 22 + - auto_setting 23 + properties: 24 + use_custom: 25 + type: boolean 26 + const: false 27 + auto_setting: 28 + type: string 29 + 30 + CustomConfig: 31 + type: object 32 + required: 33 + - use_custom 34 + - custom_value 35 + properties: 36 + use_custom: 37 + type: boolean 38 + const: true 39 + custom_value: 40 + type: integer 41 + 42 + # --- Boolean discriminator (anyOf) --- 43 + BooleanAnyOf: 44 + anyOf: 45 + - $ref: '#/components/schemas/AutoConfig' 46 + - $ref: '#/components/schemas/CustomConfig' 47 + discriminator: 48 + propertyName: use_custom 49 + mapping: 50 + 'False': '#/components/schemas/AutoConfig' 51 + 'True': '#/components/schemas/CustomConfig' 52 + 53 + # --- Integer discriminator (oneOf) --- 54 + IntegerOneOf: 55 + oneOf: 56 + - $ref: '#/components/schemas/TypeOne' 57 + - $ref: '#/components/schemas/TypeTwo' 58 + discriminator: 59 + propertyName: type_id 60 + mapping: 61 + '1': '#/components/schemas/TypeOne' 62 + '2': '#/components/schemas/TypeTwo' 63 + 64 + TypeOne: 65 + type: object 66 + required: 67 + - type_id 68 + - one_data 69 + properties: 70 + type_id: 71 + type: integer 72 + const: 1 73 + one_data: 74 + type: string 75 + 76 + TypeTwo: 77 + type: object 78 + required: 79 + - type_id 80 + - two_data 81 + properties: 82 + type_id: 83 + type: integer 84 + const: 2 85 + two_data: 86 + type: string 87 + 88 + # --- Number (float) discriminator (oneOf) --- 89 + NumberOneOf: 90 + oneOf: 91 + - $ref: '#/components/schemas/VersionAlpha' 92 + - $ref: '#/components/schemas/VersionBeta' 93 + discriminator: 94 + propertyName: version 95 + mapping: 96 + '1.0': '#/components/schemas/VersionAlpha' 97 + '2.5': '#/components/schemas/VersionBeta' 98 + 99 + VersionAlpha: 100 + type: object 101 + required: 102 + - version 103 + - alpha_field 104 + properties: 105 + version: 106 + type: number 107 + const: 1.0 108 + alpha_field: 109 + type: string 110 + 111 + VersionBeta: 112 + type: object 113 + required: 114 + - version 115 + - beta_field 116 + properties: 117 + version: 118 + type: number 119 + const: 2.5 120 + beta_field: 121 + type: string 122 + 123 + # --- Integer discriminator (allOf) --- 124 + IntegerAllOfBase: 125 + type: object 126 + required: 127 + - kind 128 + properties: 129 + kind: 130 + type: integer 131 + discriminator: 132 + propertyName: kind 133 + mapping: 134 + '1': '#/components/schemas/IntegerAllOfChildA' 135 + '2': '#/components/schemas/IntegerAllOfChildB' 136 + 137 + IntegerAllOfChildA: 138 + allOf: 139 + - $ref: '#/components/schemas/IntegerAllOfBase' 140 + - type: object 141 + required: 142 + - child_a_field 143 + properties: 144 + child_a_field: 145 + type: string 146 + 147 + IntegerAllOfChildB: 148 + allOf: 149 + - $ref: '#/components/schemas/IntegerAllOfBase' 150 + - type: object 151 + required: 152 + - child_b_field 153 + properties: 154 + child_b_field: 155 + type: string
+194
specs/3.1.x/discriminator-non-string.yaml
··· 1 + openapi: 3.1.0 2 + info: 3 + title: Non-string discriminator test 4 + version: 1 5 + components: 6 + schemas: 7 + # --- Boolean discriminator (oneOf) --- 8 + BooleanOneOf: 9 + oneOf: 10 + - $ref: '#/components/schemas/AutoConfig' 11 + - $ref: '#/components/schemas/CustomConfig' 12 + discriminator: 13 + propertyName: use_custom 14 + mapping: 15 + 'False': '#/components/schemas/AutoConfig' 16 + 'True': '#/components/schemas/CustomConfig' 17 + 18 + AutoConfig: 19 + type: object 20 + required: 21 + - use_custom 22 + - auto_setting 23 + properties: 24 + use_custom: 25 + type: boolean 26 + const: false 27 + auto_setting: 28 + type: string 29 + 30 + CustomConfig: 31 + type: object 32 + required: 33 + - use_custom 34 + - custom_value 35 + properties: 36 + use_custom: 37 + type: boolean 38 + const: true 39 + custom_value: 40 + type: integer 41 + 42 + # --- Boolean discriminator (anyOf) --- 43 + BooleanAnyOf: 44 + anyOf: 45 + - $ref: '#/components/schemas/AutoConfig' 46 + - $ref: '#/components/schemas/CustomConfig' 47 + discriminator: 48 + propertyName: use_custom 49 + mapping: 50 + 'False': '#/components/schemas/AutoConfig' 51 + 'True': '#/components/schemas/CustomConfig' 52 + 53 + # --- Integer discriminator (oneOf) --- 54 + IntegerOneOf: 55 + oneOf: 56 + - $ref: '#/components/schemas/TypeOne' 57 + - $ref: '#/components/schemas/TypeTwo' 58 + discriminator: 59 + propertyName: type_id 60 + mapping: 61 + '1': '#/components/schemas/TypeOne' 62 + '2': '#/components/schemas/TypeTwo' 63 + 64 + TypeOne: 65 + type: object 66 + required: 67 + - type_id 68 + - one_data 69 + properties: 70 + type_id: 71 + type: integer 72 + const: 1 73 + one_data: 74 + type: string 75 + 76 + TypeTwo: 77 + type: object 78 + required: 79 + - type_id 80 + - two_data 81 + properties: 82 + type_id: 83 + type: integer 84 + const: 2 85 + two_data: 86 + type: string 87 + 88 + # --- Number (float) discriminator (oneOf) --- 89 + NumberOneOf: 90 + oneOf: 91 + - $ref: '#/components/schemas/VersionAlpha' 92 + - $ref: '#/components/schemas/VersionBeta' 93 + discriminator: 94 + propertyName: version 95 + mapping: 96 + '1.0': '#/components/schemas/VersionAlpha' 97 + '2.5': '#/components/schemas/VersionBeta' 98 + 99 + VersionAlpha: 100 + type: object 101 + required: 102 + - version 103 + - alpha_field 104 + properties: 105 + version: 106 + type: number 107 + const: 1.0 108 + alpha_field: 109 + type: string 110 + 111 + VersionBeta: 112 + type: object 113 + required: 114 + - version 115 + - beta_field 116 + properties: 117 + version: 118 + type: number 119 + const: 2.5 120 + beta_field: 121 + type: string 122 + 123 + # --- Integer discriminator (allOf) --- 124 + IntegerAllOfBase: 125 + type: object 126 + required: 127 + - kind 128 + properties: 129 + kind: 130 + type: integer 131 + discriminator: 132 + propertyName: kind 133 + mapping: 134 + '1': '#/components/schemas/IntegerAllOfChildA' 135 + '2': '#/components/schemas/IntegerAllOfChildB' 136 + 137 + IntegerAllOfChildA: 138 + allOf: 139 + - $ref: '#/components/schemas/IntegerAllOfBase' 140 + - type: object 141 + required: 142 + - child_a_field 143 + properties: 144 + child_a_field: 145 + type: string 146 + 147 + IntegerAllOfChildB: 148 + allOf: 149 + - $ref: '#/components/schemas/IntegerAllOfBase' 150 + - type: object 151 + required: 152 + - child_b_field 153 + properties: 154 + child_b_field: 155 + type: string 156 + 157 + # --- 3.1.x-specific: discriminator property with type array (e.g. nullable integer) --- 158 + NullableIntegerOneOf: 159 + oneOf: 160 + - $ref: '#/components/schemas/NullableVariantX' 161 + - $ref: '#/components/schemas/NullableVariantY' 162 + discriminator: 163 + propertyName: tag 164 + mapping: 165 + '10': '#/components/schemas/NullableVariantX' 166 + '20': '#/components/schemas/NullableVariantY' 167 + 168 + NullableVariantX: 169 + type: object 170 + required: 171 + - tag 172 + - x_data 173 + properties: 174 + tag: 175 + type: 176 + - integer 177 + - 'null' 178 + const: 10 179 + x_data: 180 + type: string 181 + 182 + NullableVariantY: 183 + type: object 184 + required: 185 + - tag 186 + - y_data 187 + properties: 188 + tag: 189 + type: 190 + - integer 191 + - 'null' 192 + const: 20 193 + y_data: 194 + type: string