UNPKG

@tuzhanai/schema-manager

Version:
343 lines (342 loc) 12 kB
"use strict"; /** * @gz/schema-manager * * @author Zongmin Lei <leizongmin@gmail.com> * @author Yourtion Guo <yourtion@gmail.com> */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseTypeName = exports.SchemaType = exports.SchemaManager = void 0; const assert = __importStar(require("assert")); const value_type_manager_1 = require("@tuzhanai/value-type-manager"); __exportStar(require("@tuzhanai/value-type-manager"), exports); class SchemaManager { constructor(options = {}) { this.options = options; this.map = new Map(); if (options.type) { this.type = options.type; } else { this.type = new value_type_manager_1.ValueTypeManager(); } } /** * 获取 abortEarly 选项 */ get isAbortEarly() { return this.options.abortEarly || false; } /** * 注册Schema * @param name 名称 * @param fields 字段具体信息 */ register(name, fields) { this.map.set(name, new SchemaType(this, fields, name)); return this; } /** * 是否已注册指定Schema * @param type 名称 */ has(type) { return this.map.has(type); } /** * 获取指定Schema * @param type 名称 */ get(type) { const s = this.map.get(type); assert.ok(s, `schema type "${type}" does not exists`); return s; } /** * 遍历类型 * @param iter 迭代函数 */ forEach(iter) { return this.map.forEach(iter); } /** * 创建Schema但不自动注册 * @param fields 字段信息 * @param name Schema名称 */ create(fields, name) { return new SchemaType(this, fields, name); } /** * 检查指定Schema并返回值 * @param type 名称 * @param input 输入值 */ value(type, input) { const { name, isArray } = parseTypeName(type); if (this.has(name)) { return this.get(type).value(input, isArray); } const ret = this.baseTypeValue(name, isArray, input, {}, undefined); if (ret.ok) return ret; return { ...ret, missingParamaters: [], invalidParamaters: [], invalidParamaterTypes: [] }; } /** * 检查指定基本类型并返回值 * @param type 名称 * @param isArray 是否为数组 * @param input 输入值 * @param params 类型参数 * @param format 是否格式化 */ baseTypeValue(type, isArray, input, params, format) { if (isArray) { // 数组 if (!Array.isArray(input)) { return { ok: false, message: `expected an ${type} array but got ${input}`, value: input }; } const messages = []; const values = []; for (let i = 0; i < input.length; i++) { const ret = this.type.value(type, input[i], params, format); if (!ret.ok) { messages.push(`at array index ${i}: ${ret.message}`); if (this.isAbortEarly) break; } values.push(ret.value); } if (messages.length > 0) { return { ok: false, message: messages.join("\n"), value: values }; } return { ok: true, message: "success", value: values }; } return this.type.value(type, input, params, format); } } exports.SchemaManager = SchemaManager; class SchemaType { constructor(manager, fields, name = "") { this.manager = manager; this.fields = fields; this.name = name; } /** * 从当前Schema获取仅包含指定字段的新Schema * @param fieldNames */ pick(...fieldNames) { fieldNames.sort(); const fields = {}; for (const n of fieldNames) { fields[n] = this.fields[n]; } return new SchemaType(this.manager, fields, this.name && `Pick<${this.name}, ${fieldNames.map((n) => `"${n}"`).join(" | ")}>`); } /** * 从当前Schema获取所有字段为可选的新Schema */ partial() { const fields = {}; for (const n in this.fields) { fields[n] = { ...this.fields[n], required: false }; } return new SchemaType(this.manager, fields, this.name && `Partial<${this.name}>`); } /** * 从当前Schema获取所有字段为必填的新Schema */ required() { const fields = {}; for (const n in this.fields) { fields[n] = { ...this.fields[n], required: true }; } return new SchemaType(this.manager, fields, this.name && `Required<${this.name}>`); } /** * 检查Schema并返回值 * @param input */ value(input, isArray = false) { if (isArray) { // 数组 if (!Array.isArray(input)) { return { ok: false, message: `expected an ${this.name} array but got ${input}`, value: input }; } const messages = []; const values = []; for (let i = 0; i < input.length; i++) { const ret = this.value(input[i], false); if (!ret.ok) { messages.push(`at array index ${i}: ${ret.message}`); if (this.manager.isAbortEarly) break; } values.push(ret.value); } if (messages.length > 0) { return { ok: false, message: messages.join("\n"), value: values }; } return { ok: true, message: "success", value: values }; } const messages = []; const missingParamaters = []; const invalidParamaters = []; const invalidParamaterTypes = []; const values = {}; // 遍历处理输入的 input 信息,与 对应的 Schema Type 的配置进行比较 for (const n in this.fields) { const f = this.fields[n]; const v = input[n] === undefined ? f.default : input[n]; // 判断是否缺失某个字段 if (v === undefined) { if (f.required) { messages.push(`missing required paramater ${n}`); missingParamaters.push(n); if (this.manager.isAbortEarly) break; } // 没有该字段,直接循环下一个 字段 continue; } let ret; if (f.type instanceof SchemaType) { ret = f.type.value(v); } else { const { name, isArray } = parseTypeName(f.type); if (this.manager.has(name)) { ret = this.manager.get(name).value(v, isArray); } else { ret = this.manager.baseTypeValue(name, isArray, v, f.params, f.format); } } if (!ret.ok) { messages.push(`at paramater ${n}: ${ret.message}`); invalidParamaters.push(n); invalidParamaterTypes.push(f.type instanceof SchemaType ? f.type.name : f.type); if (this.manager.isAbortEarly) break; } values[n] = ret.value; } if (messages.length > 0) { return { ok: false, message: messages.join("\n"), missingParamaters, invalidParamaters, invalidParamaterTypes, value: values, }; } return { ok: true, message: "success", value: values }; } /** * 获取 schema swagger 的展开信息 * @param {boolean} [isArray=false] * @returns {Record<string, any>} ISchemaTypeFieldInfo * @memberof SchemaType */ swaggerValue() { const values = {}; const required = []; for (const n in this.fields) { const f = this.fields[n]; values[n] = {}; // 判断是否是嵌套的 Schema if (f.type instanceof SchemaType) { let { required, properties } = f.type.swaggerValue(); values[n] = { type: "object", required: required, properties: properties, }; } else { const { name, isArray } = parseTypeName(f.type); if (this.manager.has(name) && isArray) { let { required, properties } = this.manager.get(name).swaggerValue(); values[n].type = "array"; values[n].description = f.comment || ""; values[n].items = { type: "object", required: required, properties: properties, }; } else if (this.manager.has(name)) { let { required, properties } = this.manager.get(name).swaggerValue(); values[n] = { type: "object", required: required, description: f.comment || "", properties: properties, }; } else if (isArray) { values[n] = { type: "array", description: f.comment, enum: f.params || [], items: { type: this.manager.type.get(name).info.swaggerType, }, }; f.default ? (values[n].default = f.default) : ""; f.required ? required.push(n) : ""; } else { values[n] = { type: this.manager.type.get(name).info.swaggerType, description: f.comment, enum: f.params || [], }; f.default ? (values[n].default = f.default) : ""; f.required ? required.push(n) : ""; } } } return { required, properties: values, }; } } exports.SchemaType = SchemaType; /** * 解析类型名称 判断在书写类型的时候 是否是 "type[]" 这种格式,是否是数组类型 * @param type 类型 */ function parseTypeName(type) { if (type.slice(-2) === "[]") { return { name: type.slice(0, -2), isArray: true }; } return { name: type, isArray: false }; } exports.parseTypeName = parseTypeName; exports.default = SchemaManager;