Skip to content

Commit e552257

Browse files
authored
chore : updated function to extract nested handlers with test cases (#325)
updated function to extract nested handlers
1 parent 90cc709 commit e552257

File tree

15 files changed

+352
-7
lines changed

15 files changed

+352
-7
lines changed

nodejs/index.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ const newrelic = require('newrelic')
1414
const fs = require('node:fs')
1515
const path = require('node:path')
1616

17+
function getNestedHandler(object, nestedProperty) {
18+
return nestedProperty.split('.').reduce((nested, key) => {
19+
return nested && nested[key]
20+
}, object)
21+
}
22+
23+
1724
function getHandlerPath() {
1825
let handler
1926
const { NEW_RELIC_LAMBDA_HANDLER } = process.env
@@ -32,9 +39,12 @@ function getHandlerPath() {
3239
)
3340
}
3441

35-
const handlerToWrap = parts[parts.length - 1]
36-
const moduleToImport = handler.slice(0, handler.lastIndexOf('.'))
37-
return { moduleToImport, handlerToWrap }
42+
const lastSlashIndex = handler.lastIndexOf('/') + 1
43+
const firstDotAfterSlash = handler.indexOf('.', lastSlashIndex)
44+
const moduleToImport = handler.slice(0, firstDotAfterSlash)
45+
const handlerToWrap = handler.slice(firstDotAfterSlash + 1)
46+
47+
return {moduleToImport, handlerToWrap}
3848
}
3949

4050
function handleRequireImportError(e, moduleToImport) {
@@ -117,14 +127,17 @@ if (process.env.NEW_RELIC_USE_ESM === 'true') {
117127
}
118128

119129
async function getHandler() {
120-
const userHandler = (await getModuleWithImport(LAMBDA_TASK_ROOT, moduleToImport))[handlerToWrap]
130+
const userModule = await getModuleWithImport(LAMBDA_TASK_ROOT, moduleToImport)
131+
const userHandler = getNestedHandler(userModule, handlerToWrap)
132+
121133
validateHandlerDefinition(userHandler, handlerToWrap, moduleToImport)
122134

123135
return userHandler
124136
}
125137

126138
function getHandlerSync() {
127-
const userHandler = getModuleWithRequire(LAMBDA_TASK_ROOT, moduleToImport)[handlerToWrap]
139+
const userModule = getModuleWithRequire(LAMBDA_TASK_ROOT, moduleToImport)
140+
const userHandler = getNestedHandler(userModule, handlerToWrap)
128141
validateHandlerDefinition(userHandler, handlerToWrap, moduleToImport)
129142

130143
return userHandler

nodejs/test/integration/cjs/handler.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,40 @@ module.exports.TestPayloadSchemaValidation = (event, context, callback) => {
186186
statusCode: 200,
187187
})
188188
}
189+
190+
module.exports.nested = {
191+
contextDoneHandler: function contextDoneHandler(event, context) {
192+
context.done(null, {
193+
body: JSON.stringify('foo'),
194+
statusCode: 200,
195+
})
196+
},
197+
198+
contextSucceedHandler: function contextSucceedHandler(event, context) {
199+
context.succeed({
200+
body: JSON.stringify('foo'),
201+
statusCode: 200,
202+
})
203+
},
204+
205+
callbackHandler: function callbackHandler(event, context, callback) {
206+
callback(null, {
207+
body: JSON.stringify('foo'),
208+
statusCode: 200,
209+
})
210+
},
211+
212+
promiseHandler: function promiseHandler() {
213+
return Promise.resolve({
214+
body: JSON.stringify('foo'),
215+
statusCode: 200,
216+
})
217+
},
218+
219+
asyncFunctionHandler: async function asyncFunctionHandler() {
220+
return {
221+
body: JSON.stringify('foo'),
222+
statusCode: 200,
223+
}
224+
},
225+
}

