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

[Klaviyo Actions] | Added MultiStatus Batch Support on TrackEvent #2498

Merged
merged 23 commits into from
Dec 10, 2024
Merged
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
Expand Up @@ -130,7 +130,7 @@ Object {
"data": Object {
"attributes": Object {
"anonymous_id": "mTdOx(Nl)",
"email": "[email protected]",
"email": "[email protected]",
"external_id": "mTdOx(Nl)",
"phone_number": "+5694788449",
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
import { SegmentEvent, createTestEvent, createTestIntegration } from '@segment/actions-core'
import nock from 'nock'
import { API_URL } from '../config'
import Klaviyo from '../index'

beforeEach(() => nock.cleanAll())

const testDestination = createTestIntegration(Klaviyo)

const settings = {
api_key: 'my-api-key'
}

const timestamp = '2024-07-22T20:08:49.7931Z'

describe('MultiStatus', () => {
describe('trackEvent', () => {
const mapping = {
profile: {
'@path': '$.properties'
},
metric_name: {
'@path': '$.event'
},
properties: {
'@path': '$.properties'
},
time: {
'@path': '$.timestamp'
},
unique_id: {
'@path': '$.messageId'
}
}

it("should successfully handle those payload where phone_number is invalid and couldn't be converted to E164 format", async () => {
nock(API_URL).post('/event-bulk-create-jobs/').reply(202, {})

const events: SegmentEvent[] = [
// Event with invalid phone_number
createTestEvent({
type: 'track',
timestamp,
properties: {
country_code: 'IN',
phone_number: '701271',
email: '[email protected]'
}
}),
// Valid Event
createTestEvent({
type: 'track',
timestamp,
properties: {
email: '[email protected]'
}
})
]

const response = await testDestination.executeBatch('trackEvent', {
events,
settings,
mapping
})

// The First event fails as pre-request validation fails for having invalid phone_number and could not be converted to E164 format
expect(response[0]).toMatchObject({
status: 400,
errortype: 'PAYLOAD_VALIDATION_FAILED',
errormessage: 'Phone number could not be converted to E.164 format.',
errorreporter: 'INTEGRATIONS'
})

// The Second event doesn't fail as there is no error reported by Klaviyo API
expect(response[1]).toMatchObject({
status: 200,
body: '{}'
})
})

it('should successfully handle a batch of events with complete success response from Klaviyo API', async () => {
nock(API_URL).post('/event-bulk-create-jobs/').reply(202, {})

const events: SegmentEvent[] = [
// Valid Event
createTestEvent({
type: 'track',
timestamp,
properties: {
email: '[email protected]'
}
}),
// Event without any user identifier
createTestEvent({
type: 'track',
timestamp
}),
//Event with invalid Email
createTestEvent({
type: 'track',
timestamp,
properties: {
email: '[email protected]',
list_id: '123'
}
})
]

const response = await testDestination.executeBatch('trackEvent', {
events,
settings,
mapping
})

// The first event doesn't fail as there is no error reported by Klaviyo API
expect(response[0]).toMatchObject({
status: 200,
body: '{}'
})

// The second event fails as pre-request validation fails for not having any user identifier
expect(response[1]).toMatchObject({
status: 400,
errortype: 'PAYLOAD_VALIDATION_FAILED',
errormessage: 'One of External ID, Anonymous ID, Phone Number or Email is required.',
errorreporter: 'INTEGRATIONS'
})
// The third event fails as pre-request validation fails for having invalid email
expect(response[2]).toMatchObject({
status: 400,
errortype: 'PAYLOAD_VALIDATION_FAILED',
errormessage: 'Email must be a valid email address string but it was not.',
errorreporter: 'INTEGRATIONS'
})
})

it('should successfully handle a batch of events with failure response from Klaviyo API', async () => {
// Mocking a 400 response from Klaviyo API
const mockResponse = {
errors: [
{
id: '752f7ece-af20-44e0-aa3a-b13290d98e72',
status: 400,
code: 'invalid',
title: 'Invalid input.',
detail: 'Invalid input',
source: {
pointer: '/data/attributes/events-bulk-create/data/0/attributes/email'
},
links: {},
meta: {}
}
]
}
nock(API_URL).post('/event-bulk-create-jobs/').reply(400, mockResponse)

const events: SegmentEvent[] = [
// Invalid Event
createTestEvent({
type: 'track',
timestamp,
properties: {
email: '[email protected]'
}
}),
// Valid Event
createTestEvent({
type: 'track',
timestamp,
properties: {
external_id: 'Xi1234'
}
})
]

const response = await testDestination.executeBatch('trackEvent', {
events,
settings,
mapping
})

// The first doesn't fail as there is no error reported by Klaviyo API
expect(response[0]).toMatchObject({
status: 400,
errortype: 'BAD_REQUEST',
errormessage: 'Invalid input',
sent: {
profile: {
email: '[email protected]'
},
metric_name: 'Test Event',
properties: {
email: '[email protected]'
},
time: timestamp
},
body: '{"id":"752f7ece-af20-44e0-aa3a-b13290d98e72","status":400,"code":"invalid","title":"Invalid input.","detail":"Invalid input","source":{"pointer":"/data/attributes/events-bulk-create/data/0/attributes/email"},"links":{},"meta":{}}'
})

// The second event fails as Klaviyo API reports an error
expect(response[1]).toMatchObject({
status: 429,
sent: {
profile: {
external_id: 'Xi1234'
},
metric_name: 'Test Event',
properties: {
external_id: 'Xi1234'
},
time: timestamp
},
body: 'Retry'
})
})

it('should successfully handle a batch when all payload is invalid', async () => {
const events: SegmentEvent[] = [
// Event with invalid phone_number
createTestEvent({
type: 'track',
timestamp,
properties: {
country_code: 'IN',
phone_number: '701271',
email: '[email protected]'
}
}),
// Event without any user identifier
createTestEvent({
type: 'track',
timestamp,
properties: {}
})
]

const response = await testDestination.executeBatch('trackEvent', {
events,
settings,
mapping
})

// The First event fails as pre-request validation fails for having invalid phone_number and could not be converted to E164 format
expect(response[0]).toMatchObject({
status: 400,
errortype: 'PAYLOAD_VALIDATION_FAILED',
errormessage: 'Phone number could not be converted to E.164 format.',
errorreporter: 'INTEGRATIONS'
})

// The second event fails as pre-request validation fails for not having any user identifier
expect(response[1]).toMatchObject({
status: 400,
errortype: 'PAYLOAD_VALIDATION_FAILED',
errormessage: 'One of External ID, Anonymous ID, Phone Number or Email is required.',
errorreporter: 'INTEGRATIONS'
})
})
})
})
Loading
Loading