@segment/analytics-browser-actions-friendbuy 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +21 -0
- package/src/__tests__/index.test.ts +38 -0
- package/src/generated-types.ts +8 -0
- package/src/index.ts +91 -0
- package/src/trackCustomEvent/__tests__/index.test.ts +98 -0
- package/src/trackCustomEvent/generated-types.ts +30 -0
- package/src/trackCustomEvent/index.ts +49 -0
- package/src/trackCustomer/__tests__/index.test.ts +196 -0
- package/src/trackCustomer/generated-types.ts +74 -0
- package/src/trackCustomer/index.ts +51 -0
- package/src/trackPage/__tests__/index.test.ts +63 -0
- package/src/trackPage/generated-types.ts +16 -0
- package/src/trackPage/index.ts +58 -0
- package/src/trackPurchase/__tests__/index.test.ts +211 -0
- package/src/trackPurchase/generated-types.ts +91 -0
- package/src/trackPurchase/index.ts +85 -0
- package/src/trackSignUp/__tests__/index.test.ts +92 -0
- package/src/trackSignUp/generated-types.ts +62 -0
- package/src/trackSignUp/index.ts +52 -0
- package/src/types.ts +3 -0
- package/tsconfig.json +9 -0
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@segment/analytics-browser-actions-friendbuy",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "./dist/cjs",
|
|
6
|
+
"module": "./dist/esm",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "yarn build:esm && yarn build:cjs",
|
|
9
|
+
"build:cjs": "tsc --module commonjs --outDir ./dist/cjs",
|
|
10
|
+
"build:esm": "tsc --outDir ./dist/esm"
|
|
11
|
+
},
|
|
12
|
+
"typings": "./dist/esm",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@segment/actions-core": "^3.71.0",
|
|
15
|
+
"@segment/actions-shared": "^1.53.0",
|
|
16
|
+
"@segment/browser-destination-runtime": "^1.0.0"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@segment/analytics-next": "*"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import friendbuyDestination, { destination } from '../index'
|
|
3
|
+
import nock from 'nock'
|
|
4
|
+
|
|
5
|
+
const subscriptions = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'trackCustomer',
|
|
8
|
+
name: 'Track Customer',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "identify"',
|
|
11
|
+
mapping: {}
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
describe('Friendbuy', () => {
|
|
16
|
+
const merchantId = '0ebece2e-b04c-4504-97f2-16cd9f423612'
|
|
17
|
+
|
|
18
|
+
test('loading', async () => {
|
|
19
|
+
jest.spyOn(destination, 'initialize')
|
|
20
|
+
|
|
21
|
+
nock('https://static.fbot.me').get('/friendbuy.js').reply(200, {})
|
|
22
|
+
nock('https://campaign.fbot.me')
|
|
23
|
+
.get(/^\/[^/]*\/campaigns.js$/)
|
|
24
|
+
.reply(200, {})
|
|
25
|
+
|
|
26
|
+
const [event] = await friendbuyDestination({
|
|
27
|
+
merchantId,
|
|
28
|
+
subscriptions
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
await event.load(Context.system(), {} as Analytics)
|
|
32
|
+
expect(destination.initialize).toHaveBeenCalled()
|
|
33
|
+
|
|
34
|
+
const expectedFriendbuyAPI = [['merchant', merchantId]] as any
|
|
35
|
+
expectedFriendbuyAPI.merchantId = merchantId
|
|
36
|
+
expect(window.friendbuyAPI).toEqual(expectedFriendbuyAPI)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Settings {
|
|
4
|
+
/**
|
|
5
|
+
* Find your Friendbuy Merchant ID by logging in to your [Friendbuy account](https://retailer.friendbuy.io/) and going to Developer Center > Friendbuy Code.
|
|
6
|
+
*/
|
|
7
|
+
merchantId: string
|
|
8
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { BrowserDestinationDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
import type { DestinationDefinition } from '@segment/actions-core'
|
|
3
|
+
import { browserDestination } from '@segment/browser-destination-runtime/shim'
|
|
4
|
+
import { defaultValues } from '@segment/actions-core'
|
|
5
|
+
|
|
6
|
+
import type { Settings } from './generated-types'
|
|
7
|
+
import type { FriendbuyAPI } from './types'
|
|
8
|
+
import trackCustomer, { trackCustomerDefaultSubscription } from './trackCustomer'
|
|
9
|
+
import trackPurchase, { browserTrackPurchaseFields, trackPurchaseDefaultSubscription } from './trackPurchase'
|
|
10
|
+
import trackSignUp, { browserTrackSignUpFields, trackSignUpDefaultSubscription } from './trackSignUp'
|
|
11
|
+
import trackPage, { trackPageDefaultSubscription, trackPageFields } from './trackPage'
|
|
12
|
+
import trackCustomEvent from './trackCustomEvent'
|
|
13
|
+
import { trackCustomerFields } from '@segment/actions-shared'
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
interface Window {
|
|
17
|
+
friendbuyAPI?: FriendbuyAPI
|
|
18
|
+
friendbuyBaseHost?: string
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Presets are shown in Segment configuration flow as "Pre-Built Subscriptions".
|
|
23
|
+
const presets: DestinationDefinition['presets'] = [
|
|
24
|
+
{
|
|
25
|
+
name: 'Track Customer',
|
|
26
|
+
subscribe: trackCustomerDefaultSubscription,
|
|
27
|
+
partnerAction: 'trackCustomer',
|
|
28
|
+
mapping: defaultValues(trackCustomerFields)
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Track Purchase',
|
|
32
|
+
subscribe: trackPurchaseDefaultSubscription,
|
|
33
|
+
partnerAction: 'trackPurchase',
|
|
34
|
+
mapping: defaultValues(browserTrackPurchaseFields)
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Track Sign Up',
|
|
38
|
+
subscribe: trackSignUpDefaultSubscription,
|
|
39
|
+
partnerAction: 'trackSignUp',
|
|
40
|
+
mapping: defaultValues(browserTrackSignUpFields)
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Track Page',
|
|
44
|
+
subscribe: trackPageDefaultSubscription,
|
|
45
|
+
partnerAction: 'trackPage',
|
|
46
|
+
mapping: defaultValues(trackPageFields)
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
export const destination: BrowserDestinationDefinition<Settings, FriendbuyAPI> = {
|
|
51
|
+
name: 'Friendbuy (Actions)',
|
|
52
|
+
slug: 'actions-friendbuy',
|
|
53
|
+
mode: 'device',
|
|
54
|
+
|
|
55
|
+
settings: {
|
|
56
|
+
merchantId: {
|
|
57
|
+
label: 'Friendbuy Merchant ID',
|
|
58
|
+
description:
|
|
59
|
+
'Find your Friendbuy Merchant ID by logging in to your [Friendbuy account](https://retailer.friendbuy.io/) and going to Developer Center > Friendbuy Code.',
|
|
60
|
+
type: 'string',
|
|
61
|
+
format: 'uuid',
|
|
62
|
+
required: true
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
initialize: async ({ settings /* , analytics */ }, dependencies) => {
|
|
67
|
+
let friendbuyAPI: FriendbuyAPI
|
|
68
|
+
window.friendbuyAPI = friendbuyAPI = window.friendbuyAPI || ([] as unknown as FriendbuyAPI)
|
|
69
|
+
const friendbuyBaseHost = window.friendbuyBaseHost ?? 'fbot.me'
|
|
70
|
+
|
|
71
|
+
friendbuyAPI.merchantId = settings.merchantId
|
|
72
|
+
friendbuyAPI.push(['merchant', settings.merchantId])
|
|
73
|
+
|
|
74
|
+
// The Friendbuy JavaScript can be loaded asynchronously.
|
|
75
|
+
void dependencies.loadScript(`https://static.${friendbuyBaseHost}/friendbuy.js`)
|
|
76
|
+
void dependencies.loadScript(`https://campaign.${friendbuyBaseHost}/${settings.merchantId}/campaigns.js`)
|
|
77
|
+
|
|
78
|
+
return friendbuyAPI
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
presets,
|
|
82
|
+
actions: {
|
|
83
|
+
trackCustomer,
|
|
84
|
+
trackPurchase,
|
|
85
|
+
trackSignUp,
|
|
86
|
+
trackPage,
|
|
87
|
+
trackCustomEvent
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export default browserDestination(destination)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import friendbuyDestination from '../../index'
|
|
3
|
+
import trackCustomEventObject, { browserTrackCustomEventFields } from '../index'
|
|
4
|
+
|
|
5
|
+
import { loadScript } from '@segment/browser-destination-runtime/load-script'
|
|
6
|
+
jest.mock('@segment/browser-destination-runtime/load-script')
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
// Prevent friendbuy.js and campaigns.js from being loaded.
|
|
9
|
+
;(loadScript as jest.Mock).mockResolvedValue(true)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
describe('Friendbuy.trackCustomEvent', () => {
|
|
13
|
+
const subscriptions = [
|
|
14
|
+
{
|
|
15
|
+
partnerAction: 'trackCustomEvent',
|
|
16
|
+
name: trackCustomEventObject.title,
|
|
17
|
+
enabled: true,
|
|
18
|
+
subscribe: 'type = "track" and event = "download"',
|
|
19
|
+
mapping: Object.fromEntries(
|
|
20
|
+
Object.entries(browserTrackCustomEventFields).map(([name, value]) => [name, value.default])
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
test('all fields', async () => {
|
|
26
|
+
const merchantId = '1993d0f1-8206-4336-8c88-64e170f2419e'
|
|
27
|
+
|
|
28
|
+
const [trackCustomEvent] = await friendbuyDestination({
|
|
29
|
+
merchantId,
|
|
30
|
+
subscriptions
|
|
31
|
+
})
|
|
32
|
+
// console.log('trackCustomEvent', JSON.stringify(trackCustomEvent, null, 2), trackCustomEvent)
|
|
33
|
+
expect(trackCustomEvent).toBeDefined()
|
|
34
|
+
|
|
35
|
+
await trackCustomEvent.load(Context.system(), {} as Analytics)
|
|
36
|
+
|
|
37
|
+
// console.log(window.friendbuyAPI)
|
|
38
|
+
jest.spyOn(window.friendbuyAPI as any, 'push')
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
// Non-download events are not sent.
|
|
42
|
+
const context1 = new Context({
|
|
43
|
+
type: 'track',
|
|
44
|
+
event: 'upload',
|
|
45
|
+
properties: { type: 'application', fileId: 'MyApp', deduplicationId: '1234' }
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
trackCustomEvent.track?.(context1)
|
|
49
|
+
|
|
50
|
+
expect(window.friendbuyAPI?.push).not.toHaveBeenCalled()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
// Download events are sent.
|
|
55
|
+
const context2 = new Context({
|
|
56
|
+
type: 'track',
|
|
57
|
+
event: 'download',
|
|
58
|
+
properties: { type: 'application', fileId: 'MyApp', deduplicationId: '1234' }
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
trackCustomEvent.track?.(context2)
|
|
62
|
+
|
|
63
|
+
expect(window.friendbuyAPI?.push).toHaveBeenNthCalledWith(1, [
|
|
64
|
+
'track',
|
|
65
|
+
'download',
|
|
66
|
+
{ type: 'application', fileId: 'MyApp', deduplicationId: '1234' }
|
|
67
|
+
])
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
// Customer is sent if customer fields are present.
|
|
72
|
+
const userId = 'john-doe-1234'
|
|
73
|
+
const anonymousId = '960efa33-6d3b-4eb9-a4e7-d95412d9829e'
|
|
74
|
+
const email = 'john.doe@example.com'
|
|
75
|
+
const firstName = 'John'
|
|
76
|
+
const lastName = 'Doe'
|
|
77
|
+
const context3 = new Context({
|
|
78
|
+
type: 'track',
|
|
79
|
+
event: 'download',
|
|
80
|
+
userId,
|
|
81
|
+
anonymousId,
|
|
82
|
+
properties: { type: 'application', fileId: 'MyApp', email, firstName, lastName }
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
trackCustomEvent.track?.(context3)
|
|
86
|
+
|
|
87
|
+
expect(window.friendbuyAPI?.push).toHaveBeenNthCalledWith(2, [
|
|
88
|
+
'track',
|
|
89
|
+
'download',
|
|
90
|
+
{
|
|
91
|
+
type: 'application',
|
|
92
|
+
fileId: 'MyApp',
|
|
93
|
+
customer: { id: userId, anonymousId, email, firstName, lastName, name: `${firstName} ${lastName}` }
|
|
94
|
+
}
|
|
95
|
+
])
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The type of the event to track.
|
|
6
|
+
*/
|
|
7
|
+
eventType: string
|
|
8
|
+
/**
|
|
9
|
+
* Object containing the properties for the event being tracked. All of the fields in this object will be sent in the root of the Friendbuy track event.
|
|
10
|
+
*/
|
|
11
|
+
eventProperties: {
|
|
12
|
+
[k: string]: unknown
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An identifier for the event being tracked to prevent the same event from being rewarded more than once.
|
|
16
|
+
*/
|
|
17
|
+
deduplicationId?: string
|
|
18
|
+
/**
|
|
19
|
+
* The user's customer ID.
|
|
20
|
+
*/
|
|
21
|
+
customerId: string
|
|
22
|
+
/**
|
|
23
|
+
* The user's anonymous id
|
|
24
|
+
*/
|
|
25
|
+
anonymousId?: string
|
|
26
|
+
/**
|
|
27
|
+
* The user's email address.
|
|
28
|
+
*/
|
|
29
|
+
email?: string
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
|
|
3
|
+
import type { FriendbuyAPI } from '../types'
|
|
4
|
+
import type { Settings } from '../generated-types'
|
|
5
|
+
import type { Payload } from './generated-types'
|
|
6
|
+
import type { ConvertFun, EventMap } from '@segment/actions-shared'
|
|
7
|
+
|
|
8
|
+
import { AnalyticsPayload, COPY, DROP, ROOT, mapEvent } from '@segment/actions-shared'
|
|
9
|
+
import { trackCustomEventFields } from '@segment/actions-shared'
|
|
10
|
+
import { addName, enjoinInteger, enjoinString, moveEventPropertiesToRoot, parseDate } from '@segment/actions-shared'
|
|
11
|
+
|
|
12
|
+
export const browserTrackCustomEventFields = trackCustomEventFields({}) // @@ email required?
|
|
13
|
+
|
|
14
|
+
const trackCustomEventPub: EventMap = {
|
|
15
|
+
fields: {
|
|
16
|
+
eventType: DROP,
|
|
17
|
+
deduplicationId: COPY,
|
|
18
|
+
|
|
19
|
+
// CUSTOMER FIELDS
|
|
20
|
+
customerId: { name: ['customer', 'id'], convert: enjoinString as ConvertFun },
|
|
21
|
+
anonymousId: { name: ['customer', 'anonymousId'] },
|
|
22
|
+
email: { name: ['customer', 'email'] },
|
|
23
|
+
isNewCustomer: { name: ['customer', 'isNewCustomer'] },
|
|
24
|
+
loyaltyStatus: { name: ['customer', 'loyaltyStatus'] },
|
|
25
|
+
firstName: { name: ['customer', 'firstName'] },
|
|
26
|
+
lastName: { name: ['customer', 'lastName'] },
|
|
27
|
+
name: { name: ['customer', 'name'] },
|
|
28
|
+
age: { name: ['customer', 'age'], convert: enjoinInteger as ConvertFun },
|
|
29
|
+
birthday: { name: ['customer', 'birthday'], convert: parseDate as ConvertFun }
|
|
30
|
+
},
|
|
31
|
+
unmappedFieldObject: ROOT
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const action: BrowserActionDefinition<Settings, FriendbuyAPI, Payload> = {
|
|
35
|
+
title: 'Track Custom Event',
|
|
36
|
+
description: 'Record when a customer completes any custom event that you define.',
|
|
37
|
+
// trackCustomEvent has no default subscription.
|
|
38
|
+
platform: 'web',
|
|
39
|
+
fields: browserTrackCustomEventFields,
|
|
40
|
+
|
|
41
|
+
perform: (friendbuyAPI, { payload }) => {
|
|
42
|
+
const analyticsPayload = moveEventPropertiesToRoot(payload as unknown as AnalyticsPayload)
|
|
43
|
+
addName(analyticsPayload)
|
|
44
|
+
const friendbuyPayload = mapEvent(trackCustomEventPub, analyticsPayload)
|
|
45
|
+
friendbuyAPI.push(['track', payload.eventType, friendbuyPayload])
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default action
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import friendbuyDestination from '../../index'
|
|
3
|
+
import trackCustomerObject, { trackCustomerDefaultSubscription } from '../index'
|
|
4
|
+
import { trackCustomerFields } from '@segment/actions-shared'
|
|
5
|
+
|
|
6
|
+
import { loadScript } from '@segment/browser-destination-runtime/load-script'
|
|
7
|
+
jest.mock('@segment/browser-destination-runtime/load-script')
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
// Prevent friendbuy.js and campaigns.js from being loaded.
|
|
10
|
+
;(loadScript as jest.Mock).mockResolvedValue(true)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('Friendbuy.trackCustomer', () => {
|
|
14
|
+
// console.log('trackCustomer', JSON.stringify(trackCustomer, null, 2))
|
|
15
|
+
|
|
16
|
+
const subscriptions = [
|
|
17
|
+
{
|
|
18
|
+
partnerAction: 'trackCustomer',
|
|
19
|
+
name: trackCustomerObject.title,
|
|
20
|
+
enabled: true,
|
|
21
|
+
subscribe: trackCustomerDefaultSubscription,
|
|
22
|
+
mapping: Object.fromEntries(
|
|
23
|
+
Object.entries(trackCustomerFields)
|
|
24
|
+
.map(([name, value]) => [name, value.default])
|
|
25
|
+
.concat(
|
|
26
|
+
['customerSince', 'loyaltyStatus', 'isNewCustomer'].map((name) => [name, { '@path': `$.traits.${name}` }])
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
// console.log('subscriptions', JSON.stringify(subscriptions, null, 2))
|
|
33
|
+
|
|
34
|
+
test('all fields', async () => {
|
|
35
|
+
const merchantId = '1993d0f1-8206-4336-8c88-64e170f2419e'
|
|
36
|
+
const userId = 'john-doe-12345'
|
|
37
|
+
const anonymousId = '18aedb99-e756-40fa-8e83-d35f90998fb4'
|
|
38
|
+
const customerSince = '2021-10-20T14:20:15Z'
|
|
39
|
+
const isNewCustomer = false
|
|
40
|
+
const loyaltyStatus = 'in'
|
|
41
|
+
const firstName = 'John'
|
|
42
|
+
const lastName = 'Doe'
|
|
43
|
+
const name = `${firstName} ${lastName}`
|
|
44
|
+
const email = 'john.doe@example.com'
|
|
45
|
+
const age = 25
|
|
46
|
+
const birthday = '1996-02-29'
|
|
47
|
+
const language = 'en-US'
|
|
48
|
+
const country = 'US'
|
|
49
|
+
const state = 'CA'
|
|
50
|
+
const city = 'Beverly Hills'
|
|
51
|
+
const zipCode = '90210'
|
|
52
|
+
const friendbuyAttributes = { custom1: 'custom1', custom2: 'custom2' }
|
|
53
|
+
|
|
54
|
+
const [trackCustomer] = await friendbuyDestination({
|
|
55
|
+
merchantId,
|
|
56
|
+
subscriptions
|
|
57
|
+
})
|
|
58
|
+
// console.log('trackCustomer', JSON.stringify(trackCustomer, null, 2), trackCustomer)
|
|
59
|
+
expect(trackCustomer).toBeDefined()
|
|
60
|
+
|
|
61
|
+
await trackCustomer.load(Context.system(), {} as Analytics)
|
|
62
|
+
|
|
63
|
+
// console.log(window.friendbuyAPI)
|
|
64
|
+
jest.spyOn(window.friendbuyAPI as any, 'push')
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
// all fields
|
|
68
|
+
const context1 = new Context({
|
|
69
|
+
type: 'identify',
|
|
70
|
+
userId,
|
|
71
|
+
anonymousId,
|
|
72
|
+
traits: {
|
|
73
|
+
email,
|
|
74
|
+
customerSince,
|
|
75
|
+
isNewCustomer,
|
|
76
|
+
loyaltyStatus,
|
|
77
|
+
firstName,
|
|
78
|
+
lastName,
|
|
79
|
+
name,
|
|
80
|
+
age,
|
|
81
|
+
birthday,
|
|
82
|
+
language,
|
|
83
|
+
address: { country, state, city, postalCode: zipCode },
|
|
84
|
+
friendbuyAttributes
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
// console.log('context1', JSON.stringify(context1, null, 2))
|
|
88
|
+
|
|
89
|
+
trackCustomer.identify?.(context1)
|
|
90
|
+
|
|
91
|
+
// console.log('trackCustomer request', JSON.stringify(window.friendbuyAPI.push.mock.calls[0], null, 2))
|
|
92
|
+
expect(window.friendbuyAPI?.push).toHaveBeenNthCalledWith(1, [
|
|
93
|
+
'track',
|
|
94
|
+
'customer',
|
|
95
|
+
{
|
|
96
|
+
id: userId,
|
|
97
|
+
email,
|
|
98
|
+
customerSince,
|
|
99
|
+
isNewCustomer,
|
|
100
|
+
loyaltyStatus,
|
|
101
|
+
firstName,
|
|
102
|
+
lastName,
|
|
103
|
+
name,
|
|
104
|
+
age,
|
|
105
|
+
birthday: { year: 1996, month: 2, day: 29 },
|
|
106
|
+
language,
|
|
107
|
+
country,
|
|
108
|
+
state,
|
|
109
|
+
city,
|
|
110
|
+
zipCode,
|
|
111
|
+
anonymousId,
|
|
112
|
+
...friendbuyAttributes
|
|
113
|
+
},
|
|
114
|
+
true
|
|
115
|
+
])
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
{
|
|
119
|
+
// name derived from firstName and lastName
|
|
120
|
+
const context2 = new Context({
|
|
121
|
+
type: 'identify',
|
|
122
|
+
userId,
|
|
123
|
+
traits: {
|
|
124
|
+
firstName,
|
|
125
|
+
lastName
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
trackCustomer.identify?.(context2)
|
|
130
|
+
|
|
131
|
+
expect(window.friendbuyAPI?.push).toHaveBeenNthCalledWith(2, [
|
|
132
|
+
'track',
|
|
133
|
+
'customer',
|
|
134
|
+
{
|
|
135
|
+
id: userId,
|
|
136
|
+
firstName,
|
|
137
|
+
lastName,
|
|
138
|
+
name
|
|
139
|
+
},
|
|
140
|
+
true
|
|
141
|
+
])
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
// name without firstName and lastName
|
|
146
|
+
const context3 = new Context({
|
|
147
|
+
type: 'identify',
|
|
148
|
+
userId,
|
|
149
|
+
traits: {
|
|
150
|
+
email,
|
|
151
|
+
name
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
trackCustomer.identify?.(context3)
|
|
156
|
+
|
|
157
|
+
expect(window.friendbuyAPI?.push).toHaveBeenNthCalledWith(3, [
|
|
158
|
+
'track',
|
|
159
|
+
'customer',
|
|
160
|
+
{
|
|
161
|
+
id: userId,
|
|
162
|
+
email,
|
|
163
|
+
name
|
|
164
|
+
},
|
|
165
|
+
true
|
|
166
|
+
])
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
{
|
|
170
|
+
// enjoined fields are converted
|
|
171
|
+
const context4 = new Context({
|
|
172
|
+
type: 'identify',
|
|
173
|
+
userId: 12345,
|
|
174
|
+
traits: {
|
|
175
|
+
email,
|
|
176
|
+
age: '44',
|
|
177
|
+
address: { postalCode: 90210 }
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
trackCustomer.identify?.(context4)
|
|
182
|
+
|
|
183
|
+
expect(window.friendbuyAPI?.push).toHaveBeenNthCalledWith(4, [
|
|
184
|
+
'track',
|
|
185
|
+
'customer',
|
|
186
|
+
{
|
|
187
|
+
id: '12345',
|
|
188
|
+
email,
|
|
189
|
+
age: 44,
|
|
190
|
+
zipCode: '90210'
|
|
191
|
+
},
|
|
192
|
+
true
|
|
193
|
+
])
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The user's customer ID.
|
|
6
|
+
*/
|
|
7
|
+
customerId: string
|
|
8
|
+
/**
|
|
9
|
+
* The user's anonymous id.
|
|
10
|
+
*/
|
|
11
|
+
anonymousId?: string
|
|
12
|
+
/**
|
|
13
|
+
* The user's email address.
|
|
14
|
+
*/
|
|
15
|
+
email: string
|
|
16
|
+
/**
|
|
17
|
+
* The user's given name.
|
|
18
|
+
*/
|
|
19
|
+
firstName?: string
|
|
20
|
+
/**
|
|
21
|
+
* The user's surname.
|
|
22
|
+
*/
|
|
23
|
+
lastName?: string
|
|
24
|
+
/**
|
|
25
|
+
* The user's full name. If the name trait doesn't exist then it will be automatically derived from the firstName and lastName traits if they are defined.
|
|
26
|
+
*/
|
|
27
|
+
name?: string
|
|
28
|
+
/**
|
|
29
|
+
* The user's age.
|
|
30
|
+
*/
|
|
31
|
+
age?: number
|
|
32
|
+
/**
|
|
33
|
+
* The user's birthday in the format "YYYY-MM-DD", or "0000-MM-DD" to omit the year.
|
|
34
|
+
*/
|
|
35
|
+
birthday?: string
|
|
36
|
+
/**
|
|
37
|
+
* The user's language.
|
|
38
|
+
*/
|
|
39
|
+
language?: string
|
|
40
|
+
/**
|
|
41
|
+
* The user's country.
|
|
42
|
+
*/
|
|
43
|
+
addressCountry?: string
|
|
44
|
+
/**
|
|
45
|
+
* The user's state.
|
|
46
|
+
*/
|
|
47
|
+
addressState?: string
|
|
48
|
+
/**
|
|
49
|
+
* The user's city.
|
|
50
|
+
*/
|
|
51
|
+
addressCity?: string
|
|
52
|
+
/**
|
|
53
|
+
* The user's postal code.
|
|
54
|
+
*/
|
|
55
|
+
addressPostalCode?: string
|
|
56
|
+
/**
|
|
57
|
+
* The date the user became a customer.
|
|
58
|
+
*/
|
|
59
|
+
customerSince?: string
|
|
60
|
+
/**
|
|
61
|
+
* The status of the user in your loyalty program. Valid values are "in", "out", or "blocked".
|
|
62
|
+
*/
|
|
63
|
+
loyaltyStatus?: string
|
|
64
|
+
/**
|
|
65
|
+
* Flag to indicate whether the user is a new customer.
|
|
66
|
+
*/
|
|
67
|
+
isNewCustomer?: boolean
|
|
68
|
+
/**
|
|
69
|
+
* Custom attributes to send to Friendbuy. You should pass an object whose keys are the names of the custom attributes and whose values are strings. Non-string-valued attributes will be dropped.
|
|
70
|
+
*/
|
|
71
|
+
friendbuyAttributes?: {
|
|
72
|
+
[k: string]: unknown
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
|
|
3
|
+
import type { FriendbuyAPI } from '../types'
|
|
4
|
+
import type { Settings } from '../generated-types'
|
|
5
|
+
import type { Payload } from './generated-types'
|
|
6
|
+
import type { AnalyticsPayload, ConvertFun, EventMap } from '@segment/actions-shared'
|
|
7
|
+
|
|
8
|
+
import { COPY, ROOT, mapEvent } from '@segment/actions-shared'
|
|
9
|
+
import { trackCustomerFields } from '@segment/actions-shared'
|
|
10
|
+
import { addName, enjoinInteger, enjoinString, parseDate } from '@segment/actions-shared'
|
|
11
|
+
|
|
12
|
+
// see https://segment.com/docs/config-api/fql/
|
|
13
|
+
export const trackCustomerDefaultSubscription = 'type = "identify"'
|
|
14
|
+
|
|
15
|
+
const trackCustomerPub: EventMap = {
|
|
16
|
+
fields: {
|
|
17
|
+
customerId: { name: 'id', convert: enjoinString as ConvertFun },
|
|
18
|
+
anonymousID: COPY,
|
|
19
|
+
email: COPY,
|
|
20
|
+
isNewCustomer: COPY,
|
|
21
|
+
loyaltyStatus: COPY,
|
|
22
|
+
firstName: COPY,
|
|
23
|
+
lastName: COPY,
|
|
24
|
+
name: COPY,
|
|
25
|
+
age: { convert: enjoinInteger as ConvertFun },
|
|
26
|
+
// fbt-merchant-api complains about birthday being an object but passes it anyway.
|
|
27
|
+
birthday: { convert: parseDate as ConvertFun },
|
|
28
|
+
language: COPY,
|
|
29
|
+
addressCountry: { name: 'country' },
|
|
30
|
+
addressState: { name: 'state' },
|
|
31
|
+
addressCity: { name: 'city' },
|
|
32
|
+
addressPostalCode: { name: 'zipCode', convert: enjoinString as ConvertFun }
|
|
33
|
+
},
|
|
34
|
+
unmappedFieldObject: ROOT
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const action: BrowserActionDefinition<Settings, FriendbuyAPI, Payload> = {
|
|
38
|
+
title: 'Track Customer',
|
|
39
|
+
description: 'Create a new customer profile or update an existing customer profile.',
|
|
40
|
+
defaultSubscription: trackCustomerDefaultSubscription,
|
|
41
|
+
platform: 'web',
|
|
42
|
+
fields: trackCustomerFields,
|
|
43
|
+
|
|
44
|
+
perform: (friendbuyAPI, { payload }) => {
|
|
45
|
+
addName(payload)
|
|
46
|
+
const friendbuyPayload = mapEvent(trackCustomerPub, payload as unknown as AnalyticsPayload)
|
|
47
|
+
friendbuyAPI.push(['track', 'customer', friendbuyPayload, true])
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default action
|