feat: Add placement event mapping for event attributes#57
Conversation
There was a problem hiding this comment.
Pull request overview
Expands the Rokt kit’s placement event mapping to also support mapping event attributes (with optional conditional operators) to Rokt local session attributes, while keeping the existing event-type/category/name mapping behavior intact.
Changes:
- Added
placementEventAttributeMappingLookupplus rule evaluation (exists/equals/contains) and application duringprocessEvent. - Parsed new
placementEventAttributeMappingsetting and exposed a test helper to build the lookup. - Added unit tests for lookup generation and conditional attribute mapping behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/Rokt-Kit.js |
Implements attribute-based mapping rules and applies them during event processing. |
test/src/tests.js |
Adds unit tests covering lookup generation and condition evaluation for attribute mappings. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (conditions.length === 0) { | ||
| return true; | ||
| } | ||
|
|
||
| var actualValue = getEventAttributeValue(event, rule.eventAttributeKey); |
There was a problem hiding this comment.
When a mapping has no conditions (or conditions default to an empty array), doesEventMatchRule returns true without checking whether the referenced event attribute actually exists on the event. This can cause mapped local session attributes to be set for every processed event even when event.EventAttributes[rule.eventAttributeKey] is missing. Consider treating an empty/missing conditions array as an implicit exists check (or explicitly requiring the attribute key to be present before returning true).
| if (conditions.length === 0) { | |
| return true; | |
| } | |
| var actualValue = getEventAttributeValue(event, rule.eventAttributeKey); | |
| var actualValue = getEventAttributeValue(event, rule.eventAttributeKey); | |
| // When no explicit conditions are provided, require that the attribute exists. | |
| if (conditions.length === 0) { | |
| return actualValue !== null; | |
| } |
| it('should map multiple attributes for the same mapped key (AND across rules)', async () => { | ||
| const placementEventAttributeMapping = JSON.stringify([ | ||
| { | ||
| jsmap: null, | ||
| map: 'URL', | ||
| maptype: 'EventAttributeClass.Name', | ||
| value: 'saleSeeker', | ||
| conditions: [ | ||
| { | ||
| operator: 'contains', | ||
| attributeValue: 'sale', | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| jsmap: null, | ||
| map: 'URL', | ||
| maptype: 'EventAttributeClass.Name', | ||
| value: 'saleSeeker1', | ||
| conditions: [ | ||
| { | ||
| operator: 'contains', | ||
| attributeValue: 'items', | ||
| }, | ||
| ], | ||
| }, | ||
| ]); |
There was a problem hiding this comment.
This test name/description is misleading: it says "multiple attributes for the same mapped key (AND across rules)", but the sample mapping uses two different mapped keys (saleSeeker and saleSeeker1) and the assertions validate both keys can be set (which is not an AND-across-rules scenario). Please rename the test (or adjust the mapping to actually cover multiple rules for the same mapped key) to avoid confusion about the intended behavior.
| it('should require ALL rules for the same mapped key to match (AND across rules)', async () => { | ||
| const placementEventAttributeMapping = JSON.stringify([ | ||
| { | ||
| jsmap: null, | ||
| map: 'URL', | ||
| maptype: 'EventAttributeClass.Name', | ||
| value: 'saleSeeker', | ||
| conditions: [ | ||
| { | ||
| operator: 'contains', | ||
| attributeValue: 'sale', | ||
| }, | ||
| { | ||
| operator: 'contains', | ||
| attributeValue: 'items', | ||
| }, | ||
| ], | ||
| }, | ||
| ]); |
There was a problem hiding this comment.
The "AND across rules" behavior in applyPlacementEventAttributeMapping is implemented across multiple mapping entries with the same mapped key (i.e., multiple rules in rulesForMappedAttributeKey). This test only validates AND behavior across multiple conditions within a single mapping entry, so it doesn't exercise the multi-rule aggregation logic. Add a test case with two mapping objects that share the same value to ensure all rules must match before setting the mapped attribute.
There was a problem hiding this comment.
We actually want to check if the mapping lookup is empty early so we can avoid any unnecessary processing later in this function. I know in your applyPlacementEventAttributeMapping funciton, you have some guard clauses, but from a readability standpoint, it's probably better to do them here so we avoid cognitive complexity by having to bounce around between functions.
| if ( | ||
| !self.placementEventAttributeMappingLookup || | ||
| isEmpty(self.placementEventAttributeMappingLookup) | ||
| ) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
I don't think you need to check if it exists since isEmpty should be doing that too,
| if ( | |
| !self.placementEventAttributeMappingLookup || | |
| isEmpty(self.placementEventAttributeMappingLookup) | |
| ) { | |
| return; | |
| } | |
| if (isEmpty(self.placementEventAttributeMappingLookup)) { | |
| return; | |
| } |
| if ( | ||
| typeof actualValue !== 'string' || | ||
| typeof expectedValue !== 'string' | ||
| ) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
You're directly checking if an operator is a string in a lot places. I know it's not a lot of code, but I would recommend creating a simple isString utility function. It'll clean up a lot of these sections and make them more readable.
| if ( | |
| typeof actualValue !== 'string' || | |
| typeof expectedValue !== 'string' | |
| ) { | |
| return false; | |
| } | |
| if (!isString(actualValue) || !isString(expectedValue)) { | |
| return false; | |
| } |
| if ( | ||
| !rulesForMappedAttributeKey || | ||
| !rulesForMappedAttributeKey.length | ||
| ) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
| if ( | |
| !rulesForMappedAttributeKey || | |
| !rulesForMappedAttributeKey.length | |
| ) { | |
| continue; | |
| } | |
| if (isEmpty(rulesForMappedAttributeKey)) { | |
| continue; | |
| } |
Summary
Currently in the Rokt Kit, you can only map specific events (Event Type, Event Category, & Event Name) to a Rokt attribute.

This PR expands the existing functionality to map event attributes matching certain conditions to a Rokt attribute.
Example: "Events with url attribute containing
salewill map tosaleseekerRokt attribute.The existing functionality still works as expected. The conditions are stored in a separate object called placementEventAttributeMappingLookup. It will map an array of conditions to the mapped attribute key.
Testing Plan
Testing locally, unit tests, going to make changes to the connection level to add the JSON custom field to allow for the placement event mapping rules