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

Di rework #123

Merged
merged 4 commits into from
Jan 8, 2025
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
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"**/node_modules": true,
"**/out": true,
"**/lib": true,
"**/cypress": true,
"**/dist": true,
"common/temp": true
},
"eslint.workingDirectories": [
Expand Down
12 changes: 7 additions & 5 deletions cloud-agnostic/core/src/Bindable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Container } from "inversify";

import { Dependency } from "./Dependency";
import { DependenciesConfig, DependencyConfig } from "./DependencyConfig";
import { DependencyFactory } from "./DependencyFactory";
import { DIContainer } from "./DIContainer";
import { DependencyError } from "./internal";
import { Types } from "./Types";

Expand Down Expand Up @@ -34,7 +34,7 @@ export abstract class Bindable {
}

private bindNamedDependencies(
container: Container,
container: DIContainer,
factory: DependencyFactory,
configs: DependencyConfig[]
): void {
Expand All @@ -46,15 +46,17 @@ export abstract class Bindable {
}

private bindDependency(
container: Container,
container: DIContainer,
factory: DependencyFactory,
config: DependencyConfig
) {
factory.getDependency(config.dependencyName).register(container, config);
}

protected bindDependencies(container: Container): void {
const config = container.get<DependenciesConfig>(Types.dependenciesConfig);
protected bindDependencies(container: DIContainer): void {
const config = container.resolve<DependenciesConfig>(
Types.dependenciesConfig
);

this._dependencyFactories.forEach((factory) => {
const dependencyConfig = config[factory.dependencyType];
Expand Down
31 changes: 31 additions & 0 deletions cloud-agnostic/core/src/DIContainer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

import { DIIdentifier } from "./internal";

export abstract class DIContainer {
abstract registerFactory<T>(
key: DIIdentifier<T>,
factory: (container: DIContainer) => T
): void;

abstract registerNamedFactory<T>(
key: DIIdentifier<T>,
factory: (container: DIContainer) => T,
name: string
): void;

abstract registerInstance<T>(key: DIIdentifier<T>, instance: T): void;

abstract unregister<T>(key: DIIdentifier<T>): void;

abstract resolve<T>(key: DIIdentifier<T>): T;

abstract resolveNamed<T>(key: DIIdentifier<T>, name: string): T;

abstract resolveAll<T>(key: DIIdentifier<T>): T[];

abstract createChild(): DIContainer;
}
4 changes: 2 additions & 2 deletions cloud-agnostic/core/src/Dependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Container } from "inversify";

import { DependencyConfig } from "./DependencyConfig";
import { DIContainer } from "./DIContainer";

export abstract class Dependency {
public abstract dependencyName: string;
public abstract dependencyType: string;
public abstract register(
container: Container,
container: DIContainer,
config?: DependencyConfig
): void;
}
47 changes: 24 additions & 23 deletions cloud-agnostic/core/src/NamedDependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Container, interfaces } from "inversify";

import { Dependency } from "./Dependency";
import { DependencyConfig } from "./DependencyConfig";
import { DIContainer } from "./DIContainer";
import { DIIdentifier } from "./internal";

export class NamedInstance<T> {
constructor(
Expand All @@ -16,13 +17,13 @@ export class NamedInstance<T> {

export abstract class NamedDependency extends Dependency {
protected abstract _registerInstance(
container: Container,
childContainer: Container,
container: DIContainer,
childContainer: DIContainer,
config: DependencyConfig
): void;

public registerInstance(
container: Container,
container: DIContainer,
config: DependencyConfig
): void {
const childContainer = container.createChild();
Expand All @@ -32,26 +33,26 @@ export abstract class NamedDependency extends Dependency {
}

protected bindNamed<T>(
container: Container,
childContainer: Container,
serviceIdentifier: interfaces.ServiceIdentifier<T>,
container: DIContainer,
childContainer: DIContainer,
serviceIdentifier: DIIdentifier<T>,
instanceName: string
): void {
container
.bind(NamedInstance<T>)
.toDynamicValue(() => {
return new NamedInstance<T>(
childContainer.get(serviceIdentifier),
instanceName
);
})
.inSingletonScope();
container
.bind(serviceIdentifier)
.toDynamicValue(() => {
return childContainer.get(serviceIdentifier);
})
.inSingletonScope()
.whenTargetNamed(instanceName);
container.registerFactory<NamedInstance<T>>(NamedInstance<T>, () => {
return new NamedInstance<T>(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
childContainer.resolve(serviceIdentifier),
instanceName
);
});
container.registerNamedFactory<T>(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
serviceIdentifier,
() => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
return childContainer.resolve(serviceIdentifier);
},
instanceName
);
}
}
2 changes: 2 additions & 0 deletions cloud-agnostic/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

export * from "./DIContainer";
export * from "./Bindable";
export * from "./Dependency";
export * from "./NamedDependency";
Expand Down
12 changes: 12 additions & 0 deletions cloud-agnostic/core/src/internal/Types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Constructor<T> = new (...args: any[]) => T;
export interface Abstract<T> {
prototype: T;
}

export type DIIdentifier<T> = symbol | Constructor<T> | Abstract<T>;
1 change: 1 addition & 0 deletions cloud-agnostic/core/src/internal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
*--------------------------------------------------------------------------------------------*/
export * from "./Errors";
export * from "./Helpers";
export * from "./Types";
80 changes: 80 additions & 0 deletions cloud-agnostic/core/src/inversify/InversifyWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import "reflect-metadata";

import { Container, decorate, injectable, METADATA_KEY } from "inversify";

import { DIContainer } from "../DIContainer";
import { Abstract, Constructor, DIIdentifier } from "../internal";

export class InversifyWrapper extends DIContainer {
constructor(private _container: Container) {
super();
}

public static create(): DIContainer {
return new InversifyWrapper(new Container());
}

private needsDecoration<T>(
key: DIIdentifier<T>
): key is Constructor<T> | Abstract<T> {
if (typeof key === "symbol") return false;
return Reflect.hasOwnMetadata(METADATA_KEY.PARAM_TYPES, key);
}

public override registerFactory<T>(
key: DIIdentifier<T>,
factory: (container: DIContainer) => T
): void {
if (this.needsDecoration(key)) {
decorate(injectable(), key);
}

this._container
.bind<T>(key)
.toDynamicValue(() => factory(this))
.inSingletonScope();
}

public override registerNamedFactory<T>(
key: DIIdentifier<T>,
factory: (container: DIContainer) => T,
name: string
): void {
if (this.needsDecoration(key)) {
decorate(injectable(), key);
}

this._container
.bind<T>(key)
.toDynamicValue(() => factory(this))
.whenTargetNamed(name);
}

public override registerInstance<T>(key: DIIdentifier<T>, instance: T): void {
this._container.bind<T>(key).toConstantValue(instance);
}

public override unregister<T>(key: DIIdentifier<T>): void {
this._container.unbind(key);
}

public override resolve<T>(key: DIIdentifier<T>): T {
return this._container.get<T>(key);
}

public override resolveNamed<T>(key: DIIdentifier<T>, name: string): T {
return this._container.getNamed<T>(key, name);
}

public override resolveAll<T>(key: DIIdentifier<T>): T[] {
return this._container.getAll<T>(key);
}

public override createChild(): DIContainer {
return new InversifyWrapper(this._container.createChild());
}
}
5 changes: 5 additions & 0 deletions cloud-agnostic/core/src/inversify/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
export * from "./InversifyWrapper";
29 changes: 16 additions & 13 deletions cloud-agnostic/core/src/test/Bindable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect } from "chai";
import { Container } from "inversify";

import { Bindable, DependenciesConfig, NamedInstance } from "..";
import { DependencyError, DependencyTypeError } from "../internal";
import { InversifyWrapper } from "../inversify";

import { ConcreteTest, Test, TestConfig } from "./Test";
import { ConcreteTestDependencyBindings } from "./TestDependency";
Expand Down Expand Up @@ -63,17 +63,20 @@ function validateTestObject(test: Test, config: TestConfig) {

describe(`${Bindable.name}`, () => {
it(`should resolve registered dependency`, () => {
const setup = new TestSetup(new Container(), dependenciesConfig);
const setup = new TestSetup(InversifyWrapper.create(), dependenciesConfig);

setup.start();

const test = setup.container.get(Test);
const test = setup.container.resolve(Test);
expect(test instanceof ConcreteTest).to.be.true;
expect(test.property === "testProperty").to.be.true;
});

it(`should throw if dependency factory is not registered`, () => {
const setup = new TestSetupNoFactory(new Container(), dependenciesConfig);
const setup = new TestSetupNoFactory(
InversifyWrapper.create(),
dependenciesConfig
);

const testedFunction = () =>
setup.useBindings(ConcreteTestDependencyBindings);
Expand All @@ -87,7 +90,7 @@ describe(`${Bindable.name}`, () => {

it(`should throw if testName dependency is not registered`, () => {
const setup = new TestSetupNoDefaultDependencies(
new Container(),
InversifyWrapper.create(),
dependenciesConfig
);

Expand All @@ -102,7 +105,7 @@ describe(`${Bindable.name}`, () => {

it(`should throw if testName does not support named dependency instances`, () => {
const setup = new TestSetup(
new Container(),
InversifyWrapper.create(),
dependenciesConfigWithMultipleInstances
);

Expand All @@ -117,29 +120,29 @@ describe(`${Bindable.name}`, () => {

it(`should resolve registered named dependency instance`, () => {
const setup = new TestSetupWithNamedInstances(
new Container(),
InversifyWrapper.create(),
dependenciesConfigWithOneInstance
);

setup.start();

const test = setup.container.getNamed(Test, "instanceName");
const test = setup.container.resolveNamed(Test, "instanceName");
validateTestObject(test, testConfigWithOneInstance[0]);
});

it(`should resolve multiple registered named dependency instances by name`, () => {
const setup = new TestSetupWithNamedInstances(
new Container(),
InversifyWrapper.create(),
dependenciesConfigWithMultipleInstances
);

setup.start();

const test = setup.container.getNamed(
const test = setup.container.resolveNamed(
Test,
testConfigWithMultipleInstances[0].instanceName!
);
const test2 = setup.container.getNamed(
const test2 = setup.container.resolveNamed(
Test,
testConfigWithMultipleInstances[1].instanceName!
);
Expand All @@ -150,13 +153,13 @@ describe(`${Bindable.name}`, () => {

it(`should resolve multiple registered named dependency instances as array`, () => {
const setup = new TestSetupWithNamedInstances(
new Container(),
InversifyWrapper.create(),
dependenciesConfigWithMultipleInstances
);

setup.start();

const tests: NamedInstance<Test>[] = setup.container.getAll(
const tests: NamedInstance<Test>[] = setup.container.resolveAll(
NamedInstance<Test>
);

Expand Down
Loading
Loading