Skip to content

Commit

Permalink
Updates generateData to be a shared function so it can be exported an…
Browse files Browse the repository at this point in the history
…d unit tested directly. Fixes minor issues and the first unit test is passing
  • Loading branch information
nick-Ag committed Jul 28, 2024
1 parent 8ec6462 commit fa0131d
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import createRequestClient from '../../../../../core/src/request-client'
import FacebookClient, { BASE_URL } from '../fbca-operations'
import FacebookClient, { BASE_URL, generateData } from '../fbca-operations'
import { Settings } from '../generated-types'
import nock from 'nock'
import { Payload } from '../sync/generated-types'
import { baseURL } from 'src/destinations/apolloio'
import { SCHEMA_PROPERTIES } from '../fbca-properties'
import { createHash } from 'crypto'

const requestClient = createRequestClient()
Expand Down Expand Up @@ -39,8 +37,6 @@ describe('Facebook Custom Audiences', () => {
})

describe('generateData', () => {
const audienceId = '123'

it('should generate data correctly for a single user', async () => {
const payloads: Payload[] = [
{
Expand All @@ -54,34 +50,25 @@ describe('Facebook Custom Audiences', () => {
}
]

nock(`${baseURL}`)
.post(`/${audienceId}/users`, {
payload: {
schema: SCHEMA_PROPERTIES,
data: [
[
EMPTY, // external_id
hash(payloads[0].email || ''), // email
hash(payloads[0].phone || ''), // phone
EMPTY, // gender
EMPTY, // year
EMPTY, // month
EMPTY, // day
hash(payloads[0].name?.last || ''), // last_name
hash(payloads[0].name?.first || ''), // first_name
EMPTY, // first_initial
EMPTY, // city
EMPTY, // state
EMPTY, // zip
EMPTY, // mobile_advertiser_id,
EMPTY // country
]
]
}
})
.reply(200)

await facebookClient.syncAudience({ audienceId, payloads })
expect(generateData(payloads)).toEqual([
[
hash(payloads[0].externalId || ''), // external_id
hash(payloads[0].email || ''), // email
hash(payloads[0].phone || ''), // phone
EMPTY, // gender
EMPTY, // year
EMPTY, // month
EMPTY, // day
hash(payloads[0].name?.last || ''), // last_name
hash(payloads[0].name?.first || ''), // first_name
EMPTY, // first_initial
EMPTY, // city
EMPTY, // state
EMPTY, // zip
EMPTY, // mobile_advertiser_id,
EMPTY // country
]
])
})

it('should generate data correctly for multiple users', async () => {})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { DynamicFieldItem, DynamicFieldError, RequestClient } from '@segment/actions-core'
import { DynamicFieldItem, DynamicFieldError, RequestClient, IntegrationError } from '@segment/actions-core'
import { Payload } from './sync/generated-types'
import { createHash } from 'crypto'
import { segmentSchemaKeyToArrayIndex, SCHEMA_PROPERTIES } from './fbca-properties'
import { IntegrationError } from '@segment/actions-core/*'

const FACEBOOK_API_VERSION = 'v20.0'
// exported for unit testing
Expand Down Expand Up @@ -32,6 +31,48 @@ interface FacebookResponseError {
}
}

// exported for unit testing. Also why these are not members of the class
export const generateData = (payloads: Payload[]): (string | number)[][] => {
const data: (string | number)[][] = new Array(payloads.length)

payloads.forEach((payload, index) => {
const row: (string | number)[] = new Array(SCHEMA_PROPERTIES.length).fill('')

Object.entries(payload).forEach(([key, value]) => {
if (typeof value === 'object') {
Object.entries(value).forEach(([nestedKey, value]) => {
appendToDataRow(nestedKey, value as string | number, row)
})
} else {
appendToDataRow(key, value as string | number, row)
}
})

data[index] = row
})

return data
}

const appendToDataRow = (key: string, value: string | number, row: (string | number)[]) => {
const index = segmentSchemaKeyToArrayIndex.get(key)

if (index === undefined) {
throw new IntegrationError(`Invalid schema key: ${key}`, 'INVALID_SCHEMA_KEY', 500)
}

if (typeof value === 'number') {
row[index] = value
return
}

row[index] = hash(value)
}

const hash = (value: string): string => {
return createHash('sha256').update(value).digest('hex')
}

export default class FacebookClient {
request: RequestClient
adAccountId: string
Expand Down Expand Up @@ -81,7 +122,7 @@ export default class FacebookClient {
}

syncAudience = async (input: { audienceId: string; payloads: Payload[]; deleteUsers?: boolean }) => {
const data = this.generateData(input.payloads)
const data = generateData(input.payloads)
console.log('data', data)

const params = {
Expand All @@ -101,47 +142,6 @@ export default class FacebookClient {
}
}

private generateData = (payloads: Payload[]): (string | number)[][] => {
const data: (string | number)[][] = new Array(payloads.length)

payloads.forEach((payload, index) => {
const row: (string | number)[] = new Array(SCHEMA_PROPERTIES.length)

Object.entries(payload).forEach(([key, value]) => {
if (typeof value === 'object') {
Object.entries(value).forEach(([nestedKey, value]) => {
this.appendToDataRow(nestedKey, value as string | number, row)
})
} else {
this.appendToDataRow(key, value as string | number, row)
}
})

data[index] = row
})

return data
}

private appendToDataRow = (key: string, value: string | number, row: (string | number)[]) => {
const index = segmentSchemaKeyToArrayIndex.get(key)

if (index === undefined) {
throw new IntegrationError(`Invalid schema key: ${key}`, 'INVALID_SCHEMA_KEY', 500)
}

if (typeof value === 'number') {
row[index] = value
return
}

row[index] = this.hash(value)
}

private hash = (value: string): string => {
return createHash('sha256').update(value).digest('hex')
}

private formatAdAccount(adAccountId: string) {
if (adAccountId.startsWith('act_')) {
return adAccountId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,26 @@ export const SCHEMA_PROPERTIES = [
'COUNTRY'
]

// A list of the segment equivalent to each Facebook schema property
// It is important that each segment schema property is in the same order as the Facebook schema properties
export const SEGMENT_SCHEMA_PROPERTIES = [
'externalId',
'email',
'phone',
'gender',
'year',
'month',
'day',
'last',
'first',
'firstInitial',
'city',
'state',
'zip',
'mobileAdId',
'country'
]

export const segmentSchemaKeyToArrayIndex = new Map<string, number>(
SCHEMA_PROPERTIES.map((property, index) => [property, index])
SEGMENT_SCHEMA_PROPERTIES.map((property, index) => [property, index])
)

0 comments on commit fa0131d

Please sign in to comment.