nodejs/test/integration/cjs/index.tap.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,36 @@ tap.test('CJS Handler Integration Tests', (t) => {
134134
path: '/dev/callback-inside-promise-handler',
135135
status: 200,
136136
},
137+
{
138+
description: 'when nested handler uses context.done',
139+
expected: 'foo',
140+
path: '/dev/nested/context-done-handler',
141+
status: 200,
142+
},
143+
{
144+
description: 'when nested handler uses context.succeed',
145+
expected: 'foo',
146+
path: '/dev/nested/context-succeed-handler',
147+
status: 200,
148+
},
149+
{
150+
description: 'when nested handler uses a callback',
151+
expected: 'foo',
152+
path: '/dev/nested/callback-handler',
153+
status: 200,
154+
},
155+
{
156+
description: 'when nested handler returns a promise',
157+
expected: 'foo',
158+
path: '/dev/nested/promise-handler',
159+
status: 200,
160+
},
161+
{
162+
description: 'when nested handler uses an async function',
163+
expected: 'foo',
164+
path: '/dev/nested/async-function-handler',
165+
status: 200,
166+
},
137167
].forEach(({ description, expected, path, status }) => {
138168
t.test(description, async(t) => {
139169
const url = new URL(path, BASE_URL)

nodejs/test/integration/esm/handler.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export function callbackWithContextDoneHandler(event, context, callback) {
125125
})
126126
}
127127

128+
128129
export function callbackWithPromiseHandler(event, context, callback) {
129130
callback(null, {
130131
body: stringify('Hello Callback!'),
@@ -168,3 +169,39 @@ export const BadAnswerInPromiseHandler = async() => {
168169
export const BadAnswerInCallbackHandler = (event, context, callback) => {
169170
callback(null, {})
170171
}
172+
export const nested = {
173+
contextDoneHandler: function contextDoneHandler(event, context) {
174+
context.done(null, {
175+
body: JSON.stringify('foo'),
176+
statusCode: 200,
177+
})
178+
},
179+
180+
contextSucceedHandler: function contextSucceedHandler(event, context) {
181+
context.succeed({
182+
body: JSON.stringify('foo'),
183+
statusCode: 200,
184+
})
185+
},
186+
187+
callbackHandler: function callbackHandler(event, context, callback) {
188+
callback(null, {
189+
body: JSON.stringify('foo'),
190+
statusCode: 200,
191+
})
192+
},
193+
194+
promiseHandler: function promiseHandler() {
195+
return Promise.resolve({
196+
body: JSON.stringify('foo'),
197+
statusCode: 200,
198+
})
199+
},
200+
201+
asyncFunctionHandler: async function asyncFunctionHandler() {
202+
return {
203+
body: JSON.stringify('foo'),
204+
statusCode: 200,
205+
}
206+
},
207+
}

nodejs/test/integration/esm/index.tap.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,36 @@ tap.test('ESM Handler Integration Tests', (t) => {
135135
path: '/dev/callback-inside-promise-handler',
136136
status: 200,
137137
},
138+
{
139+
description: 'when nested handler uses context.done',
140+
expected: 'foo',
141+
path: '/dev/nested/context-done-handler',
142+
status: 200,
143+
},
144+
{
145+
description: 'when nested handler uses context.succeed',
146+
expected: 'foo',
147+
path: '/dev/nested/context-succeed-handler',
148+
status: 200,
149+
},
150+
{
151+
description: 'when nested handler uses a callback',
152+
expected: 'foo',
153+
path: '/dev/nested/callback-handler',
154+
status: 200,
155+
},
156+
{
157+
description: 'when nested handler returns a promise',
158+
expected: 'foo',
159+
path: '/dev/nested/promise-handler',
160+
status: 200,
161+
},
162+
{
163+
description: 'when nested handler uses an async function',
164+
expected: 'foo',
165+
path: '/dev/nested/async-function-handler',
166+
status: 200,
167+
},
138168
].forEach(({ description, expected, path, status }) => {
139169
t.test(description, async(t) => {
140170
const url = new URL(path, BASE_URL)

nodejs/test/integration/serverless.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,58 @@ functions:
221221
NEW_RELIC_LAMBDA_HANDLER: ./${env:MODULE_TYPE}/handler.BadAnswerInCallbackHandler
222222
NEW_RELIC_LAMBDA_EXTENSION_ENABLED: false
223223
LAMBDA_TASK_ROOT: ./
224+
nestedContextDoneHandler:
225+
events:
226+
- http:
227+
method: get
228+
path: nested/context-done-handler
229+
handler: ${env:HANDLER}
230+
environment:
231+
NEW_RELIC_USE_ESM: ${env:NEW_RELIC_USE_ESM}
232+
NEW_RELIC_LAMBDA_HANDLER: ./${env:MODULE_TYPE}/handler.nested.contextDoneHandler
233+
NEW_RELIC_LAMBDA_EXTENSION_ENABLED: false
234+
LAMBDA_TASK_ROOT: ./
235+
nestedContextSucceedHandler:
236+
events:
237+
- http:
238+
method: get
239+
path: nested/context-succeed-handler
240+
handler: ${env:HANDLER}
241+
environment:
242+
NEW_RELIC_USE_ESM: ${env:NEW_RELIC_USE_ESM}
243+
NEW_RELIC_LAMBDA_HANDLER: ./${env:MODULE_TYPE}/handler.nested.contextSucceedHandler
244+
NEW_RELIC_LAMBDA_EXTENSION_ENABLED: false
245+
LAMBDA_TASK_ROOT: ./
246+
nestedCallbackHandler:
247+
events:
248+
- http:
249+
method: get
250+
path: nested/callback-handler
251+
handler: ${env:HANDLER}
252+
environment:
253+
NEW_RELIC_USE_ESM: ${env:NEW_RELIC_USE_ESM}
254+
NEW_RELIC_LAMBDA_HANDLER: ./${env:MODULE_TYPE}/handler.nested.callbackHandler
255+
NEW_RELIC_LAMBDA_EXTENSION_ENABLED: false
256+
LAMBDA_TASK_ROOT: ./
257+
nestedPromiseHandler:
258+
events:
259+
- http:
260+
method: get
261+
path: nested/promise-handler
262+
handler: ${env:HANDLER}
263+
environment:
264+
NEW_RELIC_USE_ESM: ${env:NEW_RELIC_USE_ESM}
265+
NEW_RELIC_LAMBDA_HANDLER: ./${env:MODULE_TYPE}/handler.nested.promiseHandler
266+
NEW_RELIC_LAMBDA_EXTENSION_ENABLED: false
267+
LAMBDA_TASK_ROOT: ./
268+
nestedAsyncFunctionHandler:
269+
events:
270+
- http:
271+
method: get
272+
path: nested/async-function-handler
273+
handler: ${env:HANDLER}
274+
environment:
275+
NEW_RELIC_USE_ESM: ${env:NEW_RELIC_USE_ESM}
276+
NEW_RELIC_LAMBDA_HANDLER: ./${env:MODULE_TYPE}/handler.nested.asyncFunctionHandler
277+
NEW_RELIC_LAMBDA_EXTENSION_ENABLED: false
278+
LAMBDA_TASK_ROOT: ./

nodejs/test/unit/cjsErrorStates.tap.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,26 @@ const handlerAndPath = [
3535
handlerFile: 'badImport',
3636
method: 'handler'
3737
},
38+
{
39+
handlerFile: 'nestedHandler',
40+
handlerMethod: 'nested.contextDoneHandler'
41+
},
42+
{
43+
handlerFile: 'nestedHandler',
44+
handlerMethod: 'nested.contextSucceedHandler'
45+
},
46+
{
47+
handlerFile: 'nestedHandler',
48+
handlerMethod: 'nested.callbackHandler'
49+
},
50+
{
51+
handlerFile: 'nestedHandler',
52+
handlerMethod: 'nested.promiseHandler'
53+
},
54+
{
55+
handlerFile: 'nestedHandler',
56+
handlerMethod: 'nested.asyncFunctionHandler'
57+
}
3858
]
3959

4060
tap.test('CJS Edge Cases', (t) => {

nodejs/test/unit/cjsHandler.tap.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ tap.test('Layer Handler - CJS Function', (t) => {
2222
})
2323
t.context.helper = helper
2424
t.context.handler = handler
25+
t.context.newrelic = newrelic
2526
})
2627

2728
t.afterEach((t) => {
@@ -46,4 +47,25 @@ tap.test('Layer Handler - CJS Function', (t) => {
4647
t.same(res, { statusCode: 200, body: 'response body this is a test' }, 'response should be correct')
4748
await promise
4849
})
50+
51+
t.test('should wrap nested asyncFunctionHandler in transaction', async(t) => {
52+
process.env.NEW_RELIC_LAMBDA_HANDLER = 'test/unit/fixtures/cjs/handler.nested.asyncFunctionHandler'
53+
54+
const { handler } = proxyquire('../../index', {
55+
'newrelic': t.context.newrelic
56+
})
57+
t.context.handler = handler
58+
59+
const promise = new Promise((resolve) => {
60+
t.context.helper.agent.on('transactionFinished', (transaction) => {
61+
t.equal(transaction.name, 'OtherTransaction/Function/testFn', 'transaction should be properly named')
62+
resolve()
63+
})
64+
})
65+
66+
t.equal(typeof handler, 'function', 'handler should be a function')
67+
const res = await handler({ key: 'this is a test'}, { functionName: 'testFn'})
68+
t.same(res, { statusCode: 200, body: JSON.stringify('foo') }, 'response should be correct')
69+
await promise
70+
})
4971
})

nodejs/test/unit/esmErrorStates.tap.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@ const testCases = [
3030
handlerFile: 'errors',
3131
handlerMethod: 'notAfunction'
3232
},
33+
{
34+
handlerFile: 'nestedHandler',
35+
handlerMethod: 'nested.contextDoneHandler'
36+
},
37+
{
38+
handlerFile: 'nestedHandler',
39+
handlerMethod: 'nested.contextSucceedHandler'
40+
},
41+
{
42+
handlerFile: 'nestedHandler',
43+
handlerMethod: 'nested.callbackHandler'
44+
},
45+
{
46+
handlerFile: 'nestedHandler',
47+
handlerMethod: 'nested.promiseHandler'
48+
},
49+
{
50+
handlerFile: 'nestedHandler',
51+
handlerMethod: 'nested.asyncFunctionHandler'
52+
}
3353
]
3454

3555
tap.test('Early-throwing ESM Edge Cases', (t) => {

nodejs/test/unit/esmHandler.tap.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ tap.test('Layer Handler - ESM Function', (t) => {
1616

1717
const newrelic = helper.getAgentApi()
1818
await td.replaceEsm('newrelic', {}, newrelic)
19-
const { handler } = await import('../../esm.mjs')
19+
const { handler } = await import('../../esm.mjs')
2020
t.context.helper = helper
2121
t.context.handler = handler
2222
t.context.originalEnv = originalEnv
23+
t.context.newrelic = newrelic
2324
})
2425

2526
t.afterEach((t) => {
@@ -44,4 +45,25 @@ tap.test('Layer Handler - ESM Function', (t) => {
4445
t.same(res, { statusCode: 200, body: 'response body this is a test' }, 'response should be correct')
4546
await promise
4647
})
48+
49+
t.test('should wrap nested asyncFunctionHandler in transaction', async(t) => {
50+
process.env.NEW_RELIC_LAMBDA_HANDLER = 'test/unit/fixtures/esm/handler.nested.asyncFunctionHandler'
51+
52+
const newrelic = t.context.newrelic
53+
await td.replaceEsm('newrelic', {}, newrelic)
54+
const { handler } = await import('../../esm.mjs')
55+
t.context.handler = handler
56+
57+
const promise = new Promise((resolve) => {
58+
t.context.helper.agent.on('transactionFinished', (transaction) => {
59+
t.equal(transaction.name, 'OtherTransaction/Function/testFn', 'transaction should be properly named')
60+
resolve()
61+
})
62+
})
63+
64+
t.equal(typeof handler, 'function', 'handler should be a function')
65+
const res = await handler({ key: 'this is a test'}, { functionName: 'testFn'})
66+
t.same(res, { statusCode: 200, body: JSON.stringify('foo') }, 'response should be correct')
67+
await promise
68+
})
4769
})

0 commit comments

Comments
 (0)