@opra/common
Version:
Opra common package
200 lines (199 loc) • 8.32 kB
JavaScript
import { isConstructor } from '@jsopen/objects';
import { resolveThunk } from '../../helpers/index.js';
import { MQ_CONTROLLER_METADATA } from '../constants.js';
import { MQApi } from '../mq/mq-api.js';
import { MQController } from '../mq/mq-controller.js';
import { MQHeader } from '../mq/mq-header.js';
import { MQOperation } from '../mq/mq-operation.js';
import { MQOperationResponse } from '../mq/mq-operation-response.js';
import { DataTypeFactory } from './data-type.factory.js';
/**
* @class MQApiFactory
*/
export class MQApiFactory {
/**
* Generates MQApi
* @param context
* @param init
*/
static async createApi(context, init) {
const api = new MQApi(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(MQ_CONTROLLER_METADATA, thunk);
if (!metadata)
return context.addError(`Class "${thunk.name}" doesn't have a valid MQController metadata`);
ctor = thunk;
}
else {
// If thunk is an instance of a class decorated with MQController()
ctor = Object.getPrototypeOf(thunk).constructor;
metadata = Reflect.getMetadata(MQ_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 MQController()`);
name = name || metadata.name;
if (!name)
throw new TypeError(`Controller name required`);
const controller = new MQController(parent, {
...metadata,
name,
instance,
ctor,
});
if (metadata.types) {
await context.enterAsync('.types', async () => {
await DataTypeFactory.addDataTypes(context, controller, metadata.types);
});
}
if (metadata.headers) {
await context.enterAsync('.headers', async () => {
let i = 0;
for (const v of metadata.headers) {
await context.enterAsync(`[${i++}]`, async () => {
const prmArgs = { ...v };
await context.enterAsync('.type', async () => {
if (v.type)
prmArgs.type = controller.node.findDataType(v.type);
if (!prmArgs.type && typeof v.type === 'object') {
prmArgs.type = await DataTypeFactory.createDataType(context, controller, v.type);
}
if (!prmArgs.type)
prmArgs.type = controller.node.getDataType('any');
});
const prm = new MQHeader(controller, prmArgs);
controller.headers.push(prm);
});
}
});
}
if (metadata.operations) {
await context.enterAsync('.operations', async () => {
for (const [operationName, operationMeta] of Object.entries(metadata.operations)) {
await context.enterAsync(`[${operationName}]`, async () => {
const operation = new MQOperation(controller, {
...operationMeta,
name: operationName,
types: undefined,
type: undefined,
keyType: undefined,
});
await this._initMQOperation(context, operation, operationMeta);
controller.operations.set(operation.name, operation);
});
}
});
}
return controller;
}
/**
* Initializes MQOperation
* @param context
* @param operation
* @param metadata
* @protected
*/
static async _initMQOperation(context, operation, metadata) {
if (metadata.types) {
await context.enterAsync('.types', async () => {
await DataTypeFactory.addDataTypes(context, operation, metadata.types);
});
}
operation.type = await DataTypeFactory.resolveDataType(context, operation, metadata.type);
if (metadata.keyType) {
operation.keyType = await DataTypeFactory.resolveDataType(context, operation, metadata.keyType);
}
if (metadata.headers) {
await context.enterAsync('.headers', async () => {
let i = 0;
for (const v of metadata.headers) {
await context.enterAsync(`[${i++}]`, async () => {
const prmArgs = { ...v };
await context.enterAsync('.type', async () => {
prmArgs.type = await DataTypeFactory.resolveDataType(context, operation, v.type);
});
const prm = new MQHeader(operation, prmArgs);
operation.headers.push(prm);
});
}
});
}
if (metadata.response) {
await context.enterAsync('.response', async () => {
const response = new MQOperationResponse(operation, {
...metadata.response,
type: undefined,
keyType: undefined,
});
await this._initMQOperationResponse(context, response, metadata.response);
operation.response = response;
});
}
}
/**
* Initializes MQOperationResponse
* @param context
* @param response
* @param metadata
* @protected
*/
static async _initMQOperationResponse(context, response, metadata) {
response.type = await DataTypeFactory.resolveDataType(context, response, metadata.type);
if (metadata.keyType) {
response.keyType = await DataTypeFactory.resolveDataType(context, response, metadata.keyType);
}
if (metadata.headers) {
await context.enterAsync('.headers', async () => {
let i = 0;
for (const v of metadata.headers) {
await context.enterAsync(`[${i++}]`, async () => {
const prmArgs = { ...v };
await context.enterAsync('.type', async () => {
prmArgs.type = await DataTypeFactory.resolveDataType(context, response, v.type);
});
const prm = new MQHeader(response, prmArgs);
response.headers.push(prm);
});
}
});
}
}
}