Skip to content

Commit f808447

Browse files
authored
feat: Handle duplicate agent APIs (#1418)
1 parent 754c4eb commit f808447

File tree

5 files changed

+81
-31
lines changed

5 files changed

+81
-31
lines changed

src/loaders/api/api.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ export function setTopLevelCallers () {
3232
function caller (fnName, ...args) {
3333
let returnVals = []
3434
Object.values(nr.initializedAgents).forEach(val => {
35-
if (!val || !val.api) {
35+
if (!val || !val.api || !val.runtime) {
3636
warn(38, fnName)
37-
} else if (val.exposed && val.api[fnName]) {
37+
} else if (val.exposed && val.api[fnName] && val.runtime.loaderType !== 'micro-agent') {
3838
returnVals.push(val.api[fnName](...args))
3939
}
4040
})
41-
return returnVals.length > 1 ? returnVals : returnVals[0]
41+
return returnVals[0]
4242
}
4343
}
4444

tests/specs/npm/micro-agent.e2e.js

+17
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,21 @@ describe('micro-agent', () => {
9191
return true
9292
}
9393
})
94+
95+
it('returns null on top-level spa api interaction call', async () => {
96+
await browser.url(await browser.testHandle.assetURL('test-builds/browser-agent-wrapper/micro-agent.html'))
97+
98+
const ixn = await browser.execute(function () {
99+
var opts = {
100+
info: NREUM.info,
101+
init: NREUM.init
102+
}
103+
window.agent1 = new MicroAgent({ ...opts, info: { ...opts.info, applicationID: 1 } })
104+
105+
// top-level interaction call should return null since there is no non-micro agent to respond to the api call
106+
return window.newrelic.interaction()
107+
})
108+
109+
expect(ixn).toBeNull()
110+
})
94111
})

tests/specs/npm/multi-agent.e2e.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* globals MicroAgent */
2+
3+
describe('multi-agent', () => {
4+
it('has a single interaction response on top-level spa api interaction call', async () => {
5+
await browser.url(await browser.testHandle.assetURL('test-builds/browser-agent-wrapper/multi-agent.html'))
6+
7+
const ixn = await browser.execute(function () {
8+
var opts = {
9+
info: NREUM.info,
10+
init: NREUM.init
11+
}
12+
window.agent1 = new MicroAgent({ ...opts, info: { ...opts.info, applicationID: 1 } })
13+
14+
// micro-agents are forbidden to respond to a top-level api call, so only the non-micro agent's interaction should exist
15+
return window.newrelic.interaction()
16+
})
17+
18+
// a single interaction should have api methods defined on them
19+
expect(ixn.get).toBeDefined()
20+
})
21+
})

tests/unit/loaders/api/api.test.js

+22-28
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe('setTopLevelCallers', () => {
3232
expect(typeof nreum.wrapLogger).toEqual('function')
3333
})
3434

35-
test('should forward calls to initialized and exposed agents', () => {
35+
test('should forward calls to the first initialized and exposed agent that is not a micro-agent', () => {
3636
setTopLevelCallers()
3737

3838
const nreum = gosCDN()
@@ -41,18 +41,27 @@ describe('setTopLevelCallers', () => {
4141
exposed: true,
4242
api: {
4343
setErrorHandler: jest.fn()
44+
},
45+
runtime: {
46+
loaderType: 'micro-agent'
4447
}
4548
},
4649
[faker.string.uuid()]: {
4750
exposed: true,
4851
api: {
4952
setErrorHandler: jest.fn()
53+
},
54+
runtime: {
55+
loaderType: 'browser-agent'
5056
}
5157
},
5258
[faker.string.uuid()]: {
5359
exposed: false,
5460
api: {
5561
setErrorHandler: jest.fn()
62+
},
63+
runtime: {
64+
loaderType: 'browser-agent'
5665
}
5766
}
5867
}
@@ -61,7 +70,7 @@ describe('setTopLevelCallers', () => {
6170
nreum.setErrorHandler(errorHandler)
6271

6372
Object.values(nreum.initializedAgents).forEach(agent => {
64-
if (agent.exposed) {
73+
if (agent.exposed && agent.runtime.loaderType === 'browser-agent') {
6574
expect(agent.api.setErrorHandler).toHaveBeenCalledTimes(1)
6675
expect(agent.api.setErrorHandler).toHaveBeenCalledWith(errorHandler)
6776
} else {
@@ -70,31 +79,7 @@ describe('setTopLevelCallers', () => {
7079
})
7180
})
7281

73-
test('should return a single value when only one exposed agent returns a value', () => {
74-
setTopLevelCallers()
75-
76-
const nreum = gosCDN()
77-
const expected = faker.string.uuid()
78-
nreum.initializedAgents = {
79-
[faker.string.uuid()]: {
80-
exposed: true,
81-
api: {
82-
interaction: jest.fn().mockReturnValue(expected)
83-
}
84-
},
85-
[faker.string.uuid()]: {
86-
exposed: false,
87-
api: {
88-
interaction: jest.fn().mockReturnValue(expected)
89-
}
90-
}
91-
}
92-
93-
const result = nreum.interaction()
94-
expect(result).toEqual(expected)
95-
})
96-
97-
test('should return an array of values for each exposed agent that returns a value', () => {
82+
test('should return a single value for the first exposed browser-agent in the nreum initialized agents array', () => {
9883
setTopLevelCallers()
9984

10085
const nreum = gosCDN()
@@ -104,23 +89,32 @@ describe('setTopLevelCallers', () => {
10489
exposed: true,
10590
api: {
10691
interaction: jest.fn().mockReturnValue(expected)
92+
},
93+
runtime: {
94+
loaderType: 'browser-agent'
10795
}
10896
},
10997
[faker.string.uuid()]: {
11098
exposed: true,
11199
api: {
112100
interaction: jest.fn().mockReturnValue(expected)
101+
},
102+
runtime: {
103+
loaderType: 'browser-agent'
113104
}
114105
},
115106
[faker.string.uuid()]: {
116107
exposed: false,
117108
api: {
118109
interaction: jest.fn().mockReturnValue(expected)
110+
},
111+
runtime: {
112+
loaderType: 'browser-agent'
119113
}
120114
}
121115
}
122116

123117
const result = nreum.interaction()
124-
expect(result).toEqual([expected, expected])
118+
expect(result).toEqual(expected)
125119
})
126120
})

tools/test-builds/browser-agent-wrapper/webpack.config.js

+18
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ const htmlTemplate = (script) => `<html>
1313
<h1>This is a generic page that is instrumented by the NPM agent</h1>
1414
</body>
1515
</html>`
16+
const multiAgentHtmlTemplate = () => `<html>
17+
<head>
18+
<title>RUM Unit Test</title>
19+
{init}
20+
{config}
21+
<script src="browser-agent.js"></script>
22+
<script src="micro-agent.js"></script>
23+
</head>
24+
<body>
25+
<h1>This is a generic page that is instrumented by the NPM agent. It has a main agent and a micro agent running together.</h1>
26+
</body>
27+
</html>`
1628
const workerHtmlTemplate = `<html>
1729
<head>
1830
<title>RUM Unit Test</title>
@@ -115,6 +127,12 @@ const config = [
115127
minify: false,
116128
inject: false,
117129
templateContent: htmlTemplate('micro-agent')
130+
}),
131+
new HtmlWebpackPlugin({
132+
filename: 'multi-agent.html',
133+
minify: false,
134+
inject: false,
135+
templateContent: multiAgentHtmlTemplate()
118136
})
119137
]
120138
},

0 commit comments

Comments
 (0)