Skip to content

Commit

Permalink
Merge branch 'main' into soap-api-support
Browse files Browse the repository at this point in the history
  • Loading branch information
nick-Ag committed Nov 26, 2024
2 parents 57ceb10 + bb253f2 commit 2b03777
Show file tree
Hide file tree
Showing 46 changed files with 1,516 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -806,4 +806,36 @@ describe('Set Configuration Fields action', () => {
send_page_view: false
})
})

it('should convert consent values to lower case', async () => {
defaultSettings.enableConsentMode = true

const [setConfigurationEventPlugin] = await googleAnalytics4Web({
...defaultSettings,
subscriptions
})
setConfigurationEvent = setConfigurationEventPlugin
await setConfigurationEventPlugin.load(Context.system(), {} as Analytics)

const context = new Context({
event: 'setConfigurationFields',
type: 'page',
properties: {
ads_storage_consent_state: 'GRANTED',
analytics_storage_consent_state: 'Granted'
}
})

setConfigurationEvent.page?.(context)

expect(mockGtag).toHaveBeenCalledWith('consent', 'update', {
ad_storage: 'granted',
analytics_storage: 'granted'
})
expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', {
allow_ad_personalization_signals: false,
allow_google_signals: false,
send_page_view: true
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,23 @@ const action: BrowserActionDefinition<Settings, Function, Payload> = {
description:
'Consent state indicated by the user for ad cookies. Value must be “granted” or “denied.” This is only used if the Enable Consent Mode setting is on.',
label: 'Ads Storage Consent State',
type: 'string'
type: 'string',
choices: [
{ label: 'Granted', value: 'granted' },
{ label: 'Denied', value: 'denied' }
],
default: undefined
},
analytics_storage_consent_state: {
description:
'Consent state indicated by the user for ad cookies. Value must be “granted” or “denied.” This is only used if the Enable Consent Mode setting is on.',
label: 'Analytics Storage Consent State',
type: 'string'
type: 'string',
choices: [
{ label: 'Granted', value: 'granted' },
{ label: 'Denied', value: 'denied' }
],
default: undefined
},
ad_user_data_consent_state: {
description:
Expand Down Expand Up @@ -140,10 +150,10 @@ const action: BrowserActionDefinition<Settings, Function, Payload> = {
ad_personalization?: ConsentParamsArg
} = {}
if (payload.ads_storage_consent_state) {
consentParams.ad_storage = payload.ads_storage_consent_state as ConsentParamsArg
consentParams.ad_storage = payload.ads_storage_consent_state.toLowerCase() as ConsentParamsArg
}
if (payload.analytics_storage_consent_state) {
consentParams.analytics_storage = payload.analytics_storage_consent_state as ConsentParamsArg
consentParams.analytics_storage = payload.analytics_storage_consent_state.toLowerCase() as ConsentParamsArg
}
if (payload.ad_user_data_consent_state) {
consentParams.ad_user_data = payload.ad_user_data_consent_state as ConsentParamsArg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,52 @@ describe('Hubspot.upsertContact', () => {
}
])
})

test('trims string traits', async () => {
const context = new Context({
type: 'identify',
userId: 'mike',
traits: {
friendly: false,
email: '[email protected]',
address: {
street: '6th St',
city: ' San Francisco ', // to be trimmed
state: 'CA',
postalCode: '94103',
country: 'USA'
},
equipment: {
type: '🚘',
color: ' red ', // to be trimmed
make: {
make: 'Tesla',
model: 'Model S',
year: 2019
}
}
}
})

await upsertContactEvent.identify?.(context)
expect(mockHubspot.push).toHaveBeenCalledTimes(1)
expect(mockHubspot.push).toHaveBeenCalledWith([
'identify',
{
email: '[email protected]',
id: 'mike',
friendly: false,
address: '6th St',
country: 'USA',
state: 'CA',
city: 'San Francisco',
zip: '94103',
equipment_type: '🚘',
equipment_color: 'red',
equipment_make_make: 'Tesla',
equipment_make_model: 'Model S',
equipment_make_year: 2019
}
])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const action: BrowserActionDefinition<Settings, Hubspot, Payload> = {
}
},
custom_properties: {
description: 'A list of key-value pairs that describe the contact. Please see [HubSpot`s documentation](https://knowledge.hubspot.com/account/prevent-contact-properties-update-through-tracking-code-api) for limitations in updating contact properties.',
description:
'A list of key-value pairs that describe the contact. Please see [HubSpot`s documentation](https://knowledge.hubspot.com/account/prevent-contact-properties-update-through-tracking-code-api) for limitations in updating contact properties.',
label: 'Custom Properties',
type: 'object',
required: false,
Expand Down Expand Up @@ -103,6 +104,15 @@ const action: BrowserActionDefinition<Settings, Hubspot, Payload> = {
return
}

payload.email = payload.email?.trim()
payload.id = payload.id?.trim()
payload.company = payload.company?.trim()
payload.country = payload.country?.trim()
payload.state = payload.state?.trim()
payload.city = payload.city?.trim()
payload.address = payload.address?.trim()
payload.zip = payload.zip?.trim()

// custom properties should be key-value pairs of strings, therefore, filtering out any non-primitive
const { custom_properties, ...rest } = payload
let flattenProperties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export function flatten(
const flattened = flatten(data[key] as Properties, `${prefix}_${key}`, skipList, keyTransformation)
result = { ...result, ...flattened }
} else {
result[keyTransformation(`${prefix}_${key}`.replace(/^_/, ''))] = data[key] as JSONPrimitive
result[keyTransformation(`${prefix}_${key}`.replace(/^_/, ''))] = (
typeof data[key] === 'string' ? (data[key] as string).trim() : data[key]
) as JSONPrimitive
}
}
return result
Expand Down
53 changes: 51 additions & 2 deletions packages/core/src/__tests__/destination-kit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,19 @@ const multiStatusCompatibleDestination: DestinationDefinition<JSONObject> = {
label: 'Email',
description: 'The user email',
type: 'string'
},
phone: {
label: 'Phone',
description: 'The user phone number',
type: 'string'
}
},
perform: (_request, { payload }) => {
// Emulate an API error
if (payload.phone) {
throw new IntegrationError('Phone number validation failed', 'Invalid Phone Number', 400)
}

if (payload.email) {
throw new IntegrationError('Email is required', 'Missing required fields', 400)
}
Expand All @@ -367,6 +377,21 @@ const multiStatusCompatibleDestination: DestinationDefinition<JSONObject> = {
performBatch: (_request, { payload }) => {
const response = new MultiStatusResponse()
payload.forEach((event) => {
// Emulate an API error
if (event?.phone) {
response.pushErrorResponse({
status: 400,
errortype: ErrorCodes.BAD_REQUEST,
errormessage: 'Phone number validation failed',
sent: event,
body: {
events_processed: 0,
message: 'Phone number validation failed'
}
})
return
}

if (event?.email) {
response.pushSuccessResponse({
body: {},
Expand Down Expand Up @@ -1803,6 +1828,15 @@ describe('destination kit', () => {
email: '[email protected]'
},
receivedAt
},
{
// Valid Event with emulated rejection
event: 'Add to Cart',
type: 'track',
properties: {
phone: '1234567890'
},
receivedAt
}
]

Expand All @@ -1813,7 +1847,8 @@ describe('destination kit', () => {
partnerAction: 'trackEvent',
mapping: {
name: { '@path': '$.event' },
email: { '@path': '$.properties.email' }
email: { '@path': '$.properties.email' },
phone: { '@path': '$.properties.phone' }
}
}
}
Expand Down Expand Up @@ -1856,7 +1891,7 @@ describe('destination kit', () => {
},
Object {
"errormessage": "Email is required",
"errorreporter": "DESTINATION",
"errorreporter": "INTEGRATIONS",
"errortype": "PAYLOAD_VALIDATION_FAILED",
"status": 400,
},
Expand All @@ -1865,6 +1900,20 @@ describe('destination kit', () => {
"sent": Object {},
"status": 200,
},
Object {
"body": Object {
"events_processed": 0,
"message": "Phone number validation failed",
},
"errormessage": "Phone number validation failed",
"errorreporter": "DESTINATION",
"errortype": "BAD_REQUEST",
"sent": Object {
"name": "Add to Cart",
"phone": "1234567890",
},
"status": 400,
},
],
},
]
Expand Down
11 changes: 9 additions & 2 deletions packages/core/src/destination-kit/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,16 @@ export class Action<Settings, Payload extends JSONLikeObject, AudienceSettings =

// Check if response is a failed response
if (response instanceof ActionDestinationErrorResponse) {
const responseValue = response.value()

// Check if the error has a 'sent' or 'body' field set, we assume it to be an error from the API Call
// Else we assume it to be an error from the Integration validations
multiStatusResponse[i] = {
...response.value(),
errorreporter: MultiStatusErrorReporter.DESTINATION
...responseValue,
errorreporter:
responseValue.sent || responseValue.body
? MultiStatusErrorReporter.DESTINATION
: MultiStatusErrorReporter.INTEGRATIONS
}

// Add datadog stats for events that are discarded by Destination
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/destination-kit/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ export type Deletion<Settings, Return = any> = (
) => MaybePromise<Return>

/** The supported sync mode values */
export const syncModeTypes = ['add', 'update', 'upsert', 'delete'] as const
export const syncModeTypes = ['add', 'update', 'upsert', 'delete', 'mirror'] as const
export type SyncMode = typeof syncModeTypes[number]

export interface SyncModeOption {
Expand Down
2 changes: 1 addition & 1 deletion packages/destination-actions/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@segment/action-destinations",
"description": "Destination Actions engine and definitions.",
"version": "3.345.0",
"version": "3.345.1",
"repository": {
"type": "git",
"url": "https://github.com/segmentio/action-destinations",
Expand Down
Loading

0 comments on commit 2b03777

Please sign in to comment.