Skip to content

Commit

Permalink
fix: captureMethod correctly detect method name when used with extern…
Browse files Browse the repository at this point in the history
…al decorators (#1109)
  • Loading branch information
dreamorosi authored Oct 4, 2022
1 parent f25f7bf commit a574406
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 5 deletions.
8 changes: 5 additions & 3 deletions packages/tracer/src/Tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ class Tracer extends Utility implements TracerInterface {
* @decorator Class
*/
public captureMethod(options?: HandlerOptions): MethodDecorator {
return (_target, _propertyKey, descriptor) => {
return (_target, propertyKey, descriptor) => {
// The descriptor.value is the method this decorator decorates, it cannot be undefined.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const originalMethod = descriptor.value!;
Expand All @@ -434,12 +434,14 @@ class Tracer extends Utility implements TracerInterface {
return originalMethod.apply(this, [...args]);
}

return tracerRef.provider.captureAsyncFunc(`### ${originalMethod.name}`, async subsegment => {
const methodName = String(propertyKey);

return tracerRef.provider.captureAsyncFunc(`### ${methodName}`, async subsegment => {
let result;
try {
result = await originalMethod.apply(this, [...args]);
if (options?.captureResponse ?? true) {
tracerRef.addResponseAsMetadata(result, originalMethod.name);
tracerRef.addResponseAsMetadata(result, methodName);
}
} catch (error) {
tracerRef.addErrorAsMetadata(error as Error);
Expand Down
67 changes: 65 additions & 2 deletions packages/tracer/tests/unit/Tracer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ interface LambdaInterface {
}

type CaptureAsyncFuncMock = jest.SpyInstance<unknown, [name: string, fcn: (subsegment?: Subsegment) => unknown, parent?: Segment | Subsegment]>;
const createCaptureAsyncFuncMock = function(provider: ProviderServiceInterface): CaptureAsyncFuncMock {
const createCaptureAsyncFuncMock = function(provider: ProviderServiceInterface, subsegment?: Subsegment): CaptureAsyncFuncMock {
return jest.spyOn(provider, 'captureAsyncFunc')
.mockImplementation(async (methodName, callBackFn) => {
const subsegment = new Subsegment(`### ${methodName}`);
if (!subsegment) {
subsegment = new Subsegment(`### ${methodName}`);
}
jest.spyOn(subsegment, 'flush').mockImplementation(() => null);
await callBackFn(subsegment);
});
Expand Down Expand Up @@ -1239,6 +1241,67 @@ describe('Class: Tracer', () => {

});

test('when used as decorator together with another external decorator, the method name is detected properly', async () => {

// Prepare
const tracer: Tracer = new Tracer();
const newSubsegment: Segment | Subsegment | undefined = new Subsegment('### dummyMethod');
jest.spyOn(tracer.provider, 'getSegment')
.mockImplementation(() => newSubsegment);
setContextMissingStrategy(() => null);
createCaptureAsyncFuncMock(tracer.provider, newSubsegment);

// Creating custom external decorator
// eslint-disable-next-line func-style
function passThrough() {
// A decorator that calls the original method.
return (
_target: unknown,
_propertyKey: string,
descriptor: PropertyDescriptor
) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const originalMethod = descriptor.value!;
descriptor.value = function (...args: unknown[]) {
return originalMethod.apply(this, [...args]);
};
};
}

class Lambda implements LambdaInterface {
@tracer.captureMethod()
@passThrough()
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
public async dummyMethod(): Promise<string> {
return `foo`;
}

public async handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): Promise<void> {
await this.dummyMethod();

return;
}

}

// Act / Assess
const lambda = new Lambda();
const handler = lambda.handler.bind(lambda);
await handler({}, context, () => console.log('Lambda invoked!'));

// Assess
expect(newSubsegment).toEqual(expect.objectContaining({
metadata: {
'hello-world': {
// Assess that the method name is added correctly
'dummyMethod response': 'foo',
},
}
}));

});

});

describe('Method: captureAWS', () => {
Expand Down

0 comments on commit a574406

Please sign in to comment.