Skip to content

Commit 16aabca

Browse files
committed
Squashed commit of the following:
commit 349c8cb Author: Nick Aguilar <[email protected]> Date: Thu Jul 3 09:26:43 2025 -0700 Only append tags when isLiquidDirective is true commit b29a712 Author: Nick Aguilar <[email protected]> Date: Thu Jul 3 09:14:02 2025 -0700 Dry suggestion: uses finally block to call histogram commit c2d8704 Author: Nick Aguilar <[email protected]> Date: Mon Jun 30 16:55:09 2025 -0700 Reverts packages/browser-destinations/webpack.config.js commit 1964c15 Author: Nick Aguilar <[email protected]> Date: Mon Jun 30 16:45:22 2025 -0700 Removes perf_hooks usage since it proved to be a never-ending rabbithole of import errors commit 8850513 Author: Nick Aguilar <[email protected]> Date: Mon Jun 30 16:32:42 2025 -0700 Does not attempt to resolve the perf_hooks package as it's not browser supported commit 084b860 Author: Nick Aguilar <[email protected]> Date: Thu Jun 26 16:44:14 2025 -0700 Throws error only after logging out metrics commit 28b117c Author: Nick Aguilar <[email protected]> Date: Thu Jun 26 16:32:56 2025 -0700 Imports performance commit 5508da6 Author: Nick Aguilar <[email protected]> Date: Thu Jun 26 16:10:24 2025 -0700 Removes Subscription values which are undefined commit 856634a Author: Nick Aguilar <[email protected]> Date: Thu Jun 26 09:46:21 2025 -0700 Adds action and destination metadata values to tags commit 9ce22c5 Author: Nick Aguilar <[email protected]> Date: Thu Jun 26 11:14:39 2025 -0700 WIP - pipes through statsContext to evaluateLiquid directive function Tracks liquid metrics when batching as well WIP passes tags hopefully commit 6e6149c Author: Nick Aguilar <[email protected]> Date: Tue Jun 24 17:21:26 2025 -0700 WIP - pipes through statsContext to evaluateLiquid directive function commit b367ebd Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue Aug 12 17:13:24 2025 +0100 Publish (#3172) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> commit db16abc Author: Joe Ayoub <[email protected]> Date: Tue Aug 12 17:07:03 2025 +0100 register yonoma (#3170) commit 8a11fb1 Author: Vanessa Sun <[email protected]> Date: Tue Aug 12 23:54:11 2025 +0800 Stackadapt-Audience: add isPii field to upsertExternalAudienceMapping query (#3095) * add isPii field to upsertExternalAudienceMapping query * update snapshot for stackadapt-audiences * fix query input string format commit 7a17d8e Author: Joe Ayoub <[email protected]> Date: Tue Aug 12 16:53:39 2025 +0100 STRATCONN-6040 - [Yonomo] - New Destination (#3140) * First Action * Actions completed * testAuthentication * starting on tests * saving progress * Updated tests * all tests done * removing unnecesary tests * fixing timestamp in snapshots commit 0e0a155 Author: nk1107 <[email protected]> Date: Tue Aug 12 21:07:20 2025 +0530 Stratconn 6058/batch size (#3106) * add enable-batch and batch-size * change description * update test files * Revert "update test files" This reverts commit b4a2c43. * extract batch properties finally * update max limit * change label * Remove batching configuration fields from payload before sending to Customer.io * Remove batching configuration fields from BasePayload type commit b8d7e8c Author: Arijit Ray <[email protected]> Date: Tue Aug 12 21:06:19 2025 +0530 Dont throw 404 if profile is not found in intercom Ondelete (#3144) * Dont throw 404 if profile is not found in intercom Ondelete * fix tests commit 1924208 Author: Joe Ayoub <[email protected]> Date: Tue Aug 12 16:36:06 2025 +0100 [Attentive] - bugfix (#3151) * minor bugfix * fixing tests commit c6ed179 Author: Varadarajan V <[email protected]> Date: Tue Aug 12 21:05:48 2025 +0530 [STRATCONN-6023] - Add feature flag to facilitate GZIP Compression Rollout (#3152) * [STRATCONN-6023]- Add feature flag for rolling out compression * Revert batch size * add gzipcompress flag * Add tests
1 parent f6388f8 commit 16aabca

