moleculer-decorators
Version:
decorators for moleculer
130 lines (107 loc) • 4.29 kB
text/typescript
import { ServiceSchema, Action, ActionHandler, LoggerInstance, ServiceMethods, ServiceEvents, Actions, Context, ServiceSettingSchema, GenericObject } from 'moleculer';
import * as _ from 'lodash';
import Bluebird = require('bluebird');
const blacklist = ['created', 'started', 'stopped', 'actions', 'methods', 'events'];
const blacklist2 = ['metadata', 'settings', 'mixins', 'name', 'version'].concat(blacklist);
const defaultServiceOptions: Options = {
constructOverride: true
}
export class BaseSchema {
logger: LoggerInstance;
name: string;
version?: string | number;
settings?: ServiceSettingSchema;
metadata?: GenericObject;
mixins?: Array<ServiceSchema>;
actions?: Actions;
methods?: ServiceMethods;
events?: ServiceEvents;
}
export interface Options extends Partial<ServiceSchema> {
name?: string
constructOverride?: boolean
}
export interface ActionOptions extends Partial<Action> {
name?: string,
handler?: ActionHandler // Not really used
}
export function Method(target, key, descriptor) {
(target.methods || (target.methods = {}))[key] = descriptor.value
}
export function Event(target, key, descriptor) {
(target.events || (target.events = {}))[key] = descriptor.value
}
export function Action(options: ActionOptions = {}) {
return function(target, key, descriptor) {
(target.actions || (target.actions = {}))[key] = (options ? {
...options,
handler: descriptor.value
} : descriptor.value);
}
}
export function Service(options: Options = {}) : any {
return function(target) {
let base = {}
const _options = _.extend({}, defaultServiceOptions, options);
Object.defineProperty(base, 'name', {
value: options.name || target.name,
writable: false,
enumerable: true
});
if (options.name) {
delete options.name; // not needed
}
Object.assign(base, _.omit(options, _.keys(defaultServiceOptions))); // Apply
const proto = target.prototype;
const vars = [];
Object.getOwnPropertyNames(proto).forEach(function (key) {
if (key === 'constructor') {
if (_options.constructOverride) { // Override properties defined in @Service
const ServiceClass = new target.prototype[key];
Object.getOwnPropertyNames(ServiceClass).forEach(function(key) {
if (blacklist.indexOf(key) === -1 && !_.isFunction(ServiceClass[key])) {
base[key] = Object.getOwnPropertyDescriptor(ServiceClass, key)!.value
if (blacklist2.indexOf(key) === -1) { // Needed, otherwize if the service is used as a mixin, these variables will overwrite the toplevel's
vars[key] = Object.getOwnPropertyDescriptor(ServiceClass, key)!.value
}
}
});
/* Insane hack below :D
* It's needed since moleculer don't transfer all defined props in the
* schema to the actual service, so we have to do it.
*/
const bypass: any = Object.defineProperty, // typescript fix
obj: any = {}; // placeholder
bypass(obj, 'created', {
value: function created() {
for (let key in vars) {
this[key] = vars[key];
}
if (!_.isNil(Object.getOwnPropertyDescriptor(proto, 'created'))) {
Object.getOwnPropertyDescriptor(proto, 'created').value.call(this);
}
},
writable: true,
enumerable: true,
configurable: true
});
base['created'] = obj.created;
}
return;
}
const descriptor = Object.getOwnPropertyDescriptor(proto, key)!
if (key === 'created' && !_options.constructOverride) {
base[key] = descriptor.value;
}
if (key === 'started' || key === 'stopped') {
base[key] = descriptor.value;
return;
}
if (key === 'events' || key === 'methods' || key === 'actions') {
(base[key] ? Object.assign(base[key], descriptor.value) : base[key] = descriptor.value)
return;
}
});
return base;
}
}