Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 8 additions & 1 deletion lib/chartmogul/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ const errors = require('./errors');
const util = require('./util');
const Config = require('./config');

// Explicitly strips undefined fields; null is preserved so callers can
// intentionally clear a field (e.g. external_id: null).
function stripUndefined (obj) {
if (!obj || typeof obj !== 'object') return obj;
return JSON.parse(JSON.stringify(obj));
}
Comment on lines +9 to +12
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this helper function to ensure that request body fields with a value of undefined are stripped, while null values are preserved so as to support the functionality that I have added to the Contact model with respect to the external_id field. 👍


// HTTP verb mapping
const mappings = {
all: 'GET',
Expand Down Expand Up @@ -53,7 +60,7 @@ class Resource {
}

const qs = method === 'GET' ? data : {};
const body = method === 'GET' ? {} : data;
const body = method === 'GET' ? {} : stripUndefined(data);

const options = {
qs,
Expand Down
94 changes: 90 additions & 4 deletions test/chartmogul/contact.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('Contact', () => {
customer_uuid: 'cus_919d5d7c-9e23-11ed-a936-97fbf69ba02b',
data_source_uuid: 'ds_87832fac-ab61-11ec-a8d8-6fb18044a151',
data_source_customer_external_id: 'scus_606e14228cff7d9db01be55f4e32e5e4',
external_id: 'contact_external_id_001',
first_name: 'First name',
last_name: 'Last name',
position: 9,
Expand All @@ -37,6 +38,7 @@ describe('Contact', () => {
customer_uuid: 'cus_00000000-0000-0000-0000-000000000000',
data_source_uuid: 'ds_00000000-0000-0000-0000-000000000000',
data_source_customer_external_id: 'external_001',
external_id: 'contact_external_id_001',
first_name: 'First name',
last_name: 'Last name',
position: 9,
Expand All @@ -57,6 +59,70 @@ describe('Contact', () => {
expect(contact).to.have.property('uuid');
});

it('creates a new contact with external_id as null', async () => {
const postBody = {
/* eslint-disable camelcase */
customer_uuid: 'cus_919d5d7c-9e23-11ed-a936-97fbf69ba02b',
data_source_uuid: 'ds_87832fac-ab61-11ec-a8d8-6fb18044a151',
data_source_customer_external_id: 'scus_606e14228cff7d9db01be55f4e32e5e4',
external_id: null,
first_name: 'First name',
last_name: 'Last name',
position: 9,
title: 'Title',
email: 'test@example.com',
phone: '+1234567890',
linked_in: 'https://linkedin.com/not_found',
twitter: 'https://twitter.com/not_found',
notes: 'Heading\nBody\nFooter',
custom: [
{ key: 'Booleanz', value: false },
{ key: 'MyIntegerAttribute', value: 123 }
]
/* eslint-enable camelcase */
};

let requestBody;
nock(config.API_BASE)
.post('/v1/contacts', body => { requestBody = body; return true; })
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found in nock's documentation that you can capture the body of the request being sent. This enables us to validate that a given field has the expected value.

I've updated this test and the two I added below to capture the request body and check that the value of the external_id field has the expected value. @briwa Please let me know if this is an acceptable way to prove that we are not seeing false positives in the case of the three tests I added in this PR. 🙏

.reply(200, { uuid: 'con_00000000-0000-0000-0000-000000000000' });

await Contact.create(config, postBody);
// eslint-disable-next-line no-unused-expressions
expect(requestBody).to.have.property('external_id').that.is.null;
});

it('creates a new contact without external_id', async () => {
const postBody = {
/* eslint-disable camelcase */
customer_uuid: 'cus_919d5d7c-9e23-11ed-a936-97fbf69ba02b',
data_source_uuid: 'ds_87832fac-ab61-11ec-a8d8-6fb18044a151',
data_source_customer_external_id: 'scus_606e14228cff7d9db01be55f4e32e5e4',
first_name: 'First name',
last_name: 'Last name',
position: 9,
title: 'Title',
email: 'test@example.com',
phone: '+1234567890',
linked_in: 'https://linkedin.com/not_found',
twitter: 'https://twitter.com/not_found',
notes: 'Heading\nBody\nFooter',
custom: [
{ key: 'Booleanz', value: false },
{ key: 'MyIntegerAttribute', value: 123 }
]
/* eslint-enable camelcase */
};

let requestBody;
nock(config.API_BASE)
.post('/v1/contacts', body => { requestBody = body; return true; })
.reply(200, { uuid: 'con_00000000-0000-0000-0000-000000000000' });

await Contact.create(config, postBody);
expect(requestBody).to.not.have.property('external_id');
});

it('should list all contacts with pagination', async () => {
const query = {
per_page: 1,
Expand Down Expand Up @@ -118,6 +184,7 @@ describe('Contact', () => {
customer_uuid: 'cus_00000000-0000-0000-0000-000000000000',
data_source_uuid: 'ds_00000000-0000-0000-0000-000000000000',
customer_external_id: 'external_001',
external_id: 'contact_external_id_001',
first_name: 'First name',
last_name: 'Last name',
position: 9,
Expand All @@ -142,23 +209,42 @@ describe('Contact', () => {
const contactUuid = 'con_00000000-0000-0000-0000-000000000000';

/* eslint-disable camelcase */
const postBody = { email: 'test2@example.com' };
const patchBody = { email: 'test2@example.com', external_id: 'contact_external_id_002' };
/* eslint-enable camelcase */

nock(config.API_BASE)
.patch(`/v1/contacts/${contactUuid}`, postBody)
.patch(`/v1/contacts/${contactUuid}`, patchBody)
.reply(200, {
/* eslint-disable camelcase */
uuid: contactUuid,
customer_uuid: 'cus_00000000-0000-0000-0000-000000000000',
data_source_uuid: 'ds_00000000-0000-0000-0000-000000000000',
customer_external_id: 'external_001',
email: 'test2@example.com'
email: 'test2@example.com',
external_id: 'contact_external_id_002'
/* eslint-enable camelcase */
});

const contact = await Contact.modify(config, contactUuid, postBody);
const contact = await Contact.modify(config, contactUuid, patchBody);
expect(contact.email).to.be.equal('test2@example.com');
expect(contact.external_id).to.be.equal('contact_external_id_002');
});

it('updates a contact with external_id as null', async () => {
const contactUuid = 'con_00000000-0000-0000-0000-000000000000';

/* eslint-disable camelcase */
const patchBody = { external_id: null };
/* eslint-enable camelcase */

let requestBody;
nock(config.API_BASE)
.patch(`/v1/contacts/${contactUuid}`, body => { requestBody = body; return true; })
.reply(200, { uuid: contactUuid });

await Contact.modify(config, contactUuid, patchBody);
// eslint-disable-next-line no-unused-expressions
expect(requestBody).to.have.property('external_id').that.is.null;
});

it('deletes a contact', async () => {
Expand Down
10 changes: 10 additions & 0 deletions test/chartmogul/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ describe('Resource', () => {
expect(e).to.be.instanceOf(ChartMogul.ConfigurationError);
});
});

it('should strip undefined values from POST request body', async () => {
let requestBody;
nock(config.API_BASE)
.post('/', body => { requestBody = body; return true; })
.reply(200, {});

await Resource.request(config, 'POST', '/', { email: 'x', external_id: undefined });
expect(requestBody).to.not.have.property('external_id');
});
});

describe('Resource Retry', () => {
Expand Down
Loading