Skip to content

Commit

Permalink
Merge pull request #212 from asteasolutions/feature/#211-support-for-…
Browse files Browse the repository at this point in the history
…record-enum-keys

handle enums as keys for zod records
  • Loading branch information
AGalabov authored Mar 1, 2024
2 parents 142f119 + 3a0682d commit a6e0115
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 1 deletion.
83 changes: 83 additions & 0 deletions spec/types/record.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,87 @@ describe('record', () => {
},
});
});

describe('Enum keys', () => {
it('supports records with enum keys', () => {
const continents = z.enum(['EUROPE', 'AFRICA']);

const countries = z.enum(['USA', 'CAN']);

const countryContent = z
.object({ countries: countries.array() })
.openapi('Content');

const Geography = z
.record(continents, countryContent)
.openapi('Geography');

expectSchema([Geography], {
Content: {
type: 'object',
properties: {
countries: {
type: 'array',
items: {
type: 'string',
enum: ['USA', 'CAN'],
},
},
},
required: ['countries'],
},

Geography: {
type: 'object',
properties: {
EUROPE: { $ref: '#/components/schemas/Content' },
AFRICA: { $ref: '#/components/schemas/Content' },
},
},
});
});

it('supports records with native enum keys', () => {
enum Continents {
EUROPE,
AFRICA,
}

const continents = z.nativeEnum(Continents);

const countries = z.enum(['USA', 'CAN']);

const countryContent = z
.object({ countries: countries.array() })
.openapi('Content');

const Geography = z
.record(continents, countryContent)
.openapi('Geography');

expectSchema([Geography], {
Content: {
type: 'object',
properties: {
countries: {
type: 'array',
items: {
type: 'string',
enum: ['USA', 'CAN'],
},
},
},
required: ['countries'],
},

Geography: {
type: 'object',
properties: {
EUROPE: { $ref: '#/components/schemas/Content' },
AFRICA: { $ref: '#/components/schemas/Content' },
},
},
});
});
});
});
4 changes: 4 additions & 0 deletions src/lib/lodash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@ export function uniq<T>(values: T[]) {

return [...set.values()];
}

export function isString(val: unknown): val is string {
return typeof val === 'string';
}
29 changes: 28 additions & 1 deletion src/openapi-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { enumInfo } from './lib/enum-info';
import {
compact,
isNil,
isString,
mapValues,
objectEquals,
omit,
Expand Down Expand Up @@ -1009,10 +1010,36 @@ export class OpenAPIGenerator {

if (isZodType(zodSchema, 'ZodRecord')) {
const propertiesType = zodSchema._def.valueType;
const keyType = zodSchema._def.keyType;

const propertiesSchema = this.generateSchemaWithRef(propertiesType);

if (
isZodType(keyType, 'ZodEnum') ||
isZodType(keyType, 'ZodNativeEnum')
) {
// Native enums have their keys as both number and strings however the number is an
// internal representation and the string is the access point for a documentation
const keys = Object.values(keyType.enum).filter(isString);

const properties = keys.reduce(
(acc, curr) => ({
...acc,
[curr]: propertiesSchema,
}),
{} as SchemaObject['properties']
);

return {
...this.mapNullableType('object', isNullable),
properties,
default: defaultValue,
};
}

return {
...this.mapNullableType('object', isNullable),
additionalProperties: this.generateSchemaWithRef(propertiesType),
additionalProperties: propertiesSchema,
default: defaultValue,
};
}
Expand Down

0 comments on commit a6e0115

Please sign in to comment.