Skip to content

Commit

Permalink
Merge pull request moleculerjs#1272 from marceliwac/typed-lifecycle-h…
Browse files Browse the repository at this point in the history
…andlers

feat: Expand the type for ServiceSchema to allow for typed lifecycle handlers
  • Loading branch information
icebob authored Apr 27, 2024
2 parents 6a5c249 + 4c6eff6 commit 2bfa49a
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 83 deletions.
16 changes: 9 additions & 7 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -720,12 +720,14 @@ declare namespace Moleculer {
version?: string | number;
}

type ServiceSyncLifecycleHandler<S = ServiceSettingSchema> = (this: Service<S>) => void;
type ServiceAsyncLifecycleHandler<S = ServiceSettingSchema> = (
this: Service<S>
type ServiceSyncLifecycleHandler<S = ServiceSettingSchema, T = Service<S>> = (
this: T
) => void;
type ServiceAsyncLifecycleHandler<S = ServiceSettingSchema, T = Service<S>> = (
this: T
) => void | Promise<void>;

interface ServiceSchema<S = ServiceSettingSchema> {
interface ServiceSchema<S = ServiceSettingSchema, T = void> {
name: string;
version?: string | number;
settings?: S;
Expand All @@ -737,9 +739,9 @@ declare namespace Moleculer {
hooks?: ServiceHooks;

events?: ServiceEvents;
created?: ServiceSyncLifecycleHandler<S> | ServiceSyncLifecycleHandler<S>[];
started?: ServiceAsyncLifecycleHandler<S> | ServiceAsyncLifecycleHandler<S>[];
stopped?: ServiceAsyncLifecycleHandler<S> | ServiceAsyncLifecycleHandler<S>[];
created?: ServiceSyncLifecycleHandler<S, T> | ServiceSyncLifecycleHandler<S, T>[];
started?: ServiceAsyncLifecycleHandler<S, T> | ServiceAsyncLifecycleHandler<S, T>[];
stopped?: ServiceAsyncLifecycleHandler<S, T> | ServiceAsyncLifecycleHandler<S, T>[];

[name: string]: any;
}
Expand Down
205 changes: 133 additions & 72 deletions test/typescript/hello-world/greeter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,88 +2,149 @@

import { Service } from "../../../";
import { Context } from "../../../";
import { ActionHooks, ServiceHooks, ServiceHooksAfter, ServiceSchema } from "../../../";
import { ServiceSettingSchema, ServiceSchema } from "../../../";

type GreeterWelcomeParams = {
name: string
};
export interface ActionWelcomeParams {
name: string;
}

export default class GreeterService extends Service {
constructor(broker) {
super(broker);
interface GreeterSettings extends ServiceSettingSchema {
defaultName: string;
}

this.parseServiceSchema({
name: "greeter",
hooks: {
before: {
welcome(ctx: Context<GreeterWelcomeParams>): void {
// console.log(`Service hook "before".`);
ctx.params.name = ctx.params.name.toUpperCase();
}
interface GreeterMethods {
uppercase(str: string): string;
}

interface GreeterLocalVars {
myVar: string;
}

type GreeterThis = Service<GreeterSettings> & GreeterMethods & GreeterLocalVars;

const GreeterService: ServiceSchema<GreeterSettings, GreeterThis> = {
name: "greeter",

/**
* Hooks
*/
hooks: {
before: {
welcome(this: GreeterThis, ctx: Context<ActionWelcomeParams>): void {
// console.log(`Service hook "before".`);
ctx.params.name = this.uppercase(ctx.params.name);
}
},
after: {
hello: "anotherHookAfter",
welcome: [
function (this: GreeterThis, ctx: Context<ActionWelcomeParams>, res: any): any {
// console.log(`Service sync hook "after".`);
return res;
},
after: {
hello: "anotherHookAfter",
welcome: [
function (ctx: Context<GreeterWelcomeParams>, res: any): any {
// console.log(`Service sync hook "after".`);
return res;
},
async (ctx: Context<GreeterWelcomeParams>, res: any): Promise<any> => {
// console.log(`Service async hook "after".`);
return await Promise.resolve(res);
},
"anotherHookAfter"
],
} as ServiceHooksAfter,
error: {
welcome(ctx: Context<GreeterWelcomeParams>, err: Error): void {
// console.log(`Service hook "error".`);
throw err;
}
}
} as ServiceHooks,
actions: {
hello: {
handler: this.hello,
hooks: {
after: [
function (ctx: Context<GreeterWelcomeParams>, res: any): any {
// console.log(`Action sync hook "after".`);
return res;
},
async (ctx: Context<GreeterWelcomeParams>, res: any): Promise<any> => {
// console.log(`Action async hook "after".`);
return await Promise.resolve(res);
},
"anotherHookAfter"
]
} as ActionHooks
async function (this: GreeterThis, ctx: Context<ActionWelcomeParams>, res: any): Promise<any> {
// console.log(`Service async hook "after".`);
return await Promise.resolve(res);
},
welcome: this.welcome
"anotherHookAfter"
],
},
error: {
welcome(this: GreeterThis, ctx: Context<ActionWelcomeParams>, err: Error): void {
// console.log(`Service hook "error".`);
throw err;
}
}
},

/**
* Settings
*/
settings: {
defaultName: "Moleculer",
},

/**
* Dependencies
*/
dependencies: [],

/**
* Actions
*/
actions: {
hello: {
rest: {
method: "GET",
path: "/hello",
},
handler(this: GreeterThis, ctx: Context): string {
return `Hello ${this.settings.defaultName}`;
},
hooks: {
after: [
function (this: GreeterThis, ctx: Context, res: any): any {
// console.log(`Action sync hook "after".`);
return res;
},
async function (ctx: Context, res: any): Promise<any> {
// console.log(`Action async hook "after".`);
return await Promise.resolve(res);
},
"anotherHookAfter"
]
}
} as ServiceSchema);
}
},

welcome: {
rest: "GET /welcome/:name",
params: {
name: "string",
},
handler(this: GreeterThis, ctx: Context<ActionWelcomeParams>): string {
return `Welcome, ${ctx.params.name}`;
},
},
},

/**
* Events
*/
events: {},

/**
* Say a 'Hello'
*
* @returns
* Methods
*/
hello() {
return "Hello Moleculer TS";
}
methods: {
uppercase(this: GreeterThis, str: string): string {
return str.toUpperCase();
},

async anotherHookAfter(this: GreeterThis, ctx: Context, res: any): Promise<void> {
return await Promise.resolve(res);
}
},

/**
* Service created lifecycle event handler
*/
created(this: GreeterThis) {
this.logger.info(`${this.name} service - lifecycle method "created" called.`);
},

/**
* Welcome a username
*
* @param {String} name - User name
* Service started lifecycle event handler
*/
welcome(ctx: Context<GreeterWelcomeParams>) {
return `Welcome, ${ctx.params.name}!`;
}

async anotherHookAfter(ctx: Context<GreeterWelcomeParams>, res: any): Promise<any> {
// console.log(`Another async hook "after".`);
return await Promise.resolve(res);
}
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;
6 changes: 2 additions & 4 deletions test/typescript/hello-world/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 2bfa49a

Please sign in to comment.