Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New APIs to destination actions and code improvements #2626

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f3f55c5
Saving progress
wtnelso Nov 6, 2024
457ecfa
Update packages/destination-actions/src/destinations/attentive/index.ts
wtnelso Nov 12, 2024
c4a3cdb
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
2796e25
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
12dd973
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
63abf12
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
dd36acc
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
c49dda1
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
72ef7a5
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
4d872e0
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
8b0a24b
Update packages/destination-actions/src/destinations/attentive/custom…
wtnelso Nov 12, 2024
72e079e
Update index.ts
wtnelso Nov 12, 2024
2bf43f9
refacting the fields and request
joe-ayoub-segment Nov 13, 2024
90c6af7
correcting yarn version
joe-ayoub-segment Nov 13, 2024
0e91cb5
minor tweaks
joe-ayoub-segment Nov 13, 2024
ffbfb9b
minor tweaks
joe-ayoub-segment Nov 13, 2024
65eb733
adding types
joe-ayoub-segment Nov 13, 2024
20b8cbe
Update index.test.ts
wtnelso Nov 14, 2024
cc05c0a
Update index.test.ts
wtnelso Nov 14, 2024
76c82d8
Update index.test.ts
wtnelso Nov 14, 2024
48a985f
Update index.test.ts
wtnelso Nov 14, 2024
8faa1d0
adding types
joe-ayoub-segment Nov 15, 2024
4a82258
New endpoints
wtnelso Nov 15, 2024
cbf2af3
added of endpoints
wtnelso Nov 15, 2024
a06e672
resolved conflicts
wtnelso Nov 15, 2024
5eb242d
bug fixes
wtnelso Nov 15, 2024
5aa5d3d
config file added
wtnelso Nov 15, 2024
fd3af7d
index updates
wtnelso Nov 15, 2024
a16c78f
updating snapshot and fixing a field type issue
joe-ayoub-segment Nov 18, 2024
056e5bc
adding a unit test
joe-ayoub-segment Nov 19, 2024
9fce8e1
new unit test
wtnelso Nov 19, 2024
7428f7b
fixing breaking test
joe-ayoub-segment Nov 20, 2024
461a8cf
New endpoints and code improvements
wtnelso Dec 4, 2024
a84383d
Merge branch 'main' into attentive
wtnelso Dec 4, 2024
3393d70
Requsted updates
wtnelso Dec 9, 2024
9a6b8e2
updates
wtnelso Dec 9, 2024
a2f8a8d
Merge branch 'main' into attentive
wtnelso Dec 12, 2024
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
1 change: 1 addition & 0 deletions action-destinations
Submodule action-destinations added at bb253f
1 change: 1 addition & 0 deletions packages/destination-actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"ajv-formats": "^2.1.1",
"aws4": "^1.12.0",
"cheerio": "^1.0.0-rc.10",
"cloud": "^2.0.2",
"dayjs": "^1.10.7",
"escape-goat": "^3",
"google-libphonenumber": "^3.2.31",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import nock from 'nock'
import { createTestEvent, createTestIntegration, SegmentEvent, PayloadValidationError } from '@segment/actions-core'
import Definition from '../../index'
import { Settings } from '../../generated-types'
import { API_URL, API_VERSION } from '../config'
import { getECommEventTestValidPayload, getECommEventTestExpectedPayload } from '../test-data'

let testDestination = createTestIntegration(Definition)
const timestamp = '2024-01-08T13:52:50.212Z'
const endpoint = '/events/ecommerce/add-to-cart'

const settings: Settings = {
apiKey: 'test-api-key'
}

// Valid payload for testing
const validPayload = getECommEventTestValidPayload(timestamp) as Partial<SegmentEvent>

// Expected payload for Attentive API
const expectedPayload = getECommEventTestExpectedPayload(validPayload)

beforeEach(() => {
testDestination = createTestIntegration(Definition)
nock.cleanAll()
})

