UNPKG

@opra/common

Version:
154 lines (153 loc) 6.23 kB
import { isConstructor } from '@jsopen/objects'; import { resolveThunk } from '../../helpers/index.js'; import { WS_CONTROLLER_METADATA, WS_PARAM_METADATA } from '../constants.js'; import { WSApi } from '../ws/ws-api.js'; import { WSController } from '../ws/ws-controller.js'; import { WSOperation } from '../ws/ws-operation.js'; import { DataTypeFactory } from './data-type.factory.js'; /** * @class WSApiFactory */ export class WSApiFactory { /** * Generates MQApi * @param context * @param init */ static async createApi(context, init) { const api = new WSApi(init); if (init.controllers) { await context.enterAsync('.controllers', async () => { if (Array.isArray(init.controllers)) { for (const c of init.controllers) { const controller = await this._createController(context, api, c); if (controller) api.controllers.set(controller.name, controller); } } else { for (const [k, v] of Object.entries(init.controllers)) { const controller = await this._createController(context, api, v, k); if (controller) api.controllers.set(controller.name, controller); } } }); } return api; } static async _createController(context, parent, thunk, name) { if (typeof thunk === 'function' && !isConstructor(thunk)) { thunk = thunk(); } thunk = await resolveThunk(thunk); let ctor; let metadata; let instance; // If thunk is a class if (typeof thunk === 'function') { metadata = Reflect.getMetadata(WS_CONTROLLER_METADATA, thunk); if (!metadata) return context.addError(`Class "${thunk.name}" doesn't have a valid WSController metadata`); ctor = thunk; } else { // If thunk is an instance of a class decorated with WSController() ctor = Object.getPrototypeOf(thunk).constructor; metadata = Reflect.getMetadata(WS_CONTROLLER_METADATA, ctor); if (metadata) instance = thunk; else { // If thunk is a DecoratorMetadata or InitArguments metadata = thunk; if (thunk.instance === 'object') { instance = thunk.instance; ctor = Object.getPrototypeOf(instance).constructor; } } } if (!metadata) return context.addError(`Class "${ctor.name}" is not decorated with WSController()`); name = name || metadata.name; if (!name) throw new TypeError(`Controller name required`); const controller = new WSController(parent, { ...metadata, name, instance, ctor, }); if (metadata.types) { await context.enterAsync('.types', async () => { await DataTypeFactory.addDataTypes(context, controller, metadata.types); }); } if (metadata.operations) { await context.enterAsync('.operations', async () => { for (const [operationName, _operationMeta] of Object.entries(metadata.operations)) { await context.enterAsync(`[${operationName}]`, async () => { const argumentsMetadata = Reflect.getMetadata(WS_PARAM_METADATA, ctor.prototype, operationName); let operationMeta = _operationMeta; if (argumentsMetadata) { operationMeta = { ...operationMeta, arguments: argumentsMetadata, }; } const operation = new WSOperation(controller, { ...operationMeta, name: operationName, types: undefined, type: undefined, keyType: undefined, arguments: undefined, response: undefined, }); await this._initWSOperation(context, operation, operationMeta); controller.operations.set(operationName, operation); }); } }); } return controller; } /** * Initializes WSOperation * @param context * @param operation * @param metadata * @protected */ static async _initWSOperation(context, operation, metadata) { if (metadata.types) { await context.enterAsync('.types', async () => { await DataTypeFactory.addDataTypes(context, operation, metadata.types); }); } if (metadata.arguments?.length) { await context.enterAsync('.arguments', async () => { operation.arguments = []; let x; for (x of metadata.arguments) { const xx = {}; if (typeof x === 'object' && x.type && x.parameterIndex !== null) { Object.assign(xx, x); } else xx.type = x; const t = await DataTypeFactory.resolveDataType(context, operation, xx.type); operation.arguments?.push({ type: t, parameterIndex: xx.parameterIndex, }); } }); operation.arguments.sort((a, b) => a.parameterIndex - b.parameterIndex); } if (metadata.response) { await context.enterAsync('.response', async () => { operation.response = await DataTypeFactory.resolveDataType(context, operation, metadata.response); }); } } }