Skip to content

Commit

Permalink
Merge pull request #2 from talon-one/sc-7503-add-destination-endpoint…
Browse files Browse the repository at this point in the history
…s-to-work-with-customer

feat(Segment) add destination endpoints to work with customer profiles
  • Loading branch information
kaatinga authored Mar 16, 2022
2 parents 6871863 + cd1022f commit d391a8e
Show file tree
Hide file tree
Showing 52 changed files with 1,157 additions and 116 deletions.
2 changes: 1 addition & 1 deletion packages/actions-shared/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@segment/actions-shared",
"description": "Shared destination action methods and definitions.",
"version": "1.3.1",
"version": "1.4.0",
"repository": {
"type": "git",
"url": "https://github.com/segmentio/action-destinations",
Expand Down
4 changes: 2 additions & 2 deletions packages/browser-destinations/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@segment/browser-destinations",
"version": "3.32.2",
"version": "3.33.0",
"description": "Action based browser destinations",
"author": "Netto Farah",
"private": true,
Expand Down Expand Up @@ -30,7 +30,7 @@
"dependencies": {
"@braze/web-sdk": "^3.5.0",
"@fullstory/browser": "^1.4.9",
"@segment/actions-shared": "^1.3.1",
"@segment/actions-shared": "^1.4.0",
"@segment/analytics-next": "^1.29.3",
"@segment/destination-subscriptions": "^3.9.0",
"dayjs": "^1.10.7",
Expand Down
1 change: 1 addition & 0 deletions packages/browser-destinations/src/destinations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ register('60fb01aec459242d3b6f20c1', './braze')
register('60f9d0d048950c356be2e4da', './braze-cloud-plugins')
register('6170a348128093cd0245e0ea', './friendbuy')
register('6141153ee7500f15d3838703', './fullstory')
register('6230c835c0d6535357ee950d', './koala')
register('61d8859be4f795335d5c677c', './stackadapt')
register('61d8c74d174a9acd0e138b31', './sprig-web')

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { Subscription } from '../../../../lib/browser-destinations'
import { Analytics, Context } from '@segment/analytics-next'
import KoalaDestination, { destination } from '../../index'

import { loadScript } from '../../../../runtime/load-script'
jest.mock('../../../../runtime/load-script')
beforeEach(async () => {
;(loadScript as jest.Mock).mockResolvedValue(true)
})

const subscriptions: Subscription[] = [
{
partnerAction: 'identifyVisitor',
name: 'Identify Visitor',
enabled: true,
subscribe: 'type = "identify"',
mapping: {
traits: {
'@path': '$.traits'
}
}
}
]

describe('Koala.identifyVisitor', () => {
test('it maps traits and passes them into ko.identify', async () => {
window.ko = {
ready: jest.fn(),
track: jest.fn().mockResolvedValueOnce(undefined),
identify: jest.fn().mockResolvedValueOnce(undefined)
}
window.KoalaSDK = {
load: jest.fn().mockResolvedValueOnce(window.ko)
}

const [event] = await KoalaDestination({
subscriptions,
project_slug: 'koala-test'
})

const ajs = new Analytics({ writeKey: 'w_123' })
await event.load(Context.system(), ajs)
jest.spyOn(destination.actions.identifyVisitor, 'perform')

await event.identify?.(
new Context({
type: 'identify',
traits: {
name: 'Matt'
}
})
)

expect(destination.actions.identifyVisitor.perform).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
payload: {
traits: {
name: 'Matt'
}
}
})
)
expect(window.ko.identify).toHaveBeenCalledWith(
expect.objectContaining({
name: 'Matt'
})
)
})
})

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { BrowserActionDefinition } from '../../../lib/browser-destinations'
import type { Settings } from '../generated-types'
import type { Payload } from './generated-types'
import type { Koala } from '../types'

const action: BrowserActionDefinition<Settings, Koala, Payload> = {
title: 'Identify Visitor',
description: 'Update visitor traits in Koala.',
defaultSubscription: 'type = "identify"',
platform: 'web',
fields: {
traits: {
type: 'object',
label: 'Traits',
description: 'Traits to associate with the visitor in Koala.',
required: true,
default: { '@path': '$.traits' },
defaultObjectUI: 'object'
}
},
perform: (koala, { payload }) => {
if (payload?.traits) {
return koala.identify(payload.traits)
}
}
}

export default action
66 changes: 66 additions & 0 deletions packages/browser-destinations/src/destinations/koala/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { Settings } from './generated-types'
import type { BrowserDestinationDefinition } from '../../lib/browser-destinations'
import type { KoalaSDK, Koala } from './types'
import { defaultValues } from '@segment/actions-core'
import { browserDestination } from '../../runtime/shim'
import { initScript } from './init-script'
import trackEvent from './trackEvent'
import identifyVisitor from './identifyVisitor'