describe('Attentive.addToCart', () => {
it('should send an add to cart event to Attentive', async () => {
const event = createTestEvent(validPayload)

nock(`${API_URL}${API_VERSION}`).post(endpoint, expectedPayload).reply(200, {})

const responses = await testDestination.testAction('addToCart', {
event,
settings,
useDefaultMappings: true
})

expect(responses.length).toBe(1)
expect(responses[0].status).toBe(200)
})

it('should throw error if no identifiers are provided', async () => {
const badPayload = {
...validPayload,
context: { traits: {} }, // Remove identifiers
userId: undefined
}

const event = createTestEvent(badPayload)

await expect(
testDestination.testAction('addToCart', {
event,
settings,
useDefaultMappings: true
})
).rejects.toThrowError(new PayloadValidationError('At least one user identifier is required.'))
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { createTestEvent, createTestIntegration } from '@segment/actions-core'
import { generateTestData } from '../../../../lib/test-data'
import destination from '../../index'
import nock from 'nock'

const testDestination = createTestIntegration(destination)
const actionSlug = 'addToCart'
const destinationSlug = 'Attentive'
const seedName = `${destinationSlug}#${actionSlug}`

describe(`Testing snapshot for ${destinationSlug}'s ${actionSlug} destination action:`, () => {
it('required fields', async () => {
const action = destination.actions[actionSlug]
const [eventData, settingsData] = generateTestData(seedName, destination, action, true)

nock(/.*/).persist().get(/.*/).reply(200)
nock(/.*/).persist().post(/.*/).reply(200)
nock(/.*/).persist().put(/.*/).reply(200)

const event = createTestEvent({
properties: eventData
})

const responses = await testDestination.testAction(actionSlug, {
event: event,
mapping: event.properties,
settings: settingsData,
auth: undefined
})

const request = responses[0].request
const rawBody = await request.text()

try {
const json = JSON.parse(rawBody)
expect(json).toMatchSnapshot()
return
} catch (err) {
expect(rawBody).toMatchSnapshot()
}

expect(request.headers).toMatchSnapshot()
})

it('all fields', async () => {
const action = destination.actions[actionSlug]
const [eventData, settingsData] = generateTestData(seedName, destination, action, false)

nock(/.*/).persist().get(/.*/).reply(200)
nock(/.*/).persist().post(/.*/).reply(200)
nock(/.*/).persist().put(/.*/).reply(200)

const event = createTestEvent({
properties: eventData
})

const responses = await testDestination.testAction(actionSlug, {
event: event,
mapping: event.properties,
settings: settingsData,
auth: undefined
})

const request = responses[0].request
const rawBody = await request.text()

try {
const json = JSON.parse(rawBody)
expect(json).toMatchSnapshot()
return
} catch (err) {
expect(rawBody).toMatchSnapshot()
}
})
})

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,40 @@
import type { ActionDefinition } from '@segment/actions-core'
import type { Settings } from '../generated-types'
import type { Payload } from './generated-types'
import { items, occurredAt, userIdentifiers } from '../properties'
import { API_URL, API_VERSION } from '../config'
import { formatProductItemsArray, buildECommEventObject } from '../functions'

const action: ActionDefinition<Settings, Payload> = {
title: 'Add to Cart',
description: 'Send Segment purchase events to Attentive.',
defaultSubscription: 'type = "track" and event = "Product Added"',
fields: {
items: { ...items, description: 'List of items added to cart.' },
userIdentifiers: { ...userIdentifiers },
occurredAt: { ...occurredAt }
},
perform: (request, { payload }) => {
const {
items: rawProductItemsArray,
occurredAt,
userIdentifiers: { phone, email, clientUserId, ...customIdentifiers }
} = payload

const items = formatProductItemsArray(rawProductItemsArray)

// Validate that at least one user identifier is provided
if (!email && !phone && !clientUserId && Object.keys(customIdentifiers).length === 0) {
throw new PayloadValidationError('At least one user identifier is required.')
}

const json = buildECommEventObject(items, occurredAt, phone, email, clientUserId, customIdentifiers)

return request(`${API_URL}${API_VERSION}/events/ecommerce/add-to-cart`, {
method: 'POST',
json
})
}
}

export default action
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const API_URL = 'https://api.attentivemobile.com'
export const API_VERSION = '/v1'
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Testing snapshot for Attentive's customEvents destination action: all fields 1`] = `
Object {
"externalEventId": "808f3a12-c154-500d-86ba-36b96b797f92",
"occurredAt": "*8ANaCHkFXR*[r23fd",
"properties": Object {
"testType": "*8ANaCHkFXR*[r23fd",
},
"type": "*8ANaCHkFXR*[r23fd",
"user": Object {
"email": "zat@kulihesa.dm",
"externalIdentifiers": Object {
"clientUserId": "808f3a12-c154-500d-86ba-36b96b797f92",
},
"phone": "*8ANaCHkFXR*[r23fd",
},
}
`;

exports[`Testing snapshot for Attentive's customEvents destination action: required fields 1`] = `
Object {
"type": "*8ANaCHkFXR*[r23fd",
"user": Object {
"externalIdentifiers": Object {},
},
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,29 @@ import { createTestEvent, createTestIntegration, SegmentEvent, PayloadValidation
import Definition from '../../index'
import { Settings } from '../../generated-types'

import { API_URL, API_VERSION } from '../config'
import {
getCustomEventsTestValidPayload,
getCustomEventsTestMapping,
getCustomEventsTestExpectedPayload
} from '../test-data'

let testDestination = createTestIntegration(Definition)
const timestamp = '2024-01-08T13:52:50.212Z'
const endpoint = '/events/custom'

const settings: Settings = {
apiKey: 'test-api-key'
}

const validPayload = {
timestamp: timestamp,
event: 'Event Type 1',
messageId: '123e4567-e89b-12d3-a456-426614174000',
type: 'track',
userId: '123e4567-e89b-12d3-a456-426614174000',
context: {
traits: {
phone: '+3538675765689',
email: 'test@test.com'
}
},
properties: {
tracking_url: 'https://tracking-url.com',
product_name: 'Product X'
}
} as Partial<SegmentEvent>
// Valid payload for testing
const validPayload = getCustomEventsTestValidPayload(timestamp) as Partial<SegmentEvent>

const mapping = {
type: { '@path': '$.event' },
userIdentifiers: {
phone: { '@path': '$.context.traits.phone' },
email: { '@path': '$.context.traits.email' },
clientUserId: { '@path': '$.userId' }
},
properties: { '@path': '$.properties' },
externalEventId: { '@path': '$.messageId' },
occurredAt: { '@path': '$.timestamp' }
}
// Mapping configuration for test transformation
const mapping = getCustomEventsTestMapping()

const expectedPayload = {
type: 'Event Type 1',
properties: {
tracking_url: 'https://tracking-url.com',
product_name: 'Product X'
},
externalEventId: '123e4567-e89b-12d3-a456-426614174000',
occurredAt: '2024-01-08T13:52:50.212Z',
user: {
phone: '+3538675765689',
email: 'test@test.com',
externalIdentifiers: {
clientUserId: '123e4567-e89b-12d3-a456-426614174000'
}
}
}
// Expected payload for Attentive API
const expectedPayload = getCustomEventsTestExpectedPayload(validPayload)

beforeEach((done) => {
testDestination = createTestIntegration(Definition)
Expand All @@ -67,7 +37,7 @@ describe('Attentive.customEvents', () => {
it('should send a custom event to Attentive', async () => {
const event = createTestEvent(validPayload)

nock('https://api.attentivemobile.com').post('/v1/events/custom', expectedPayload).reply(200, {})
nock(`${API_URL}${API_VERSION}`).post(endpoint, expectedPayload).reply(200, {})

const responses = await testDestination.testAction('customEvents', {
event,
Expand All @@ -80,14 +50,12 @@ describe('Attentive.customEvents', () => {
expect(responses[0].status).toBe(200)
})

it('should throw error if no identifiers provided', async () => {
it('should throw error if no identifiers are provided', async () => {
const badPayload = {
...validPayload
...validPayload,
context: { traits: {} }, // Remove identifiers
userId: undefined
}
delete badPayload?.context?.traits?.phone
delete badPayload?.context?.traits?.email
badPayload.userId = undefined

const event = createTestEvent(badPayload)

await expect(
Expand Down
Loading
Loading