Skip to content

Commit

Permalink
feat(di): allow Factory of Custom provider to have hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Apr 4, 2022
1 parent 4351ba3 commit 9b97a6d
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 6 deletions.
4 changes: 4 additions & 0 deletions docs/docs/custom-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ In order to inject a custom provider, we use the @@Inject@@ decorator. This deco

<<< @/docs/snippets/providers/custom-provider-use-value-usage.ts

::: tip
Since v6.110.0, factory and custom provider can register his own [hooks](/docs/hooks.md)!
:::

## Use Async Factory

The `useAsyncFactory` is a way of creating asynchronous providers dynamically.
Expand Down
25 changes: 25 additions & 0 deletions docs/docs/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,28 @@ export class MyModule implements BeforeInit {
::: tip Note
Database connection can be performed with Asynchronous Provider since v5.26. See [custom providers](/docs/custom-providers.md)
:::

Since v6.110.0, it's also possible to register hooks on custom provider:

```typescript
import {Configuration, registerProvider} from "@tsed/di";
import {DatabaseConnection} from "connection-lib";

export const CONNECTION = Symbol.for("CONNECTION");

registerProvider<DatabaseConnection>({
provide: CONNECTION,
deps: [Configuration],
useFactory(configuration: Configuration) {
const options = configuration.get<any>("myOptions");

return new DatabaseConnection(options);
},
hooks: {
$onDestroy(connection) {
// called when provider instance is destroyed
return connection.close();
}
}
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import {DatabaseConnection} from "connection-lib";

export const CONNECTION = Symbol.for("CONNECTION");

registerProvider({
registerProvider<DatabaseConnection>({
provide: CONNECTION,
deps: [Configuration],
useFactory(configuration: Configuration) {
const options = configuration.get<any>("myOptions");

return new DatabaseConnection(options);
},
hooks: {
$onDestroy(connection) {
return connection.close();
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@ export const CONNECTION = Symbol.for("CONNECTION");

registerProvider({
provide: CONNECTION,
useValue: connection
useValue: connection,
hooks: {
$onDestroy(connection: any) {
return connection.close();
}
}
});
3 changes: 2 additions & 1 deletion packages/core/src/utils/objects/methodsOf.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {classOf} from "./classOf";
import {ancestorsOf} from "./ancestorsOf";
import {prototypeOf} from "./prototypeOf";
import {Type} from "../../domain/Type";

/**
* Return all methods for a given class.
* @param target
*/
export function methodsOf(target: any) {
export function methodsOf(target: any): {target: Type; propertyKey: string}[] {
const methods = new Map();
target = classOf(target);

Expand Down
1 change: 1 addition & 0 deletions packages/di/src/domain/Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class Provider<T = any> implements ProviderOpts<T> {
public useFactory: Function;
public useAsyncFactory: Function;
public useValue: any;
public hooks?: Record<string, (instance: T, ...args: any[]) => Promise<void> | void>;
private _useClass: Type<T>;
private _provide: TokenProvider;
private _store: Store;
Expand Down
4 changes: 4 additions & 0 deletions packages/di/src/interfaces/ProviderOpts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export interface ProviderOpts<T = any> {
*/
resolvers?: DIResolver[];

/**
* hooks to intercept custom events
*/
hooks?: Record<string, (instance: T, ...args: any[]) => Promise<void> | void>;
/**
*
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/di/src/registries/ProviderRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ GlobalProviders.createRegistry(ProviderType.CONTROLLER, Provider);
* Register a provider configuration.
* @param {ProviderOpts<any>} provider
*/
export function registerProvider(provider: Partial<ProviderOpts>): void {
export function registerProvider<Type = any>(provider: Partial<ProviderOpts<Type>>): void {
if (!provider.provide) {
throw new Error("Provider.provide is required");
}
Expand Down
6 changes: 6 additions & 0 deletions packages/di/src/services/InjectorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,12 @@ export class InjectorService extends Container {
this.bindInjectableProperties(instance, locals, options);
}

if (instance && provider.hooks) {
Object.entries(provider.hooks).forEach(([key, cb]) => {
instance[key] = (...args: any[]) => cb(instance, ...args);
});
}

return instance;
}

Expand Down
12 changes: 10 additions & 2 deletions packages/di/test/integration/async-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ describe("DI", () => {
registerProvider({
provide: ASYNC_FACTORY,
useAsyncFactory() {
return Promise.resolve({connection: true});
return Promise.resolve({
connection: true,
close() {}
});
},
hooks: {
$onDestroy(instance: any) {
return instance.close();
}
}
});

Expand Down Expand Up @@ -38,7 +46,7 @@ describe("DI", () => {
await injector.load(container);

expect(isPromise(server.asyncFactory)).to.eq(false);
expect(server.asyncFactory).to.deep.eq({connection: true});
expect(server.asyncFactory.connection).to.deep.eq(true);
});
});
});

0 comments on commit 9b97a6d

Please sign in to comment.