Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { AMPLITUDE_API_VERSION } from './versioning-info'

export const endpoints = {
batch: {
north_america: 'https://api2.amplitude.com/batch',
europe: 'https://api.eu.amplitude.com/batch'
},
deletions: {
north_america: 'https://amplitude.com/api/2/deletions/users',
europe: 'https://analytics.eu.amplitude.com/api/2/deletions/users'
north_america: `https://amplitude.com/api/${AMPLITUDE_API_VERSION}/deletions/users`,
europe: `https://analytics.eu.amplitude.com/api/${AMPLITUDE_API_VERSION}/deletions/users`
},
httpapi: {
north_america: 'https://api2.amplitude.com/2/httpapi',
europe: 'https://api.eu.amplitude.com/2/httpapi'
north_america: `https://api2.amplitude.com/${AMPLITUDE_API_VERSION}/httpapi`,
europe: `https://api.eu.amplitude.com/${AMPLITUDE_API_VERSION}/httpapi`
},
identify: {
north_america: 'https://api2.amplitude.com/identify',
Expand All @@ -24,8 +26,8 @@ export const endpoints = {
europe: 'https://api.eu.amplitude.com/usermap'
},
usersearch: {
north_america: 'https://amplitude.com/api/2/usersearch',
europe: 'https://analytics.eu.amplitude.com/api/2/usersearch'
north_america: `https://amplitude.com/api/${AMPLITUDE_API_VERSION}/usersearch`,
europe: `https://analytics.eu.amplitude.com/api/${AMPLITUDE_API_VERSION}/usersearch`
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** AMPLITUDE_API_VERSION
* Used for deletions, httpapi, usersearch endpoints.
* API reference: https://developers.amplitude.com/docs/http-api-v2
*/
export const AMPLITUDE_API_VERSION = '2'
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const API_VERSION = '21.0'
export const CANARY_API_VERSION = '24.0'
import { FACEBOOK_CONVERSIONS_API_VERSION, FACEBOOK_CONVERSIONS_CANARY_API_VERSION } from './versioning-info'

export const API_VERSION = FACEBOOK_CONVERSIONS_API_VERSION
export const CANARY_API_VERSION = FACEBOOK_CONVERSIONS_CANARY_API_VERSION
export const CURRENCY_ISO_CODES = new Set([
'AED',
'AFN',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** FACEBOOK_CONVERSIONS_API_VERSION
* Facebook Conversions API version.
* API reference: https://developers.facebook.com/docs/marketing-api/overview/versioning
*/
export const FACEBOOK_CONVERSIONS_API_VERSION = '21.0'

/** FACEBOOK_CONVERSIONS_CANARY_API_VERSION
* Facebook Conversions API canary version.
* API reference: https://developers.facebook.com/docs/marketing-api/overview/versioning
*/
export const FACEBOOK_CONVERSIONS_CANARY_API_VERSION = '24.0'
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,17 @@ import type { Payload as ClickConversionPayload2 } from './uploadClickConversion
import { RefreshTokenResponse } from '.'
import { STATUS_CODE_MAPPING } from './constants'
import { processHashing } from '../../lib/hashing-utils'
export const API_VERSION = 'v21'
export const CANARY_API_VERSION = 'v21'
import {
GOOGLE_ENHANCED_CONVERSIONS_API_VERSION,
GOOGLE_ENHANCED_CONVERSIONS_CANARY_API_VERSION
} from './versioning-info'

export const API_VERSION = GOOGLE_ENHANCED_CONVERSIONS_API_VERSION
export const CANARY_API_VERSION = GOOGLE_ENHANCED_CONVERSIONS_CANARY_API_VERSION
export const FLAGON_NAME = 'google-enhanced-canary-version'
export const FLAGON_NAME_PHONE_VALIDATION_CHECK = 'google-enhanced-phone-validation-check'
import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber'



const phoneUtil = PhoneNumberUtil.getInstance()

type GoogleAdsErrorData = {
Expand Down Expand Up @@ -226,7 +229,7 @@ export function convertTimestamp(timestamp: string | undefined): string | undefi
}

export function timestampToEpochMicroseconds(timestamp: string): string | undefined {
if(!timestamp){
if (!timestamp) {
return undefined
}
const date = new Date(timestamp)
Expand Down Expand Up @@ -1118,9 +1121,8 @@ export function getSessionAttributesKeyValuePairs(payload: ClickConversionPayloa
} = {}
} = payload

const sessionStartTimeUsec = typeof session_start_time_usec === 'string'
? timestampToEpochMicroseconds(session_start_time_usec)
: undefined
const sessionStartTimeUsec =
typeof session_start_time_usec === 'string' ? timestampToEpochMicroseconds(session_start_time_usec) : undefined

const entries: [KeyValueItem['sessionAttributeKey'], string | undefined][] = [
['gad_source', gad_source],
Expand All @@ -1132,16 +1134,11 @@ export function getSessionAttributesKeyValuePairs(payload: ClickConversionPayloa
]

const keyValuePairList: KeyValuePairList = entries
.filter(
([_, value]) => value !== undefined && value !== null && value !== ''
)
.filter(([_, value]) => value !== undefined && value !== null && value !== '')
.map(([key, value]) => ({
sessionAttributeKey: key,
sessionAttributeValue: value
}))

return (keyValuePairList.length > 0
? { sessionAttributesKeyValuePairs: { keyValuePairs: keyValuePairList } }
: {}
)
return keyValuePairList.length > 0 ? { sessionAttributesKeyValuePairs: { keyValuePairs: keyValuePairList } } : {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
formatRegion,
cleanData
} from './formatter'
import { GOOGLE_ENHANCED_CONVERSIONS_EVENTS_API_VERSION } from '../versioning-info'

interface GoogleError {
status: string
Expand Down Expand Up @@ -212,7 +213,7 @@ const action: ActionDefinition<Settings, Payload> = {
},

perform: async (request, { payload, settings }) => {
/* Enforcing this here since Conversion ID is required for the Enhanced Conversions API
/* Enforcing this here since Conversion ID is required for the Enhanced Conversions API
but not for the Google Ads API. */
if (!settings.conversionTrackingId) {
throw new PayloadValidationError(
Expand Down Expand Up @@ -253,7 +254,7 @@ const action: ActionDefinition<Settings, Payload> = {
})

try {
return await request('https://www.google.com/ads/event/api/v1', {
return await request(`https://www.google.com/ads/event/api/${GOOGLE_ENHANCED_CONVERSIONS_EVENTS_API_VERSION}`, {
method: 'post',
searchParams: {
conversion_tracking_id: settings.conversionTrackingId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/** GOOGLE_ENHANCED_CONVERSIONS_API_VERSION
* Google Ads API version for enhanced conversions.
* API reference: https://developers.google.com/google-ads/api/docs/upgrade
*/
export const GOOGLE_ENHANCED_CONVERSIONS_API_VERSION = 'v21'
export const GOOGLE_ENHANCED_CONVERSIONS_CANARY_API_VERSION = 'v21'

/** GOOGLE_ENHANCED_CONVERSIONS_EVENTS_API_VERSION
* Google Ads event API version.
* API reference: https://developers.google.com/google-ads/api/docs/upgrade
*/
export const GOOGLE_ENHANCED_CONVERSIONS_EVENTS_API_VERSION = 'v1'
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ActionDefinition } from '@segment/actions-core'
import type { Settings } from '../generated-types'
import { eventDefinitionKey, contactKeyAPIEvent, eventData } from '../sfmc-properties'
import type { Payload } from './generated-types'
import { SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION } from '../versioning-info'

const action: ActionDefinition<Settings, Payload> = {
title: 'Send API Event',
Expand All @@ -12,10 +13,13 @@ const action: ActionDefinition<Settings, Payload> = {
data: eventData
},
perform: (request, { settings, payload }) => {
return request(`https://${settings.subdomain}.rest.marketingcloudapis.com/interaction/v1/events`, {
method: 'POST',
json: payload
})
return request(
`https://${settings.subdomain}.rest.marketingcloudapis.com/interaction/${SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION}/events`,
{
method: 'POST',
json: payload
}
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ActionDefinition } from '@segment/actions-core'
import type { Settings } from '../generated-types'
import { contactKey } from '../sfmc-properties'
import type { Payload } from './generated-types'
import { SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION } from '../versioning-info'

const action: ActionDefinition<Settings, Payload> = {
title: 'Create Contact',
Expand All @@ -11,13 +12,16 @@ const action: ActionDefinition<Settings, Payload> = {
contactKey: { ...contactKey, required: true }
},
perform: (request, { settings, payload }) => {
return request(`https://${settings.subdomain}.rest.marketingcloudapis.com/contacts/v1/contacts`, {
method: 'POST',
json: {
contactKey: payload.contactKey,
attributeSets: []
return request(
`https://${settings.subdomain}.rest.marketingcloudapis.com/contacts/${SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION}/contacts`,
{
method: 'POST',
json: {
contactKey: payload.contactKey,
attributeSets: []
}
}
})
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import { ErrorResponse, ErrorData } from './types'
import { OnMappingSaveInputs } from './dataExtensionV2/generated-types'
import { Settings } from './generated-types'
import { xml2js } from 'xml-js'
import {
SALESFORCE_MARKETING_CLOUD_HUB_API_VERSION,
SALESFORCE_MARKETING_CLOUD_AUTH_API_VERSION,
SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION
} from './versioning-info'

function generateRows(payloads: payload_dataExtension[] | payload_contactDataExtension[]): Record<string, any>[] {
const rows: Record<string, any>[] = []
Expand Down Expand Up @@ -52,15 +57,21 @@ export function upsertRows(
}
const rows = generateRows(payloads)
if (key) {
return request(`https://${subdomain}.rest.marketingcloudapis.com/hub/v1/dataevents/key:${key}/rowset`, {
method: 'POST',
json: rows
})
return request(
`https://${subdomain}.rest.marketingcloudapis.com/hub/${SALESFORCE_MARKETING_CLOUD_HUB_API_VERSION}/dataevents/key:${key}/rowset`,
{
method: 'POST',
json: rows
}
)
} else {
return request(`https://${subdomain}.rest.marketingcloudapis.com/hub/v1/dataevents/${id}/rowset`, {
method: 'POST',
json: rows
})
return request(
`https://${subdomain}.rest.marketingcloudapis.com/hub/${SALESFORCE_MARKETING_CLOUD_HUB_API_VERSION}/dataevents/${id}/rowset`,
{
method: 'POST',
json: rows
}
)
}
}

Expand All @@ -79,10 +90,13 @@ export function upsertRowsV2(
}

const rows = generateRows(payloads)
return request(`https://${subdomain}.rest.marketingcloudapis.com/hub/v1/dataevents/${dataExtensionId}/rowset`, {
method: 'POST',
json: rows
})
return request(
`https://${subdomain}.rest.marketingcloudapis.com/hub/${SALESFORCE_MARKETING_CLOUD_HUB_API_VERSION}/dataevents/${dataExtensionId}/rowset`,
{
method: 'POST',
json: rows
}
)
}

export async function executeUpsertWithMultiStatus(
Expand Down Expand Up @@ -162,17 +176,17 @@ export async function executeUpsertWithMultiStatus(
errormessage: additionalError ? additionalError[0].message : errData?.message || '',
sent: rows[index] as Object as JSONLikeObject,
/*
Setting the error response body to the additionalError array as there is no way to determine which error corresponds to which event.
Setting the error response body to the additionalError array as there is no way to determine which error corresponds to which event.
We receive an array of errors, but some errors may not be repeated even if they occur in multiple events, while others may be.
Additionally, there is no consistent order to the errors in the array. For example, errors like

Additionally, there is no consistent order to the errors in the array. For example, errors like
'At least one existing field is required in the values property' consistently appear at the top of the array.
Furthermore, every event that is processed correctly before the first erroneous event is accepted.
However, any events that occur after the first error, regardless of whether they are correct or not, are rejected.

Furthermore, every event that is processed correctly before the first erroneous event is accepted.
However, any events that occur after the first error, regardless of whether they are correct or not, are rejected.
This means that all valid events following the first error will not be processed.

For more information, please refer to:
For more information, please refer to:
https://salesforce.stackexchange.com/questions/292770/import-contacts-sfmc-via-api-vs-ftp/292774#292774
*/
body: additionalError ? (additionalError as Object as JSONLikeObject) : (errData as Object as JSONLikeObject)
Expand Down Expand Up @@ -239,7 +253,7 @@ const getAccessToken = async (
request: RequestClient,
settings: Settings
): Promise<{ accessToken: string; soapInstanceUrl: string }> => {
const baseUrl = `https://${settings.subdomain}.auth.marketingcloudapis.com/v2/token`
const baseUrl = `https://${settings.subdomain}.auth.marketingcloudapis.com/${SALESFORCE_MARKETING_CLOUD_AUTH_API_VERSION}/token`
const res = await request<RefreshTokenResponse>(`${baseUrl}`, {
method: 'POST',
body: new URLSearchParams({
Expand Down Expand Up @@ -288,7 +302,7 @@ const dataExtensionRequest = async (

try {
const response = await request<DataExtensionCreationResponse>(
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects`,
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/${SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION}/customobjects`,
{
method: 'POST',
json: {
Expand Down Expand Up @@ -365,7 +379,7 @@ const selectDataExtensionRequest = async (

try {
const response = await request<DataExtensionSelectionResponse>(
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects/${hookInputs.dataExtensionId}`,
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/${SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION}/customobjects/${hookInputs.dataExtensionId}`,
{
method: 'GET',
headers: {
Expand Down Expand Up @@ -462,7 +476,7 @@ const getDataExtensionsRequest = async (
): Promise<{ results?: DynamicFieldItem[]; error?: DynamicFieldError }> => {
try {
const response = await request<DataExtensionSearchResponse>(
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects`,
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/${SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION}/customobjects`,
{
method: 'get',
searchParams: {
Expand Down Expand Up @@ -550,7 +564,7 @@ const getDataExtensionFieldsRequest = async (
): Promise<{ results?: DynamicFieldItem[]; error?: DynamicFieldError }> => {
try {
const response = await request<DataExtensionFieldsResponse>(
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/v1/customobjects/${dataExtensionId}/fields`,
`https://${auth.subdomain}.rest.marketingcloudapis.com/data/${SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION}/customobjects/${dataExtensionId}/fields`,
{
method: 'GET',
headers: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/** SALESFORCE_MARKETING_CLOUD_AUTH_API_VERSION
* Salesforce Marketing Cloud auth token API version.
* API reference: https://developer.salesforce.com/docs/marketing/marketing-cloud/references/mc_rest_auth?meta=Summary
*/
export const SALESFORCE_MARKETING_CLOUD_AUTH_API_VERSION = 'v2'

/** SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION
* Salesforce Marketing Cloud data API version.
* API reference: https://developer.salesforce.com/docs/marketing/marketing-cloud/references/mc-custom_objects?meta=Summary
*/
export const SALESFORCE_MARKETING_CLOUD_DATA_API_VERSION = 'v1'

/** SALESFORCE_MARKETING_CLOUD_HUB_API_VERSION
* Salesforce Marketing Cloud hub API version.
* API reference: https://developer.salesforce.com/docs/marketing/marketing-cloud/references/mc-data_extension_rows_sync?meta=Summary
*/
export const SALESFORCE_MARKETING_CLOUD_HUB_API_VERSION = 'v1'
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Payload } from './generated-types'
import { formatEmails, formatPhones, formatUserIds, formatString, formatAddress } from './formatter'
import { WEB, CRM, TRAVEL_FIELDS, VEHICLE_FIELDS } from './constants'
import { TTJSON, TTAutoProps, TTBaseProps, TTTravelProps, TTUser } from './types'
import { TIKTOK_CONVERSIONS_API_VERSION } from '../versioning-info'

export function send(request: RequestClient, settings: Settings, payload: Payload) {
const {
Expand Down Expand Up @@ -53,7 +54,7 @@ export function send(request: RequestClient, settings: Settings, payload: Payloa
]
}

return request('https://business-api.tiktok.com/open_api/v1.3/event/track/', {
return request(`https://business-api.tiktok.com/open_api/${TIKTOK_CONVERSIONS_API_VERSION}/event/track/`, {
method: 'post',
json: requestJson
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** TIKTOK_CONVERSIONS_API_VERSION
* TikTok web conversions API version.
* API reference: https://business-api.tiktok.com/portal/docs?id=1771101303285761
*/
export const TIKTOK_CONVERSIONS_API_VERSION = 'v1.3'
Loading