Skip to content

Commit

Permalink
Merge pull request #202 from inversify/feat/add-resolve-binding-deact…
Browse files Browse the repository at this point in the history
…ivation

Add resolveBindingDeactivations
  • Loading branch information
notaphplover authored Dec 20, 2024
2 parents 2d7d9bf + cb83c41 commit f56ee1f
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-grapes-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inversifyjs/core": minor
---

Added `resolveBindingDeactivations`.
4 changes: 4 additions & 0 deletions packages/container/libraries/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ import { PlanServiceNodeParent } from './planning/models/PlanServiceNodeParent';
import { PlanServiceRedirectionBindingNode } from './planning/models/PlanServiceRedirectionBindingNode';
import { PlanTree } from './planning/models/PlanTree';
import { resolve } from './resolution/actions/resolve';
import { resolveBindingDeactivations } from './resolution/actions/resolveBindingDeactivations';
import { DeactivationParams } from './resolution/models/DeactivationParams';
import { GetOptions } from './resolution/models/GetOptions';
import { GetOptionsTagConstraint } from './resolution/models/GetOptionsTagConstraint';
import { ResolutionContext } from './resolution/models/ResolutionContext';
Expand All @@ -79,6 +81,7 @@ export type {
ClassMetadata,
ClassMetadataLifecycle,
ConstantValueBinding,
DeactivationParams,
DynamicValueBinding,
DynamicValueBuilder,
Factory,
Expand Down Expand Up @@ -128,6 +131,7 @@ export {
plan,
preDestroy,
resolve,
resolveBindingDeactivations,
tagged,
unmanaged,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals';

import { ServiceIdentifier } from '@inversifyjs/common';

import { BindingDeactivation } from '../../binding/models/BindingDeactivation';
import { DeactivationParams } from '../models/DeactivationParams';
import { resolveBindingDeactivations } from './resolveBindingDeactivations';

describe(resolveBindingDeactivations.name, () => {
describe('having a non promise value', () => {
let paramsMock: jest.Mocked<DeactivationParams>;
let serviceIdentifierFixture: ServiceIdentifier;
let valueFixture: unknown;

beforeAll(() => {
paramsMock = {
getDeactivations: jest.fn(),
};
serviceIdentifierFixture = 'service-id';
valueFixture = Symbol();
});

describe('when called, and params.getActivations() returns undefined', () => {
let result: unknown;

beforeAll(() => {
result = resolveBindingDeactivations(
paramsMock,
serviceIdentifierFixture,
valueFixture,
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call params.getActivations', () => {
expect(paramsMock.getDeactivations).toHaveBeenCalledTimes(1);
expect(paramsMock.getDeactivations).toHaveBeenCalledWith(
serviceIdentifierFixture,
);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});

describe('when called, and params.getActivations() returns sync activations', () => {
let deactivationMock: jest.Mock<BindingDeactivation>;

let result: unknown;

beforeAll(() => {
deactivationMock = jest.fn();
deactivationMock.mockReturnValueOnce(undefined);

paramsMock.getDeactivations.mockReturnValueOnce([deactivationMock]);

result = resolveBindingDeactivations(
paramsMock,
serviceIdentifierFixture,
valueFixture,
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call params.getActivations', () => {
expect(paramsMock.getDeactivations).toHaveBeenCalledTimes(1);
expect(paramsMock.getDeactivations).toHaveBeenCalledWith(
serviceIdentifierFixture,
);
});

it('should call activation', () => {
expect(deactivationMock).toHaveBeenCalledTimes(1);
expect(deactivationMock).toHaveBeenCalledWith(valueFixture);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});

describe('when called, and params.getActivations() returns async activations', () => {
let deactivationMock: jest.Mock<BindingDeactivation>;

let result: unknown;

beforeAll(async () => {
deactivationMock = jest.fn();
deactivationMock.mockReturnValueOnce(Promise.resolve(undefined));

paramsMock.getDeactivations.mockReturnValueOnce([deactivationMock]);

result = await resolveBindingDeactivations(
paramsMock,
serviceIdentifierFixture,
valueFixture,
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call params.getActivations', () => {
expect(paramsMock.getDeactivations).toHaveBeenCalledTimes(1);
expect(paramsMock.getDeactivations).toHaveBeenCalledWith(
serviceIdentifierFixture,
);
});

it('should call activation', () => {
expect(deactivationMock).toHaveBeenCalledTimes(1);
expect(deactivationMock).toHaveBeenCalledWith(valueFixture);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});
});

describe('having a promise value', () => {
let paramsMock: jest.Mocked<DeactivationParams>;
let serviceIdentifierFixture: ServiceIdentifier;
let valueFixture: unknown;

beforeAll(() => {
paramsMock = {
getDeactivations: jest.fn(),
};
serviceIdentifierFixture = 'service-id';
valueFixture = Symbol();
});

describe('when called, and params.getActivations() returns undefined', () => {
let result: unknown;

beforeAll(async () => {
result = await resolveBindingDeactivations(
paramsMock,
serviceIdentifierFixture,
Promise.resolve(valueFixture),
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call params.getActivations', () => {
expect(paramsMock.getDeactivations).toHaveBeenCalledTimes(1);
expect(paramsMock.getDeactivations).toHaveBeenCalledWith(
serviceIdentifierFixture,
);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});

describe('when called, and params.getActivations() returns sync activations', () => {
let deactivationMock: jest.Mock<BindingDeactivation>;

let result: unknown;

beforeAll(async () => {
deactivationMock = jest.fn();
deactivationMock.mockReturnValueOnce(undefined);

paramsMock.getDeactivations.mockReturnValueOnce([deactivationMock]);

result = await resolveBindingDeactivations(
paramsMock,
serviceIdentifierFixture,
Promise.resolve(valueFixture),
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call params.getActivations', () => {
expect(paramsMock.getDeactivations).toHaveBeenCalledTimes(1);
expect(paramsMock.getDeactivations).toHaveBeenCalledWith(
serviceIdentifierFixture,
);
});

it('should call activation', () => {
expect(deactivationMock).toHaveBeenCalledTimes(1);
expect(deactivationMock).toHaveBeenCalledWith(valueFixture);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});

describe('when called, and params.getActivations() returns async activations', () => {
let deactivationMock: jest.Mock<BindingDeactivation>;

let result: unknown;

beforeAll(async () => {
deactivationMock = jest.fn();
deactivationMock.mockReturnValueOnce(Promise.resolve(undefined));

paramsMock.getDeactivations.mockReturnValueOnce([deactivationMock]);

result = await resolveBindingDeactivations(
paramsMock,
serviceIdentifierFixture,
Promise.resolve(valueFixture),
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call params.getActivations', () => {
expect(paramsMock.getDeactivations).toHaveBeenCalledTimes(1);
expect(paramsMock.getDeactivations).toHaveBeenCalledWith(
serviceIdentifierFixture,
);
});

it('should call activation', () => {
expect(deactivationMock).toHaveBeenCalledTimes(1);
expect(deactivationMock).toHaveBeenCalledWith(valueFixture);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ServiceIdentifier } from '@inversifyjs/common';

import { BindingDeactivation } from '../../binding/models/BindingDeactivation';
import { isPromise } from '../../common/calculations/isPromise';
import { DeactivationParams } from '../models/DeactivationParams';
import { Resolved, SyncResolved } from '../models/Resolved';

export function resolveBindingDeactivations<TActivated>(
params: DeactivationParams,
serviceIdentifier: ServiceIdentifier<TActivated>,
value: Resolved<TActivated>,
): void | Promise<void> {
const activations: Iterable<BindingDeactivation<TActivated>> | undefined =
params.getDeactivations(serviceIdentifier);

if (activations === undefined) {
return undefined;
}

if (isPromise(value)) {
return resolveBindingDeactivationsFromIteratorAsync(
value,
activations[Symbol.iterator](),
);
}

return resolveBindingDeactivationsFromIterator(
value,
activations[Symbol.iterator](),
);
}

function resolveBindingDeactivationsFromIterator<TActivated>(
value: SyncResolved<TActivated>,
deactivationsIterator: Iterator<BindingDeactivation<TActivated>>,
): void | Promise<void> {
let deactivationIteratorResult: IteratorResult<
BindingDeactivation<TActivated>
> = deactivationsIterator.next();

while (deactivationIteratorResult.done !== true) {
const nextDeactivationValue: void | Promise<void> =
deactivationIteratorResult.value(value);

if (isPromise(nextDeactivationValue)) {
return resolveBindingDeactivationsFromIteratorAsync(
value,
deactivationsIterator,
);
}

deactivationIteratorResult = deactivationsIterator.next();
}
}

async function resolveBindingDeactivationsFromIteratorAsync<TActivated>(
value: Resolved<TActivated>,
deactivationsIterator: Iterator<BindingDeactivation<TActivated>>,
): Promise<void> {
const resolvedValue: SyncResolved<TActivated> = await value;

let deactivationIteratorResult: IteratorResult<
BindingDeactivation<TActivated>
> = deactivationsIterator.next();

while (deactivationIteratorResult.done !== true) {
await deactivationIteratorResult.value(resolvedValue);

deactivationIteratorResult = deactivationsIterator.next();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ServiceIdentifier } from '@inversifyjs/common';

import { BindingDeactivation } from '../../binding/models/BindingDeactivation';

export interface DeactivationParams {
getDeactivations: <TActivated>(
serviceIdentifier: ServiceIdentifier<TActivated>,
) => Iterable<BindingDeactivation<TActivated>> | undefined;
}

0 comments on commit f56ee1f

Please sign in to comment.