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

Configure Feed

Select the types of activity you want to include in your feed.

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

OpenAPI discriminator mappings use string keys, but the actual discriminator
property may be boolean, integer, or number. Previously, all discriminator
values were hardcoded as type 'string'. This change detects the actual
property type from the schema and converts mapping values accordingly.

+852 -31
+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 + };
+63 -5
packages/shared/src/openApi/2.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 { 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>; 46 + }): DiscriminatorPropertyType => { 47 + for (const schema of schemas) { 48 + const resolved = schema.$ref ? context.resolveRef<SchemaObject>(schema.$ref) : schema; 49 + 50 + // Check direct properties 51 + const property = resolved.properties?.[propertyName]; 52 + if (property) { 53 + const resolvedProperty = property.$ref 54 + ? context.resolveRef<SchemaObject>(property.$ref) 55 + : property; 56 + if ( 57 + resolvedProperty.type === 'boolean' || 58 + resolvedProperty.type === 'integer' || 59 + resolvedProperty.type === 'number' 60 + ) { 61 + return resolvedProperty.type; 62 + } 63 + } 64 + 65 + // Check allOf chains 66 + if (resolved.allOf) { 67 + const foundType = findDiscriminatorPropertyType({ 68 + context, 69 + propertyName, 70 + schemas: resolved.allOf, 71 + }); 72 + if (foundType !== 'string') { 73 + return foundType; 74 + } 75 + } 76 + } 77 + 78 + return 'string'; 28 79 }; 29 80 30 81 const parseSchemaJsDoc = ({ ··· 336 387 // `$ref` should be passed from the root `parseSchema()` call 337 388 if (ref.discriminator && state.$ref) { 338 389 const values = discriminatorValues(state.$ref); 339 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => ({ 340 - const: value, 341 - type: 'string', 342 - })); 390 + 391 + // Detect the actual type of the discriminator property 392 + const propertyType = findDiscriminatorPropertyType({ 393 + context, 394 + propertyName: ref.discriminator, 395 + schemas: [ref], 396 + }); 397 + 398 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => 399 + convertDiscriminatorValue(value, propertyType), 400 + ); 343 401 const irDiscriminatorSchema: IR.SchemaObject = { 344 402 properties: { 345 403 [ref.discriminator]:
+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]:
+90 -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) { 57 + const resolvedProperty = property.$ref 58 + ? context.resolveRef<SchemaObject>(property.$ref) 59 + : property; 60 + // Handle both single type and array of types (3.1.x supports type arrays) 61 + const propertyTypes = Array.isArray(resolvedProperty.type) 62 + ? resolvedProperty.type 63 + : resolvedProperty.type 64 + ? [resolvedProperty.type] 65 + : []; 66 + for (const propType of propertyTypes) { 67 + if (propType === 'boolean' || propType === 'integer' || propType === 'number') { 68 + return propType; 69 + } 70 + } 71 + } 72 + 73 + // Check allOf chains 74 + if (resolved.allOf) { 75 + const foundType = findDiscriminatorPropertyType({ 76 + context, 77 + propertyName, 78 + schemas: resolved.allOf, 79 + }); 80 + if (foundType !== 'string') { 81 + return foundType; 82 + } 83 + } 84 + } 85 + 86 + return 'string'; 32 87 }; 33 88 34 89 /** ··· 564 619 // Use allValues if we found children, otherwise use the original values 565 620 const finalValues = allValues.length > 0 ? allValues : values; 566 621 567 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = finalValues.map((value) => ({ 568 - const: value, 569 - type: 'string', 570 - })); 622 + // Detect the actual type of the discriminator property 623 + const propertyType = findDiscriminatorPropertyType({ 624 + context, 625 + propertyName: discriminator.propertyName, 626 + schemas: compositionSchemas, 627 + }); 628 + 629 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = finalValues.map((value) => 630 + convertDiscriminatorValue(value, propertyType), 631 + ); 571 632 572 633 const discriminatorProperty: IR.SchemaObject = 573 634 valueSchemas.length > 1 ··· 743 804 744 805 const compositionSchemas = schema.anyOf; 745 806 807 + const discriminatorPropertyType = schema.discriminator 808 + ? findDiscriminatorPropertyType({ 809 + context, 810 + propertyName: schema.discriminator.propertyName, 811 + schemas: compositionSchemas, 812 + }) 813 + : undefined; 814 + 746 815 for (const compositionSchema of compositionSchemas) { 747 816 let irCompositionSchema = schemaToIrSchema({ 748 817 context, ··· 753 822 // `$ref` should be defined with discriminators 754 823 if (schema.discriminator && irCompositionSchema.$ref != null) { 755 824 const values = discriminatorValues(irCompositionSchema.$ref, schema.discriminator.mapping); 756 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => ({ 757 - const: value, 758 - type: 'string', 759 - })); 825 + 826 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => 827 + convertDiscriminatorValue(value, discriminatorPropertyType!), 828 + ); 760 829 const irDiscriminatorSchema: IR.SchemaObject = { 761 830 properties: { 762 831 [schema.discriminator.propertyName]: ··· 894 963 895 964 const compositionSchemas = schema.oneOf; 896 965 966 + const discriminatorPropertyType = schema.discriminator 967 + ? findDiscriminatorPropertyType({ 968 + context, 969 + propertyName: schema.discriminator.propertyName, 970 + schemas: compositionSchemas, 971 + }) 972 + : undefined; 973 + 897 974 for (const compositionSchema of compositionSchemas) { 898 975 let irCompositionSchema = schemaToIrSchema({ 899 976 context, ··· 904 981 // `$ref` should be defined with discriminators 905 982 if (schema.discriminator && irCompositionSchema.$ref != null) { 906 983 const values = discriminatorValues(irCompositionSchema.$ref, schema.discriminator.mapping); 907 - const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => ({ 908 - const: value, 909 - type: 'string', 910 - })); 984 + 985 + const valueSchemas: ReadonlyArray<IR.SchemaObject> = values.map((value) => 986 + convertDiscriminatorValue(value, discriminatorPropertyType!), 987 + ); 911 988 const irDiscriminatorSchema: IR.SchemaObject = { 912 989 properties: { 913 990 [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