From a5e4e44737c89a6c94fbec9218a12363ee97b37b Mon Sep 17 00:00:00 2001 From: Marceli Wac Date: Mon, 5 Feb 2024 12:56:46 +0000 Subject: [PATCH 1/5] feat: Expand the type for ServiceSchema to allow for typed lifecycle handlers See more in this PR: https://github.com/moleculerjs/moleculer-template-project-typescript/issues/70 --- index.d.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index c6dbcf354..d95ddafbe 100644 --- a/index.d.ts +++ b/index.d.ts @@ -720,12 +720,14 @@ declare namespace Moleculer { version?: string | number; } - type ServiceSyncLifecycleHandler = (this: Service) => void; - type ServiceAsyncLifecycleHandler = ( - this: Service + type ServiceSyncLifecycleHandler> = ( + this: T + ) => void; + type ServiceAsyncLifecycleHandler> = ( + this: T ) => void | Promise; - interface ServiceSchema { + interface ServiceSchema { name: string; version?: string | number; settings?: S; @@ -737,9 +739,9 @@ declare namespace Moleculer { hooks?: ServiceHooks; events?: ServiceEvents; - created?: ServiceSyncLifecycleHandler | ServiceSyncLifecycleHandler[]; - started?: ServiceAsyncLifecycleHandler | ServiceAsyncLifecycleHandler[]; - stopped?: ServiceAsyncLifecycleHandler | ServiceAsyncLifecycleHandler[]; + created?: ServiceSyncLifecycleHandler | ServiceSyncLifecycleHandler[]; + started?: ServiceAsyncLifecycleHandler | ServiceAsyncLifecycleHandler[]; + stopped?: ServiceAsyncLifecycleHandler | ServiceAsyncLifecycleHandler[]; [name: string]: any; } From 1677c553b797eb11f004d05b8f2d8dc443cbd2f9 Mon Sep 17 00:00:00 2001 From: Marceli Wac Date: Mon, 5 Feb 2024 14:16:02 +0000 Subject: [PATCH 2/5] test: Updated TS tests to use the latest template. The tests use the latest template from https://github.com/moleculerjs/moleculer-template-project-typescript The service also provides the types for the lifecycle methods which now call the logger to demostrate that correct object is passed. --- .../typescript/hello-world/greeter.service.ts | 160 +++++++++--------- test/typescript/hello-world/index.ts | 6 +- 2 files changed, 86 insertions(+), 80 deletions(-) diff --git a/test/typescript/hello-world/greeter.service.ts b/test/typescript/hello-world/greeter.service.ts index decf7c2d9..d7c919242 100644 --- a/test/typescript/hello-world/greeter.service.ts +++ b/test/typescript/hello-world/greeter.service.ts @@ -2,88 +2,96 @@ import { Service } from "../../../"; import { Context } from "../../../"; -import { ActionHooks, ServiceHooks, ServiceHooksAfter, ServiceSchema } from "../../../"; +import { ServiceSettingSchema, ServiceSchema } from "../../../"; -type GreeterWelcomeParams = { - name: string -}; +export interface ActionHelloParams { + name: string; +} + +interface GreeterSettings extends ServiceSettingSchema { + defaultName: string; +} + +interface GreeterMethods { + uppercase(str: string): string; +} + +interface GreeterLocalVars { + myVar: string; +} -export default class GreeterService extends Service { - constructor(broker) { - super(broker); - - this.parseServiceSchema({ - name: "greeter", - hooks: { - before: { - welcome(ctx: Context): void { - // console.log(`Service hook "before".`); - ctx.params.name = ctx.params.name.toUpperCase(); - } - }, - after: { - hello: "anotherHookAfter", - welcome: [ - function (ctx: Context, res: any): any { - // console.log(`Service sync hook "after".`); - return res; - }, - async (ctx: Context, res: any): Promise => { - // console.log(`Service async hook "after".`); - return await Promise.resolve(res); - }, - "anotherHookAfter" - ], - } as ServiceHooksAfter, - error: { - welcome(ctx: Context, err: Error): void { - // console.log(`Service hook "error".`); - throw err; - } - } - } as ServiceHooks, - actions: { - hello: { - handler: this.hello, - hooks: { - after: [ - function (ctx: Context, res: any): any { - // console.log(`Action sync hook "after".`); - return res; - }, - async (ctx: Context, res: any): Promise => { - // console.log(`Action async hook "after".`); - return await Promise.resolve(res); - }, - "anotherHookAfter" - ] - } as ActionHooks - }, - welcome: this.welcome - } - } as ServiceSchema); - } +type GreeterThis = Service & GreeterMethods & GreeterLocalVars; + +const GreeterService: ServiceSchema = { + name: "greeter", /** - * Say a 'Hello' - * - * @returns + * Settings */ - hello() { - return "Hello Moleculer TS"; - } + settings: { + defaultName: "Moleculer", + }, /** - * Welcome a username - * - * @param {String} name - User name + * Dependencies */ - welcome(ctx: Context) { - return `Welcome, ${ctx.params.name}!`; - } - - async anotherHookAfter(ctx: Context, res: any): Promise { - // console.log(`Another async hook "after".`); - return await Promise.resolve(res); - } + dependencies: [], + + /** + * Actions + */ + actions: { + hello: { + rest: { + method: "GET", + path: "/hello", + }, + handler(this: GreeterThis/* , ctx: Context */): string { + return `Hello ${this.settings.defaultName}`; + }, + }, + + welcome: { + rest: "GET /welcome/:name", + params: { + name: "string", + }, + handler(this: GreeterThis, ctx: Context): string { + return `Welcome, ${ctx.params.name}`; + }, + }, + }, + + /** + * Events + */ + events: {}, + + /** + * Methods + */ + methods: {}, + + /** + * Service created lifecycle event handler + */ + created(this: GreeterThis) { + this.logger.info(`${this.name} service - lifecycle method "created" called.`); + }, + + /** + * Service started lifecycle event handler + */ + async started(this: GreeterThis) { + this.logger.info(`${this.name} service - lifecycle method "started" called.`); + }, + + /** + * Service stopped lifecycle event handler + */ + async stopped(this: GreeterThis) { + this.logger.info(`${this.name} service - lifecycle method "stopped" called.`); + }, }; + +export default GreeterService; diff --git a/test/typescript/hello-world/index.ts b/test/typescript/hello-world/index.ts index ce7488e86..abc9e21b0 100644 --- a/test/typescript/hello-world/index.ts +++ b/test/typescript/hello-world/index.ts @@ -29,10 +29,8 @@ broker.loadService(path.join(__dirname, "greeter.service.ts")); await broker.call("greeter.hello"); const res = await broker.call("greeter.welcome", { name: "Typescript" }); - broker.logger.info(""); - broker.logger.info("Result: ", res); - broker.logger.info(""); - if (res != "Welcome, TYPESCRIPT!") + broker.logger.info(`Result: ${res}`); + if (res != "Welcome, Typescript") throw new Error("Result is mismatch!"); else await broker.stop(); From c18a9441792f86b6922c7d16e8f8c6877bc339b5 Mon Sep 17 00:00:00 2001 From: Marceli Wac Date: Wed, 24 Apr 2024 16:03:30 +0100 Subject: [PATCH 3/5] chore: Restore previously removed hooks, rename action type. --- .../typescript/hello-world/greeter.service.ts | 57 +++++++++++++++++-- test/typescript/hello-world/index.ts | 2 +- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/test/typescript/hello-world/greeter.service.ts b/test/typescript/hello-world/greeter.service.ts index d7c919242..6030747e4 100644 --- a/test/typescript/hello-world/greeter.service.ts +++ b/test/typescript/hello-world/greeter.service.ts @@ -4,7 +4,7 @@ import { Service } from "../../../"; import { Context } from "../../../"; import { ServiceSettingSchema, ServiceSchema } from "../../../"; -export interface ActionHelloParams { +export interface ActionWelcomeParams { name: string; } @@ -25,6 +25,38 @@ type GreeterThis = Service & GreeterMethods & GreeterLocalVars; const GreeterService: ServiceSchema = { name: "greeter", + /** + * Hooks + */ + hooks: { + before: { + welcome(ctx: Context): void { + // console.log(`Service hook "before".`); + ctx.params.name = ctx.params.name.toUpperCase(); + } + }, + after: { + hello: "anotherHookAfter", + welcome: [ + function (ctx: Context, res: any): any { + // console.log(`Service sync hook "after".`); + return res; + }, + async (ctx: Context, res: any): Promise => { + // console.log(`Service async hook "after".`); + return await Promise.resolve(res); + }, + "anotherHookAfter" + ], + }, + error: { + welcome(ctx: Context, err: Error): void { + // console.log(`Service hook "error".`); + throw err; + } + } + }, + /** * Settings */ @@ -46,9 +78,22 @@ const GreeterService: ServiceSchema = { method: "GET", path: "/hello", }, - handler(this: GreeterThis/* , ctx: Context */): string { + handler(this: GreeterThis, ctx: Context): string { return `Hello ${this.settings.defaultName}`; }, + hooks: { + after: [ + (ctx: Context, res: any): any => { + // console.log(`Action sync hook "after".`); + return res; + }, + async (ctx: Context, res: any): Promise => { + // console.log(`Action async hook "after".`); + return await Promise.resolve(res); + }, + "anotherHookAfter" + ] + } }, welcome: { @@ -56,7 +101,7 @@ const GreeterService: ServiceSchema = { params: { name: "string", }, - handler(this: GreeterThis, ctx: Context): string { + handler(this: GreeterThis, ctx: Context): string { return `Welcome, ${ctx.params.name}`; }, }, @@ -70,7 +115,11 @@ const GreeterService: ServiceSchema = { /** * Methods */ - methods: {}, + methods: { + async anotherHookAfter(ctx: Context, res: any): Promise { + return await Promise.resolve(res); + } + }, /** * Service created lifecycle event handler diff --git a/test/typescript/hello-world/index.ts b/test/typescript/hello-world/index.ts index abc9e21b0..d1313431d 100644 --- a/test/typescript/hello-world/index.ts +++ b/test/typescript/hello-world/index.ts @@ -30,7 +30,7 @@ broker.loadService(path.join(__dirname, "greeter.service.ts")); await broker.call("greeter.hello"); const res = await broker.call("greeter.welcome", { name: "Typescript" }); broker.logger.info(`Result: ${res}`); - if (res != "Welcome, Typescript") + if (res != "Welcome, TYPESCRIPT") throw new Error("Result is mismatch!"); else await broker.stop(); From 53a27c8c059ffd09b4786076b1ad8d19aebb55e9 Mon Sep 17 00:00:00 2001 From: Marceli Wac Date: Wed, 24 Apr 2024 16:22:47 +0100 Subject: [PATCH 4/5] refactor: Add actual implementation of uppercase() to methods. --- test/typescript/hello-world/greeter.service.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/typescript/hello-world/greeter.service.ts b/test/typescript/hello-world/greeter.service.ts index 6030747e4..11cbc8ef5 100644 --- a/test/typescript/hello-world/greeter.service.ts +++ b/test/typescript/hello-world/greeter.service.ts @@ -30,9 +30,9 @@ const GreeterService: ServiceSchema = { */ hooks: { before: { - welcome(ctx: Context): void { + welcome(this: GreeterThis, ctx: Context): void { // console.log(`Service hook "before".`); - ctx.params.name = ctx.params.name.toUpperCase(); + ctx.params.name = this.uppercase(ctx.params.name); } }, after: { @@ -116,7 +116,11 @@ const GreeterService: ServiceSchema = { * Methods */ methods: { - async anotherHookAfter(ctx: Context, res: any): Promise { + uppercase(this: GreeterThis, str: string): string { + return str.toUpperCase(); + }, + + async anotherHookAfter(this: GreeterThis, ctx: Context, res: any): Promise { return await Promise.resolve(res); } }, From 4c6eff6a6e7efa04bc1e6891d3c6d944653a93ce Mon Sep 17 00:00:00 2001 From: Marceli Wac Date: Wed, 24 Apr 2024 16:27:07 +0100 Subject: [PATCH 5/5] refactor: Add types for parameter this. --- test/typescript/hello-world/greeter.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/typescript/hello-world/greeter.service.ts b/test/typescript/hello-world/greeter.service.ts index 11cbc8ef5..93debc26e 100644 --- a/test/typescript/hello-world/greeter.service.ts +++ b/test/typescript/hello-world/greeter.service.ts @@ -38,11 +38,11 @@ const GreeterService: ServiceSchema = { after: { hello: "anotherHookAfter", welcome: [ - function (ctx: Context, res: any): any { + function (this: GreeterThis, ctx: Context, res: any): any { // console.log(`Service sync hook "after".`); return res; }, - async (ctx: Context, res: any): Promise => { + async function (this: GreeterThis, ctx: Context, res: any): Promise { // console.log(`Service async hook "after".`); return await Promise.resolve(res); }, @@ -50,7 +50,7 @@ const GreeterService: ServiceSchema = { ], }, error: { - welcome(ctx: Context, err: Error): void { + welcome(this: GreeterThis, ctx: Context, err: Error): void { // console.log(`Service hook "error".`); throw err; } @@ -83,11 +83,11 @@ const GreeterService: ServiceSchema = { }, hooks: { after: [ - (ctx: Context, res: any): any => { + function (this: GreeterThis, ctx: Context, res: any): any { // console.log(`Action sync hook "after".`); return res; }, - async (ctx: Context, res: any): Promise => { + async function (ctx: Context, res: any): Promise { // console.log(`Action async hook "after".`); return await Promise.resolve(res); },