Skip to content

Commit cc57a15

Browse files
author
Siggy Bilstein
authored
Extend generator to support translating oneOf to xor and implementing, also better support for anyOf and Union (#30)
* the start * need to fix * successful new node type * implements discriminator and oneOf * supporting oneOf with discriminator, anyOf in validation generation * fix
1 parent 832cd4a commit cc57a15

20 files changed

Lines changed: 319 additions & 17 deletions

src/handlerbars.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const createHandlebars = (jsonSchema: $Refs): typeof Handlebars => {
3333
return frame;
3434
};
3535

36-
[array, code, collection, comparison, date, html, i18n, inflection, markdown, math, misc, number, object, path, regex, string, url].forEach(
36+
[array, code, collection, comparison, date, html, i18n, inflection, object, markdown, math, misc, number, object, path, regex, string, url].forEach(
3737
helper => {
3838
handlebars.registerHelper(helper);
3939
},
@@ -221,7 +221,7 @@ export const createHandlebars = (jsonSchema: $Refs): typeof Handlebars => {
221221

222222
handlebars.log = (level, ...messages) => {
223223
const levels = ['debug', 'info', 'warn', 'error'];
224-
const actualLevel = typeof level === 'string' ? (levels.includes(level) ? level : 'info') : levels.at(level) ?? 'info';
224+
const actualLevel = typeof level === 'string' ? (levels.includes(level) ? level : 'info') : (levels.at(level) ?? 'info');
225225
(handlebars.logger as any)['actualLogger'][actualLevel](...messages);
226226
};
227227

src/optimizer/converter.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ export class Converter {
8989
return this.convertArray(parsedNode, parsedComponents, components);
9090
} else if (parsed.isUnion(parsedNode)) {
9191
return this.convertUnion(parsedNode, parsedComponents, components);
92+
} else if (parsed.isXor(parsedNode)) {
93+
return this.convertXor(parsedNode, parsedComponents, components);
9294
} else if (parsed.isComposite(parsedNode)) {
9395
return this.convertComposite(parsedNode, parsedComponents, components);
9496
} else if (parsed.isExclusion(parsedNode)) {
@@ -136,16 +138,37 @@ export class Converter {
136138
if (parsedNode.definitions.length === 1) {
137139
return this.convertParsedNode(parsedNode.definitions[0], parsedComponents, components);
138140
}
141+
142+
let discriminatorPropertyName: string | undefined;
143+
let discriminatorMapping: Record<string, optimized.OptimizedNode> | undefined;
144+
145+
if ('discriminatorPropertyName' in parsedNode && parsedNode.discriminatorPropertyName) {
146+
discriminatorPropertyName = parsedNode.discriminatorPropertyName;
147+
148+
if (parsedNode.discriminatorMapping) {
149+
discriminatorMapping = {};
150+
for (const [key, value] of Object.entries(parsedNode.discriminatorMapping)) {
151+
discriminatorMapping[key] = this.convertParsedNode(value, parsedComponents, components);
152+
}
153+
}
154+
}
155+
139156
return {
140157
...parsedNode,
141158
definitions: parsedNode.definitions.map(p => this.convertParsedNode(p, parsedComponents, components)),
159+
discriminatorPropertyName,
160+
discriminatorMapping,
142161
};
143162
}
144163

145164
private convertUnion(parsedNode: parsed.Union, parsedComponents: parsed.Components, components: optimized.Components) {
146165
return this.compress(parsedNode, parsedComponents, components);
147166
}
148167

168+
private convertXor(parsedNode: parsed.Xor, parsedComponents: parsed.Components, components: optimized.Components) {
169+
return this.compress(parsedNode, parsedComponents, components);
170+
}
171+
149172
private convertComposite(parsedNode: parsed.Composite, parsedComponents: parsed.Components, components: optimized.Components) {
150173
return this.compress(parsedNode, parsedComponents, components);
151174
}

src/optimizer/nodes/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export * from './header.js';
1111
export * from './array.js';
1212
export * from './union.js';
1313
export * from './composite.js';
14+
export * from './xor.js';
1415
export * from './exclusion.js';
1516
export * from './request.js';
1617
export * from './tag.js';

src/optimizer/nodes/union.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { OptimizedNode } from './optimized.node.js';
22

33
export interface Union extends OptimizedNode {
44
definitions: OptimizedNode[];
5+
discriminatorPropertyName?: string;
6+
discriminatorMapping?: Record<string, OptimizedNode>;
57
}
68

79
export const isUnion = (value: OptimizedNode): value is Union => {

src/optimizer/nodes/xor.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { OptimizedNode } from './optimized.node.js';
2+
3+
export interface Xor extends OptimizedNode {
4+
definitions: OptimizedNode[];
5+
discriminatorPropertyName?: string;
6+
discriminatorMapping?: Record<string, OptimizedNode>;
7+
}
8+
9+
export const isXor = (value: OptimizedNode): value is Xor => {
10+
return value.type === 'xor';
11+
};

src/optimizer/openapi.optimizer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ export class OpenApiOptimizer {
343343
pathRegex = { ...pathRegex, [name]: definition.format ?? definition.type };
344344
}
345345
});
346-
} else if (optimized.isComposite(found) || optimized.isUnion(found)) {
346+
} else if (optimized.isComposite(found) || optimized.isUnion(found) || optimized.isXor(found)) {
347347
// todo need to recursively check the definitions
348348

349349
found.definitions?.forEach(d => {

src/parser/openapi.parser.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,13 +908,40 @@ export abstract class OpenApiParser {
908908

909909
const definitions = oneOf.map(x => this.parseSchema(x, schema.type));
910910

911+
let discriminatorPropertyName: string | undefined;
912+
let discriminatorMapping: Record<string, ParsedNode> | undefined;
913+
914+
if (schema.discriminator) {
915+
discriminatorPropertyName = schema.discriminator.propertyName;
916+
917+
if (schema.discriminator.mapping) {
918+
discriminatorMapping = {};
919+
for (const [key, value] of Object.entries(schema.discriminator.mapping)) {
920+
// Parse the reference string into a ParsedNode
921+
const parsedValue = this.parseDiscriminatorReference(value);
922+
discriminatorMapping[key] = parsedValue;
923+
}
924+
}
925+
}
926+
911927
return {
912-
type: 'union',
928+
type: 'xor',
913929
...modifiers,
914930
definitions,
931+
discriminatorPropertyName,
932+
discriminatorMapping,
915933
};
916934
}
917935

936+
private parseDiscriminatorReference(referenceString: string): ParsedNode {
937+
// Create a reference object
938+
const referenceObject: ReferenceObject = {
939+
$ref: referenceString,
940+
};
941+
942+
return this.createReference(referenceObject);
943+
}
944+
918945
private parseNotObject(schema: ReferenceObject | SchemaObject, modifiers: Modifiers): Exclusion {
919946
return {
920947
type: 'exclusion',

src/parser/organizer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ export class Organizer {
207207
node.definitions.forEach(x => {
208208
this.traverseReferences(originalDocument, components, x);
209209
});
210+
} else if (parsed.isXor(node)) {
211+
node.definitions.forEach(x => {
212+
this.traverseReferences(originalDocument, components, x);
213+
});
210214
}
211215
}
212216

src/parser/parsed_nodes/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from './primative.js';
77
export * from './object.js';
88
export * from './composite.js';
99
export * from './union.js';
10+
export * from './xor.js';
1011
export * from './exclusion.js';
1112
export * from './array.js';
1213
export * from './link.js';

src/parser/parsed_nodes/modifiers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const compareModifiers = (a: Modifiers, b: Modifiers): boolean => {
6262
//a.required === b.required &&
6363
//a.enum === b.enum &&
6464
a.nullable === b.nullable &&
65-
//a.discriminator === b.discriminator &&
65+
a.discriminator === b.discriminator &&
6666
a.readOnly === b.readOnly &&
6767
a.writeOnly === b.writeOnly &&
6868
a.example === b.example &&

0 commit comments

Comments
 (0)