From 4c535782f6d706e1ac487f6de85c5ecf50541d82 Mon Sep 17 00:00:00 2001 From: Kamil Pavlicko Date: Mon, 25 Mar 2024 20:30:34 +0800 Subject: [PATCH] feat(sc-57429): Support Opportunities API (#98) * feat(sc-57429): Support Opportunities API * feat(sc-57429): Support Opportunities API : fix typo in tests * feat(sc-57429): Support Opportunities API : fix tests data --- README.md | 13 +++ lib/chartmogul.js | 2 + lib/chartmogul/customer.js | 11 ++ lib/chartmogul/opportunity.js | 11 ++ test/chartmogul/customer.js | 83 +++++++++++++++ test/chartmogul/opportunity.js | 178 +++++++++++++++++++++++++++++++++ 6 files changed, 298 insertions(+) create mode 100644 lib/chartmogul/opportunity.js create mode 100644 test/chartmogul/opportunity.js diff --git a/README.md b/README.md index 4fd5d6b..f5003c9 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,9 @@ ChartMogul.Customer.createContact(config, customerUuid, data) ChartMogul.Customer.notes(config, customerUuid, { per_page: 10, cursor: 'cursor==' }) ChartMogul.Customer.createNote(config, customerUuid, data) + +ChartMogul.Customer.opportunities(config, customerUuid, { per_page: 10, cursor: 'cursor==' }) +ChartMogul.Customer.createOpportunity(config, customerUuid, data) ``` #### [Contacts](https://dev.chartmogul.com/docs/contacts) @@ -151,6 +154,16 @@ ChartMogul.CustomerNote.all(config, { per_page: 10, cursor: 'cursor==', customer ``` +#### [Opportunities](https://dev.chartmogul.com/docs/opportunities) + +```js +ChartMogul.Opportunity.create(config, data) +ChartMogul.Opportunity.retrieve(config, opportunityUuid) +ChartMogul.Opportunity.patch(config, opportunityUuid, data) +ChartMogul.Opportunity.destroy(config, opportunityUuid) +ChartMogul.Opportunity.all(config, { per_page: 10, cursor: 'cursor==', customer_uuid: customerUuid}) +``` + #### [Plans](https://dev.chartmogul.com/docs/plans) diff --git a/lib/chartmogul.js b/lib/chartmogul.js index e8bb0a7..e18bbb6 100644 --- a/lib/chartmogul.js +++ b/lib/chartmogul.js @@ -7,6 +7,7 @@ const Customer = require('./chartmogul/customer'); const CustomerNote = require('./chartmogul/customer-note'); const Contact = require('./chartmogul/contact'); const DataSource = require('./chartmogul/data-source'); +const Opportunity = require('./chartmogul/opportunity'); const Plan = require('./chartmogul/plan'); const PlanGroup = require('./chartmogul/plan-group'); const Ping = require('./chartmogul/ping'); @@ -38,6 +39,7 @@ const ChartMogul = { Import, Invoice, Metrics, + Opportunity, Ping, Plan, PlanGroup, diff --git a/lib/chartmogul/customer.js b/lib/chartmogul/customer.js index d6ed157..5ccd9a1 100644 --- a/lib/chartmogul/customer.js +++ b/lib/chartmogul/customer.js @@ -3,6 +3,7 @@ const Resource = require('./resource'); const util = require('./util'); const CustomerNote = require('./customer-note'); +const Opportunity = require('./opportunity'); class Customer extends Resource { static get path () { @@ -38,6 +39,16 @@ class Customer extends Resource { const path = util.expandPath(CustomerNote.path, []); return Resource.request(config, 'POST', path, { ...params, customer_uuid: customerId }, callback); } + + static opportunities (config, customerId, params, callback) { + const path = util.expandPath(Opportunity.path, []); + return Resource.request(config, 'GET', path, { ...params, customer_uuid: customerId }, callback); + } + + static createOpportunity (config, customerId, params, callback) { + const path = util.expandPath(Opportunity.path, []); + return Resource.request(config, 'POST', path, { ...params, customer_uuid: customerId }, callback); + } } // @Override diff --git a/lib/chartmogul/opportunity.js b/lib/chartmogul/opportunity.js new file mode 100644 index 0000000..ec5043d --- /dev/null +++ b/lib/chartmogul/opportunity.js @@ -0,0 +1,11 @@ +'use strict'; + +const Resource = require('./resource'); + +class Opportunity extends Resource { + static get path () { + return '/v1/opportunities{/opportunityUuid}'; + } +} + +module.exports = Opportunity; diff --git a/test/chartmogul/customer.js b/test/chartmogul/customer.js index 4681217..2286440 100644 --- a/test/chartmogul/customer.js +++ b/test/chartmogul/customer.js @@ -242,6 +242,89 @@ describe('Customer', () => { expect(res.has_more).to.eql(false); }); }); + + it('creates a new opportunity from a customer', async () => { + const customerUuid = 'cus_00000000-0000-0000-0000-000000000000'; + const postBody = { + customer_uuid: customerUuid, + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2023-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: [{ key: 'from_campaign', value: true }] + }; + + nock(config.API_BASE) + .post('/v1/opportunities', postBody) + .reply(200, { + uuid: '00000000-0000-0000-0000-000000000000', + customer_uuid: 'cus_00000000-0000-0000-0000-000000000000', + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2023-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: { from_campaign: true }, + created_at: '2024-03-13T07:33:28.356Z', + updated_at: '2024-03-13T07:33:28.356Z' + }); + + const opportunity = await Customer.createOpportunity(config, customerUuid, postBody); + expect(opportunity.uuid).to.be.equal('00000000-0000-0000-0000-000000000000'); + expect(opportunity.customer_uuid).to.be.equal(customerUuid); + expect(opportunity.owner).to.be.equal('test1@example.org'); + expect(opportunity.pipeline).to.be.equal('New business 1'); + expect(opportunity.pipeline_stage).to.be.equal('Discovery'); + expect(opportunity.estimated_close_date).to.be.equal('2023-12-22'); + expect(opportunity.currency).to.be.equal('USD'); + expect(opportunity.amount_in_cents).to.be.equal(100); + expect(opportunity.type).to.be.equal('recurring'); + expect(opportunity.forecast_category).to.be.equal('pipeline'); + expect(opportunity.win_likelihood).to.be.equal(3); + expect(opportunity.custom).to.deep.equal({ from_campaign: true }); + }); + + it('gets all opportunities from a customer', async () => { + const customerUuid = 'cus_00000000-0000-0000-0000-000000000000'; + + nock(config.API_BASE) + .get(`/v1/opportunities?customer_uuid=${customerUuid}`) + .reply(200, { + entries: [{ + uuid: '00000000-0000-0000-0000-000000000000', + customer_uuid: 'cus_00000000-0000-0000-0000-000000000000', + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2023-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: { from_campaign: true }, + created_at: '2024-03-13T07:33:28.356Z', + updated_at: '2024-03-13T07:33:28.356Z' + }], + cursor: 'MjAyMy0wMy0xM1QxMjowMTozMi44MD==', + has_more: false + }); + + const opportunities = await Customer.opportunities(config, customerUuid); + expect(opportunities).to.have.property('entries'); + expect(opportunities.entries).to.be.instanceof(Array); + expect(opportunities.cursor).to.eql('MjAyMy0wMy0xM1QxMjowMTozMi44MD=='); + expect(opportunities.has_more).to.eql(false); + }); }); /** Suite that originally belonged in the Enrichment module. diff --git a/test/chartmogul/opportunity.js b/test/chartmogul/opportunity.js new file mode 100644 index 0000000..f3da1a3 --- /dev/null +++ b/test/chartmogul/opportunity.js @@ -0,0 +1,178 @@ +'use strict'; + +const ChartMogul = require('../../lib/chartmogul'); +const config = new ChartMogul.Config('token'); +const expect = require('chai').expect; +const nock = require('nock'); +const Opportunity = ChartMogul.Opportunity; + +describe('Opportunity', () => { + it('creates a opportunity for a customer', async () => { + const uuid = 'cus_00000000-0000-0000-0000-000000000000'; + const postBody = { + customer_uuid: uuid, + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2023-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: [{ key: 'from_campaign', value: true }] + }; + + nock(config.API_BASE) + .post('/v1/opportunities', postBody) + .reply(200, { + uuid: '00000000-0000-0000-0000-000000000000', + customer_uuid: 'cus_00000000-0000-0000-0000-000000000000', + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2023-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: { from_campaign: true }, + created_at: '2024-03-13T07:33:28.356Z', + updated_at: '2024-03-13T07:33:28.356Z' + }); + + const opportunity = await Opportunity.create(config, postBody); + expect(opportunity.customer_uuid).to.be.equal(uuid); + expect(opportunity.owner).to.be.equal('test1@example.org'); + expect(opportunity.pipeline).to.be.equal('New business 1'); + expect(opportunity.pipeline_stage).to.be.equal('Discovery'); + expect(opportunity.estimated_close_date).to.be.equal('2023-12-22'); + expect(opportunity.currency).to.be.equal('USD'); + expect(opportunity.amount_in_cents).to.be.equal(100); + expect(opportunity.type).to.be.equal('recurring'); + expect(opportunity.forecast_category).to.be.equal('pipeline'); + expect(opportunity.win_likelihood).to.be.equal(3); + expect(opportunity.custom).to.deep.equal({ from_campaign: true }); + }); + + it('lists all opportunities from a customer', async () => { + const uuid = 'cus_00000000-0000-0000-0000-000000000000'; + + nock(config.API_BASE) + .get(`/v1/opportunities/${uuid}`) + .reply(200, { + entries: [ + { + uuid: '00000000-0000-0000-0000-000000000000', + customer_uuid: 'cus_00000000-0000-0000-0000-000000000000', + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2023-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: { from_campaign: true }, + created_at: '2024-03-13T07:33:28.356Z', + updated_at: '2024-03-13T07:33:28.356Z' + } + ] + }); + + const opportunity = await Opportunity.all(config, uuid); + expect(opportunity.entries).to.have.length(1); + }); + + it('retrieves an opportunity', async () => { + const uuid = '00000000-0000-0000-0000-000000000000'; + + nock(config.API_BASE) + .get(`/v1/opportunities/${uuid}`) + .reply(200, { + uuid: '00000000-0000-0000-0000-000000000000', + customer_uuid: 'cus_00000000-0000-0000-0000-000000000000', + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2023-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: { from_campaign: true }, + created_at: '2024-03-13T07:33:28.356Z', + updated_at: '2024-03-13T07:33:28.356Z' + }); + + const opportunity = await Opportunity.retrieve(config, uuid); + expect(opportunity.uuid).to.be.equal(uuid); + expect(opportunity.customer_uuid).to.be.equal( + 'cus_00000000-0000-0000-0000-000000000000' + ); + expect(opportunity.owner).to.be.equal('test1@example.org'); + expect(opportunity.pipeline).to.be.equal('New business 1'); + expect(opportunity.pipeline_stage).to.be.equal('Discovery'); + expect(opportunity.estimated_close_date).to.be.equal('2023-12-22'); + expect(opportunity.currency).to.be.equal('USD'); + expect(opportunity.amount_in_cents).to.be.equal(100); + expect(opportunity.type).to.be.equal('recurring'); + expect(opportunity.forecast_category).to.be.equal('pipeline'); + expect(opportunity.win_likelihood).to.be.equal(3); + expect(opportunity.custom).to.deep.equal({ from_campaign: true }); + }); + + it('updates an opportunity', async () => { + const uuid = '00000000-0000-0000-0000-000000000000'; + const postBody = { + estimated_close_date: '2024-12-22' + }; + + nock(config.API_BASE) + .patch(`/v1/opportunities/${uuid}`, postBody) + .reply(200, { + uuid: '00000000-0000-0000-0000-000000000000', + customer_uuid: 'cus_00000000-0000-0000-0000-000000000000', + owner: 'test1@example.org', + pipeline: 'New business 1', + pipeline_stage: 'Discovery', + estimated_close_date: '2024-12-22', + currency: 'USD', + amount_in_cents: 100, + type: 'recurring', + forecast_category: 'pipeline', + win_likelihood: 3, + custom: { from_campaign: true }, + created_at: '2024-03-13T07:33:28.356Z', + updated_at: '2024-03-13T07:33:28.356Z' + }); + + const opportunity = await Opportunity.patch(config, uuid, postBody); + expect(opportunity.uuid).to.be.equal(uuid); + expect(opportunity.customer_uuid).to.be.equal( + 'cus_00000000-0000-0000-0000-000000000000' + ); + expect(opportunity.owner).to.be.equal('test1@example.org'); + expect(opportunity.pipeline).to.be.equal('New business 1'); + expect(opportunity.pipeline_stage).to.be.equal('Discovery'); + expect(opportunity.estimated_close_date).to.be.equal('2024-12-22'); + expect(opportunity.currency).to.be.equal('USD'); + expect(opportunity.amount_in_cents).to.be.equal(100); + expect(opportunity.type).to.be.equal('recurring'); + expect(opportunity.forecast_category).to.be.equal('pipeline'); + expect(opportunity.win_likelihood).to.be.equal(3); + expect(opportunity.custom).to.deep.equal({ from_campaign: true }); + }); + + it('deletes an opportunity', async () => { + const uuid = '00000000-0000-0000-0000-000000000000'; + + nock(config.API_BASE).delete(`/v1/opportunities/${uuid}`).reply(204); + + const result = await Opportunity.destroy(config, uuid); + expect(result).to.deep.equal({}); + }); +});