From 009b5e1044d80f9aec5418a299c8e83c50af1390 Mon Sep 17 00:00:00 2001 From: Tim Glaser Date: Thu, 25 Feb 2021 12:47:56 +0100 Subject: [PATCH 01/12] Set UTM etc when creating persons --- src/ingestion/process-event.ts | 19 ++++++--- src/ingestion/utils.ts | 23 +++++++++++ tests/shared/process-event.ts | 75 ++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 9 deletions(-) diff --git a/src/ingestion/process-event.ts b/src/ingestion/process-event.ts index e9554a8de..30e8de96a 100644 --- a/src/ingestion/process-event.ts +++ b/src/ingestion/process-event.ts @@ -24,7 +24,7 @@ import { } from '../types' import { castTimestampOrNow, UUID, UUIDT } from '../utils' import { KAFKA_EVENTS, KAFKA_SESSION_RECORDING_EVENTS } from './topics' -import { elementsToString, sanitizeEventName, timeoutGuard } from './utils' +import { elementsToString, sanitizeEventName, timeoutGuard, userInitialProperties, userShouldSetUTM } from './utils' export class EventsProcessor { pluginsServer: PluginsServer @@ -326,7 +326,6 @@ export class EventsProcessor { sentAt: DateTime | null ): Promise { event = sanitizeEventName(event) - const elements: Record[] | undefined = properties['$elements'] let elementsList: Element[] = [] if (elements && elements.length) { @@ -357,7 +356,7 @@ export class EventsProcessor { await this.storeNamesAndProperties(team, event, properties) const pdiSelectResult = await this.db.postgresQuery( - 'SELECT COUNT(*) AS pdicount FROM posthog_persondistinctid WHERE team_id = $1 AND distinct_id = $2', + 'SELECT COUNT(1) AS pdicount FROM posthog_persondistinctid WHERE team_id = $1 AND distinct_id = $2', [teamId, distinctId] ) const pdiCount = parseInt(pdiSelectResult.rows[0].pdicount) @@ -365,12 +364,20 @@ export class EventsProcessor { if (!pdiCount) { // Catch race condition where in between getting and creating, another request already created this user try { - await this.db.createPerson(sentAt || DateTime.utc(), {}, teamId, null, false, personUuid.toString(), [ - distinctId, - ]) + await this.db.createPerson( + sentAt || DateTime.utc(), + userInitialProperties(properties), + teamId, + null, + false, + personUuid.toString(), + [distinctId] + ) } catch {} } + properties = userShouldSetUTM(properties) + if (properties['$set'] || properties['$set_once']) { await this.updatePersonProperties( teamId, diff --git a/src/ingestion/utils.ts b/src/ingestion/utils.ts index a8bfcb64f..35864c4e9 100644 --- a/src/ingestion/utils.ts +++ b/src/ingestion/utils.ts @@ -163,3 +163,26 @@ export function timeoutGuard(message: string, timeout = defaultConfig.TASK_TIMEO Sentry.captureMessage(message) }, timeout) } + +const campaignParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'] + +/** These params will be set on the user as $initial_{} **/ +export function userInitialProperties(properties: Record): Record { + const initialParams = ['$browser', '$browser_version', '$current_url', '$os', '$referring_domain', '$referrer'] + return Object.fromEntries( + Object.entries(properties) + .filter(([key, value]) => [...initialParams, ...campaignParams].indexOf(key) > -1) + .map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) + ) +} + +/** If we get new UTM params, make sure we set those **/ +export function userShouldSetUTM(properties: Record): Record { + const maybeSet = Object.entries(properties).filter(([key, value]) => campaignParams.indexOf(key) > -1) + const maybeSetInitial = maybeSet.map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) + if (Object.keys(maybeSet).length > 0) { + properties.$set = { ...(properties.$set || {}), ...Object.fromEntries(maybeSet) } + properties.$set_once = { ...(properties.$set_once || {}), ...Object.fromEntries(maybeSetInitial) } + } + return properties +} diff --git a/tests/shared/process-event.ts b/tests/shared/process-event.ts index 8c9763c0a..9fc81d9f7 100644 --- a/tests/shared/process-event.ts +++ b/tests/shared/process-event.ts @@ -217,6 +217,13 @@ export const createProcessEventTests = ( properties: { distinct_id: 2, token: team.api_token, + $browser: 'Chrome', + $current_url: 'https://test.com', + $os: 'Mac OS X', + $browser_version: 80, + $initial_referring_domain: 'https://google.com', + $initial_referrer_url: 'https://google.com/?q=posthog', + utm_medium: 'twitter', $elements: [ { tag_name: 'a', nth_child: 1, nth_of_type: 2, attr__class: 'btn btn-sm' }, { tag_name: 'div', nth_child: 1, nth_of_type: 2, $el_text: '💻' }, @@ -232,10 +239,21 @@ export const createProcessEventTests = ( if (database === 'clickhouse') { expect(queryCounter).toBe(9) } else if (database === 'postgresql') { - expect(queryCounter).toBe(12) + expect(queryCounter).toBe(14) } + let persons = await server.db.fetchPersons() + expect(persons[0].properties).toEqual({ + $initial_browser: 'Chrome', + $initial_browser_version: 80, + $initial_utm_medium: 'twitter', + $initial_current_url: 'https://test.com', + $initial_os: 'Mac OS X', + utm_medium: 'twitter', + }) + // capture a second time to verify e.g. event_names is not ['$autocapture', '$autocapture'] + // Also pass new utm params in to override await processEvent( '2', '127.0.0.1', @@ -245,6 +263,7 @@ export const createProcessEventTests = ( properties: { distinct_id: 2, token: team.api_token, + utm_medium: 'instagram', $elements: [ { tag_name: 'a', nth_child: 1, nth_of_type: 2, attr__class: 'btn btn-sm' }, { tag_name: 'div', nth_child: 1, nth_of_type: 2, $el_text: '💻' }, @@ -258,9 +277,17 @@ export const createProcessEventTests = ( ) const events = await server.db.fetchEvents() - const persons = await server.db.fetchPersons() + persons = await server.db.fetchPersons() expect(events.length).toEqual(2) expect(persons.length).toEqual(1) + expect(persons[0].properties).toEqual({ + $initial_browser: 'Chrome', + $initial_browser_version: 80, + $initial_utm_medium: 'twitter', + $initial_current_url: 'https://test.com', + $initial_os: 'Mac OS X', + utm_medium: 'instagram', + }) const [person] = persons const distinctIds = await server.db.fetchDistinctIdValues(person) @@ -285,10 +312,52 @@ export const createProcessEventTests = ( team = await getFirstTeam(server) expect(team.event_names).toEqual(['$autocapture']) expect(team.event_names_with_usage).toEqual([{ event: '$autocapture', volume: null, usage_count: null }]) - expect(team.event_properties).toEqual(['distinct_id', 'token', '$ip']) + expect(team.event_properties).toEqual([ + 'distinct_id', + 'token', + '$browser', + '$current_url', + '$os', + '$browser_version', + '$initial_referring_domain', + '$initial_referrer_url', + 'utm_medium', + '$ip', + ]) expect(team.event_properties_with_usage).toEqual([ { key: 'distinct_id', usage_count: null, volume: null }, { key: 'token', usage_count: null, volume: null }, + { key: '$browser', usage_count: null, volume: null }, + { + key: '$current_url', + usage_count: null, + volume: null, + }, + { + key: '$os', + usage_count: null, + volume: null, + }, + { + key: '$browser_version', + usage_count: null, + volume: null, + }, + { + key: '$initial_referring_domain', + usage_count: null, + volume: null, + }, + { + key: '$initial_referrer_url', + usage_count: null, + volume: null, + }, + { + key: 'utm_medium', + usage_count: null, + volume: null, + }, { key: '$ip', usage_count: null, volume: null }, ]) }) From 74a7859fc0addafa5ed241cfc4e09e8a5416aafd Mon Sep 17 00:00:00 2001 From: Tim Glaser Date: Thu, 25 Feb 2021 15:11:17 +0100 Subject: [PATCH 02/12] fix test --- tests/shared/process-event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/shared/process-event.ts b/tests/shared/process-event.ts index 9fc81d9f7..02a74f589 100644 --- a/tests/shared/process-event.ts +++ b/tests/shared/process-event.ts @@ -237,7 +237,7 @@ export const createProcessEventTests = ( ) if (database === 'clickhouse') { - expect(queryCounter).toBe(9) + expect(queryCounter).toBe(11) } else if (database === 'postgresql') { expect(queryCounter).toBe(14) } From b293afa59b41e886da168fe0aee6be2b795b2647 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 25 Feb 2021 15:30:29 +0100 Subject: [PATCH 03/12] Use slightly faster COUNT(*) --- src/ingestion/process-event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ingestion/process-event.ts b/src/ingestion/process-event.ts index 30e8de96a..952d150d3 100644 --- a/src/ingestion/process-event.ts +++ b/src/ingestion/process-event.ts @@ -356,7 +356,7 @@ export class EventsProcessor { await this.storeNamesAndProperties(team, event, properties) const pdiSelectResult = await this.db.postgresQuery( - 'SELECT COUNT(1) AS pdicount FROM posthog_persondistinctid WHERE team_id = $1 AND distinct_id = $2', + 'SELECT COUNT(*) AS pdicount FROM posthog_persondistinctid WHERE team_id = $1 AND distinct_id = $2', [teamId, distinctId] ) const pdiCount = parseInt(pdiSelectResult.rows[0].pdicount) From 1f8bea053e07a5ea405fcc7b6f4c5c7900370f5d Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 25 Feb 2021 15:43:47 +0100 Subject: [PATCH 04/12] Rename userShouldSetUTM to ensurePersonUpdateOnUtm for clarity --- src/ingestion/process-event.ts | 4 ++-- src/ingestion/utils.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ingestion/process-event.ts b/src/ingestion/process-event.ts index 952d150d3..6df600725 100644 --- a/src/ingestion/process-event.ts +++ b/src/ingestion/process-event.ts @@ -24,7 +24,7 @@ import { } from '../types' import { castTimestampOrNow, UUID, UUIDT } from '../utils' import { KAFKA_EVENTS, KAFKA_SESSION_RECORDING_EVENTS } from './topics' -import { elementsToString, sanitizeEventName, timeoutGuard, userInitialProperties, userShouldSetUTM } from './utils' +import { elementsToString, sanitizeEventName, timeoutGuard, userInitialProperties, ensurePersonUpdateOnUtm } from './utils' export class EventsProcessor { pluginsServer: PluginsServer @@ -376,7 +376,7 @@ export class EventsProcessor { } catch {} } - properties = userShouldSetUTM(properties) + properties = ensurePersonUpdateOnUtm(properties) if (properties['$set'] || properties['$set_once']) { await this.updatePersonProperties( diff --git a/src/ingestion/utils.ts b/src/ingestion/utils.ts index 35864c4e9..5c30ac2a5 100644 --- a/src/ingestion/utils.ts +++ b/src/ingestion/utils.ts @@ -1,3 +1,4 @@ +import { Properties } from '@posthog/plugin-scaffold' import * as Sentry from '@sentry/node' import crypto from 'crypto' @@ -176,13 +177,13 @@ export function userInitialProperties(properties: Record): Record): Record { const maybeSet = Object.entries(properties).filter(([key, value]) => campaignParams.indexOf(key) > -1) const maybeSetInitial = maybeSet.map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) if (Object.keys(maybeSet).length > 0) { properties.$set = { ...(properties.$set || {}), ...Object.fromEntries(maybeSet) } properties.$set_once = { ...(properties.$set_once || {}), ...Object.fromEntries(maybeSetInitial) } +/** Return new properties object with $set and $set_once including UTM tags - if UTM tags present in properties. */ +export function ensurePersonUpdateOnUtm(properties: Properties): Properties { } return properties } From 8e3c531af320335e77ba57c90f681a41b78970e4 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 25 Feb 2021 15:51:19 +0100 Subject: [PATCH 05/12] Refactor ensurePersonUpdateOnUtm a bit --- src/ingestion/utils.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ingestion/utils.ts b/src/ingestion/utils.ts index 5c30ac2a5..67255a828 100644 --- a/src/ingestion/utils.ts +++ b/src/ingestion/utils.ts @@ -177,13 +177,14 @@ export function userInitialProperties(properties: Record): Record campaignParams.indexOf(key) > -1) - const maybeSetInitial = maybeSet.map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) - if (Object.keys(maybeSet).length > 0) { - properties.$set = { ...(properties.$set || {}), ...Object.fromEntries(maybeSet) } - properties.$set_once = { ...(properties.$set_once || {}), ...Object.fromEntries(maybeSetInitial) } /** Return new properties object with $set and $set_once including UTM tags - if UTM tags present in properties. */ export function ensurePersonUpdateOnUtm(properties: Properties): Properties { + const propertiesCopy = { ...properties } + const setEntries = Object.entries(propertiesCopy).filter(([key]) => campaignParams.includes(key)) + const setInitialEntries = setEntries.map(([key, value]) => [`$initial_${key.replace(/^\$/, '')}`, value]) + if (Object.keys(setEntries).length > 0) { + propertiesCopy.$set = { ...propertiesCopy.$set, ...Object.fromEntries(setEntries) } + propertiesCopy.$set_once = { ...propertiesCopy.$set_once, ...Object.fromEntries(setInitialEntries) } } - return properties + return propertiesCopy } From 782918d022a3fba73a641a9e1a9d28343a610db2 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 25 Feb 2021 16:28:12 +0100 Subject: [PATCH 06/12] Prettier --- src/ingestion/process-event.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ingestion/process-event.ts b/src/ingestion/process-event.ts index 6df600725..ab748c78d 100644 --- a/src/ingestion/process-event.ts +++ b/src/ingestion/process-event.ts @@ -24,7 +24,13 @@ import { } from '../types' import { castTimestampOrNow, UUID, UUIDT } from '../utils' import { KAFKA_EVENTS, KAFKA_SESSION_RECORDING_EVENTS } from './topics' -import { elementsToString, sanitizeEventName, timeoutGuard, userInitialProperties, ensurePersonUpdateOnUtm } from './utils' +import { + elementsToString, + ensurePersonUpdateOnUtm, + sanitizeEventName, + timeoutGuard, + userInitialProperties, +} from './utils' export class EventsProcessor { pluginsServer: PluginsServer From 999d121c7f19552dfbcd53f3f7b00e127f5b07df Mon Sep 17 00:00:00 2001 From: Tim Glaser Date: Thu, 25 Feb 2021 22:48:37 +0100 Subject: [PATCH 07/12] only use $set and $set_once --- src/ingestion/process-event.ts | 16 +++++----------- src/ingestion/utils.ts | 17 +++++------------ 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/ingestion/process-event.ts b/src/ingestion/process-event.ts index 30e8de96a..10aec1b2b 100644 --- a/src/ingestion/process-event.ts +++ b/src/ingestion/process-event.ts @@ -24,7 +24,7 @@ import { } from '../types' import { castTimestampOrNow, UUID, UUIDT } from '../utils' import { KAFKA_EVENTS, KAFKA_SESSION_RECORDING_EVENTS } from './topics' -import { elementsToString, sanitizeEventName, timeoutGuard, userInitialProperties, userShouldSetUTM } from './utils' +import { elementsToString, sanitizeEventName, timeoutGuard, userInitialAndUTMProperties } from './utils' export class EventsProcessor { pluginsServer: PluginsServer @@ -364,19 +364,13 @@ export class EventsProcessor { if (!pdiCount) { // Catch race condition where in between getting and creating, another request already created this user try { - await this.db.createPerson( - sentAt || DateTime.utc(), - userInitialProperties(properties), - teamId, - null, - false, - personUuid.toString(), - [distinctId] - ) + await this.db.createPerson(sentAt || DateTime.utc(), {}, teamId, null, false, personUuid.toString(), [ + distinctId, + ]) } catch {} } - properties = userShouldSetUTM(properties) + properties = userInitialAndUTMProps(properties) if (properties['$set'] || properties['$set_once']) { await this.updatePersonProperties( diff --git a/src/ingestion/utils.ts b/src/ingestion/utils.ts index 35864c4e9..0dbf86680 100644 --- a/src/ingestion/utils.ts +++ b/src/ingestion/utils.ts @@ -165,21 +165,14 @@ export function timeoutGuard(message: string, timeout = defaultConfig.TASK_TIMEO } const campaignParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'] - -/** These params will be set on the user as $initial_{} **/ -export function userInitialProperties(properties: Record): Record { - const initialParams = ['$browser', '$browser_version', '$current_url', '$os', '$referring_domain', '$referrer'] - return Object.fromEntries( - Object.entries(properties) - .filter(([key, value]) => [...initialParams, ...campaignParams].indexOf(key) > -1) - .map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) - ) -} +const initialParams = ['$browser', '$browser_version', '$current_url', '$os', '$referring_domain', '$referrer'] /** If we get new UTM params, make sure we set those **/ -export function userShouldSetUTM(properties: Record): Record { +export function userInitialAndUTMProperties(properties: Record): Record { const maybeSet = Object.entries(properties).filter(([key, value]) => campaignParams.indexOf(key) > -1) - const maybeSetInitial = maybeSet.map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) + const maybeSetInitial = Object.entries(properties) + .filter(([key, value]) => [...campaignParams, ...initialParams].indexOf(key) > -1) + .map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) if (Object.keys(maybeSet).length > 0) { properties.$set = { ...(properties.$set || {}), ...Object.fromEntries(maybeSet) } properties.$set_once = { ...(properties.$set_once || {}), ...Object.fromEntries(maybeSetInitial) } From 1ba5b324f748ec543ee4e8350c5edd34803be775 Mon Sep 17 00:00:00 2001 From: Tim Glaser Date: Fri, 26 Feb 2021 10:02:34 +0100 Subject: [PATCH 08/12] fix --- src/ingestion/process-event.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ingestion/process-event.ts b/src/ingestion/process-event.ts index 3f2a8357d..32db65af5 100644 --- a/src/ingestion/process-event.ts +++ b/src/ingestion/process-event.ts @@ -24,7 +24,7 @@ import { } from '../types' import { castTimestampOrNow, UUID, UUIDT } from '../utils' import { KAFKA_EVENTS, KAFKA_SESSION_RECORDING_EVENTS } from './topics' -import { elementsToString, personInitialAndUTMProperties,sanitizeEventName, timeoutGuard } from './utils' +import { elementsToString, personInitialAndUTMProperties, sanitizeEventName, timeoutGuard } from './utils' export class EventsProcessor { pluginsServer: PluginsServer @@ -370,7 +370,7 @@ export class EventsProcessor { } catch {} } - properties = personInitialAndUTMProps(properties) + properties = personInitialAndUTMProperties(properties) if (properties['$set'] || properties['$set_once']) { await this.updatePersonProperties( From 3fcbc4977adf26eb469389bf296610dfce504849 Mon Sep 17 00:00:00 2001 From: Tim Glaser Date: Fri, 26 Feb 2021 10:06:54 +0100 Subject: [PATCH 09/12] Add new prop --- tests/helpers/sql.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/helpers/sql.ts b/tests/helpers/sql.ts index c36fbb968..81f922ab8 100644 --- a/tests/helpers/sql.ts +++ b/tests/helpers/sql.ts @@ -113,6 +113,7 @@ export async function createUserTeamAndOrganization( opt_out_capture: false, is_demo: false, api_token: `THIS IS NOT A TOKEN FOR TEAM ${teamId}`, + test_account_filters: JSON.stringify([]), }) } From 5331ce2811d1c54a74f02b96cd774bd666e0565a Mon Sep 17 00:00:00 2001 From: Tim Glaser Date: Fri, 26 Feb 2021 10:12:23 +0100 Subject: [PATCH 10/12] remove new team prop --- tests/helpers/sql.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/helpers/sql.ts b/tests/helpers/sql.ts index 81f922ab8..c36fbb968 100644 --- a/tests/helpers/sql.ts +++ b/tests/helpers/sql.ts @@ -113,7 +113,6 @@ export async function createUserTeamAndOrganization( opt_out_capture: false, is_demo: false, api_token: `THIS IS NOT A TOKEN FOR TEAM ${teamId}`, - test_account_filters: JSON.stringify([]), }) } From 5773a68b56f3de0dbf7e189af5c8045e33f9d4ee Mon Sep 17 00:00:00 2001 From: Tim Glaser Date: Fri, 26 Feb 2021 10:53:04 +0100 Subject: [PATCH 11/12] use sets --- src/ingestion/utils.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ingestion/utils.ts b/src/ingestion/utils.ts index d20daf375..b09fed84b 100644 --- a/src/ingestion/utils.ts +++ b/src/ingestion/utils.ts @@ -165,15 +165,16 @@ export function timeoutGuard(message: string, timeout = defaultConfig.TASK_TIMEO }, timeout) } -const campaignParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'] -const initialParams = ['$browser', '$browser_version', '$current_url', '$os', '$referring_domain', '$referrer'] +const campaignParams = new Set(['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']) +const initialParams = new Set(['$browser', '$browser_version', '$current_url', '$os', '$referring_domain', '$referrer']) +const combinedParams = new Set([...campaignParams, ...initialParams]) /** If we get new UTM params, make sure we set those **/ export function personInitialAndUTMProperties(properties: Record): Record { const propertiesCopy = { ...properties } - const maybeSet = Object.entries(properties).filter(([key, value]) => campaignParams.indexOf(key) > -1) + const maybeSet = Object.entries(properties).filter(([key, value]) => campaignParams.has(key)) const maybeSetInitial = Object.entries(properties) - .filter(([key, value]) => [...campaignParams, ...initialParams].indexOf(key) > -1) + .filter(([key, value]) => combinedParams.has(key)) .map(([key, value]) => [`$initial_${key.replace('$', '')}`, value]) if (Object.keys(maybeSet).length > 0) { propertiesCopy.$set = { ...(properties.$set || {}), ...Object.fromEntries(maybeSet) } From 8b15362be7b02c8c2f40dff15e9aa8b3746331b2 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 26 Feb 2021 11:27:52 +0100 Subject: [PATCH 12/12] fix type --- src/ingestion/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ingestion/utils.ts b/src/ingestion/utils.ts index b09fed84b..ec184d188 100644 --- a/src/ingestion/utils.ts +++ b/src/ingestion/utils.ts @@ -170,7 +170,7 @@ const initialParams = new Set(['$browser', '$browser_version', '$current_url', ' const combinedParams = new Set([...campaignParams, ...initialParams]) /** If we get new UTM params, make sure we set those **/ -export function personInitialAndUTMProperties(properties: Record): Record { +export function personInitialAndUTMProperties(properties: Properties): Properties { const propertiesCopy = { ...properties } const maybeSet = Object.entries(properties).filter(([key, value]) => campaignParams.has(key)) const maybeSetInitial = Object.entries(properties)