mirror of
https://github.com/nestjs/nest.git
synced 2026-02-23 07:42:52 +00:00
Compare commits
2 Commits
fix/flaky-
...
fix/instan
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f273041594 | ||
|
|
da8ebded25 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -50,4 +50,3 @@ build/config\.gypi
|
||||
|
||||
.npmrc
|
||||
pnpm-lock.yaml
|
||||
/.history
|
||||
|
||||
@@ -27,14 +27,10 @@ describe('Durable providers', () => {
|
||||
tenantId: number,
|
||||
end: (err?: any) => void,
|
||||
endpoint = '/durable',
|
||||
opts: {
|
||||
forceError: boolean;
|
||||
} = { forceError: false },
|
||||
) =>
|
||||
request(server)
|
||||
.get(endpoint)
|
||||
.set({ ['x-tenant-id']: tenantId })
|
||||
.set({ ['x-force-error']: opts.forceError ? 'true' : 'false' })
|
||||
.end((err, res) => {
|
||||
if (err) return end(err);
|
||||
end(res);
|
||||
@@ -88,23 +84,6 @@ describe('Durable providers', () => {
|
||||
);
|
||||
expect(result.body).deep.equal({ tenantId: '3' });
|
||||
});
|
||||
|
||||
it(`should not cache durable providers that throw errors`, async () => {
|
||||
let result: request.Response;
|
||||
|
||||
result = await new Promise<request.Response>(resolve =>
|
||||
performHttpCall(10, resolve, '/durable/echo', { forceError: true }),
|
||||
);
|
||||
|
||||
expect(result.statusCode).equal(412);
|
||||
|
||||
// The second request should be successful
|
||||
result = await new Promise<request.Response>(resolve =>
|
||||
performHttpCall(10, resolve, '/durable/echo'),
|
||||
);
|
||||
|
||||
expect(result.body).deep.equal({ tenantId: '10' });
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
||||
@@ -6,8 +6,6 @@ const tenants = new Map<string, ContextId>();
|
||||
export class DurableContextIdStrategy implements ContextIdStrategy {
|
||||
attach(contextId: ContextId, request: Request) {
|
||||
const tenantId = request.headers['x-tenant-id'] as string;
|
||||
const forceError = request.headers['x-force-error'] === 'true';
|
||||
|
||||
let tenantSubTreeId: ContextId;
|
||||
|
||||
if (tenants.has(tenantId)) {
|
||||
@@ -16,18 +14,10 @@ export class DurableContextIdStrategy implements ContextIdStrategy {
|
||||
tenantSubTreeId = { id: +tenantId } as ContextId;
|
||||
tenants.set(tenantId, tenantSubTreeId);
|
||||
}
|
||||
|
||||
const payload: {
|
||||
tenantId: string;
|
||||
forceError?: boolean;
|
||||
} = { tenantId };
|
||||
if (forceError) {
|
||||
payload.forceError = true;
|
||||
}
|
||||
return {
|
||||
resolve: (info: HostComponentInfo) =>
|
||||
info.isTreeDurable ? tenantSubTreeId : contextId,
|
||||
payload,
|
||||
payload: { tenantId },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
import {
|
||||
Inject,
|
||||
Injectable,
|
||||
PreconditionFailedException,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { REQUEST } from '@nestjs/core';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST, durable: true })
|
||||
export class DurableService {
|
||||
public instanceCounter = 0;
|
||||
|
||||
constructor(
|
||||
@Inject(REQUEST)
|
||||
public readonly requestPayload: { tenantId: string; forceError: boolean },
|
||||
) {
|
||||
if (requestPayload.forceError) {
|
||||
throw new PreconditionFailedException('Forced error');
|
||||
}
|
||||
}
|
||||
constructor(@Inject(REQUEST) public readonly requestPayload: unknown) {}
|
||||
|
||||
greeting() {
|
||||
++this.instanceCounter;
|
||||
|
||||
@@ -170,11 +170,6 @@ export class Injector {
|
||||
inquirer,
|
||||
);
|
||||
} catch (err) {
|
||||
wrapper.removeInstanceByContextId(
|
||||
this.getContextId(contextId, wrapper),
|
||||
inquirerId,
|
||||
);
|
||||
|
||||
settlementSignal.error(err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -168,21 +168,6 @@ export class InstanceWrapper<T = any> {
|
||||
collection.set(contextId, value);
|
||||
}
|
||||
|
||||
public removeInstanceByContextId(contextId: ContextId, inquirerId?: string) {
|
||||
if (this.scope === Scope.TRANSIENT && inquirerId) {
|
||||
return this.removeInstanceByInquirerId(contextId, inquirerId);
|
||||
}
|
||||
this.values.delete(contextId);
|
||||
}
|
||||
|
||||
public removeInstanceByInquirerId(contextId: ContextId, inquirerId: string) {
|
||||
const collection = this.transientMap.get(inquirerId);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
collection.delete(contextId);
|
||||
}
|
||||
|
||||
public addCtorMetadata(index: number, wrapper: InstanceWrapper) {
|
||||
if (!this[INSTANCE_METADATA_SYMBOL].dependencies) {
|
||||
this[INSTANCE_METADATA_SYMBOL].dependencies = [];
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { createContextId } from '../../helpers';
|
||||
import { STATIC_CONTEXT } from '../../injector/constants';
|
||||
import { InstanceWrapper } from '../../injector/instance-wrapper';
|
||||
|
||||
@@ -738,53 +737,6 @@ describe('InstanceWrapper', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeInstanceByContextId', () => {
|
||||
describe('without inquirer', () => {
|
||||
it('should remove instance for given context', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
|
||||
const contextId = createContextId();
|
||||
wrapper.setInstanceByContextId(contextId, { instance: {} });
|
||||
|
||||
const existingContext = wrapper.getInstanceByContextId(contextId);
|
||||
expect(existingContext.instance).to.be.not.undefined;
|
||||
wrapper.removeInstanceByContextId(contextId);
|
||||
|
||||
const removedContext = wrapper.getInstanceByContextId(contextId);
|
||||
expect(removedContext.instance).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('when transient and inquirer has been passed', () => {
|
||||
it('should remove instance for given context', () => {
|
||||
const wrapper = new InstanceWrapper({
|
||||
scope: Scope.TRANSIENT,
|
||||
});
|
||||
|
||||
wrapper.setInstanceByContextId(
|
||||
STATIC_CONTEXT,
|
||||
{ instance: {} },
|
||||
'inquirerId',
|
||||
);
|
||||
|
||||
const existingContext = wrapper.getInstanceByContextId(
|
||||
STATIC_CONTEXT,
|
||||
'inquirerId',
|
||||
);
|
||||
expect(existingContext.instance).to.be.not.undefined;
|
||||
wrapper.removeInstanceByContextId(STATIC_CONTEXT, 'inquirerId');
|
||||
|
||||
const removedContext = wrapper.getInstanceByContextId(
|
||||
STATIC_CONTEXT,
|
||||
'inquirerId',
|
||||
);
|
||||
expect(removedContext.instance).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInRequestScope', () => {
|
||||
describe('when tree and context are not static and is not transient', () => {
|
||||
it('should return true', () => {
|
||||
|
||||
@@ -39,25 +39,36 @@ export class ListenerMetadataExplorer {
|
||||
const instancePrototype = Object.getPrototypeOf(instance);
|
||||
return this.metadataScanner
|
||||
.getAllMethodNames(instancePrototype)
|
||||
.map(method => this.exploreMethodMetadata(instancePrototype, method))
|
||||
.map(method =>
|
||||
this.exploreMethodMetadata(instance, instancePrototype, method),
|
||||
)
|
||||
.filter(metadata => metadata);
|
||||
}
|
||||
|
||||
public exploreMethodMetadata(
|
||||
instance: Controller,
|
||||
instancePrototype: object,
|
||||
methodKey: string,
|
||||
): EventOrMessageListenerDefinition {
|
||||
const targetCallback = instancePrototype[methodKey];
|
||||
const prototypeCallback = instancePrototype[methodKey];
|
||||
const handlerType = Reflect.getMetadata(
|
||||
PATTERN_HANDLER_METADATA,
|
||||
targetCallback,
|
||||
prototypeCallback,
|
||||
);
|
||||
if (isUndefined(handlerType)) {
|
||||
return;
|
||||
}
|
||||
const patterns = Reflect.getMetadata(PATTERN_METADATA, targetCallback);
|
||||
const transport = Reflect.getMetadata(TRANSPORT_METADATA, targetCallback);
|
||||
const extras = Reflect.getMetadata(PATTERN_EXTRAS_METADATA, targetCallback);
|
||||
const patterns = Reflect.getMetadata(PATTERN_METADATA, prototypeCallback);
|
||||
const transport = Reflect.getMetadata(
|
||||
TRANSPORT_METADATA,
|
||||
prototypeCallback,
|
||||
);
|
||||
const extras = Reflect.getMetadata(
|
||||
PATTERN_EXTRAS_METADATA,
|
||||
prototypeCallback,
|
||||
);
|
||||
|
||||
const targetCallback = instance[methodKey];
|
||||
return {
|
||||
methodKey,
|
||||
targetCallback,
|
||||
|
||||
@@ -71,6 +71,7 @@ describe('ListenerMetadataExplorer', () => {
|
||||
});
|
||||
it(`should return undefined when "handlerType" metadata is undefined`, () => {
|
||||
const metadata = instance.exploreMethodMetadata(
|
||||
test,
|
||||
Object.getPrototypeOf(test),
|
||||
'noPattern',
|
||||
);
|
||||
@@ -80,6 +81,7 @@ describe('ListenerMetadataExplorer', () => {
|
||||
describe('@MessagePattern', () => {
|
||||
it(`should return pattern properties when "handlerType" metadata is not undefined`, () => {
|
||||
const metadata = instance.exploreMethodMetadata(
|
||||
test,
|
||||
Object.getPrototypeOf(test),
|
||||
'testMessage',
|
||||
);
|
||||
@@ -96,6 +98,7 @@ describe('ListenerMetadataExplorer', () => {
|
||||
});
|
||||
it(`should return multiple patterns when more than one is declared`, () => {
|
||||
const metadata = instance.exploreMethodMetadata(
|
||||
test,
|
||||
Object.getPrototypeOf(test),
|
||||
'testMultipleMessage',
|
||||
);
|
||||
@@ -116,6 +119,7 @@ describe('ListenerMetadataExplorer', () => {
|
||||
describe('@EventPattern', () => {
|
||||
it(`should return pattern properties when "handlerType" metadata is not undefined`, () => {
|
||||
const metadata = instance.exploreMethodMetadata(
|
||||
test,
|
||||
Object.getPrototypeOf(test),
|
||||
'testEvent',
|
||||
);
|
||||
@@ -132,6 +136,7 @@ describe('ListenerMetadataExplorer', () => {
|
||||
});
|
||||
it(`should return multiple patterns when more than one is declared`, () => {
|
||||
const metadata = instance.exploreMethodMetadata(
|
||||
test,
|
||||
Object.getPrototypeOf(test),
|
||||
'testMultipleEvent',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user