UNPKG

turtle-express

Version:

`turtle-express` is kinda a framework or a library based on `express.js` with an opinionated express router with type safety and schema validation with zod. Also many [more features](https://github.com/mm-ninja-turtles/turtle-express/discussions/7) planni

215 lines 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createHandler = void 0; const request_validation_1 = require("./request-validation"); const resolver_validation_1 = require("./resolver-validation"); function createHandler(options) { const resolverType = options.resolverType ?? 'controlled'; const _handlerFunc = async (req, res, next) => { const handlerContext = { requestValidation: { params: null, query: null, body: null, }, resolverValidation: null, resolver: null, response: { _failedReason: '', success: true, status: 200, message: 'Success.', data: null, error: null, }, }; const state = { previous: 'idle', current: 'idle', }; Promise.resolve() .then(async () => { // STATE: 1 - idle if (state.current === 'idle') { state.previous = state.current; // idle state.current = 'request_validation'; } // STATE: 2 - request_validation if (state.current === 'request_validation') { if (state.previous !== 'idle') throw new Error('[case `request_validation`]: Invalid state'); // if request schema is provided if (options.request) { // run request validation const validation = (0, request_validation_1.requestValidation)(req, options.request); // store validation result in context handlerContext.requestValidation = validation.context; // if at least one validation failed if (validation.ifOneValidationFailed()) { // prepare response and response back with validation error state.previous = state.current; // request_validation state.current = 'before_response'; } // if no validation failed else { // continue to resolver function state.previous = state.current; // request_validation state.current = 'resolver'; } } // if request schema is not provided else { // skip to resolver function state.previous = state.current; // request_validation state.current = 'resolver'; } } // STATE: 3 - resolver if (state.current === 'resolver') { if (state.previous !== 'request_validation') throw new Error('[case `resolver`]: Invalid state'); // get request validations from context const { params, query, body } = handlerContext.requestValidation; // construct resolver function context object const ctx = { params: params?.success ? params.data : null, query: query?.success ? query.data : null, body: body?.success ? body.data : null, }; // execute the resolver function and store the result in context handlerContext.resolver = await options.resolver(resolverType === 'controlled' ? { ctx, req } // only provide context and request if resolver is controlled : { ctx, req, res }); state.previous = state.current; // resolver // go to resolver_validation state if resolver is controlled if (resolverType === 'controlled') state.current = 'resolver_validation'; // skip to final if resolver is not controlled else state.current = 'final'; } // STATE: 4 - resolver_validation if (state.current === 'resolver_validation') { if (state.previous !== 'resolver') throw new Error('[case `resolver_validation`]: Invalid state'); // validate resolver result with response schema and store in context handlerContext.resolverValidation = (0, resolver_validation_1.resolverValidation)(handlerContext.resolver, options.response); // go to before_response to prepare response object state.previous = state.current; // resolver_validation state.current = 'before_response'; } // STATE: 5 - before_response if (state.current === 'before_response') { // STATE: 5.1 - if previous state is request_validation if (state.previous === 'request_validation') { // request validation might have failed // get request validation from context const { params, query, body } = handlerContext.requestValidation; // construct error object const error = { params: params?.success === true ? null : params?.error.format(), query: query?.success === true ? null : query?.error.format(), body: body?.success === true ? null : body?.error.format(), }; // set error response to context handlerContext.response = { _failedReason: 'request_validation', status: 400, success: false, message: 'Validation Failed.', data: null, error, }; } // STATE: 5.2 - if previous state is resolver_validation else if (state.previous === 'resolver_validation') { // get resolver result const resolverResult = handlerContext.resolver; // get resolver validation result const resolverValidation = handlerContext.resolverValidation; // get the http status code to response // by getting the first result from the resolver result // which is the result of the resolver execution let status = Object.keys(resolverResult)[0]; // set the response message default to Success. let message = 'Success.'; // if http status is not in error range // and resolver validation failed, // validation failed if (status < 400 && resolverValidation?.success === false) { // set status to 400 with validation failed message. status = 400; message = 'Response Validation Failed.'; } // if http status is in error range // and resolver validation is success, // resolver failed response else if (status >= 400 && resolverValidation?.success === true) { // change message to Failed. message = 'Failed.'; } // construct response object and store in context handlerContext.response = { _failedReason: 'resolver_validation', status, success: resolverValidation?.success ?? false, message, data: resolverValidation?.success === true ? resolverValidation.data : null, error: resolverValidation?.success === true ? null : resolverValidation?.error.format(), }; } // STATE: 5.3 - otherwise, throw error, something might have gone wrong else throw new Error('[case `before_response`]: Invalid state'); // go to final state state.previous = state.current; // before_response state.current = 'final'; } // STATE: 6 - final if (state.current === 'final') { // STATE: 6.1 - if previous state is from resolver if (state.previous === 'resolver') { // resolver is uncontrolled type // just return the resolver result return handlerContext.resolver; } // STATE: 6.2 - if previous state is from before_response else if (state.previous === 'before_response') { // get response from context const response = handlerContext.response; const status = Number(response.status); // construct response body with context const body = { success: response.success, message: response.message, data: response.data, error: response.error, }; // response with status and body return res.status(status).send(body); } // STATE: 6.3 - otherwise, throw error something might have wrong else throw new Error('[case `final`]: Invalid state'); } }) .catch(next); }; return { _id: options.id, _middlewares: options.middlewares ?? [], _getOpenApiSchemas() { return options; }, _getOpenApiPathItem() { return options; }, _handlerFunc, }; } exports.createHandler = createHandler; //# sourceMappingURL=create-handler.js.map