Skip to content

Commit

Permalink
Merge pull request #169 from inversify/refactor/update-resolution-flo…
Browse files Browse the repository at this point in the history
…w-with-binding-activations

Update resolution flow with binding activations
  • Loading branch information
notaphplover authored Dec 8, 2024
2 parents ebfa3b8 + 4ed0503 commit 74e939d
Show file tree
Hide file tree
Showing 21 changed files with 310 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ describe(stringifyBinding.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'service-id',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
},
`[ type: "${bindingTypeValues.ConstantValue}", serviceIdentifier: "service-id", scope: "${bindingScopeValues.Singleton}" ]`,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export type BindingActivation<T = unknown> = (injectable: T) => T | Promise<T>;
import { Resolved } from '../../resolution/models/Resolved';

export type BindingActivation<T = unknown> = (injectable: T) => Resolved<T>;
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Resolved } from '../../resolution/models/Resolved';
import { bindingScopeValues } from './BindingScope';
import { bindingTypeValues } from './BindingType';
import { ScopedBinding } from './ScopedBinding';

export type ConstantValueBinding<TActivated> = ScopedBinding<
typeof bindingTypeValues.ConstantValue,
typeof bindingScopeValues.Singleton,
TActivated | Promise<TActivated>
>;
export interface ConstantValueBinding<TActivated>
extends ScopedBinding<
typeof bindingTypeValues.ConstantValue,
typeof bindingScopeValues.Singleton,
TActivated | Promise<TActivated>
> {
readonly value: Resolved<TActivated>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ describe(ActivationsService.name, () => {
beforeAll(() => {
bindingActivationFixture = Symbol() as unknown as BindingActivation;

activationMapsMock.get.mockReturnValueOnce(
[bindingActivationFixture].values(),
);
activationMapsMock.get.mockReturnValueOnce([bindingActivationFixture]);

result = activationsService.get(serviceIdFixture);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class ActivationsService {

public get(
serviceIdentifier: ServiceIdentifier,
): BindingActivation[] | undefined {
): Iterable<BindingActivation> | undefined {
const bindings: Iterable<BindingActivation> | undefined =
this.#activationMaps.get(
ActivationRelationKind.serviceId,
Expand All @@ -53,7 +53,7 @@ export class ActivationsService {
return undefined;
}

return [...bindings];
return bindings;
}

public removeAllByModuleId(moduleId: number): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe(BindingServiceImplementation.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'service-identifier-fixture',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};
});

Expand Down Expand Up @@ -82,6 +83,7 @@ describe(BindingServiceImplementation.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: Symbol(),
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};
});

Expand Down Expand Up @@ -128,6 +130,7 @@ describe(BindingServiceImplementation.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: Symbol(),
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe(
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'service-id',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
},
parent: Symbol() as unknown as BindingNodeParent,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ describe(plan.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: ServiceIds.constantValue,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

dynamicValueBinding = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe(plan.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

planParamsMock.getBindings.mockReturnValueOnce([constantValueBinding]);
Expand Down Expand Up @@ -191,6 +192,7 @@ describe(plan.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

planParamsMock.getBindings.mockReturnValueOnce([constantValueBinding]);
Expand Down Expand Up @@ -361,6 +363,7 @@ describe(plan.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};
constructorArgumentMetadata = {
kind: ClassElementMetadataKind.multipleInjection,
Expand Down Expand Up @@ -536,6 +539,7 @@ describe(plan.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};
constructorArgumentMetadata = {
kind: ClassElementMetadataKind.multipleInjection,
Expand Down Expand Up @@ -715,6 +719,7 @@ describe(plan.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};
constructorArgumentMetadata = {
kind: ClassElementMetadataKind.singleInjection,
Expand Down Expand Up @@ -1108,6 +1113,7 @@ describe(plan.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: serviceRedirectionBinding.targetServiceIdentifier,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

planParamsMock.getBindings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ Trying to resolve bindings for "${stringifiedServiceIdentifier} (Root service)".
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'target-service-id',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
},
parent: parentNode,
};
Expand Down Expand Up @@ -369,6 +370,7 @@ Trying to resolve bindings for "${stringifiedServiceIdentifier} (Root service)".
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'target-service-id',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
},
parent: parentNode,
},
Expand All @@ -386,6 +388,7 @@ Trying to resolve bindings for "${stringifiedServiceIdentifier} (Root service)".
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'target-service-id',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
},
parent: parentNode,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from '@jest/globals';

import 'reflect-metadata';

import { Newable, Right, ServiceIdentifier } from '@inversifyjs/common';
import { Newable, ServiceIdentifier } from '@inversifyjs/common';
import { getReflectMetadata } from '@inversifyjs/reflect-metadata-utils';

import { BindingActivation } from '../../binding/models/BindingActivation';
Expand Down Expand Up @@ -39,6 +39,7 @@ import { resolve } from './resolve';

enum ServiceIds {
constantValue = 'constant-value-service-id',
constantValueWithActivation = 'constant-value-with-activation-service-id',
dynamicValue = 'dynamic-value-service-id',
factory = 'factory-service-id',
instance = 'instance-service-id',
Expand All @@ -59,7 +60,10 @@ class Foo {
}

describe(resolve.name, () => {
let activatedResolvedResult: unknown;

let constantValueBinding: ConstantValueBinding<unknown>;
let constantValueBindingWithActivation: ConstantValueBinding<unknown>;
let dynamicValueBinding: DynamicValueBinding<unknown>;
let factoryBinding: FactoryBinding<Factory<unknown>>;
let instanceBinding: InstanceBinding<unknown>;
Expand All @@ -74,10 +78,12 @@ describe(resolve.name, () => {
let resolutionContext: ResolutionContext;

beforeAll(() => {
activatedResolvedResult = { foo: 'bar' };

constantValueBinding = {
cache: {
isRight: true,
value: Symbol(),
isRight: false,
value: undefined,
},
id: 0,
isSatisfiedBy: () => true,
Expand All @@ -87,6 +93,23 @@ describe(resolve.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: ServiceIds.constantValue,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

constantValueBindingWithActivation = {
cache: {
isRight: false,
value: undefined,
},
id: 0,
isSatisfiedBy: () => true,
moduleId: undefined,
onActivation: () => activatedResolvedResult,
onDeactivation: undefined,
scope: bindingScopeValues.Singleton,
serviceIdentifier: ServiceIds.constantValueWithActivation,
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

dynamicValueBinding = {
Expand Down Expand Up @@ -178,7 +201,15 @@ describe(resolve.name, () => {
activationService = new ActivationsService();
bindingService = new BindingServiceImplementation();

activationService.add(
constantValueBindingWithActivation.onActivation as BindingActivation,
{
serviceId: constantValueBindingWithActivation.serviceIdentifier,
},
);

bindingService.set(constantValueBinding);
bindingService.set(constantValueBindingWithActivation);
bindingService.set(dynamicValueBinding);
bindingService.set(factoryBinding);
bindingService.set(instanceBinding);
Expand Down Expand Up @@ -353,7 +384,15 @@ describe(resolve.name, () => {
isMultiple: false,
serviceIdentifier: constantValueBinding.serviceIdentifier,
}),
() => (constantValueBinding.cache as Right<unknown>).value,
() => constantValueBinding.value,
],
[
'with constant value bound service with activation',
(): PlanParamsConstraint => ({
isMultiple: false,
serviceIdentifier: constantValueBindingWithActivation.serviceIdentifier,
}),
() => activatedResolvedResult,
],
[
'with dynamic value bound service',
Expand Down Expand Up @@ -387,9 +426,7 @@ describe(resolve.name, () => {
}),
() =>
((): Foo => {
const instance: Foo = new Foo(
(constantValueBinding.cache as Right<symbol>).value,
);
const instance: Foo = new Foo(constantValueBinding.value as symbol);

(instance as Writable<Foo>).property = dynamicValueBinding.value(
resolutionContext,
Expand All @@ -404,7 +441,7 @@ describe(resolve.name, () => {
isMultiple: false,
serviceIdentifier: serviceRedirectionBinding.serviceIdentifier,
}),
() => (constantValueBinding.cache as Right<unknown>).value,
() => constantValueBinding.value,
],
])(
'having plan params %s',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ describe(resolve.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'service-id',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

const bindingNode: PlanBindingNode = {
Expand Down Expand Up @@ -241,6 +242,7 @@ describe(resolve.name, () => {
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'service-id',
type: bindingTypeValues.ConstantValue,
value: Symbol(),
};

const bindingNode: PlanBindingNode = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { ServiceIdentifier } from '@inversifyjs/common';

import { BindingActivation } from '../../binding/models/BindingActivation';
import { isPromise } from '../../common/calculations/isPromise';
import { ResolutionParams } from '../models/ResolutionParams';
import { Resolved } from '../models/Resolved';

export function resolveBindingActivations<TActivated>(
params: ResolutionParams,
serviceIdentifier: ServiceIdentifier<TActivated>,
value: Resolved<TActivated>,
): Resolved<TActivated> {
const activations: Iterable<BindingActivation<TActivated>> | undefined =
params.getActivations(serviceIdentifier);

if (activations !== undefined) {
if (isPromise(value)) {
return resolveBindingActivationsFromIteratorAsync(
value,
activations[Symbol.iterator](),
);
} else {
return resolveBindingActivationsFromIterator(
value,
activations[Symbol.iterator](),
);
}
}

return value;
}

function resolveBindingActivationsFromIterator<TActivated>(
value: Awaited<TActivated>,
activationsIterator: Iterator<BindingActivation<TActivated>>,
): Resolved<TActivated> {
let activatedValue: Awaited<TActivated> = value;

let activationIteratorResult: IteratorResult<BindingActivation<TActivated>> =
activationsIterator.next();

while (activationIteratorResult.done !== true) {
const nextActivatedValue: Resolved<TActivated> =
activationIteratorResult.value(activatedValue);

if (isPromise(nextActivatedValue)) {
return resolveBindingActivationsFromIteratorAsync(
nextActivatedValue,
activationsIterator,
);
} else {
activatedValue = nextActivatedValue;
}

activationIteratorResult = activationsIterator.next();
}

return activatedValue;
}

async function resolveBindingActivationsFromIteratorAsync<TActivated>(
value: Promise<TActivated>,
activationsIterator: Iterator<BindingActivation<TActivated>>,
): Promise<Awaited<TActivated>> {
let activatedValue: Awaited<TActivated> = await value;

let activationIteratorResult: IteratorResult<BindingActivation<TActivated>> =
activationsIterator.next();

while (activationIteratorResult.done !== true) {
activatedValue = await activationIteratorResult.value(activatedValue);

activationIteratorResult = activationsIterator.next();
}

return activatedValue;
}
Loading

0 comments on commit 74e939d

Please sign in to comment.