diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts index 15203f2dc5..6e74fad141 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/_tests_/multistatus.test.ts @@ -6,7 +6,6 @@ import { Settings } from '../generated-types' beforeEach(() => nock.cleanAll()) const testDestination = createTestIntegration(sfmc) -// const timestamp = '2022-05-12T15:21:15.449Z' const settings: Settings = { subdomain: 'test123', client_id: 'test123', @@ -62,7 +61,168 @@ describe('Multistatus', () => { mapping }) - console.log(response) + expect(response[0]).toMatchObject({ + status: 200, + body: {} + }) + + expect(response[1]).toMatchObject({ + status: 200, + body: {} + }) + }) + it('should handle the case where both key and id are missing', async () => { + const events: SegmentEvent[] = [ + createTestEvent({ + type: 'track', + userId: 'harry-1', + properties: { + keys: { + id: 'HS1' + }, + values: { + name: 'Harry Styles' + } + } + }), + createTestEvent({ + type: 'track', + userId: 'harry-2', + properties: { + keys: { + id: 'HP1' + }, + values: { + name: 'Harry Potter' + } + } + }) // No key and id provided + ] + + const response = await testDestination.executeBatch('dataExtension', { + events, + settings, + mapping + }) + + expect(response[0]).toMatchObject({ + status: 400, + errortype: 'PAYLOAD_VALIDATION_FAILED', + errormessage: + 'In order to send an event to a data extension either Data Extension ID or Data Extension Key must be defined.', + errorreporter: 'INTEGRATIONS' + }) + + expect(response[1]).toMatchObject({ + status: 400, + errortype: 'PAYLOAD_VALIDATION_FAILED', + errormessage: + 'In order to send an event to a data extension either Data Extension ID or Data Extension Key must be defined.', + errorreporter: 'INTEGRATIONS' + }) + }) + it('should handle multistatus errors when some events fail with specific error messages', async () => { + const errorResponse = { + status: 400, + message: 'Invalid keys for ID: HS1', + additionalErrors: [ + { + message: 'No record found for ID: HS2' + } + ] + } + + nock(requestUrl).post('').reply(400, errorResponse) + + const events: SegmentEvent[] = [ + createTestEvent({ + type: 'track', + userId: 'harry-1', + properties: { + id: '1234567890', + keys: { + id: 'HS1' // Valid key + }, + values: { + name: 'Harry Styles' + } + } + }), + createTestEvent({ + type: 'track', + userId: 'harry-2', + properties: { + id: '1234567890', + keys: { + id: 'HS2' // Invalid key + }, + values: { + name: 'Harry Potter' + } + } + }) + ] + + const response = await testDestination.executeBatch('dataExtension', { + events, + settings, + mapping + }) + + expect(response[0]).toMatchObject({ + status: 400, + errortype: 'BAD_REQUEST', + errormessage: 'No record found for ID: HS2', + errorreporter: 'DESTINATION' + }) + + expect(response[1]).toMatchObject({ + status: 400, + errortype: 'BAD_REQUEST', + errormessage: 'No record found for ID: HS2', + errorreporter: 'DESTINATION' + }) + }) + }) + + describe('contactDataExtension', () => { + it('should successfully handle a batch of events with complete success response from SFMC API', async () => { + nock(requestUrl).post('').reply(200, {}) + + const events: SegmentEvent[] = [ + createTestEvent({ + type: 'track', + properties: { + id: '1234567890', + keys: { + contactKey: 'harry-1', + id: 'HS1' + }, + values: { + name: 'Harry Styles' + } + } + }), + createTestEvent({ + type: 'track', + properties: { + id: '1234567890', + keys: { + contactKey: 'harry-2', + id: 'HP1' + }, + values: { + name: 'Harry Potter' + } + } + }) + ] + + const response = await testDestination.executeBatch('contactDataExtension', { + events, + settings, + mapping + }) expect(response[0]).toMatchObject({ status: 200, @@ -74,5 +234,123 @@ describe('Multistatus', () => { body: {} }) }) + + it('should handle the case where both key and id are missing', async () => { + const events: SegmentEvent[] = [ + createTestEvent({ + type: 'track', + userId: 'harry-1', + properties: { + keys: { + contactKey: 'harry-1', + id: 'HS1' + }, + values: { + name: 'Harry Styles' + } + } + }), + createTestEvent({ + type: 'track', + userId: 'harry-2', + properties: { + keys: { + contactKey: 'harry-2', + id: 'HP1' + }, + values: { + name: 'Harry Potter' + } + } + }) // No key and id provided + ] + + const response = await testDestination.executeBatch('contactDataExtension', { + events, + settings, + mapping + }) + + expect(response[0]).toMatchObject({ + status: 400, + errortype: 'PAYLOAD_VALIDATION_FAILED', + errormessage: + 'In order to send an event to a data extension either Data Extension ID or Data Extension Key must be defined.', + errorreporter: 'INTEGRATIONS' + }) + + expect(response[1]).toMatchObject({ + status: 400, + errortype: 'PAYLOAD_VALIDATION_FAILED', + errormessage: + 'In order to send an event to a data extension either Data Extension ID or Data Extension Key must be defined.', + errorreporter: 'INTEGRATIONS' + }) + }) + + it('should handle multistatus errors when some events fail with specific error messages', async () => { + const errorResponse = { + status: 400, + message: 'Invalid keys for ID: HS1', + additionalErrors: [ + { + message: 'No record found for ID: HS2' + } + ] + } + + nock(requestUrl).post('').reply(400, errorResponse) + + const events: SegmentEvent[] = [ + createTestEvent({ + type: 'track', + userId: 'harry-1', + properties: { + id: '1234567890', + keys: { + contactKey: 'harry-1', + id: 'HS1' + }, + values: { + name: 'Harry Styles' + } + } + }), + createTestEvent({ + type: 'track', + userId: 'harry-2', + properties: { + id: '1234567890', + keys: { + contactKey: 'harry-2', + id: 'HS2' + }, + values: { + name: 'Harry Potter' + } + } + }) + ] + + const response = await testDestination.executeBatch('contactDataExtension', { + events, + settings, + mapping + }) + + expect(response[0]).toMatchObject({ + status: 400, + errortype: 'BAD_REQUEST', + errormessage: 'No record found for ID: HS2', + errorreporter: 'DESTINATION' + }) + + expect(response[1]).toMatchObject({ + status: 400, + errortype: 'BAD_REQUEST', + errormessage: 'No record found for ID: HS2', + errorreporter: 'DESTINATION' + }) + }) }) }) diff --git a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts index 1f2e1ad38e..7df358f646 100644 --- a/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts +++ b/packages/destination-actions/src/destinations/salesforce-marketing-cloud/sfmc-operations.ts @@ -1,4 +1,10 @@ -import { RequestClient, MultiStatusResponse, JSONLikeObject, ModifiedResponse } from '@segment/actions-core' +import { + RequestClient, + MultiStatusResponse, + JSONLikeObject, + ModifiedResponse, + IntegrationError +} from '@segment/actions-core' import { Payload as payload_dataExtension } from './dataExtension/generated-types' import { Payload as payload_contactDataExtension } from './contactDataExtension/generated-types' import { ErrorResponse } from './types' @@ -13,6 +19,13 @@ export async function upsertRows( let response: ModifiedResponse | undefined const { key, id } = payloads[0] if (!key && !id) { + if (payloads.length === 1) { + throw new IntegrationError( + `In order to send an event to a data extension either Data Extension ID or Data Extension Key must be defined.`, + 'Misconfigured required field', + 400 + ) + } payloads.forEach((_, index) => { multiStatusResponse.setErrorResponseAtIndex(index, { status: 400, @@ -49,7 +62,7 @@ export async function upsertRows( }) } catch (error) { const err = error as ErrorResponse - if (err?.response?.data?.message === 'Not Authorized') { + if (err?.response?.data?.message === 'Not Authorized' || payloads.length === 1) { throw error } @@ -62,7 +75,6 @@ export async function upsertRows( }) }) } - console.log('MSR => ', multiStatusResponse) if (payloads.length > 1) { return multiStatusResponse