declare global {
interface Window {
ko: Koala
KoalaSDK: KoalaSDK
}
}

export const destination: BrowserDestinationDefinition<Settings, Koala> = {
name: 'Koala',
slug: 'actions-koala',
description: 'Connect Koala in Segment to send visitor events or traits to Koala.',
mode: 'device',
settings: {
project_slug: {
type: 'string',
label: 'Koala Project Slug',
description: "Please enter your project's slug found in your Koala project settings.",
required: true
}
},

initialize: async ({ settings, analytics }, deps) => {
initScript()
await deps.loadScript(`https://cdn.koala.live/v1/${settings.project_slug}/umd.js`)

const ko = await window.KoalaSDK.load({
project: settings.project_slug,
hookSegment: false
})

void analytics.ready(() => ko.ready(() => ko.identify(analytics.user().traits() as Record<string, unknown>)))

return ko
},

actions: {
trackEvent,
identifyVisitor
},

presets: [
{
name: 'Track Event',
subscribe: 'type = "track"',
partnerAction: 'trackEvent',
mapping: defaultValues(trackEvent.fields)
},
{
name: 'Identify Visitor',
subscribe: 'type = "identify"',
partnerAction: 'identifyVisitor',
mapping: defaultValues(identifyVisitor.fields)
}
]
}

export default browserDestination(destination)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable */
// @ts-nocheck
export function initScript() {
if (window.ko) {
return
}

window.ko = []
;['identify', 'track', 'removeListeners', 'open', 'on', 'off', 'qualify', 'ready'].forEach(function (method) {
ko[method] = function () {
let args = Array.from(arguments)
args.unshift(method)
ko.push(args)
return ko
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { Subscription } from '../../../../lib/browser-destinations'
import { Analytics, Context } from '@segment/analytics-next'
import KoalaDestination, { destination } from '../../index'

import { loadScript } from '../../../../runtime/load-script'
jest.mock('../../../../runtime/load-script')
beforeEach(async () => {
;(loadScript as jest.Mock).mockResolvedValue(true)
})

const subscriptions: Subscription[] = [
{
partnerAction: 'trackEvent',
name: 'Track Event',
enabled: true,
subscribe: 'type = "track"',
mapping: {
event: {
'@path': '$.event'
},
properties: {
'@path': '$.properties'
}
}
}
]

describe('Koala.trackEvent', () => {
test('it maps the event name and properties and passes them into ko.track', async () => {
window.ko = {
ready: jest.fn(),
track: jest.fn().mockResolvedValueOnce(undefined),
identify: jest.fn().mockResolvedValueOnce(undefined)
}
window.KoalaSDK = {
load: jest.fn().mockResolvedValueOnce(window.ko)
}

const [event] = await KoalaDestination({
subscriptions,
project_slug: 'koala-test'
})

const ajs = new Analytics({ writeKey: 'w_123' })
await event.load(Context.system(), ajs)
jest.spyOn(destination.actions.trackEvent, 'perform')

await event.track?.(
new Context({
type: 'track',
event: 'Form Submitted',
properties: {
is_new_subscriber: true
}
})
)

expect(destination.actions.trackEvent.perform).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
payload: {
event: 'Form Submitted',
properties: {
is_new_subscriber: true
}
}
})
)
expect(window.ko.track).toHaveBeenCalledWith(
'Form Submitted',
expect.objectContaining({
is_new_subscriber: true
})
)
})
})

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { BrowserActionDefinition } from '../../../lib/browser-destinations'
import type { Settings } from '../generated-types'
import type { Payload } from './generated-types'
import type { Koala } from '../types'

const action: BrowserActionDefinition<Settings, Koala, Payload> = {
title: 'Track Event',
description: 'Send visitor events to Koala.',
defaultSubscription: 'type = "track"',
platform: 'web',
fields: {
event: {
type: 'string',
required: true,
description: 'The event name.',
label: 'Event Name',
default: { '@path': '$.event' }
},
properties: {
type: 'object',
required: false,
description: 'Properties to send with the event.',
label: 'Event Properties',
default: { '@path': '$.properties' },
defaultObjectUI: 'object'
}
},
perform: (koala, { payload }) => {
if (payload?.event) {
return koala.track(payload.event, payload.properties)
}
}
}

export default action
9 changes: 9 additions & 0 deletions packages/browser-destinations/src/destinations/koala/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface Koala {
ready: (fn?: () => Promise<unknown> | unknown) => Promise<void>
track: (event: string, data?: { [key: string]: unknown }) => Promise<void>
identify: (traits: Record<string, unknown>) => Promise<void>
}

export interface KoalaSDK {
load: (options: { project: string; hookSegment?: boolean }) => Promise<Koala>
}
Loading

0 comments on commit d391a8e

Please sign in to comment.