@rudderstack/integrations-lib
Version:
751 lines • 117 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = __importDefault(require("axios"));
const axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
const _1 = __importDefault(require("."));
const utils = __importStar(require("./utils"));
describe('sfmc', () => {
describe('getAccessToken', () => {
it('should return existing authObject when token is not expired', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
sfmcClient.authObject = validAuthObject;
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const result = await sfmcClient.getAccessToken();
expect(result).toBe(validAuthObject);
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
});
// Handles case when authObject is initially empty or null
it('should request new token when authObject is empty', async () => {
const emptyAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
};
const newAuthObject = {
...emptyAuthObject,
access_token: 'new-token',
expires_in: 12345,
};
const sfmcClient = new _1.default(emptyAuthObject);
sfmcClient.authObject = emptyAuthObject;
jest.spyOn(utils, 'isExpired').mockReturnValue(true);
// jest.spyOn(axios, 'getToken').mockResolvedValue(newAuthObject);
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.auth.marketingcloudapis.com/v2/token')
.reply(200, newAuthObject);
jest.spyOn(process, 'hrtime').mockImplementation(() => [0, 0]);
const result = await sfmcClient.getAccessToken();
expect(result).toEqual({ ...newAuthObject, expiration: 12345 });
expect(utils.isExpired).toHaveBeenCalledWith(emptyAuthObject);
expect(utils.getToken).toHaveBeenCalledWith(emptyAuthObject);
});
it('should throw error when authObject has invalid clientId', () => {
const invalidAuthObject = {
clientId: null,
clientSecret: 'test-secret',
subDomain: 'test',
};
try {
const sfmcInstance = new _1.default(invalidAuthObject);
sfmcInstance.getAccessToken();
}
catch (e) {
expect(e).toEqual(new Error('clientId is missing or invalid'));
}
});
it('should handle concurrent token refresh requests correctly', async () => {
const emptyAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
};
const newAuthObject = {
...emptyAuthObject,
access_token: 'new-token',
expires_in: 12345,
};
const sfmcClient = new _1.default(emptyAuthObject);
sfmcClient.authObject = emptyAuthObject;
jest.spyOn(utils, 'isExpired').mockReturnValue(true);
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.auth.marketingcloudapis.com/v2/token')
.reply(200, newAuthObject);
jest.spyOn(process, 'hrtime').mockImplementation(() => [0, 0]);
let result = await sfmcClient.getAccessToken();
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
result = await sfmcClient.getAccessToken();
expect(result).toEqual({ ...newAuthObject, expiration: 12345 });
expect(utils.isExpired).toHaveBeenCalledWith(emptyAuthObject);
expect(utils.isExpired).toHaveBeenCalledWith(emptyAuthObject);
});
it('should throw error with original message when token request fails', async () => {
const emptyAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
};
const sfmcClient = new _1.default(emptyAuthObject);
sfmcClient.authObject = emptyAuthObject;
jest.spyOn(utils, 'isExpired').mockReturnValue(true);
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.auth.marketingcloudapis.com/v2/token')
.reply(400, new Error('Bad Request'));
try {
await sfmcClient.getAccessToken();
}
catch (e) {
expect(e).toEqual(new Error('Unable to generate token, with error:Error: Request failed with status code 400'));
}
});
});
describe('getDestinationFields', () => {
it('should return data extension fields', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
const validResponse = {
name: 'Test DE',
categoryID: '12345',
fields: [
{
name: 'field1',
type: 'Text',
},
],
};
mockAdapter
.onGet('https://abcdefghijklmnopqrstuvwxyz01.rest.marketingcloudapis.com/data/v1/customobjects/test-de-id/fields')
.reply(200, validResponse);
const result = await sfmcClient.getDestinationFields('test-de-id');
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
expect(result).toStrictEqual(validResponse);
});
it('should handle failure scenario', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onGet('https://abcdefghijklmnopqrstuvwxyz01.rest.marketingcloudapis.com/data/v1/customobjects/test-de-id/fields')
.reply(401, {
documentation: 'https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/error-handling.htm',
errorcode: 0,
message: 'Not Authorized',
});
try {
await sfmcClient.getDestinationFields('test-de-id');
}
catch (e) {
expect(e).toEqual(new Error('Not Authorized'));
}
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
});
});
describe('getContactAttributes', () => {
it('should return contact attributes', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
const validResponse = {
data: {
items: [
{
isReadOnly: true,
attributes: [
{
isReadOnly: true,
fullyQualifiedName: 'test.name',
dataType: 'string',
},
],
},
],
},
};
mockAdapter
.onGet('https://abcdefghijklmnopqrstuvwxyz01.rest.marketingcloudapis.com/contacts/v1/attributeSetDefinitions')
.reply(200, validResponse);
const result = await sfmcClient.getContactAttributes();
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
expect(result).toStrictEqual(validResponse);
});
it('should handle failure scenario', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onGet('https://abcdefghijklmnopqrstuvwxyz01.rest.marketingcloudapis.com/contacts/v1/attributeSetDefinitions')
.reply(401, {
documentation: 'https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/error-handling.htm',
errorcode: 0,
message: 'Not Authorized',
});
try {
await sfmcClient.getContactAttributes();
}
catch (e) {
expect(e).toEqual(new Error('Not Authorized'));
}
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
});
});
describe('createDataExtension', () => {
it('should create new data extension', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
const validResponse = {
id: '4baf4cf1-8cc9-ef11-ba6e-f40343d380c8',
name: 'rudderstackTesting0295',
key: 'rudderstackTesting0295',
isActive: true,
isSendable: false,
isTestable: false,
status: 0,
categoryId: 12345,
ownerId: 735444531,
isObjectDeletable: true,
isFieldAdditionAllowed: true,
isFieldModificationAllowed: true,
createdDate: '2025-01-02T22:41:12.5018415-06:00',
createdById: 735444531,
modifiedDate: '2025-01-02T22:41:12.5018415-06:00',
modifiedById: 735444531,
ownerName: 'RudderstackIntegration app user',
rowCount: 0,
dataRetentionProperties: {
isDeleteAtEndOfRetentionPeriod: true,
isRowBasedRetention: false,
isResetRetentionPeriodOnImport: false,
rowBasedThreshold: 1000000000,
},
fields: [
{
name: 'key1',
id: '300ba233-b7e0-4732-ba85-1bc01208fc1b',
type: 'Text',
maskType: 'None',
storageType: 'Plain',
length: 254,
ordinal: 0,
isNullable: false,
isPrimaryKey: true,
isTemplateField: false,
isInheritable: false,
isOverridable: true,
isHidden: false,
isReadOnly: false,
mustOverride: false,
},
{
name: 'key2',
id: '75a38f98-e865-43ce-9702-5b317ba29d92',
type: 'EmailAddress',
maskType: 'None',
storageType: 'Plain',
length: 254,
ordinal: 0,
isNullable: true,
isPrimaryKey: false,
isTemplateField: false,
isInheritable: false,
isOverridable: true,
isHidden: false,
isReadOnly: false,
mustOverride: false,
},
],
fieldCount: 2,
};
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.rest.marketingcloudapis.com/data/v1/customobjects')
.reply(200, validResponse);
const result = await sfmcClient.createDataExtension({
name: 'test-de',
categoryID: '12345',
dataRetentionProperties: {
isDeleteAtEndOfRetentionPeriod: true,
isRowBasedRetention: false,
isResetRetentionPeriodOnImport: false,
},
fields: [
{
name: 'key1',
type: 'Text',
length: 254,
isNullable: false,
isPrimaryKey: true,
isHidden: false,
isReadOnly: false,
isOverridable: true,
isTemplateField: false,
isInheritable: false,
mustOverride: false,
ordinal: 0,
},
{
name: 'key2',
type: 'EmailAddress',
length: 254,
isNullable: true,
isPrimaryKey: false,
isHidden: false,
isReadOnly: false,
isOverridable: true,
isTemplateField: false,
isInheritable: false,
mustOverride: false,
ordinal: 0,
},
],
});
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
expect(result).toStrictEqual(validResponse);
});
it('should handle failure scenario', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.rest.marketingcloudapis.com/data/v1/customobjects')
.reply(401, {
documentation: 'https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/error-handling.htm',
errorcode: 0,
message: 'Not Authorized',
});
try {
await sfmcClient.createDataExtension({
name: 'test-de',
categoryID: '12345',
fields: [
{
name: 'key1',
type: 'Text',
length: 254,
isNullable: false,
isPrimaryKey: true,
isHidden: false,
isReadOnly: false,
isOverridable: true,
isTemplateField: false,
isInheritable: false,
mustOverride: false,
ordinal: 0,
},
],
});
}
catch (e) {
expect(e).toEqual(new Error('Not Authorized'));
}
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
});
});
describe('getBusinessUnits', () => {
it('should get all the business units', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
const validResponse = `<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><soap:Header><wsa:Action>RetrieveResponse</wsa:Action><wsa:MessageID>urn:uuid:0bed602b-63c4-4bb4-9582-2b27c8f2b1da</wsa:MessageID><wsa:RelatesTo>urn:uuid:45dcc251-a037-4172-99ce-666a6b3b872c</wsa:RelatesTo><wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To><wsse:Security><wsu:Timestamp wsu:Id="Timestamp-d0308ae2-4adf-4578-8d85-faa720a966ce"><wsu:Created>2025-01-09T05:50:10Z</wsu:Created><wsu:Expires>2025-01-09T05:55:10Z</wsu:Expires></wsu:Timestamp></wsse:Security></soap:Header><soap:Body><RetrieveResponseMsg xmlns="http://exacttarget.com/wsdl/partnerAPI"><OverallStatus>OK</OverallStatus><RequestID>ab947caa-34bd-4e5b-84c1-54ab1e633a6f</RequestID><Results xsi:type="BusinessUnit"><PartnerKey xsi:nil="true" /><PartnerProperties><Name>id</Name><Value>534003210</Value></PartnerProperties><PartnerProperties><Name>name</Name><Value>Lansdowne Stadium LP</Value></PartnerProperties><ObjectID xsi:nil="true" /><AccountType>None</AccountType><Subscription xsi:nil="true" /></Results><Results xsi:type="BusinessUnit"><PartnerKey xsi:nil="true" /><PartnerProperties><Name>id</Name><Value>534003210</Value></PartnerProperties><PartnerProperties><Name>name</Name><Value>Lansdowne Stadium LP</Value></PartnerProperties><ObjectID xsi:nil="true" /><AccountType>None</AccountType><Subscription xsi:nil="true" /></Results></RetrieveResponseMsg></soap:Body></soap:Envelope>`;
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.soap.marketingcloudapis.com/Service.asmx')
.reply(200, validResponse);
const result = await sfmcClient.getBusinessUnits();
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
expect(result).toStrictEqual([
{ Name: 'Lansdowne Stadium LP', ID: 534003210 },
{ Name: 'Lansdowne Stadium LP', ID: 534003210 },
]);
});
it('should handle failure scenario', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.soap.marketingcloudapis.com/Service.asmx')
.reply(500, `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<wsa:Action>http://schemas.xmlsoap.org/ws/2004/08/addressing/fault</wsa:Action>
<wsa:MessageID>urn:uuid:15d3e286-97f5-4f5c-b0fa-d58673621568</wsa:MessageID>
<wsa:RelatesTo>urn:uuid:b38ee85b-8f6b-4edc-9ab9-23ab6e5a51e3</wsa:RelatesTo>
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsse:Security>
<wsu:Timestamp wsu:Id="Timestamp-dc02f8e8-af74-4799-b5e9-9e9c0299278f">
<wsu:Created>2025-01-03T04:59:45Z</wsu:Created>
<wsu:Expires>2025-01-03T05:04:45Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<soap:Fault>
<faultcode xmlns:q0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">q0:Security</faultcode>
<faultstring>Login Failed</faultstring>
<faultactor>https://mcvgkqz3c1chrbsz586k90s41hp8.soap.marketingcloudapis.com/Service.asmx</faultactor>
</soap:Fault>
</soap:Body>
</soap:Envelope>`);
try {
await sfmcClient.getBusinessUnits();
}
catch (e) {
expect(e).toEqual(new Error('Request failed with status code 500'));
}
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
});
});
describe('getDataFolders', () => {
it('should get all the data folders', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
const validResponse = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<wsa:Action>RetrieveResponse</wsa:Action>
<wsa:MessageID>urn:uuid:1cf4fd72-93a9-4360-a75c-988dc708eb7c</wsa:MessageID>
<wsa:RelatesTo>urn:uuid:527a13a5-a798-4438-b23d-b7fd084ac953</wsa:RelatesTo>
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsse:Security>
<wsu:Timestamp wsu:Id="Timestamp-9d7c5707-e8ec-4db8-b55e-7cbd2ce22fa3">
<wsu:Created>2025-01-03T05:54:16Z</wsu:Created>
<wsu:Expires>2025-01-03T05:59:16Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<RetrieveResponseMsg
xmlns="http://exacttarget.com/wsdl/partnerAPI">
<OverallStatus>OK</OverallStatus>
<RequestID>5d91708b-25d1-4b27-80a0-dc84ef0aac0c</RequestID>
<Results xsi:type="DataFolder">
<Client>
<ID>534003210</ID>
</Client>
<PartnerKey xsi:nil="true" />
<ID>11546</ID>
<ObjectID>4f591ca1-8236-47b8-92fe-cf0dcf7cffb5</ObjectID>
<Name>my emails</Name>
<Description />
<ContentType>email</ContentType>
</Results>
<Results xsi:type="DataFolder">
<Client>
<ID>534003210</ID>
</Client>
<PartnerKey xsi:nil="true" />
<ID>11547</ID>
<ObjectID>695dd7c6-d764-479d-9e77-36d141cdd342</ObjectID>
<Name>simple automated emails</Name>
<Description />
<ContentType>automated_email</ContentType>
</Results>
</RetrieveResponseMsg>
</soap:Body>
</soap:Envelope>`;
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.soap.marketingcloudapis.com/Service.asmx')
.reply(200, validResponse);
const result = await sfmcClient.getDataFolders();
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
expect(result).toStrictEqual([
{
Client: {
ID: 534003210,
},
ContentType: 'email',
Description: '',
ID: 11546,
Name: 'my emails',
ObjectID: '4f591ca1-8236-47b8-92fe-cf0dcf7cffb5',
PartnerKey: '',
},
{
Client: {
ID: 534003210,
},
ContentType: 'automated_email',
Description: '',
ID: 11547,
Name: 'simple automated emails',
ObjectID: '695dd7c6-d764-479d-9e77-36d141cdd342',
PartnerKey: '',
},
]);
});
it('should handle failure scenario', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.soap.marketingcloudapis.com/Service.asmx')
.reply(500, `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<wsa:Action>http://schemas.xmlsoap.org/ws/2004/08/addressing/fault</wsa:Action>
<wsa:MessageID>urn:uuid:15d3e286-97f5-4f5c-b0fa-d58673621568</wsa:MessageID>
<wsa:RelatesTo>urn:uuid:b38ee85b-8f6b-4edc-9ab9-23ab6e5a51e3</wsa:RelatesTo>
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsse:Security>
<wsu:Timestamp wsu:Id="Timestamp-dc02f8e8-af74-4799-b5e9-9e9c0299278f">
<wsu:Created>2025-01-03T04:59:45Z</wsu:Created>
<wsu:Expires>2025-01-03T05:04:45Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<soap:Fault>
<faultcode xmlns:q0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">q0:Security</faultcode>
<faultstring>Login Failed</faultstring>
<faultactor>https://mcvgkqz3c1chrbsz586k90s41hp8.soap.marketingcloudapis.com/Service.asmx</faultactor>
</soap:Fault>
</soap:Body>
</soap:Envelope>`);
try {
await sfmcClient.getDataFolders();
}
catch (e) {
expect(e).toEqual(new Error('Request failed with status code 500'));
}
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
});
});
describe('getDataExtensions', () => {
it('should get all the data extensions', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
const validResponse = `<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><soap:Header><wsa:Action>RetrieveResponse</wsa:Action><wsa:MessageID>urn:uuid:0214d595-39f2-42bb-b18f-782e3aec9125</wsa:MessageID><wsa:RelatesTo>urn:uuid:8c9f1fe6-584a-492f-ad1e-b62887e6b27d</wsa:RelatesTo><wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To><wsse:Security><wsu:Timestamp wsu:Id="Timestamp-749c5b90-7c3e-405e-a891-4cab9aa319d5"><wsu:Created>2025-01-09T10:01:15Z</wsu:Created><wsu:Expires>2025-01-09T10:06:15Z</wsu:Expires></wsu:Timestamp></wsse:Security></soap:Header><soap:Body><RetrieveResponseMsg xmlns="http://exacttarget.com/wsdl/partnerAPI"><OverallStatus>OK</OverallStatus><RequestID>26123846-4046-430b-89dc-9e5c2eb587fd</RequestID><Results xsi:type="DataExtension"><PartnerKey xsi:nil="true" /><PartnerProperties><Name>ObjectId</Name><Value>b30e26db-5e59-ef11-ba6d-f40343ce89d8</Value></PartnerProperties><PartnerProperties><Name>NAME</Name><Value>rudderstackTesting13342</Value></PartnerProperties><PartnerProperties><Name>CategoryId</Name><Value>12345</Value></PartnerProperties><ObjectID xsi:nil="true" /></Results><Results xsi:type="DataExtension"><PartnerKey xsi:nil="true" /><PartnerProperties><Name>ObjectId</Name><Value>191c5b2a-57b8-ef11-ba6e-f40343d380c8</Value></PartnerProperties><PartnerProperties><Name>NAME</Name><Value>TestFromSDK1234</Value></PartnerProperties><PartnerProperties><Name>CategoryId</Name><Value>12345</Value></PartnerProperties><ObjectID xsi:nil="true" /></Results><Results xsi:type="DataExtension"><PartnerKey xsi:nil="true" /><PartnerProperties><Name>ObjectId</Name><Value>503856e3-5ab8-ef11-ba6e-f40343d380c8</Value></PartnerProperties><PartnerProperties><Name>NAME</Name><Value>rudderstackTesting121120</Value></PartnerProperties><PartnerProperties><Name>CategoryId</Name><Value>12345</Value></PartnerProperties><ObjectID xsi:nil="true" /></Results><Results xsi:type="DataExtension"><PartnerKey xsi:nil="true" /><PartnerProperties><Name>ObjectId</Name><Value>1742f6f1-79b8-ef11-ba6e-f40343d380c8</Value></PartnerProperties><PartnerProperties><Name>NAME</Name><Value>rudderstackTesting12442</Value></PartnerProperties><PartnerProperties><Name>CategoryId</Name><Value>12345</Value></PartnerProperties><ObjectID xsi:nil="true" /></Results><Results xsi:type="DataExtension"><PartnerKey xsi:nil="true" /><PartnerProperties><Name>ObjectId</Name><Value>4baf4cf1-8cc9-ef11-ba6e-f40343d380c8</Value></PartnerProperties><PartnerProperties><Name>NAME</Name><Value>rudderstackTesting0295</Value></PartnerProperties><PartnerProperties><Name>CategoryId</Name><Value>12345</Value></PartnerProperties><ObjectID xsi:nil="true" /></Results></RetrieveResponseMsg></soap:Body></soap:Envelope>`;
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.soap.marketingcloudapis.com/Service.asmx')
.reply(200, validResponse);
const result = await sfmcClient.getDataExtensions('valid-folder-id');
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
expect(result).toStrictEqual([
{
CategoryId: 12345,
Name: 'rudderstackTesting13342',
ObjectId: 'b30e26db-5e59-ef11-ba6d-f40343ce89d8',
},
{
CategoryId: 12345,
Name: 'TestFromSDK1234',
ObjectId: '191c5b2a-57b8-ef11-ba6e-f40343d380c8',
},
{
CategoryId: 12345,
Name: 'rudderstackTesting121120',
ObjectId: '503856e3-5ab8-ef11-ba6e-f40343d380c8',
},
{
CategoryId: 12345,
Name: 'rudderstackTesting12442',
ObjectId: '1742f6f1-79b8-ef11-ba6e-f40343d380c8',
},
{
CategoryId: 12345,
Name: 'rudderstackTesting0295',
ObjectId: '4baf4cf1-8cc9-ef11-ba6e-f40343d380c8',
},
]);
});
it('should handle failure scenario', async () => {
const validAuthObject = {
clientId: 'test-client',
clientSecret: 'test-secret',
subDomain: 'abcdefghijklmnopqrstuvwxyz01',
access_token: 'valid-token',
expiration: process.hrtime()[0] + 3600,
};
const sfmcClient = new _1.default(validAuthObject);
jest.spyOn(utils, 'isExpired').mockReturnValue(false);
const requestTokenSpy = jest.spyOn(utils, 'getToken');
const mockAdapter = new axios_mock_adapter_1.default(axios_1.default, { onNoMatch: 'throwException' });
mockAdapter
.onPost('https://abcdefghijklmnopqrstuvwxyz01.soap.marketingcloudapis.com/Service.asmx')
.reply(500, `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<wsa:Action>http://schemas.xmlsoap.org/ws/2004/08/addressing/fault</wsa:Action>
<wsa:MessageID>urn:uuid:15d3e286-97f5-4f5c-b0fa-d58673621568</wsa:MessageID>
<wsa:RelatesTo>urn:uuid:b38ee85b-8f6b-4edc-9ab9-23ab6e5a51e3</wsa:RelatesTo>
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsse:Security>
<wsu:Timestamp wsu:Id="Timestamp-dc02f8e8-af74-4799-b5e9-9e9c0299278f">
<wsu:Created>2025-01-03T04:59:45Z</wsu:Created>
<wsu:Expires>2025-01-03T05:04:45Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<soap:Fault>
<faultcode xmlns:q0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">q0:Security</faultcode>
<faultstring>Login Failed</faultstring>
<faultactor>https://mcvgkqz3c1chrbsz586k90s41hp8.soap.marketingcloudapis.com/Service.asmx</faultactor>
</soap:Fault>
</soap:Body>
</soap:Envelope>`);
try {
await sfmcClient.getDataExtensions();
}
catch (e) {
expect(e).toEqual(new Error('Request failed with status code 500'));
}
expect(utils.isExpired).toHaveBeenCalledWith(validAuthObject);
expect(requestTokenSpy).not.toHaveBeenCalled();
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXgudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZGtzL3NmbWMvaW5kZXgudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLGtEQUEwQjtBQUMxQiw0RUFBa0Q7QUFDbEQseUNBQXFCO0FBQ3JCLCtDQUFpQztBQUVqQyxRQUFRLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRTtJQUNwQixRQUFRLENBQUMsZ0JBQWdCLEVBQUUsR0FBRyxFQUFFO1FBQzlCLEVBQUUsQ0FBQyw2REFBNkQsRUFBRSxLQUFLLElBQUksRUFBRTtZQUMzRSxNQUFNLGVBQWUsR0FBRztnQkFDdEIsUUFBUSxFQUFFLGFBQWE7Z0JBQ3ZCLFlBQVksRUFBRSxhQUFhO2dCQUMzQixTQUFTLEVBQUUsOEJBQThCO2dCQUN6QyxZQUFZLEVBQUUsYUFBYTtnQkFDM0IsVUFBVSxFQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJO2FBQ3ZDLENBQUM7WUFFRixNQUFNLFVBQVUsR0FBRyxJQUFJLFVBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUM3QyxVQUFVLENBQUMsVUFBVSxHQUFHLGVBQWUsQ0FBQztZQUV4QyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxXQUFXLENBQUMsQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDdEQsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsVUFBVSxDQUFDLENBQUM7WUFDdEQsTUFBTSxNQUFNLEdBQUcsTUFBTSxVQUFVLENBQUMsY0FBYyxFQUFFLENBQUM7WUFFakQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUNyQyxNQUFNLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDLG9CQUFvQixD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