File tree

49 files changed

+2018
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2018
-136
lines changed

packages/core/src/destination-kit/action.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ export class Action<Settings, Payload extends JSONLikeObject, AudienceSettings =
318318
const results: Result[] = []
319319

320320
// Resolve/transform the mapping with the input data
321-
let payload = transform(bundle.mapping, bundle.data) as Payload
321+
let payload = transform(bundle.mapping, bundle.data, bundle.statsContext) as Payload
322322
results.push({ output: 'Mappings resolved' })
323323

324324
// Remove empty values (`null`, `undefined`, `''`) when not explicitly accepted
@@ -387,7 +387,7 @@ export class Action<Settings, Payload extends JSONLikeObject, AudienceSettings =
387387

388388
const mapping: JSONObject = bundle.mapping
389389

390-
let payloads = transformBatch(mapping, bundle.data) as Payload[]
390+
let payloads = transformBatch(mapping, bundle.data, bundle.statsContext) as Payload[]
391391
const batchPayloadLength = payloads.length
392392

393393
const multiStatusResponse: ResultMultiStatusNode[] = []

packages/core/src/destination-kit/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,10 @@ export class Destination<Settings = JSONObject, AudienceSettings = JSONObject> {
726726
): Promise<Result[]> {
727727
const isBatch = Array.isArray(events)
728728

729+
if (options?.statsContext?.tags !== undefined) {
730+
options.statsContext.tags = [...options.statsContext.tags, `partnerAction:${subscription.partnerAction}`]
731+
}
732+
729733
const subscriptionStartedAt = time()
730734
const actionSlug = subscription.partnerAction
731735
const input = {

packages/core/src/mapping-kit/index.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import validate from './validate'
88
import { arrify } from '../arrify'
99
import { flattenObject } from './flatten'
1010
import { evaluateLiquid } from './liquid-directive'
11+
import { StatsContext } from '../destination-kit'
12+
import { isLiquidDirective } from './value-keys'
1113

1214
export type InputData = { [key: string]: unknown }
1315
export type Features = { [key: string]: boolean }
14-
type Directive = (options: JSONValue, payload: JSONObject) => JSONLike
16+
type Directive = (options: JSONValue, payload: JSONObject, statsContext?: StatsContext | undefined) => JSONLike
1517
type StringDirective = (value: string, payload: JSONObject) => JSONLike
1618

1719
interface Directives {
@@ -42,7 +44,7 @@ function registerStringDirective(name: string, fn: StringDirective): void {
4244
})
4345
}
4446

45-
function runDirective(obj: JSONObject, payload: JSONObject): JSONLike {
47+
function runDirective(obj: JSONObject, payload: JSONObject, statsContext?: StatsContext | undefined): JSONLike {
4648
const name = Object.keys(obj).find((key) => key.startsWith('@')) as string
4749
const directiveFn = directives[name]
4850
const value = obj[name]
@@ -51,6 +53,10 @@ function runDirective(obj: JSONObject, payload: JSONObject): JSONLike {
5153
throw new Error(`${name} is not a valid directive, got ${realTypeOf(directiveFn)}`)
5254
}
5355

56+
if (name === '@liquid') {
57+
return directiveFn(value, payload, statsContext)
58+
}
59+
5460
return directiveFn(value, payload)
5561
}
5662

@@ -326,8 +332,8 @@ registerDirective('@excludeWhenNull', (value, payload) => {
326332
return cleanNulls(resolved)
327333
})
328334

329-
registerDirective('@liquid', (opts, payload) => {
330-
return evaluateLiquid(opts, payload)
335+
registerDirective('@liquid', (opts, payload, statsContext) => {
336+
return evaluateLiquid(opts, payload, statsContext)
331337
})
332338

333339
// Recursively remove all null values from an object
@@ -381,23 +387,40 @@ function getMappingToProcess(mapping: JSONLikeObject): JSONLikeObject {
381387
* @param payload - the input data to apply to the mapping directives
382388
* @todo support arrays or array directives?
383389
*/
384-
function resolve(mapping: JSONLike, payload: JSONObject): JSONLike {
390+
function resolve(mapping: JSONLike, payload: JSONObject, statsContext?: StatsContext | undefined): JSONLike {
385391
if (!isObject(mapping) && !isArray(mapping)) {
386392
return mapping
387393
}
388394

389395
if (isDirective(mapping)) {
396+
if (isLiquidDirective(mapping)) {
397+
// Only include stats, and therefore extra fieldKey tags, if the mapping is a liquid directive to save on costs
398+
return runDirective(mapping, payload, statsContext)
399+
}
400+
390401
return runDirective(mapping, payload)
391402
}
392403

393404
if (Array.isArray(mapping)) {
394-
return mapping.map((value) => resolve(value, payload))
405+
return mapping.map((value) => resolve(value, payload, statsContext))
395406
}
396407

397408
const resolved: JSONLikeObject = {}
398409

399410
for (const key of Object.keys(mapping)) {
400-
resolved[key] = resolve(mapping[key], payload)
411+
let originalTags: string[] = []
412+
const statsTagsExist = statsContext?.tags !== undefined
413+
414+
if (statsTagsExist) {
415+
originalTags = statsContext.tags
416+
statsContext.tags = [...statsContext.tags, `fieldKey:${key}`]
417+
}
418+
419+
resolved[key] = resolve(mapping[key], payload, statsContext)
420+
421+
if (statsTagsExist) {
422+
statsContext.tags = originalTags
423+
}
401424
}
402425

403426
return resolved
@@ -409,7 +432,11 @@ function resolve(mapping: JSONLike, payload: JSONObject): JSONLike {
409432
* @param mapping - the directives and raw values
410433
* @param data - the input data to apply to directives
411434
*/
412-
function transform(mapping: JSONLikeObject, data: InputData | undefined = {}): JSONObject {
435+
function transform(
436+
mapping: JSONLikeObject,
437+
data: InputData | undefined = {},
438+
statsContext?: StatsContext | undefined
439+
): JSONObject {
413440
const realType = realTypeOf(data)
414441
if (realType !== 'object') {
415442
throw new Error(`data must be an object, got ${realType}`)
@@ -420,7 +447,7 @@ function transform(mapping: JSONLikeObject, data: InputData | undefined = {}): J
420447
// throws if the mapping config is invalid
421448
validate(mappingToProcess)
422449

423-
const resolved = resolve(mappingToProcess, data as JSONObject)
450+
const resolved = resolve(mappingToProcess, data as JSONObject, statsContext)
424451
const cleaned = removeUndefined(resolved)
425452

426453
// Cast because we know there are no `undefined` values anymore
@@ -432,7 +459,11 @@ function transform(mapping: JSONLikeObject, data: InputData | undefined = {}): J
432459
* @param mapping - the directives and raw values
433460
* @param data - the array input data to apply to directives
434461
*/
435-
function transformBatch(mapping: JSONLikeObject, data: Array<InputData> | undefined = []): JSONObject[] {
462+
function transformBatch(
463+
mapping: JSONLikeObject,
464+
data: Array<InputData> | undefined = [],
465+
statsContext?: StatsContext | undefined
466+
): JSONObject[] {
436467
const realType = realTypeOf(data)
437468
if (!isArray(data)) {
438469
throw new Error(`data must be an array, got ${realType}`)
@@ -443,7 +474,7 @@ function transformBatch(mapping: JSONLikeObject, data: Array<InputData> | undefi
443474
// throws if the mapping config is invalid
444475
validate(mappingToProcess)
445476

446-
const resolved = data.map((d) => resolve(mappingToProcess, d as JSONObject))
477+
const resolved = data.map((d) => resolve(mappingToProcess, d as JSONObject, statsContext))
447478

448479
// Cast because we know there are no `undefined` values after `removeUndefined`
449480
return removeUndefined(resolved) as JSONObject[]

packages/core/src/mapping-kit/liquid-directive.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Liquid } from 'liquidjs'
2+
import { StatsContext } from '../destination-kit'
23

34
const liquidEngine = new Liquid({
45
renderLimit: 500, // 500 ms
@@ -58,7 +59,7 @@ export function getLiquidKeys(liquidValue: string): string[] {
5859
return liquidEngine.fullVariablesSync(liquidValue)
5960
}
6061

61-
export function evaluateLiquid(liquidValue: any, event: any): string {
62+
export function evaluateLiquid(liquidValue: any, event: any, statsContext?: StatsContext | undefined): string {
6263
if (typeof liquidValue !== 'string') {
6364
// type checking of @liquid directive is done in validate.ts as well
6465
throw new Error('liquid template value must be a string')
@@ -72,7 +73,22 @@ export function evaluateLiquid(liquidValue: any, event: any): string {
7273
throw new Error('liquid template values are limited to 1000 characters')
7374
}
7475

75-
const res = liquidEngine.parseAndRenderSync(liquidValue, event)
76+
let res: string
77+
const start = Date.now()
78+
let status: 'success' | 'fail' = 'success'
79+
80+
try {
81+
res = liquidEngine.parseAndRenderSync(liquidValue, event)
82+
} catch (e) {
83+
status = 'fail'
84+
throw e
85+
} finally {
86+
const duration = Date.now() - start
87+
statsContext?.statsClient?.histogram('liquid.template.evaluation_ms', duration, [
88+
...statsContext.tags,
89+
`result:${status}`
90+
])
91+
}
7692

7793
if (typeof res !== 'string') {
7894
return 'error'

packages/destination-actions/src/destinations/attentive/functions.ts

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { PayloadValidationError } from '@segment/actions-core'
2-
import { Item, User, EcommEventJSON, CustomEventJSON, UpsertUserAttributesJSON, SubscribeUserJSON, SubscriptionType } from './types'
2+
import {
3+
Item,
4+
User,
5+
EcommEventJSON,
6+
CustomEventJSON,
7+
UpsertUserAttributesJSON,
8+
SubscribeUserJSON,
9+
SubscriptionType
10+
} from './types'
311
import { Payload as CustomEvent } from './customEvents/generated-types'
412
import { Payload as EcommEvent } from './ecommEvent/generated-types'
513
import { Payload as CustomAttributesEvent } from './upsertUserAttributes/generated-types'
614
import { Payload as SubscribeUserEvent } from './subscribeUser/generated-types'
715

8-
type UserIdentifiers =
9-
| CustomEvent['userIdentifiers']
10-
| EcommEvent['userIdentifiers']
16+
type UserIdentifiers = CustomEvent['userIdentifiers'] | EcommEvent['userIdentifiers']
1117

1218
type Items = EcommEvent['items']
1319

@@ -19,10 +25,10 @@ export function validate(payload: CustomEvent | EcommEvent | CustomAttributesEve
1925
if (!email && !phone && !clientUserId && Object.keys(customIdentifiers).length === 0) {
2026
throw new PayloadValidationError('At least one user identifier is required.')
2127
}
22-
}
28+
}
2329

2430
export function validateSubscribeUser(payload: SubscribeUserEvent) {
25-
const { userIdentifiers, locale } = payload
31+
const { userIdentifiers, locale } = payload
2632
if (!userIdentifiers && !locale) {
2733
throw new PayloadValidationError('Either locale or signUpSourceId is required.')
2834
}
@@ -49,13 +55,13 @@ function formatItems(items: Items): Array<Item> {
4955
...rest,
5056
price: {
5157
value,
52-
currency,
58+
currency
5359
}
5460
}))
5561
}
5662

57-
function formatLocale(locale?: string): { language: string, country: string } {
58-
if(!locale) {
63+
function formatLocale(locale?: string): { language: string; country: string } {
64+
if (!locale) {
5965
throw new PayloadValidationError('Locale Signup Source ID is required.')
6066
}
6167
const parts = locale.split('-')
@@ -76,15 +82,9 @@ export function formatEcommEventJSON(payload: EcommEvent): EcommEventJSON {
7682
}
7783

7884
export function formatCustomEventJSON(payload: CustomEvent): CustomEventJSON {
79-
const {
80-
externalEventId,
81-
type,
82-
properties,
83-
occurredAt,
84-
userIdentifiers
85-
} = payload
86-
87-
if (Object.values(properties ?? {}).some(value => Array.isArray(value))) {
85+
const { externalEventId, type, properties, occurredAt, userIdentifiers } = payload
86+
87+
if (Object.values(properties ?? {}).some((value) => Array.isArray(value))) {
8888
throw new PayloadValidationError('Properties cannot contain arrays.')
8989
}
9090

@@ -98,12 +98,9 @@ export function formatCustomEventJSON(payload: CustomEvent): CustomEventJSON {
9898
}
9999

100100
export function formatUpsertUserAttributesJSON(payload: CustomAttributesEvent): UpsertUserAttributesJSON {
101-
const {
102-
properties,
103-
userIdentifiers
104-
} = payload
101+
const { properties, userIdentifiers } = payload
105102

106-
if (Object.values(properties ?? {}).some(value => typeof value === 'object' || Array.isArray(value))) {
103+
if (Object.values(properties ?? {}).some((value) => typeof value === 'object' || Array.isArray(value))) {
107104
throw new PayloadValidationError('Properties cannot contain objects or arrays.')
108105
}
109106

@@ -114,14 +111,15 @@ export function formatUpsertUserAttributesJSON(payload: CustomAttributesEvent):
114111
}
115112

116113
export function formatSubscribeUserJSON(payload: SubscribeUserEvent): SubscribeUserJSON {
117-
const { externalEventId, occurredAt, userIdentifiers, subscriptionType, signUpSourceId, singleOptIn } = payload
114+
const { externalEventId, occurredAt, userIdentifiers, subscriptionType, signUpSourceId, singleOptIn, locale } =
115+
payload
118116
return {
119117
externalEventId,
120118
occurredAt,
121119
subscriptionType: subscriptionType as SubscriptionType,
122-
locale: formatLocale(payload?.locale),
123-
signUpSourceId,
120+
...(locale ? { locale: formatLocale(locale) } : {}),
121+
...(signUpSourceId ? { signUpSourceId } : {}),
124122
singleOptIn,
125123
user: formatUser(userIdentifiers)
126124
}
127-
}
125+
}

packages/destination-actions/src/destinations/attentive/subscribeUser/__tests__/__snapshots__/snapshot.test.ts.snap

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@
33
exports[`Testing snapshot for Attentive's subscribeUser destination action: all fields 1`] = `
44
Object {
55
"externalEventId": "*Cc5M#wXxFe",
6-
"locale": Object {
7-
"country": "US",
8-
"language": "en",
9-
},
106
"occurredAt": "*Cc5M#wXxFe",
11-
"signUpSourceId": "*Cc5M#wXxFe",
7+
"signUpSourceId": "test-signup-source-id",
128
"singleOptIn": true,
139
"subscriptionType": "MARKETING",
1410
"user": Object {

0 commit comments

Comments
 (0)