@athenna/http
Version:
The Athenna Http server. Built on top of fastify.
102 lines (101 loc) • 3.47 kB
JavaScript
/**
* @athenna/http
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Is } from '@athenna/common';
import { ZodValidationException } from '#src/exceptions/ZodValidationException';
export function normalizeRouteSchema(options) {
const request = {};
const response = {};
const schema = { ...options };
const swaggerSchema = { ...options };
const requestKeys = ['body', 'headers', 'params', 'querystring'];
requestKeys.forEach(key => {
if (!isZodSchema(options[key])) {
return;
}
request[key] = options[key];
schema[key] = toJsonSchema(options[key], 'input');
swaggerSchema[key] = toJsonSchema(options[key], 'input');
});
if (options.response && Is.Object(options.response)) {
schema.response = { ...options.response };
swaggerSchema.response = { ...options.response };
Object.entries(options.response).forEach(([statusCode, value]) => {
if (!isZodSchema(value)) {
return;
}
response[statusCode] = value;
swaggerSchema.response[statusCode] = toJsonSchema(value, 'output');
delete schema.response[statusCode];
});
if (!Object.keys(schema.response).length) {
delete schema.response;
}
}
const hasZodSchemas = Object.keys(request).length > 0 || Object.keys(response).length > 0;
return {
schema,
swaggerSchema,
zod: hasZodSchemas ? { request, response } : null
};
}
export async function parseRequestWithZod(req, schemas) {
const requestSchemas = schemas.request;
if (requestSchemas.body) {
req.body = await parseSchema(requestSchemas.body, req.body);
}
if (requestSchemas.headers) {
req.headers = await parseSchema(requestSchemas.headers, req.headers);
}
if (requestSchemas.params) {
req.params = await parseSchema(requestSchemas.params, req.params);
}
if (requestSchemas.querystring) {
req.query = await parseSchema(requestSchemas.querystring, req.query);
}
}
export async function parseResponseWithZod(reply, payload, schemas) {
const schema = getResponseSchema(reply.statusCode, schemas.response);
if (!schema) {
return payload;
}
const result = await schema.safeParseAsync(payload);
return result.success ? result.data : payload;
}
function getResponseSchema(statusCode, schemas) {
return (schemas[statusCode] ||
schemas[String(statusCode)] ||
schemas[`${String(statusCode)[0]}xx`] ||
schemas.default ||
null);
}
async function parseSchema(schema, data) {
const result = await schema.safeParseAsync(data);
if (!result.success) {
throw new ZodValidationException(result.error);
}
return result.data;
}
function toJsonSchema(schema, io) {
const jsonSchemaMethod = schema['~standard']?.jsonSchema?.[io] ||
schema.toJSONSchema;
if (!jsonSchemaMethod) {
return {};
}
const jsonSchema = jsonSchemaMethod({
target: 'draft-07',
libraryOptions: { unrepresentable: 'any' }
});
delete jsonSchema.$schema;
return jsonSchema;
}
function isZodSchema(value) {
return (Is.Defined(value) &&
Is.Function(value.parse) &&
Is.Function(value.safeParseAsync));
}