Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Expand the type for ServiceSchema to allow for typed lifecycle handlers #1272

Merged
merged 5 commits into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 "../../../";
marceliwac marked this conversation as resolved.
Show resolved Hide resolved
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);
icebob marked this conversation as resolved.
Show resolved Hide resolved
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
Loading