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

Update resolution flow with binding activations #169

Merged
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
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
Loading