colonelbundy / moleculer-decorators Goto Github PK
View Code? Open in Web Editor NEWdecorators for moleculer
License: MIT License
decorators for moleculer
License: MIT License
I was doing some tests on my services and noticed that the service is started 2 times and it seems to me the second instance replaces the first one, I put a console.log in the class constructor and it was fired 2 times.
The expected behavior was that the class's constructor was called only once, which is more strange is that if you have to add other parameters to the constructor (DI) the first time the constructor is called the additional parameters are undefined and only exist on the second call .
Please provide detailed steps for reproducing the issue.
Create a service using TS and classes;
Put a console.log inside the constructor;
Export the class with a function decorator;
import { Context, Service as MoleculerService, ServiceBroker } from 'moleculer'
import { Action, Service } from 'moleculer-decorators'
import { verify, sign } from 'jsonwebtoken'
import { IAuth } from '@Interfaces'
import { TimeInSeconds } from '../../src/helpers/enums/time'
@Service({ name: 'Auth' })
class AuthService extends MoleculerService {
public constructor(broker: ServiceBroker) {
super(broker)
console.log('Called At: ', new Date())
}
@Action({
cache: {
enabled: true,
ttl: TimeInSeconds.FiveMinutes,
},
params: {
token: 'string'
}
})
public async DecodeJWT(ctx: Context<{token: string}>): Promise<IAuth.JwtPayload | string | null> {
try {
return verify(ctx.params.token, process.env.JWT_SECRET)
} catch (error) {
return null
}
}
@Action({
params: {
id: 'string',
email: { type: 'string', optional: true },
name: 'string'
}
})
public async GenerateJWT(ctx: Context<IAuth.JwtDto>): Promise<string | null> {
try {
const { id, email, name } = ctx.params
return sign({id, email, name}, process.env.JWT_SECRET)
} catch (error) {
return null
}
}
}
export default (broker: ServiceBroker): MoleculerService => new AuthService(broker)
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
1.3.0
0.14.16
v14.16.1
MacOS BigSur 11.4
$ ts-node ./node_modules/moleculer/bin/moleculer-runner.js --env --repl --hot services/**/*.ts services/**/*.js
[2021-08-19T13:41:43.652Z] INFO nmac-1381.local-46462/BROKER: Moleculer v0.14.16 is starting...
[2021-08-19T13:41:43.654Z] INFO nmac-1381.local-46462/BROKER: Namespace: <not defined>
[2021-08-19T13:41:43.654Z] INFO nmac-1381.local-46462/BROKER: Node ID: nmac-1381.local-46462
[2021-08-19T13:41:43.655Z] INFO nmac-1381.local-46462/REGISTRY: Strategy: RoundRobinStrategy
[2021-08-19T13:41:43.655Z] INFO nmac-1381.local-46462/REGISTRY: Discoverer: LocalDiscoverer
[2021-08-19T13:41:43.656Z] INFO nmac-1381.local-46462/BROKER: Serializer: JSONSerializer
[2021-08-19T13:41:43.669Z] INFO nmac-1381.local-46462/BROKER: Validator: FastestValidator
[2021-08-19T13:41:43.671Z] INFO nmac-1381.local-46462/BROKER: Registered 14 internal middleware(s).
Called At: 2021-08-19T13:41:49.022Z
Called At: 2021-08-19T13:41:49.023Z
Hi @ColonelBundy , I think you forgot to compile the package for couple of recent releases?
Nice work on this project btw, thanks for your contribution.
moleculer-0.14 is about to be released (it is at 0.14-rc1). Does this package need to be updated for the new version?
i am trying to make an es7 template project with moleculer-cli generated project .now it can run with npm run dev
without error,
but it can't pass npm test , with errors:
● Test 'products' service › Test hooks › encountered a declaration exception
ServiceSchemaError: Service name can't be empty! Maybe it is not a valid Service schema. Maybe is it not a service schema?
4 | const DbMixin = require("../mixins/db.mixin");
5 |
> 6 | @Service({
| ^
7 | name: 'products',
8 | // constructOverride: false,
9 | // skipHandler: true,
at Object.parseServiceSchema (../node_modules/moleculer/src/service.js:92:10)
at new Service (../node_modules/moleculer/src/service.js:63:9)
at new ProductsService (products.service.js:6:1)
at new <anonymous> (../node_modules/moleculer-decorators/dist/index.js:128:9)
at ServiceBroker.createService (../node_modules/moleculer/src/service-broker.js:801:14)
at Suite.<anonymous> (../test/unit/services/products.spec.js:149:10)
at Suite.<anonymous> (../test/unit/services/products.spec.js:146:2)
at Object.<anonymous> (../test/unit/services/products.spec.js:9:1)
the error was begin from :
products.spec.js
...
describe("Test hooks", () => {
const broker = new ServiceBroker({ logger: false });
const createActionFn = jest.fn();
broker.createService(TestService, { // <- error start from here, while it try to merge mock action to TestService Schema
actions: {
create: {
handler: createActionFn
}
}
});
...
}
and createService run into :
...
createService(schema, schemaMods) {
let service;
schema = this.normalizeSchemaConstructor(schema);
if (Object.prototype.isPrototypeOf.call(this.ServiceFactory, schema)) {
service = new schema(this, schemaMods); // <- run into here to new Decoratored Schema, but it with schemaMods, this is why it throws error , beacause `new service(broker, schemaMods)` will call parseServiceSchema, it will check name of schemaMods , but shemaMods don't have name properties. see bellow code .
} else {
let s = schema;
if (schemaMods)
s = this.ServiceFactory.mergeSchemas(schema, schemaMods);
console.log('s', s);
service = new this.ServiceFactory(this, s);
}
}
...
i notice that , moleculer-decorators return Service as :
return class extends parentService.constructor {
constructor(broker, schema) { // <- new schema(this, schemaMods)
super(broker, schema); // <- here, it will call `Moleculer.Service`, with params broker and schemaMods
this.parseServiceSchema(base);
}
};
};
class Service {
...
constructor(broker, schema) {
if (!isObject(broker))
throw new ServiceSchemaError("Must set a ServiceBroker instance!");
this.broker = broker;
if (broker)
this.Promise = broker.Promise;
if (schema)
this.parseServiceSchema(schema); // <- ran into here
}
/**
* Parse Service schema & register as local service
*
* @param {Object} schema of Service
*/
parseServiceSchema(schema) {
if (!isObject(schema))
throw new ServiceSchemaError("The service schema can't be null. Maybe is it not a service schema?");
this.originalSchema = _.cloneDeep(schema);
if (schema.mixins) {
schema = Service.applyMixins(schema);
}
if (isFunction(schema.merged)) {
schema.merged.call(this, schema);
} else if (Array.isArray(schema.merged)) {
schema.merged.forEach(fn => fn.call(this, schema));
}
this.broker.callMiddlewareHookSync("serviceCreating", [this, schema]);
if (!schema.name) { // <- error throws here
console.error("Service name can't be empty! Maybe it is not a valid Service schema. Maybe is it not a service schema?", { schema });
throw new ServiceSchemaError("Service name can't be empty! Maybe it is not a valid Service schema. Maybe is it not a service schema?", { schema });
}
...
Hi @ColonelBundy this is not an bug issue but just wanna ask you to follow the right semver versioning scheme when releasing breaking changes, such as the recent merged PR from #13.
This PR created breaking change, released under patches 1.0.21 -> 1.0.22, causing our CI/CD process to fail....
Again thanks for the hard work on this module but respect the versioning scheme pls <3
Hi There,
I'm trying to use decorators with Typescript. In your README.md example, we can set cache value per action:
// With options
// No need for "handler:{}" here
@Action({
cache: false,
params: {
a: "number",
b: "number"
}
})
But when defining an @Action
the type checking complains as it isn't defined:
export interface ActionOptions extends Partial<Action> {
name?: string,
handler?: ActionHandler<any>, // Not really used
skipHandler?: boolean
}
It appears, "cache" should be defined in the Typescript typedef? Or perhaps I'm missing something key?
Reflect metadata throws an error when trying to use the @service() decorator on a class
import { Service, Action } from 'moleculer-decorators';
@Service()
export default class DemoController {
@Action({
rest: 'GET /welcome',
})
welcome(ctx) {
return 'Hello'
}
}
node_modules/reflect-metadata/Reflect.js
function DecorateConstructor(decorators, target) {
for (var i = decorators.length - 1; i >= 0; --i) {
var decorator = decorators[i];
var decorated = decorator(target);
if (!IsUndefined(decorated) && !IsNull(decorated)) {
//Throws error because decorated is not a function but an object
if (!IsConstructor(decorated)) throw new TypeError();
target = decorated;
}
}
return target;
}
On closer inspection of decorator i see it returns an object. A class decorator should return nothing or a new function constructor.
function (constructor) {
let base = {
name: ''
};
const _options = Object.assign({}, defaultServiceOptions, options);
Object.defineProperty(base, 'name', {
value: options.name || constructor.name,
writable: false,
enumerable: true
});
if (options.name) {
delete options.name;
}
Object.assign(base, _.omit(options, Object.keys(defaultServiceOptions)));
const parentService = constructor.prototype;
const vars = [];
Object.getOwnPropertyNames(parentService).forEach(function (key) {
if (key === 'constructor') {
if (_options.constructOverride) {
const ServiceClass = new parentService.constructor(mockServiceBroker);
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) {
vars[key] = Object.getOwnPropertyDescriptor(ServiceClass, key).value;
}
}
});
const bypass = Object.defineProperty;
const obj = {};
bypass(obj, 'created', {
value: function created(broker) {
for (let key in vars) {
this[key] = vars[key];
}
if (Object.getOwnPropertyDescriptor(parentService, 'created') !=
null) {
Object.getOwnPropertyDescriptor(parentService, 'created').value.call(this, broker);
}
},
writable: true,
enumerable: true,
configurable: true
});
base['created'] = obj.created;
}
return;
}
const descriptor = Object.getOwnPropertyDescriptor(parentService, 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;
}
if (key === 'afterConnected' ||
key === 'entityCreated' ||
key === 'entityUpdated' ||
key === 'entityRemoved') {
base[key] = descriptor.value;
return;
}
});
return base;
}
When i'm trying to use typeorm together with moleculer-decorators i throws an error decorating Service
TypeError in node_modules/reflect-metadata/Reflect.js:544:31
I have a problem, with moleculer-db
mixed into one of my services. moleculer-db
contains lifecycle entity functions, that aren't actions, methods or events. They are global in the same way as created()
, started()
and stopped()
functions.
Namely
afterConnected() {
...
},
entityCreated(json, ctx) {
...
},
entityUpdated(json, ctx) {
...
},
entityRemoved(json, ctx) {
...
},
And the problem is, that when I use db adapter, the moment when it connects to database, it's calling a function, which doesn't exists in the schema under this.schema.afterConnected
and therefore, this service and its lifecycle db event won't trigger
@Service({
name: "users",
mixins: [DbService],
adapter: new MongooseDbAdapter(process.env.MONGO_URI),
model: UserModel
})
class UsersService extends BaseSchema {
...
public async afterConnected() {
const count = await this.adapter.count();
if (count === 0) {
await this.seedDB();
} else {
this.logger.info(`${count} users in DB. Skipping seed...`);
}
}
}
Hello,
After I updated to 1.0.15, I've been getting this error when starting up my services. After some digging, I've found that BaseSchema
is no longer exported in the exported js file. Downgrading to 1.0.14 somehow fixes the issue.
Hi,
The following program works with moleculer-decorators 1.0.21, but fails with 1.1.0:
/*
tsconfig.json:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
}
package.json:
{
"name": "wobble",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "tsc"
},
"dependencies": {
"@types/node": "*",
"moleculer": "0.13.7",
"moleculer-decorators": "1.1.0",
"typescript": "^3.5.2"
}
}
*/
import { ServiceBroker } from "moleculer";
import { Service, Action } from 'moleculer-decorators';
@Service({
version: 'v1',
})
class MyService {
@Action()
test() {
return "Yep it works";
}
}
const broker = new ServiceBroker();
broker.createService(MyService);
async function run() {
await broker.start();
const result = await broker.call('v1.MyService.test');
console.log(result);
await broker.stop();
}
run();
Output with 1.1.0:
ServiceNotFoundError: Service 'v1.MyService.test' is not found.
Expected output, as produced by earlier versions:
Yep it works
I followed the exact tutorial in the Moleculer documentation, this is the code:
import moleculer from 'moleculer';
import ApiGateway from 'moleculer-web';
import { Service } from 'moleculer-decorators';
@Service({
mixins: [ApiGateway]
})
class ApiService extends moleculer.Service {
settings = {
port: process.env.PORT || 8080,
};
}
export = ApiService;
I'm getting the following error at mixins: [ApiGateway]
:
Type 'ServiceSchema<ServiceSettingSchema> & { Errors: ApiGatewayErrors; }' is not assignable to type 'ServiceSchema<ServiceSettingSchema>'.
Types of property 'dependencies' are incompatible.
Type 'string | GenericObject | string[] | GenericObject[]' is not assignable to type 'string | ServiceDependency | (string | ServiceDependency)[]'.
Type 'GenericObject' is not assignable to type 'string | ServiceDependency | (string | ServiceDependency)[]'.
Type 'GenericObject' is not assignable to type 'string'.
Am I doing anything wrong?
name: mixins are not applied properly
Please answer the following questions for yourself before submitting an issue.
MainService mixins[ServiceFoo]... so expecting main.foo
to exist
//foo.service
'use strict';
const moleculer = require('moleculer');
const { Service, Action, Method, Event } = require('moleculer-decorators');
@Service({})
class foo extends moleculer.Service {
// Optional constructor
constructor(broker,s?) {
super(broker,s);
}
@Action()
foo() {
return 'baz';
}
@Method
foo2(){
return 'baz2';
}
}
module.exports = foo;
//main.service
"use strict";
const moleculer = require('moleculer');
const { Service, Action, Event, Method } = require('moleculer-decorators');
const foo = require('./foo.service');
@Service({
mixins: [foo],
settings: {
}
})
class main extends moleculer.Service {
// Optional constructor
constructor(broker, s) {
super(broker, s)
this.settings = { // Overrides above by default, to prevent this, add "constructOverride: false" to @Service
foo: "mainbaz"
}
}
// @Action({
// skipHandler: true // Any options will be merged with the mixin's action.
// })
// foo() { // this function will never be called since a mixin will override it, unless you specify skipHandler: false.
// }
// With options
// No need for "handler:{}" here
@Action()
yo(ctx) {
return "yo"
}
}
const broker = new moleculer.ServiceBroker({
namespace: "test",
logger: console,
logLevel: "debug",
});
broker.createService(main);
broker.start();
broker.repl();
export {}
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
Good day, everyone! Firstly, thank you for your package!
I have a small question: how I can define decorators methods and properties for my class?
For example:
import { Action, Service } from 'moleculer-decorators'
@Service({ name: 'users' })
export class UsersService {
@Action()
sample() {
this.logger.info('What?')
}
}
The TS compiler doesn't know about logger
property, I can define it manually, but... Can I have beauty solution for all properties and service methods? And also how I can define mixin implementation of some methods?
Thank you!
I use moleculer 0.13, and I can't either loadService
and createService
from service that written using molecular decorator.
Test 'user_session' service › encountered a declaration exception
Invalid '[object Object]' type in validator schema!
4 | describe(`Test 'user_session' service`, () => {
5 | let broker = new ServiceBroker()
> 6 | broker.loadService('./user_session/service.ts')
| ^
7 |
8 | beforeAll(() => broker.start())
9 | afterAll(() => broker.stop())
I have a doubt with Action decorator, here is the source code:
export function Action(options: ActionOptions = {}) {
return function(target, key: string, descriptor: PropertyDescriptor) {
if (!options.skipHandler) {
options.handler = descriptor.value;
} else {
delete options.skipHandler;
}
(target.actions || (target.actions = {}))[key] = options
? {
...options
}
: options.skipHandler
? ""
: descriptor.value;
};
}
first if
:
target.actions assignation:
options
exists, then { ...options } ELSE check options.skipHandler
here is the error, because if options.skipHandler
exist is deleted in previous if
and you are checking the "else" condition of "if (options)" then, the else condition is that "options" is not defined, then it is not possible that "options.skipHandler" could be defined.This is giving me a typescript error:
TS2339: Property 'skipHandler' does not exist on type 'never'.
There are some other errors with "noImplicitAny: true" in tsconfig.json that avoid build in projects that are using moleculer-decorators with that config in tsconfig.json, maybe could be useful to add this compilerOptions:
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": false,
"noStrictGenericChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": false,
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
noted the issue still exists. Heres the test repo https://github.com/giang12/moldecormixinsbug
Originally posted by @giang12 in #15 (comment)
Is there a plan to add a typesupport for mixins?
At the moment I can't call methods which were created in a mixin. Instead Of casting to any.
Because of different moleculer type versions the following type error occours:
Type 'ActionSchema' is not assignable to type 'boolean | ActionSchema | ActionHandler<any>'.
Upgrading moleculer dependency to 0.14.7 solves this issue
Workaround: Add moleculer as peerDependency
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.