Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix query string bug #888

Merged
merged 6 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/neat-emus-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@segment/analytics-next': patch
---

Fix query string parsing bug that was causing events containing the 'search' property with a non string value to be dropped
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,39 @@ describe('Page Enrichment', () => {
test('special page properties in event.properties (url, referrer, etc) are copied to context.page', async () => {
const eventProps = { ...helpers.pageProps }
;(eventProps as any)['should_not_show_up'] = 'hello'
const ctx = await ajs.track('My Event', eventProps)
const ctx = await ajs.page('My Event', eventProps)
const page = ctx.event.context!.page
expect(page).toEqual(
pick(eventProps, ['url', 'path', 'referrer', 'search', 'title'])
)
})

test('event page properties should not be mutated', async () => {
test('special page properties in event.properties (url, referrer, etc) are not copied to context.page in non-page calls', async () => {
const eventProps = { ...helpers.pageProps }
;(eventProps as any)['should_not_show_up'] = 'hello'
const ctx = await ajs.track('My Event', eventProps)
const page = ctx.event.context!.page
expect(page).toMatchInlineSnapshot(`
Object {
"path": "/",
"referrer": "",
"search": "",
"title": "",
"url": "http://localhost/",
}
`)
})

test('event page properties should not be mutated', async () => {
const eventProps = { ...helpers.pageProps }
const ctx = await ajs.page('My Event', eventProps)
const page = ctx.event.context!.page
expect(page).toEqual(eventProps)
})

test('page properties should have defaults', async () => {
const eventProps = pick(helpers.pageProps, ['path', 'referrer'])
const ctx = await ajs.track('My Event', eventProps)
const ctx = await ajs.page('My Event', eventProps)
const page = ctx.event.context!.page
expect(page).toEqual({
...eventProps,
Expand All @@ -91,7 +107,7 @@ describe('Page Enrichment', () => {
eventProps.referrer = ''
eventProps.path = undefined as any
eventProps.title = null as any
const ctx = await ajs.track('My Event', eventProps)
const ctx = await ajs.page('My Event', eventProps)
const page = ctx.event.context!.page
expect(page).toEqual(
expect.objectContaining({ referrer: '', path: undefined, title: null })
Expand Down
21 changes: 13 additions & 8 deletions packages/browser/src/plugins/page-enrichment/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { pick } from '../../lib/pick'
import type { Context } from '../../core/context'
import type { Plugin } from '../../core/plugin'
import { EventProperties } from '@segment/analytics-core'

interface PageDefault {
[key: string]: unknown
Expand Down Expand Up @@ -75,23 +76,27 @@ function enrichPageContext(ctx: Context): Context {

const defaultPageContext = pageDefaults()

const pageContextFromEventProps =
event.properties && pick(event.properties, Object.keys(defaultPageContext))

event.context.page = {
...defaultPageContext,
...pageContextFromEventProps,
...event.context.page,
}
let pageContextFromEventProps: Pick<EventProperties, string> | undefined =
undefined

if (event.type === 'page') {
pageContextFromEventProps =
event.properties &&
pick(event.properties, Object.keys(defaultPageContext))

event.properties = {
...defaultPageContext,
...event.properties,
...(event.name ? { name: event.name } : {}),
}
}

event.context.page = {
...defaultPageContext,
...pageContextFromEventProps,
...event.context.page,
}

return ctx
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('before loading', () => {
describe('#normalize', () => {
let object: SegmentEvent
let defaultCtx: any
const withSearchParams = (search?: string) => {
const withSearchParams = (search?: any) => {
object.context = { page: { search } }
}

Expand Down Expand Up @@ -285,6 +285,18 @@ describe('before loading', () => {
assert(obj.context.campaign.name === 'overrideName')
})

it('should allow override of .search with object', () => {
withSearchParams({
someObject: 'foo',
})

normalize(analytics, object, options, {})
assert(object)
assert(object.context)
assert(object.context.campaign === undefined)
assert(object.context.referrer === undefined)
})

it('should add .referrer.id and .referrer.type (cookies)', () => {
withSearchParams('?utm_source=source&urid=medium')

Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/plugins/segmentio/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ export function normalize(
const ctx = json.context

// This guard against missing ctx.page should not be neccessary, since context.page is always defined
const query: string = ctx.page?.search || ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure where we set search for ourselves, but if it's internal maybe we should try putting our stuff in a particular nested object internal? or use _search ?

const query: string =
typeof ctx.page?.search === 'string' ? ctx.page?.search : ''
Copy link
Contributor

@silesky silesky Jul 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior needs a test, correct? 'Page calls should be able to accept search as an object.'


delete json.options
json.writeKey = settings?.apiKey
Expand Down