Skip to content

Commit

Permalink
Merge pull request #1319 from hey-api/fix/zod-plugin-constraints
Browse files Browse the repository at this point in the history
fix: Zod plugin handles value constraints and defaults
  • Loading branch information
mrlubos authored Nov 21, 2024
2 parents a134d19 + af76a77 commit 043fe20
Show file tree
Hide file tree
Showing 41 changed files with 653 additions and 539 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-hats-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hey-api/openapi-ts': patch
---

fix: Zod plugin handles value constraints and defaults
3 changes: 3 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import eslint from '@eslint/js';
import configPrettier from 'eslint-config-prettier';
import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
import pluginSortDestructureKeys from 'eslint-plugin-sort-destructure-keys';
import pluginSortKeysFix from 'eslint-plugin-sort-keys-fix';
import pluginTypeScriptSortKeys from 'eslint-plugin-typescript-sort-keys';
// import pluginVue from 'eslint-plugin-vue'
Expand All @@ -19,6 +20,7 @@ export default tseslint.config(
},
plugins: {
'simple-import-sort': pluginSimpleImportSort,
'sort-destructure-keys': pluginSortDestructureKeys,
'sort-keys-fix': pluginSortKeysFix,
'typescript-sort-keys': pluginTypeScriptSortKeys,
},
Expand All @@ -38,6 +40,7 @@ export default tseslint.config(
'object-shorthand': 'error',
'simple-import-sort/exports': 'error',
'simple-import-sort/imports': 'error',
'sort-destructure-keys/sort-destructure-keys': 'warn',
'sort-imports': 'off',
'sort-keys-fix/sort-keys-fix': 'warn',
'typescript-sort-keys/interface': 'warn',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class PetStoreComponent {
return;
}

const { name, category } = form.value as {
const { category, name } = form.value as {
category: string;
name: string;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const actions = {
* Modify game state in reaction to a guessed word. This logic always runs on
* the server, so that people can't cheat by peeking at the JavaScript
*/
enter: async ({ request, cookies }) => {
enter: async ({ cookies, request }) => {
const game = new Game(cookies.get('sverdle'));

const data = await request.formData();
Expand All @@ -51,7 +51,7 @@ export const actions = {
* Modify game state in reaction to a keypress. If client-side JavaScript
* is available, this will happen in the browser instead of here
*/
update: async ({ request, cookies }) => {
update: async ({ cookies, request }) => {
const game = new Game(cookies.get('sverdle'));

const data = await request.formData();
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"eslint": "9.6.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"eslint-plugin-sort-destructure-keys": "2.0.0",
"eslint-plugin-sort-keys-fix": "1.1.2",
"eslint-plugin-typescript-sort-keys": "3.2.0",
"eslint-plugin-vue": "9.23.0",
Expand Down
1 change: 1 addition & 0 deletions packages/openapi-ts/src/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ export const compiler = {
typeReferenceNode: types.createTypeReferenceNode,
typeTupleNode: typedef.createTypeTupleNode,
typeUnionNode: typedef.createTypeUnionNode,
valueToExpression: types.toExpression,
};
4 changes: 2 additions & 2 deletions packages/openapi-ts/src/compiler/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export const createExportAllDeclaration = ({
export type ImportExportItem = ImportExportItemObject | string;

export const createCallExpression = ({
parameters = [],
functionName,
parameters = [],
types,
}: {
functionName:
Expand Down Expand Up @@ -108,8 +108,8 @@ export const createNamedExportDeclarations = ({
* @returns ts.VariableStatement
*/
export const createConstVariable = ({
comment,
assertion,
comment,
destructure,
exportConst,
expression,
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-ts/src/compiler/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ export const createBinaryExpression = ({
};

export const createIfStatement = ({
elseStatement,
expression,
thenStatement,
elseStatement,
}: {
elseStatement?: ts.Statement;
expression: ts.Expression;
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ const initConfigs = async (userConfig: UserConfig): Promise<Config[]> => {
configFile = '',
debug = false,
dryRun = false,
exportCore = true,
experimentalParser = false,
exportCore = true,
name,
request,
useOptions = true,
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-ts/src/ir/__tests__/mediaType.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('isMediaTypeFileLike', () => {

it.each(scenarios)(
'detects $mediaType as file-like? $fileLike',
async ({ mediaType, fileLike }) => {
async ({ fileLike, mediaType }) => {
expect(isMediaTypeFileLike({ mediaType })).toEqual(fileLike);
},
);
Expand Down
16 changes: 15 additions & 1 deletion packages/openapi-ts/src/ir/ir.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,21 @@ export interface IRResponseObject {
export interface IRSchemaObject
extends Pick<
JsonSchemaDraft2020_12,
'$ref' | 'const' | 'deprecated' | 'description' | 'required' | 'title'
| '$ref'
| 'const'
| 'default'
| 'deprecated'
| 'description'
| 'exclusiveMaximum'
| 'exclusiveMinimum'
| 'maximum'
| 'maxItems'
| 'maxLength'
| 'minimum'
| 'minItems'
| 'minLength'
| 'required'
| 'title'
> {
/**
* If the schema is intended to be used as an object property, it can be
Expand Down
6 changes: 3 additions & 3 deletions packages/openapi-ts/src/legacy/handlebars/handlebars.cjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const Handlebars = require('handlebars');
const {
existsSync,
mkdirSync,
readFileSync,
readdirSync,
rmdirSync,
statSync,
existsSync,
mkdirSync,
writeFileSync,
rmdirSync,
} = require('node:fs');
const path = require('node:path');

Expand Down
36 changes: 36 additions & 0 deletions packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,46 @@ const parseSchemaMeta = ({
irSchema: IRSchemaObject;
schema: SchemaObject;
}) => {
if (schema.default !== undefined) {
irSchema.default = schema.default;
}

if (schema.exclusiveMaximum) {
if (schema.maximum !== undefined) {
irSchema.exclusiveMaximum = schema.maximum;
}
} else if (schema.maximum !== undefined) {
irSchema.maximum = schema.maximum;
}

if (schema.exclusiveMinimum) {
if (schema.minimum !== undefined) {
irSchema.exclusiveMinimum = schema.minimum;
}
} else if (schema.minimum !== undefined) {
irSchema.minimum = schema.minimum;
}

if (schema.format) {
irSchema.format = schema.format;
}

if (schema.maxItems !== undefined) {
irSchema.maxItems = schema.maxItems;
}

if (schema.maxLength !== undefined) {
irSchema.maxLength = schema.maxLength;
}

if (schema.minItems !== undefined) {
irSchema.minItems = schema.minItems;
}

if (schema.minLength !== undefined) {
irSchema.minLength = schema.minLength;
}

if (schema.readOnly) {
irSchema.accessScope = 'read';
} else if (schema.writeOnly) {
Expand Down
36 changes: 36 additions & 0 deletions packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,46 @@ const parseSchemaMeta = ({
}
}

if (schema.default !== undefined) {
irSchema.default = schema.default;
}

if (schema.exclusiveMaximum) {
irSchema.exclusiveMaximum = schema.exclusiveMaximum;
}

if (schema.exclusiveMinimum) {
irSchema.exclusiveMinimum = schema.exclusiveMinimum;
}

if (schema.format) {
irSchema.format = schema.format;
}

if (schema.maximum !== undefined) {
irSchema.maximum = schema.maximum;
}

if (schema.maxItems !== undefined) {
irSchema.maxItems = schema.maxItems;
}

if (schema.maxLength !== undefined) {
irSchema.maxLength = schema.maxLength;
}

if (schema.minimum !== undefined) {
irSchema.minimum = schema.minimum;
}

if (schema.minItems !== undefined) {
irSchema.minItems = schema.minItems;
}

if (schema.minLength !== undefined) {
irSchema.minLength = schema.minLength;
}

if (schema.readOnly) {
irSchema.accessScope = 'read';
} else if (schema.writeOnly) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('getPattern', () => {
{ expected: '\\\\/', pattern: '\\/' },
{ expected: '\\\\/\\\\/', pattern: '\\/\\/' },
{ expected: "\\'", pattern: "'" },
])('getPattern($pattern) -> $expected', ({ pattern, expected }) => {
])('getPattern($pattern) -> $expected', ({ expected, pattern }) => {
expect(getPattern(pattern)).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('parseResponseStatusCode', () => {
{ expected: '4XX', input: '4XX' },
{ expected: null, input: 'abc' },
{ expected: null, input: '-100' },
])('parseResponseStatusCode($input) -> $expected', ({ input, expected }) => {
])('parseResponseStatusCode($input) -> $expected', ({ expected, input }) => {
expect(parseResponseStatusCode(input)).toBe(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('sanitizeOperationParameterName', () => {
{ expected: 'unknownArray', input: 'unknown[]' },
])(
'sanitizeOperationParameterName($input) -> $expected',
({ input, expected }) => {
({ expected, input }) => {
expect(sanitizeOperationParameterName(input)).toEqual(expected);
},
);
Expand All @@ -30,7 +30,7 @@ describe('sanitizeNamespaceIdentifier', () => {
{ expected: 'a-b-c--d--e', input: 'a/b{c}/d/$e' },
])(
'sanitizeNamespaceIdentifier($input) -> $expected',
({ input, expected }) => {
({ expected, input }) => {
expect(sanitizeNamespaceIdentifier(input)).toEqual(expected);
},
);
Expand All @@ -45,7 +45,7 @@ describe('ensureValidTypeScriptJavaScriptIdentifier', () => {
{ expected: '_400', input: '400' },
])(
'ensureValidTypeScriptJavaScriptIdentifier($input) -> $expected',
({ input, expected }) => {
({ expected, input }) => {
expect(ensureValidTypeScriptJavaScriptIdentifier(input)).toEqual(
expected,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('getServiceVersion', () => {
{ expected: '1.0', input: '1.0' },
{ expected: '1.2', input: 'v1.2' },
{ expected: '2.4', input: 'V2.4' },
])('should get $expected when version is $input', ({ input, expected }) => {
])('should get $expected when version is $input', ({ expected, input }) => {
expect(getServiceVersion(input)).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('sort', () => {
},
])(
'should sort $input by required to produce $expected',
({ input, expected }) => {
({ expected, input }) => {
expect(toSortedByRequired(input)).toEqual(expected);
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('stripNamespace', () => {
{ expected: 'Item', input: '#/components/securitySchemes/Item' },
{ expected: 'Item', input: '#/components/links/Item' },
{ expected: 'Item', input: '#/components/callbacks/Item' },
])('stripNamespace($input) -> $expected', ({ input, expected }) => {
])('stripNamespace($input) -> $expected', ({ expected, input }) => {
expect(stripNamespace(input)).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('getMappedType', () => {
{ expected: 'unknown[]', type: 'array' },
{ expected: 'void', type: 'void' },
{ expected: undefined, type: '' },
])('should map type $type to $expected', ({ type, expected }) => {
])('should map type $type to $expected', ({ expected, type }) => {
expect(getMappedType(type)).toEqual(expected);
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-ts/src/openApi/shared/utils/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { sanitizeNamespaceIdentifier } from '../../common/parser/sanitize';
*/
export const operationToId = ({
context,
method,
id,
method,
path,
}: {
context: IRContext;
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-ts/src/openApi/v2/parser/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export const getOperation = ({
method,
op,
openApi,
types,
pathParams,
types,
url,
}: {
method: Lowercase<Operation['method']>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,9 @@ const toRequestOptions = (

export const serviceFunctionIdentifier = ({
config,
handleIllegal,
id,
operation,
handleIllegal,
}: {
config: Config;
handleIllegal?: boolean;
Expand Down
4 changes: 2 additions & 2 deletions packages/openapi-ts/src/plugins/@hey-api/services/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ const generateClassServices = ({ context }: { context: IRContext }) => {

const services = new Map<string, Array<ts.MethodDeclaration>>();

context.subscribe('operation', ({ operation, method, path }) => {
context.subscribe('operation', ({ method, operation, path }) => {
const identifierData = context.file({ id: 'types' })!.identifier({
$ref: operationIrRef({ id: operation.id, type: 'data' }),
namespace: 'type',
Expand Down Expand Up @@ -314,7 +314,7 @@ const generateFlatServices = ({ context }: { context: IRContext }) => {
const file = context.file({ id: servicesId })!;
const typesModule = file.relativePathToFile({ context, id: 'types' });

context.subscribe('operation', ({ operation, method, path }) => {
context.subscribe('operation', ({ method, operation, path }) => {
const identifierData = context.file({ id: 'types' })!.identifier({
$ref: operationIrRef({ id: operation.id, type: 'data' }),
namespace: 'type',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export const emptyModel: Model = {
};

const generateEnum = ({
leadingComment,
comments,
leadingComment,
meta,
obj,
onNode,
Expand Down
Loading

0 comments on commit 043fe20

Please sign in to comment.