[CDX-470] Add additionalTrackingKeys option#472
Conversation
…vents Adds a new `additionalTrackingKeys` client option that duplicates tracking events to additional API keys. When configured, each tracking event queued via RequestQueue is also sent to each additional key with the `key` parameter swapped in both the URL and POST body.
There was a problem hiding this comment.
Pull request overview
Adds an additionalTrackingKeys client option to support duplicating tracking events across multiple Constructor.io API keys, primarily by cloning queued RequestQueue entries with the key swapped in the URL/body.
Changes:
- Introduces
additionalTrackingKeys?: string[]onConstructorClientOptionsand wires it throughConstructorIOoptions. - Updates
RequestQueueto enqueue duplicate tracking requests per additional key. - Adds unit + integration tests validating duplication behavior and invalid-key filtering.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/request-queue.js | Duplicates queued tracking requests for each configured additional key |
| src/types/index.d.ts | Adds additionalTrackingKeys?: string[] to the public options type |
| src/constructorio.js | Plumbs additionalTrackingKeys through normalized client options |
| spec/src/utils/request-queue.js | Unit tests for duplication, empty/missing option, invalid entries, GET behavior |
| spec/src/modules/tracker.js | Integration test asserting fetch is called for both primary + additional keys |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| additionalTrackingKeys, | ||
| } = options; |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This PR adds additionalTrackingKeys to fan out tracking events to multiple API keys by duplicating queue entries in RequestQueue. The implementation is mostly sound but has a correctness bug with URL replacement, a test isolation gap, and a few reliability concerns worth addressing.
Inline comments: 6 discussions added
Overall Assessment:
| additionalTrackingKeys: [], | ||
| }); | ||
|
|
||
| requests.queue('https://ac.cnstrc.com/behavior?action=session_start&key=primary-key&_dt=123'); |
There was a problem hiding this comment.
Important Issue: The GET test ('Should add duplicate requests for GET method') asserts queue[1].body.key equals 'extra-key-1', but the primary request was queued with no explicit body argument (defaults to {}). The assertion passes only because the duplicated entry spreads {} and then sets key. However, this conflates the GET-path body handling with POST semantics. The test should also assert queue[0].body to be {} (or undefined) to document the intended contract for GET requests, making the asymmetry visible and intentional.
| callCount += 1; | ||
|
|
||
| if (callCount === 2) { | ||
| expect(fetchSpy).to.have.been.calledTwice; |
There was a problem hiding this comment.
Important Issue: The integration test relies on fetchSpy.getCall(0) and getCall(1) being the two calls from this test. But fetchSpy is a module-level variable re-assigned in each beforeEach — it is not reset between describe blocks. If a prior test in the suite has already recorded calls on the spy, getCall(0) and getCall(1) may refer to the wrong invocations.
Use fetchSpy.args relative to the current test context, or (preferred) create a dedicated spy scoped to this test:
const localSpy = sinon.spy(fetch);
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
fetch: localSpy,
...
});
// then assert localSpy.getCall(0) / localSpy.getCall(1)Also, calledTwice is asserted inside a callback that fires only once (when callCount === 2). If the first call triggers an error and second triggers success (or vice versa), getCall(0).args[0] may be the secondary-key request, not the primary. Asserting on url.includes for both keys (without assuming order) would make this more robust.
| beaconMode, | ||
| networkParameters, | ||
| humanityCheckLocation, | ||
| additionalTrackingKeys, |
There was a problem hiding this comment.
Suggestion: additionalTrackingKeys is passed through to this.options as-is without any normalisation (no filtering of non-strings, no deduplication). The filtering is deferred to RequestQueue.queue(). This is a valid design choice, but consider documenting it or — better — normalising in ConstructorIO constructor so that this.options.additionalTrackingKeys always contains only valid, deduplicated strings. This prevents surprises if other code paths ever read the option directly without going through RequestQueue.
Summary
additionalTrackingKeysoption toConstructorClientOptionsthat accepts an array of API key stringsRequestQueueis duplicated for each additional key, with thekeyswapped in both the URL query string and POST bodyUsage
Changes
src/types/index.d.ts— AddedadditionalTrackingKeys?: string[]to options interfacesrc/constructorio.js— Pass through the new optionsrc/utils/request-queue.js— Duplicate queued requests for each additional keyspec/src/utils/request-queue.js— Unit tests (5 cases)spec/src/modules/tracker.js— Integration testTest plan
Resolves CDX-470