@postxl/generators 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend-rest-api/generators/model-controller.generator.js +46 -5
- package/dist/backend-rest-api/generators/model-controller.generator.js.map +1 -1
- package/dist/backend-rest-api/rest-api.generator.js +5 -1
- package/dist/backend-rest-api/rest-api.generator.js.map +1 -1
- package/dist/backend-rest-api/template/restApi/src/restApi.utils.ts +34 -0
- package/dist/backend-rest-api/template/restApi/src/zod-exception.filter.ts +42 -0
- package/package.json +1 -1
- package/dist/backend-rest-api/template/zod-exception.filter.ts +0 -21
|
@@ -41,6 +41,8 @@ function generateModelController({ model, context, }) {
|
|
|
41
41
|
.add(model.types.id.decoderBase)
|
|
42
42
|
.add(model.types.id.toBranded)
|
|
43
43
|
.add(model.decoder.decoder.zodDecoder)
|
|
44
|
+
.add(model.types.filter.decoder)
|
|
45
|
+
.add(model.types.sort.decoder)
|
|
44
46
|
.add(context.view.service);
|
|
45
47
|
const { PascalCase, PascalCasePlural, camelCase } = model._conjugated;
|
|
46
48
|
// Find all Date fields that need to be overridden for JSON Schema compatibility
|
|
@@ -89,15 +91,35 @@ function generateModelController({ model, context, }) {
|
|
|
89
91
|
const duComment = discriminatedUnionFields.length > 0
|
|
90
92
|
? `\n// Note: Discriminated union fields omitted from REST API: ${discriminatedUnionFields.map((f) => f.name).join(', ')}`
|
|
91
93
|
: '';
|
|
94
|
+
// Generate filter fields for the API description
|
|
95
|
+
const filterableFields = Array.from(model.fields.values()).filter((f) => f.kind !== 'discriminatedUnion');
|
|
96
|
+
const filterFieldNames = filterableFields.map((f) => f.name);
|
|
97
|
+
const filterFieldsDescription = [...filterFieldNames, 'globalSearch'].join(', ');
|
|
98
|
+
// Generate a good example with two fields if possible
|
|
99
|
+
const stringField = filterableFields.find((f) => f.kind === 'scalar' && f.type === 'string' && f.name !== 'id');
|
|
100
|
+
const booleanField = filterableFields.find((f) => f.kind === 'scalar' && f.type === 'boolean');
|
|
101
|
+
const exampleParts = [];
|
|
102
|
+
if (stringField) {
|
|
103
|
+
exampleParts.push(`"${stringField.name}":{"contains":"test"}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
exampleParts.push(`"id":{"contains":"test"}`);
|
|
107
|
+
}
|
|
108
|
+
if (booleanField) {
|
|
109
|
+
exampleParts.push(`"${booleanField.name}":[true]`);
|
|
110
|
+
}
|
|
111
|
+
const filterExample = `{${exampleParts.join(',')}}`;
|
|
92
112
|
return /* ts */ `
|
|
93
113
|
import '@fastify/cookie'
|
|
94
114
|
import { Controller, Get, Logger, Param, Query } from '@nestjs/common'
|
|
115
|
+
import { ApiQuery } from '@nestjs/swagger'
|
|
95
116
|
import { createZodDto, ZodResponse } from 'nestjs-zod'
|
|
96
117
|
${imports.generate()}
|
|
97
118
|
import z from 'zod'
|
|
98
119
|
|
|
99
120
|
import { createFieldsDecoder, selectFields } from '@utils/fieldSelection'
|
|
100
|
-
import {
|
|
121
|
+
import { paginatedResultDecoder, paginationDecoderFromQuery } from '@utils/pagination'
|
|
122
|
+
import { jsonQueryParam } from './restApi.utils'
|
|
101
123
|
|
|
102
124
|
// REST API decoder safe for JSON Schema / OpenAPI generation
|
|
103
125
|
// Reuses ${model.decoder.decoder.zodDecoder.name} but overrides fields with transforms or non-JSON-representable types${duComment}
|
|
@@ -132,6 +154,8 @@ const get${PascalCase}ResponseDecoder = z.object({
|
|
|
132
154
|
|
|
133
155
|
const get${PascalCasePlural}Decoder = paginationDecoderFromQuery.extend({
|
|
134
156
|
fields: ${camelCase}FieldsDecoder,
|
|
157
|
+
filters: jsonQueryParam(${model.types.filter.decoder.name}),
|
|
158
|
+
sort: jsonQueryParam(${model.types.sort.decoder.name}),
|
|
135
159
|
})
|
|
136
160
|
|
|
137
161
|
const get${PascalCasePlural}ResponseDecoder = paginatedResultDecoder(z${PascalCase}RestApiDecoder.partial())
|
|
@@ -171,14 +195,31 @@ export class ${model.restApi.controller.name} {
|
|
|
171
195
|
/**
|
|
172
196
|
* Retrieve all ${model.userFriendlyNamePlural}.
|
|
173
197
|
*/
|
|
198
|
+
@ApiQuery({
|
|
199
|
+
name: 'filters',
|
|
200
|
+
required: false,
|
|
201
|
+
description: \`JSON filter object. Fields: ${filterFieldsDescription}. String fields accept: values (array), contains, startsWith, endsWith, exclude. Date fields accept: from, to. Boolean fields accept an array of true/false.\`,
|
|
202
|
+
example: '${filterExample}',
|
|
203
|
+
})
|
|
204
|
+
@ApiQuery({
|
|
205
|
+
name: 'sort',
|
|
206
|
+
required: false,
|
|
207
|
+
description: \`JSON array of sort objects. Each object has "field" (${filterFieldNames.join(', ')}) and "direction" (asc, desc).\`,
|
|
208
|
+
example: '[{"field":"${stringField?.name ?? filterFieldNames[0] ?? 'id'}","direction":"asc"}]',
|
|
209
|
+
})
|
|
174
210
|
@ZodResponse({ type: Get${PascalCasePlural}ResponseDto })
|
|
175
211
|
@Get('')
|
|
176
212
|
async get${PascalCasePlural}(@Query() query: Get${PascalCasePlural}Dto): Promise<Get${PascalCasePlural}Response> {
|
|
177
|
-
const
|
|
178
|
-
|
|
213
|
+
const result = await this.viewService.${model.view.service.variableName}.getFilteredPaginated({
|
|
214
|
+
user: this.viewService.users.data.rootUser,
|
|
215
|
+
filters: query.filters ?? {},
|
|
216
|
+
sort: query.sort,
|
|
217
|
+
cursor: query.cursor,
|
|
218
|
+
limit: query.limit,
|
|
219
|
+
})
|
|
179
220
|
return {
|
|
180
|
-
...
|
|
181
|
-
data:
|
|
221
|
+
...result,
|
|
222
|
+
data: result.data.map((item) => to${PascalCase}RestApi(item, query.fields)),
|
|
182
223
|
}
|
|
183
224
|
}
|
|
184
225
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-controller.generator.js","sourceRoot":"","sources":["../../../src/backend-rest-api/generators/model-controller.generator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,
|
|
1
|
+
{"version":3,"file":"model-controller.generator.js","sourceRoot":"","sources":["../../../src/backend-rest-api/generators/model-controller.generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -95,9 +95,13 @@ import { ZodExceptionFilter } from '@restApi/zod-exception.filter'
|
|
|
95
95
|
vfs.insertFromVfs({ targetPath: 'libs/restApi', vfs: vfsLib });
|
|
96
96
|
// Load static template file
|
|
97
97
|
await vfs.loadFile({
|
|
98
|
-
diskPath: path.resolve(__dirname, './template/zod-exception.filter.ts'),
|
|
98
|
+
diskPath: path.resolve(__dirname, './template/restApi/src/zod-exception.filter.ts'),
|
|
99
99
|
targetPath: 'libs/restApi/src/zod-exception.filter.ts',
|
|
100
100
|
});
|
|
101
|
+
await vfs.loadFile({
|
|
102
|
+
diskPath: path.resolve(__dirname, './template/restApi/src/restApi.utils.ts'),
|
|
103
|
+
targetPath: 'libs/restApi/src/restApi.utils.ts',
|
|
104
|
+
});
|
|
101
105
|
await vfs.loadFile({
|
|
102
106
|
diskPath: path.resolve(__dirname, './template/utils/src/fieldSelection.ts'),
|
|
103
107
|
targetPath: 'libs/utils/src/fieldSelection.ts',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-api.generator.js","sourceRoot":"","sources":["../../src/backend-rest-api/rest-api.generator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"rest-api.generator.js","sourceRoot":"","sources":["../../src/backend-rest-api/rest-api.generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Zod schema for a query parameter that accepts a JSON string
|
|
5
|
+
* and parses it into the specified schema type.
|
|
6
|
+
*
|
|
7
|
+
* OpenAPI will show this as an optional string parameter.
|
|
8
|
+
* At runtime, the string is parsed as JSON and validated against the schema.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const decoder = z.object({
|
|
12
|
+
* filters: jsonQueryParam(zMyFilterSchema),
|
|
13
|
+
* })
|
|
14
|
+
* // Accepts: ?filters={"name":{"contains":"test"}}
|
|
15
|
+
*/
|
|
16
|
+
export const jsonQueryParam = <T extends z.ZodTypeAny>(schema: T) =>
|
|
17
|
+
z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.transform((val, ctx) => {
|
|
21
|
+
if (!val) {
|
|
22
|
+
return undefined
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(val)
|
|
26
|
+
} catch (e) {
|
|
27
|
+
ctx.addIssue({
|
|
28
|
+
code: 'custom',
|
|
29
|
+
message: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}`,
|
|
30
|
+
})
|
|
31
|
+
return z.NEVER
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.pipe(schema.optional())
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ArgumentsHost, Catch, HttpException, Logger } from '@nestjs/common'
|
|
2
|
+
import { BaseExceptionFilter } from '@nestjs/core'
|
|
3
|
+
|
|
4
|
+
import { ZodSerializationException, ZodValidationException } from 'nestjs-zod'
|
|
5
|
+
import z from 'zod'
|
|
6
|
+
|
|
7
|
+
@Catch(HttpException)
|
|
8
|
+
export class ZodExceptionFilter extends BaseExceptionFilter {
|
|
9
|
+
private readonly logger = new Logger(ZodExceptionFilter.name)
|
|
10
|
+
|
|
11
|
+
override catch(exception: HttpException, host: ArgumentsHost) {
|
|
12
|
+
if (exception instanceof ZodValidationException) {
|
|
13
|
+
const zodError = exception.getZodError()
|
|
14
|
+
if (zodError instanceof z.ZodError) {
|
|
15
|
+
// Extract meaningful error messages from Zod errors
|
|
16
|
+
const errors = zodError.issues.map((issue) => ({
|
|
17
|
+
path: issue.path.join('.'),
|
|
18
|
+
message: issue.message,
|
|
19
|
+
}))
|
|
20
|
+
this.logger.warn('ZodValidationException: ' + JSON.stringify(errors))
|
|
21
|
+
|
|
22
|
+
// Create a more informative response
|
|
23
|
+
const response = host.switchToHttp().getResponse()
|
|
24
|
+
response.status(400).send({
|
|
25
|
+
statusCode: 400,
|
|
26
|
+
message: 'Validation failed',
|
|
27
|
+
errors,
|
|
28
|
+
})
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (exception instanceof ZodSerializationException) {
|
|
34
|
+
const zodError = exception.getZodError()
|
|
35
|
+
if (zodError instanceof z.ZodError) {
|
|
36
|
+
this.logger.error('ZodSerializationException: ' + zodError.message)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
super.catch(exception, host)
|
|
41
|
+
}
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { ArgumentsHost, Catch, HttpException, Logger } from '@nestjs/common'
|
|
2
|
-
import { BaseExceptionFilter } from '@nestjs/core'
|
|
3
|
-
|
|
4
|
-
import { ZodSerializationException } from 'nestjs-zod'
|
|
5
|
-
import { ZodError } from 'zod'
|
|
6
|
-
|
|
7
|
-
@Catch(HttpException)
|
|
8
|
-
export class ZodExceptionFilter extends BaseExceptionFilter {
|
|
9
|
-
private readonly logger = new Logger(ZodExceptionFilter.name)
|
|
10
|
-
|
|
11
|
-
override catch(exception: HttpException, host: ArgumentsHost) {
|
|
12
|
-
if (exception instanceof ZodSerializationException) {
|
|
13
|
-
const zodError = exception.getZodError()
|
|
14
|
-
if (zodError instanceof ZodError) {
|
|
15
|
-
this.logger.error('ZodSerializationException: ' + zodError.message)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
super.catch(exception, host)
|
|
20
|
-
}
|
|
21
|
-
}
|