UNPKG

express-cargo

Version:

express middleware for class-based request parsing

233 lines (232 loc) 9.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: Object.getOwnPropertyDescriptor(all, name).get }); } _export(exports, { get bindingCargo () { return bindingCargo; }, get getCargo () { return getCargo; } }); const _types = require("./types"); const _metadata = require("./metadata"); const _errorHandler = require("./errorHandler"); function getErrorKey(sourceKey, currentKey) { return sourceKey ? `${sourceKey}.${currentKey}` : currentKey; } function typeCasting(type, elementType, sourceKey, key, value, errors, sources, currentSource) { switch(type){ case String: return String(value); case Number: { const parsedNumber = Number(value); if (isNaN(parsedNumber) || typeof value === 'string' && value.trim() === '') { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), `${key} must be a valid number`)); return undefined; } return parsedNumber; } case Boolean: return value === true || value === 'true'; case Date: { const parsedDate = new Date(value); if (isNaN(parsedDate.getTime())) { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), `${key} must be a valid date`)); return undefined; } return parsedDate; } case Array: { if (!Array.isArray(value)) { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), `${key} must be an array`)); return undefined; } if (!elementType) return value; return value.map((element, i)=>typeCasting(elementType, undefined, sourceKey, `${key}[${i}]`, element, errors, sources, currentSource)); } default: { const nextSources = { ...sources, [currentSource]: value }; return bindObject(type, nextSources, errors, getErrorKey(sourceKey, key)); } } } function bindObject(objectClass, sources, errors, sourceKey = '') { const metaClass = new _metadata.CargoClassMetadata(objectClass.prototype); metaClass.markBindingCargoCalled(); const targetObject = new objectClass(); const fields = metaClass.getFieldList(); const requestFields = metaClass.getRequestFieldList(); const virtualFields = metaClass.getVirtualFieldList(); // request transform for (const property of requestFields){ const meta = metaClass.getFieldMetadata(property); if (!meta) continue; let value; const metaKey = meta.getKey(); const key = typeof metaKey === 'string' ? metaKey : metaKey.description; if (!key) { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), 'empty string or symbol is not allowed')); continue; } const requestTransformer = meta.getRequestTransformer(); if (!requestTransformer) { errors.push(new _types.CargoTransformFieldError(property, `${key} does not have transformer`)); continue; } try { value = requestTransformer(sources.req); if (value === undefined || value === null) { if (meta.getDefault() !== undefined) { targetObject[property] = meta.getDefault(); continue; } else if (meta.getOptional()) { targetObject[property] = null; } else { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), `${key} is required`)); } } else { targetObject[property] = value; } for (const rule of meta.getValidators()){ const error = rule.validate(targetObject[property], targetObject); if (error) { errors.push(error); } } } catch (error) { errors.push(new _types.CargoTransformFieldError(property, `Error while computing request transform field: ${error instanceof Error ? error.message : String(error)}`)); } } // source decorator parsing for (const property of fields){ const meta = metaClass.getFieldMetadata(property); if (!meta) continue; let value; const metaKey = meta.getKey(); const key = typeof metaKey === 'string' ? metaKey : metaKey.description; if (!key) { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), 'empty string or symbol is not allowed')); continue; } const currentSource = meta.getSource(); const currentSourceData = sources[currentSource]; if (currentSourceData) { value = currentSourceData[key]; } if (value === undefined || value === null) { if (meta.getDefault() !== undefined) { targetObject[property] = meta.getDefault(); continue; } else if (meta.getOptional()) { targetObject[property] = null; continue; } else { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), `${key} is required`)); continue; } } if (meta.getEnumType() !== undefined) { targetObject[property] = value; } else { targetObject[property] = typeCasting(meta.type, meta.getArrayElementType(), sourceKey, key, value, errors, sources, currentSource); } const transformer = meta.getTransformer(); if (transformer) { targetObject[property] = transformer(targetObject[property]); } for (const rule of meta.getValidators()){ const error = rule.validate(targetObject[property], targetObject); if (error) { errors.push(error); } } } // virtual transform for (const property of virtualFields){ const meta = metaClass.getFieldMetadata(property); if (!meta) continue; let value; const metaKey = meta.getKey(); const key = typeof metaKey === 'string' ? metaKey : metaKey.description; if (!key) { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), 'empty string or symbol is not allowed')); continue; } const virtualTransformer = meta.getVirtualTransformer(); if (!virtualTransformer) { errors.push(new _types.CargoTransformFieldError(property, `${key} does not have transformer`)); continue; } try { value = virtualTransformer(targetObject); if (value === undefined || value === null) { if (meta.getDefault() !== undefined) { targetObject[property] = meta.getDefault(); continue; } else if (meta.getOptional()) { targetObject[property] = null; } else { errors.push(new _types.CargoFieldError(getErrorKey(sourceKey, key), `${key} is required`)); } } else { targetObject[property] = value; } for (const rule of meta.getValidators()){ const error = rule.validate(targetObject[property], targetObject); if (error) { errors.push(error); } } } catch (error) { errors.push(new _types.CargoTransformFieldError(property, `Error while computing virtual field: ${error instanceof Error ? error.message : String(error)}`)); } } return targetObject; } function bindingCargo(cargoClass) { return (req, res, next)=>{ try { const errors = []; const sources = { req: req, body: req.body, query: req.query, params: req.params, header: req.headers, session: req.session }; const cargo = bindObject(cargoClass, sources, errors); if (errors.length > 0) { throw new _types.CargoValidationError(errors); } req._cargo = cargo; next(); } catch (err) { if (err instanceof _types.CargoValidationError) { const handler = (0, _errorHandler.getCargoErrorHandler)(); if (handler) { return handler(err, req, res, next); } } next(err); } }; } function getCargo(req) { return req._cargo; }