Support destination filters for device mode action destinations#597
Support destination filters for device mode action destinations#597danieljackins merged 21 commits intomasterfrom
Conversation
🦋 Changeset detectedLatest commit: 91f0d8e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
chrisradek
left a comment
There was a problem hiding this comment.
Overall I think this looks great, excited to see this middleware support added to action destinations!
Things I wanted to check:
- We may need to move
alternativeNamesto the plugin interface then include it in our checks here:analytics-next/packages/browser/src/core/queue/event-queue.ts
Lines 236 to 239 in 9c93579
This way if someone is currently disabling/enabling based on the current plugin name (likeFullStory (actions) trackEvent) it still works. - Can we apply the above logic for logging/metrics as well?
- I commented that we should only apply destination filters on destination plugins - wonder if that should apply for all destination middleware as well. What are your thoughts there given action destinations can have non-destination actions.
|
|
||
| return new Context(modifiedEvent) | ||
| } | ||
|
|
There was a problem hiding this comment.
Is there an opportunity here to be more DRY and shave off some bytes?
Something like:
private _createMethod(methodName: 'track' | 'page' | 'identify' | 'alias' | 'group' | 'screen') {
return async (ctx: Context): Promise<Context> => {
if (!this.action[methodName]) return ctx
const transformedContext = await this.transform(ctx)
await this.action[methodName](transformedContext)
return ctx
}
}
alias = _createMethod('track')
group = _createMethod('group')
... etc
|
The context keeps logs/stats about which plugins were ran. Currently you can see which actions are ran and some stats about them. With these changes I think we'll see the wrapper plugin's name instead, so |
| if ( | ||
| routing.length && | ||
| routingMiddleware && | ||
| plugin.type === 'destination' |
There was a problem hiding this comment.
Might be worth adding a comment that we only want to apply destination filters to destination actions so that we don't cause issues for hybrid action destinations (where we simply augment the event and send it to the cloud rather than directly to the destination)
I see future selves forgetting why we limited this 😄
| } | ||
|
|
||
| unload(ctx: Context, analytics: Analytics): Promise<unknown> | unknown { | ||
| return this.action.unload && this.action.unload(ctx, analytics) |
There was a problem hiding this comment.
just in case you aren’t a Typescript nerd, you can shorten this to “return this.action.unload?.(ctx, analytics)” IIRC
|
|
||
| const alternativeNameMatch = | ||
| p.alternativeNames && | ||
| p.alternativeNames.filter((name) => |
There was a problem hiding this comment.
Optional changing nitpick! p.alternativeNames?.filter would be a bit nicer IMO
There was a problem hiding this comment.
I typically agree - though the size savings are not insignificant due to the transpilations that occur (assuming variables/field names get minified). See this as an example. (Just a tradeoff - if it makes the code more readable then go with the optional chaining - I was just surprised how much larger the transpiled code was)
There was a problem hiding this comment.
interesting... that's a nice data point. I think I still prefer the optional chain... I wonder if babel would do a better job?
We use optional chaining all over, so IMO we should just use it and not care about size and hope the tooling gets better (or attempt to apply more optimization). or, if we're going to ban it, we should talk about it and then ban it and do a global refactor and use a lint rule to enforce. But If we're going to use it once I'd prefer to use it liberally for readability and then at some future point we target it for cost savings.
| private async transform(ctx: Context): Promise<Context> { | ||
| const modifiedEvent = await applyDestinationMiddleware( | ||
| this.name, | ||
| klona(ctx.event), |
There was a problem hiding this comment.
I wasn't aware that applyDestinationMiddleware mutated the event
- Can we avoid that? (If so, we'd want to clone the event inside of the applyDestinationMiddleware function, right?)
- Are there any tests that would fail if
klonawasn't included?
This is the kind of code smell stuff that I think can get confusing later if the why is not documented (in tests, comments, etc).
There was a problem hiding this comment.
We do need to mutate the event I believe, but I moved the cloning logic into applyDestinationMiddleware and added a test that will fail if the original context gets modified (ie. if klona gets removed) 👍
chrisradek
left a comment
There was a problem hiding this comment.
Overall looks great! Just had 1 question that might be an issue.
| // Explicit integration option takes precedence, `All: false` does not apply to Segment.io | ||
| return ( | ||
| denyList[p.name] ?? | ||
| alternativeNameMatch ?? |
There was a problem hiding this comment.
Isn't alternativeNameMismatch an array of strings here? I think that'd be evaluated as a truthy value so we'd never hit the next conditional, or am I missing something?
There was a problem hiding this comment.
Nice catch - fixed the logic and added a new test
Co-authored-by: Christopher Radek <14189820+chrisradek@users.noreply.github.com>

This PR allows destination filters to work with action destinations, by adding destination middleware support to them.
This change was tested by adding destination filters to an action destination and making sure events and properties were dropped properly.
[x] I've included a changeset (psst. run
yarn changeset. Read about changesets here).