@athenna/http 5.48.0 → 5.49.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@athenna/http",
3
- "version": "5.48.0",
3
+ "version": "5.49.0",
4
4
  "description": "The Athenna Http server. Built on top of fastify.",
5
5
  "license": "MIT",
6
6
  "author": "João Lenon <lenon@athenna.io>",
@@ -238,17 +238,25 @@ export class Route extends Macroable {
238
238
  * ```
239
239
  */
240
240
  schema(options) {
241
- const { schema, zod } = normalizeRouteSchema(options);
241
+ const { schema, swaggerSchema, zod } = normalizeRouteSchema(options);
242
242
  this.route.fastify.schema = schema;
243
243
  if (zod) {
244
244
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
245
245
  // @ts-ignore
246
246
  this.route.fastify.config.zod = zod;
247
+ if (Object.keys(zod.response).length) {
248
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
249
+ // @ts-ignore
250
+ this.route.fastify.config.swaggerSchema = swaggerSchema;
251
+ }
247
252
  }
248
253
  else {
249
254
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
250
255
  // @ts-ignore
251
256
  delete this.route.fastify.config.zod;
257
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
258
+ // @ts-ignore
259
+ delete this.route.fastify.config.swaggerSchema;
252
260
  }
253
261
  return this;
254
262
  }
@@ -23,6 +23,7 @@ export type RouteZodSchemas = {
23
23
  };
24
24
  export declare function normalizeRouteSchema(options: RouteSchemaOptions): {
25
25
  schema: FastifySchema;
26
+ swaggerSchema: FastifySchema;
26
27
  zod: RouteZodSchemas | null;
27
28
  };
28
29
  export declare function parseRequestWithZod(req: FastifyRequest, schemas: RouteZodSchemas): Promise<void>;
@@ -8,11 +8,11 @@
8
8
  */
9
9
  import { Is } from '@athenna/common';
10
10
  import { ZodValidationException } from '#src/exceptions/ZodValidationException';
11
- import { ResponseValidationException } from '#src/exceptions/ResponseValidationException';
12
11
  export function normalizeRouteSchema(options) {
13
12
  const request = {};
14
13
  const response = {};
15
14
  const schema = { ...options };
15
+ const swaggerSchema = { ...options };
16
16
  const requestKeys = ['body', 'headers', 'params', 'querystring'];
17
17
  requestKeys.forEach(key => {
18
18
  if (!isZodSchema(options[key])) {
@@ -20,20 +20,27 @@ export function normalizeRouteSchema(options) {
20
20
  }
21
21
  request[key] = options[key];
22
22
  schema[key] = toJsonSchema(options[key], 'input');
23
+ swaggerSchema[key] = toJsonSchema(options[key], 'input');
23
24
  });
24
25
  if (options.response && Is.Object(options.response)) {
25
26
  schema.response = { ...options.response };
27
+ swaggerSchema.response = { ...options.response };
26
28
  Object.entries(options.response).forEach(([statusCode, value]) => {
27
29
  if (!isZodSchema(value)) {
28
30
  return;
29
31
  }
30
32
  response[statusCode] = value;
31
- schema.response[statusCode] = toJsonSchema(value, 'output');
33
+ swaggerSchema.response[statusCode] = toJsonSchema(value, 'output');
34
+ delete schema.response[statusCode];
32
35
  });
36
+ if (!Object.keys(schema.response).length) {
37
+ delete schema.response;
38
+ }
33
39
  }
34
40
  const hasZodSchemas = Object.keys(request).length > 0 || Object.keys(response).length > 0;
35
41
  return {
36
42
  schema,
43
+ swaggerSchema,
37
44
  zod: hasZodSchemas ? { request, response } : null
38
45
  };
39
46
  }
@@ -57,9 +64,8 @@ export async function parseResponseWithZod(reply, payload, schemas) {
57
64
  if (!schema) {
58
65
  return payload;
59
66
  }
60
- return parseSchema(schema, payload).catch(error => {
61
- throw new ResponseValidationException(error);
62
- });
67
+ const result = await schema.safeParseAsync(payload);
68
+ return result.success ? result.data : payload;
63
69
  }
64
70
  function getResponseSchema(statusCode, schemas) {
65
71
  return (schemas[statusCode] ||
@@ -138,6 +138,7 @@ export declare class ServerImpl extends Macroable {
138
138
  options(options: Omit<RouteJson, 'methods'>): void;
139
139
  private toRouteHooks;
140
140
  private getFastifyOptionsWithOpenApiSchema;
141
+ private configureSwaggerTransform;
141
142
  private getOpenApiRouteSchema;
142
143
  private getOpenApiPathCandidates;
143
144
  private normalizePath;
@@ -282,13 +282,21 @@ export class ServerImpl extends Macroable {
282
282
  const automaticSchema = this.getOpenApiRouteSchema(options);
283
283
  const fastifyOptions = { ...options.fastify };
284
284
  if (!automaticSchema) {
285
+ this.configureSwaggerTransform(fastifyOptions);
285
286
  return fastifyOptions;
286
287
  }
287
288
  const normalizedSchema = normalizeRouteSchema(automaticSchema);
288
289
  const currentConfig = { ...(fastifyOptions.config || {}) };
290
+ const currentSwaggerSchema =
291
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
292
+ // @ts-ignore
293
+ currentConfig.swaggerSchema || fastifyOptions.schema;
289
294
  fastifyOptions.schema = this.mergeFastifySchemas(normalizedSchema.schema, fastifyOptions.schema);
290
295
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
291
296
  // @ts-ignore
297
+ currentConfig.swaggerSchema = this.mergeFastifySchemas(normalizedSchema.swaggerSchema, currentSwaggerSchema);
298
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
299
+ // @ts-ignore
292
300
  const currentZod = currentConfig.zod;
293
301
  const mergedZod = this.mergeZodSchemas(normalizedSchema.zod, currentZod);
294
302
  if (mergedZod) {
@@ -297,8 +305,31 @@ export class ServerImpl extends Macroable {
297
305
  currentConfig.zod = mergedZod;
298
306
  }
299
307
  fastifyOptions.config = currentConfig;
308
+ this.configureSwaggerTransform(fastifyOptions);
300
309
  return fastifyOptions;
301
310
  }
311
+ configureSwaggerTransform(fastifyOptions) {
312
+ const config = fastifyOptions?.config;
313
+ if (!config?.swaggerSchema) {
314
+ return;
315
+ }
316
+ const customTransform = config.swaggerTransform;
317
+ if (customTransform === false) {
318
+ return;
319
+ }
320
+ config.swaggerTransform = (args) => {
321
+ const transformed = Is.Function(customTransform)
322
+ ? customTransform(args)
323
+ : args;
324
+ if (transformed === false) {
325
+ return false;
326
+ }
327
+ return {
328
+ ...transformed,
329
+ schema: this.mergeFastifySchemas(transformed?.schema || args.schema, config.swaggerSchema)
330
+ };
331
+ };
332
+ }
302
333
  getOpenApiRouteSchema(options) {
303
334
  const paths = Config.get('openapi.paths', {});
304
335
  const methods = options.methods || [];
@@ -1,5 +0,0 @@
1
- import type { ZodError } from 'zod';
2
- import { HttpException } from '#src/exceptions/HttpException';
3
- export declare class ResponseValidationException extends HttpException {
4
- constructor(error: ZodError);
5
- }
@@ -1,11 +0,0 @@
1
- import { HttpException } from '#src/exceptions/HttpException';
2
- export class ResponseValidationException extends HttpException {
3
- constructor(error) {
4
- const name = 'ResponseValidationException';
5
- const code = 'E_RESPONSE_VALIDATION_ERROR';
6
- const status = 500;
7
- const message = 'The server failed to generate a valid response.';
8
- const details = error.issues;
9
- super({ name, message, status, code, details });
10
- }
11
- }