Skip to content

Commit 1a8129a

Browse files
committed
fix(utils): 🐛 Handle single-element oneOf without union
1 parent b519156 commit 1a8129a

5 files changed

Lines changed: 49 additions & 0 deletions

File tree

src/utils/json-schema-to-typed-dict.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ export function convertToTypedDict(name: string, schema: OpenAPI.SchemaObject |
9797
typingImports.add('Any');
9898
return 'Any';
9999
}
100+
if (s.oneOf.length === 1) {
101+
return toType(s.oneOf[0] as any, className);
102+
}
100103
typingImports.add('Union');
101104
const parts = s.oneOf.map((sub, idx) => toType(sub as any, `${className}Option${idx}`)).join(', ');
102105
return `Union[${parts}]`;
@@ -107,6 +110,9 @@ export function convertToTypedDict(name: string, schema: OpenAPI.SchemaObject |
107110
typingImports.add('Any');
108111
return 'Any';
109112
}
113+
if (s.anyOf.length === 1) {
114+
return toType(s.anyOf[0] as any, className);
115+
}
110116
typingImports.add('Union');
111117
const parts = s.anyOf.map((sub, idx) => toType(sub as any, `${className}Option${idx}`)).join(', ');
112118
return `Union[${parts}]`;

src/utils/json-schema-to-zod.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export function convertSchema(schema: OpenAPI.SchemaObject | OpenAPI.ReferenceOb
3333
if (items.length === 0) {
3434
return 'z.any()';
3535
}
36+
if (items.length === 1) {
37+
return walk(items[0]!);
38+
}
3639
const parts = items.map((sub) => walk(sub)).join(', ');
3740
return `z.union([${parts}])`;
3841
}
@@ -42,6 +45,9 @@ export function convertSchema(schema: OpenAPI.SchemaObject | OpenAPI.ReferenceOb
4245
if (items.length === 0) {
4346
return 'z.any()';
4447
}
48+
if (items.length === 1) {
49+
return walk(items[0]!);
50+
}
4551
const parts = items.map((sub) => walk(sub)).join(', ');
4652
return `z.union([${parts}])`;
4753
}

tests/__snapshots__/json-schema-to-typed-dict.spec.ts.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class Place(TypedDict, total=False):
3939
location: Required[PlaceLocation]"
4040
`;
4141

42+
exports[`generate-python-dict > handles oneOf with a single ref 1`] = `
43+
"from typing import TypedDict
44+
MessageSingle = A"
45+
`;
46+
4247
exports[`generate-python-dict > handles oneOf with refs 1`] = `
4348
"from typing import TypedDict, Union
4449
Message = Union[A, B]"

tests/json-schema-to-typed-dict.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ describe('generate-python-dict', () => {
126126
expect(content).toMatchSnapshot();
127127
});
128128

129+
it('handles oneOf with a single ref', () => {
130+
const doc: OpenAPI.Document = {
131+
openapi: '3.1.0',
132+
info: { title: 't', version: '1' },
133+
paths: {},
134+
components: {
135+
schemas: {
136+
A: { type: 'object', properties: { id: { type: 'string' } } },
137+
MessageSingle: { oneOf: [{ $ref: '#/components/schemas/A' }] }
138+
}
139+
}
140+
};
141+
const schemas = extractSchemas(doc, null);
142+
const { definition, typingImports } = convertToTypedDict('MessageSingle', schemas.MessageSingle as OpenAPI.SchemaObject);
143+
const typingLine = `from typing import ${Array.from(typingImports).join(', ')}`;
144+
const content = [typingLine, '', definition, ''].filter(Boolean).join('\n');
145+
const tmp = path.join(__dirname, 'tmp_oneof_single.py');
146+
fs.writeFileSync(tmp, content);
147+
child_process.execSync(`python3 -m py_compile ${tmp}`);
148+
fs.unlinkSync(tmp);
149+
expect(content).toMatchSnapshot();
150+
});
151+
129152
it('handles inline object properties', () => {
130153
const doc: OpenAPI.Document = {
131154
openapi: '3.1.0',

tests/json-schema-to-zod.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ describe('convertSchema', () => {
6363
expect(imports.has('B')).toBe(true);
6464
});
6565

66+
it('handles oneOf with a single ref', () => {
67+
const schema = {
68+
oneOf: [{ $ref: '#/components/schemas/A' }]
69+
} as OpenAPI.SchemaObject;
70+
const { zodString, imports } = convertSchema(schema);
71+
expect(zodString).toBe('A');
72+
expect(imports.has('A')).toBe(true);
73+
});
74+
6675
it('converts inline object properties', () => {
6776
const schema = {
6877
type: 'object',

0 commit comments

Comments
 (0)