From 9f2bb10889fa67d49c80fea1b89efea22f28e273 Mon Sep 17 00:00:00 2001 From: SoeunSona <78331453+SoeunSona@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:17:39 +0100 Subject: [PATCH 1/4] Python client notes and call logs API support https://app.shortcut.com/chartmogul/story/55270/python-client-notes-and-call-logs-api-support --- README.md | 13 +++- chartmogul/__init__.py | 1 + chartmogul/api/customer.py | 2 + chartmogul/api/customer_note.py | 29 ++++++++ test/api/test_customer.py | 61 ++++++++++++++- test/api/test_customer_note.py | 127 ++++++++++++++++++++++++++++++++ 6 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 chartmogul/api/customer_note.py create mode 100644 test/api/test_customer_note.py diff --git a/README.md b/README.md index acaf1cc..484f66c 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,8 @@ chartmogul.Customer.connectSubscriptions(config, uuid='cus_5915ee5a-babd-406b-b8 }) chartmogul.Customer.contacts(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', cursor='aabbcc', per_page=20) chartmogul.Customer.createContact(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={}) +chartmogul.Customer.notes(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', cursor='aabbcc', per_page=20) +chartmogul.Customer.createrNote(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={}) ``` #### [Contacts](https://dev.chartmogul.com/docs/contacts) @@ -161,6 +163,15 @@ chartmogul.Contact.modify(config, uuid='con_5915ee5a-babd-406b-b8ce-d207133fb4cb chartmogul.Contact.destroy(config, uuid='con_5915ee5a-babd-406b-b8ce-d207133fb4cb') ``` +#### [Customer Notes](https://dev.chartmogul.com/docs/customer_notes) +```python +chartmogul.CustomerNote.create(config, data={}) +chartmogul.CustomerNote.all(config, cursor='aabbcc', per_page=20, customer_uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb') +chartmogul.CustomerNote.retrieve(config, uuid='note_5915ee5a-babd-406b-b8ce-d207133fb4cb') +chartmogul.CustomerNote.patch(config, uuid='note_5915ee5a-babd-406b-b8ce-d207133fb4cb') +chartmogul.CustomerNote.destroy(config, uuid='note_5915ee5a-babd-406b-b8ce-d207133fb4cb') +``` + #### [Customer Attributes](https://dev.chartmogul.com/docs/customer-attributes) Note that the returned attributes of type date are not parsed and stay in string. @@ -256,7 +267,7 @@ chartmogul.Transaction.create(config, uuid='inv_745df1d4-819f-48ee-873d-b5204801 ```python import chartmogul chartmogul.SubscriptionEvent.all(config) -chartmogul.SubscriptionEvent.create(config, data={ +chartmogul.SubscriptionEvent.create(config, data={ 'subscription_event' : { 'external_id' : 'evnt_026', 'customer_external_id' : 'scus_022', diff --git a/chartmogul/__init__.py b/chartmogul/__init__.py index 7c8ff4b..b61733c 100644 --- a/chartmogul/__init__.py +++ b/chartmogul/__init__.py @@ -11,6 +11,7 @@ from .api.attributes import Attributes from .api.custom_attrs import CustomAttributes from .api.customer import Customer +from .api.customer_note import CustomerNote from .api.contact import Contact from .api.data_source import DataSource from .api.invoice import Invoice diff --git a/chartmogul/api/customer.py b/chartmogul/api/customer.py index 3bb7199..5fd11d8 100644 --- a/chartmogul/api/customer.py +++ b/chartmogul/api/customer.py @@ -82,3 +82,5 @@ def make(self, data, **kwargs): Customer.createContact = Contact._method( "create", "post", "/customers/{uuid}/contacts", useCallerClass=True ) +Customer.notes = Customer._method("all", "get", "/customer_notes?customer_uuid={uuid}") +Customer.createNote = Customer._method("create", "post", "/customer_notes") diff --git a/chartmogul/api/customer_note.py b/chartmogul/api/customer_note.py new file mode 100644 index 0000000..3022256 --- /dev/null +++ b/chartmogul/api/customer_note.py @@ -0,0 +1,29 @@ +from marshmallow import Schema, fields, post_load, EXCLUDE +from ..resource import Resource +from collections import namedtuple + + +class CustomerNote(Resource): + """ + https://dev.chartmogul.com/v1.0/reference#customer-notes + """ + + _path = "/customer_notes{/uuid}" + _root_key = "entries" + _many = namedtuple("CustomerNotes", [_root_key, "has_more", "cursor"]) + + class _Schema(Schema): + uuid = fields.String() + customer_uuid = fields.String(allow_none=True) + type = fields.String(allow_none=True) + text = fields.String(allow_none=True) + author_email = fields.String(allow_none=True) + call_duration = fields.Int(allow_none=True) + created_at = fields.DateTime(allow_none=True) + updated_at = fields.DateTime(allow_none=True) + + @post_load + def make(self, data, **kwargs): + return CustomerNote(**data) + + _schema = _Schema(unknown=EXCLUDE) diff --git a/test/api/test_customer.py b/test/api/test_customer.py index d82fc73..4c5bafb 100644 --- a/test/api/test_customer.py +++ b/test/api/test_customer.py @@ -1,5 +1,5 @@ import unittest -from chartmogul import Customer, Contact, Config +from chartmogul import Customer, Contact, Config, CusotmerNote from chartmogul.api.customer import Attributes, Address from datetime import datetime from chartmogul import APIError @@ -279,6 +279,25 @@ ], } +note = { + "uuid": "note_00000000-0000-0000-0000-000000000000", + "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", + "type": "note", + "text": "This is a note", + "call_duration": 0, + "author": "John Doe (john@example.com)", + "created_at": "2015-06-09T13:16:00-04:00", + "updated_at": "2015-06-09T13:16:00-04:00" +} + +createNote = { + "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", + "type": "note", + "text": "This is a note", + "authoer_email": "john@xample.com" +} + +allNotes = {"entries": [note], "cursor": "cursor==", "has_more": True} class CustomerTestCase(unittest.TestCase): """ @@ -480,3 +499,43 @@ def test_createContact(self, mock_requests): self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), createContact) self.assertTrue(isinstance(expected, Contact)) + + @requests_mock.mock() + def test_notes(self, mock_requests): + mock_requests.register_uri( + "GET", + "https://api.chartmogul.com/v1/customer_notes?customer_uuid=cus_00000000-0000-0000-0000-000000000000", + status_code=200, + json=allNotes, + ) + + config = Config("token") + notes = Customer.notes(config, uuid="cus_00000000-0000-0000-0000-000000000000").get() + expected = Customer._many(**allNotes) + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.text, None) + self.assertEqual(sorted(dir(notes)), sorted(dir(expected))) + self.assertTrue(isinstance(notes.entries[0], dict)) + self.assertEqual(notes.cursor, "cursor==") + self.assertTrue(notes.has_more) + + @requests_mock.mock() + def test_createNote(self, mock_requests): + mock_requests.register_uri( + "POST", + "https://api.chartmogul.com/v1/customer_notes", + status_code=200, + json=note, + ) + + config = Config("token") + expected = Customer.createNote( + config, data=note + ).get() + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.json(), createNote) + self.assertTrue(isinstance(expected, CusotmerNote) diff --git a/test/api/test_customer_note.py b/test/api/test_customer_note.py new file mode 100644 index 0000000..19f46e8 --- /dev/null +++ b/test/api/test_customer_note.py @@ -0,0 +1,127 @@ +import unittest +from chartmogul import CustomerNote, Config +import requests_mock + +from pprint import pprint + +note = { + "uuid": "note_00000000-0000-0000-0000-000000000000", + "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", + "type": "note", + "text": "This is a note", + "call_duration": 0, + "author": "John Doe (john@example.com)", + "created_at": "2015-06-09T13:16:00-04:00", + "updated_at": "2015-06-09T13:16:00-04:00" +} + +createNote = { + "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", + "type": "note", + "text": "This is a note", + "authoer_email": "john@xample.com" +} + +allNotes = {"entries": [note], "cursor": "cursor==", "has_more": False} + + +class CustomerNoteTestCase(unittest.TestCase): + """ + Tests complex nested structure & assymetric create/retrieve schema. + """ + + @requests_mock.mock() + def test_all(self, mock_requests): + mock_requests.register_uri( + "GET", + "https://api.chartmogul.com/v1/customer_notes?cursor=Ym9veWFo&per_page=1&customer_uuid=cus_00000000-0000-0000-0000-000000000000", + status_code=200, + json=allNotes, + ) + + config = Config("token") + notes = CustomerNote.all( + config, + data_source_uuid="ds_00000000-0000-0000-0000-000000000000", + cursor="Ym9veWFo", + per_page=1, + ).get() + expected = CustomerNote._many(**allNotes) + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual( + mock_requests.last_request.qs, + { + "cursor": ["ym9vewfo"], + "per_page": ["1"], + "customer_uuid": ["cus_00000000-0000-0000-0000-000000000000"], + }, + ) + self.assertEqual(mock_requests.last_request.text, None) + self.assertEqual(dir(notes), dir(expected)) + self.assertTrue(isinstance(notes.entries[0], CustomerNote)) + self.assertFalse(notes.has_more) + self.assertEqual(notes.cursor, "cursor==") + + @requests_mock.mock() + def test_create(self, mock_requests): + mock_requests.register_uri( + "POST", "https://api.chartmogul.com/v1/customer_notes", status_code=200, json=note + ) + + config = Config("token") + CustomerNote.create(config, data=createNote).get() + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.json(), createNote) + + @requests_mock.mock() + def test_patch(self, mock_requests): + mock_requests.register_uri( + "PATCH", + "https://api.chartmogul.com/v1/customer_notes/note_00000000-0000-0000-0000-000000000000", + status_code=200, + json=note, + ) + + config = Config("token") + CustomerNote.patch( + config, uuid="note_00000000-0000-0000-0000-000000000000", data=createNote + ).get() + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.json(), createNote) + + @requests_mock.mock() + def test_retrieve(self, mock_requests): + mock_requests.register_uri( + "GET", + "https://api.chartmogul.com/v1/customer_notes/note_00000000-0000-0000-0000-000000000000", + status_code=200, + json=note, + ) + + config = Config("token") + expected = CustomerNote.retrieve( + config, uuid="note_00000000-0000-0000-0000-000000000000" + ).get() + + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertTrue(isinstance(expected, CustomerNote)) + + @requests_mock.mock() + def test_destroy(self, mock_requests): + mock_requests.register_uri( + "DELETE", + "https://api.chartmogul.com/v1/customer_notes/note_00000000-0000-0000-0000-000000000000", + status_code=200, + json=note, + ) + + config = Config("token") + CustomerNote.destroy( + config, uuid="note_00000000-0000-0000-0000-000000000000" + ).get() + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) From 2170a939238d61f7392f9619b26b3452fd68fecc Mon Sep 17 00:00:00 2001 From: SoeunSona <78331453+SoeunSona@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:51:48 +0100 Subject: [PATCH 2/4] fix broken specs --- chartmogul/api/customer.py | 5 +++-- test/api/test_customer.py | 28 +++++++++++++++++++--------- test/api/test_customer_note.py | 2 +- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/chartmogul/api/customer.py b/chartmogul/api/customer.py index 5fd11d8..83f86bb 100644 --- a/chartmogul/api/customer.py +++ b/chartmogul/api/customer.py @@ -10,6 +10,7 @@ from collections import namedtuple from .attributes import Attributes from .contact import Contact +from .customer_note import CustomerNote class Address(DataObject): @@ -82,5 +83,5 @@ def make(self, data, **kwargs): Customer.createContact = Contact._method( "create", "post", "/customers/{uuid}/contacts", useCallerClass=True ) -Customer.notes = Customer._method("all", "get", "/customer_notes?customer_uuid={uuid}") -Customer.createNote = Customer._method("create", "post", "/customer_notes") +Customer.notes = CustomerNote._method("all", "get", "/customer_notes?customer_uuid={uuid}", useCallerClass=True) +Customer.createNote = CustomerNote._method("create", "post", "/customer_notes", useCallerClass=True) diff --git a/test/api/test_customer.py b/test/api/test_customer.py index 4c5bafb..e77d4fb 100644 --- a/test/api/test_customer.py +++ b/test/api/test_customer.py @@ -1,5 +1,5 @@ import unittest -from chartmogul import Customer, Contact, Config, CusotmerNote +from chartmogul import Customer, Contact, Config, CustomerNote from chartmogul.api.customer import Attributes, Address from datetime import datetime from chartmogul import APIError @@ -280,12 +280,11 @@ } note = { - "uuid": "note_00000000-0000-0000-0000-000000000000", "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", "type": "note", "text": "This is a note", "call_duration": 0, - "author": "John Doe (john@example.com)", + "author_email": "john@example.com", "created_at": "2015-06-09T13:16:00-04:00", "updated_at": "2015-06-09T13:16:00-04:00" } @@ -294,10 +293,21 @@ "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", "type": "note", "text": "This is a note", - "authoer_email": "john@xample.com" + "author_email": "john@xample.com" } -allNotes = {"entries": [note], "cursor": "cursor==", "has_more": True} +noteEntry = { + "uuid": "cus_00000000-0000-0000-0000-000000000000", + "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", + "type": "note", + "text": "This is a note", + "call_duration": 0, + "author_email": "john@example.com", + "created_at": "2015-06-09T13:16:00-04:00", + "updated_at": "2015-06-09T13:16:00-04:00" +} + +allNotes = {"entries": [noteEntry], "cursor": "cursor==", "has_more": True} class CustomerTestCase(unittest.TestCase): """ @@ -514,10 +524,10 @@ def test_notes(self, mock_requests): expected = Customer._many(**allNotes) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.qs, {'customer_uuid': ['cus_00000000-0000-0000-0000-000000000000']}) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(sorted(dir(notes)), sorted(dir(expected))) - self.assertTrue(isinstance(notes.entries[0], dict)) + self.assertTrue(isinstance(notes.entries[0], CustomerNote)) self.assertEqual(notes.cursor, "cursor==") self.assertTrue(notes.has_more) @@ -532,10 +542,10 @@ def test_createNote(self, mock_requests): config = Config("token") expected = Customer.createNote( - config, data=note + config, data=createNote ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), createNote) - self.assertTrue(isinstance(expected, CusotmerNote) + self.assertTrue(isinstance(expected, CustomerNote)) diff --git a/test/api/test_customer_note.py b/test/api/test_customer_note.py index 19f46e8..e441345 100644 --- a/test/api/test_customer_note.py +++ b/test/api/test_customer_note.py @@ -42,7 +42,7 @@ def test_all(self, mock_requests): config = Config("token") notes = CustomerNote.all( config, - data_source_uuid="ds_00000000-0000-0000-0000-000000000000", + customer_uuid="cus_00000000-0000-0000-0000-000000000000", cursor="Ym9veWFo", per_page=1, ).get() From ec798a9a497249f958b1942c65fb40f1930cefc7 Mon Sep 17 00:00:00 2001 From: SoeunSona <78331453+SoeunSona@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:24:35 +0100 Subject: [PATCH 3/4] Change code after code review --- chartmogul/api/customer_note.py | 14 +++++++------- test/api/test_customer.py | 11 ++++++++--- test/api/test_customer_note.py | 33 +++++++++++++++++++++++---------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/chartmogul/api/customer_note.py b/chartmogul/api/customer_note.py index 3022256..d122cf1 100644 --- a/chartmogul/api/customer_note.py +++ b/chartmogul/api/customer_note.py @@ -14,13 +14,13 @@ class CustomerNote(Resource): class _Schema(Schema): uuid = fields.String() - customer_uuid = fields.String(allow_none=True) - type = fields.String(allow_none=True) - text = fields.String(allow_none=True) - author_email = fields.String(allow_none=True) - call_duration = fields.Int(allow_none=True) - created_at = fields.DateTime(allow_none=True) - updated_at = fields.DateTime(allow_none=True) + customer_uuid = fields.String() + type = fields.String() + text = fields.String() + author = fields.String() + call_duration = fields.Int() + created_at = fields.DateTime() + updated_at = fields.DateTime() @post_load def make(self, data, **kwargs): diff --git a/test/api/test_customer.py b/test/api/test_customer.py index e77d4fb..c6c1405 100644 --- a/test/api/test_customer.py +++ b/test/api/test_customer.py @@ -514,17 +514,22 @@ def test_createContact(self, mock_requests): def test_notes(self, mock_requests): mock_requests.register_uri( "GET", - "https://api.chartmogul.com/v1/customer_notes?customer_uuid=cus_00000000-0000-0000-0000-000000000000", + "https://api.chartmogul.com/v1/customer_notes?customer_uuid=cus_00000000-0000-0000-0000-000000000000&cursor=ym9vewfo&per_page=1", status_code=200, json=allNotes, ) config = Config("token") - notes = Customer.notes(config, uuid="cus_00000000-0000-0000-0000-000000000000").get() + notes = Customer.notes( + config, + uuid="cus_00000000-0000-0000-0000-000000000000", + cursor="ym9vewfo", + per_page=1, + ).get() expected = Customer._many(**allNotes) self.assertEqual(mock_requests.call_count, 1, "expected call") - self.assertEqual(mock_requests.last_request.qs, {'customer_uuid': ['cus_00000000-0000-0000-0000-000000000000']}) + self.assertEqual(mock_requests.last_request.qs, {'customer_uuid': ['cus_00000000-0000-0000-0000-000000000000'], 'cursor': ['ym9vewfo'], 'per_page': ['1']}) self.assertEqual(mock_requests.last_request.text, None) self.assertEqual(sorted(dir(notes)), sorted(dir(expected))) self.assertTrue(isinstance(notes.entries[0], CustomerNote)) diff --git a/test/api/test_customer_note.py b/test/api/test_customer_note.py index e441345..1fbd34e 100644 --- a/test/api/test_customer_note.py +++ b/test/api/test_customer_note.py @@ -19,7 +19,7 @@ "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", "type": "note", "text": "This is a note", - "authoer_email": "john@xample.com" + "author_email": "john@example.com" } allNotes = {"entries": [note], "cursor": "cursor==", "has_more": False} @@ -34,7 +34,7 @@ class CustomerNoteTestCase(unittest.TestCase): def test_all(self, mock_requests): mock_requests.register_uri( "GET", - "https://api.chartmogul.com/v1/customer_notes?cursor=Ym9veWFo&per_page=1&customer_uuid=cus_00000000-0000-0000-0000-000000000000", + "https://api.chartmogul.com/v1/customer_notes?cursor=ym9vewfo&per_page=1&customer_uuid=cus_00000000-0000-0000-0000-000000000000", status_code=200, json=allNotes, ) @@ -43,7 +43,7 @@ def test_all(self, mock_requests): notes = CustomerNote.all( config, customer_uuid="cus_00000000-0000-0000-0000-000000000000", - cursor="Ym9veWFo", + cursor="ym9vewfo", per_page=1, ).get() expected = CustomerNote._many(**allNotes) @@ -70,27 +70,39 @@ def test_create(self, mock_requests): ) config = Config("token") - CustomerNote.create(config, data=createNote).get() + expected = CustomerNote.create(config, data=createNote).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) self.assertEqual(mock_requests.last_request.json(), createNote) + self.assertTrue(expected, note) @requests_mock.mock() def test_patch(self, mock_requests): + new_note = { + "uuid": "note_00000000-0000-0000-0000-000000000000", + "customer_uuid": "cus_00000000-0000-0000-0000-000000000000", + "type": "note", + "text": "new text", + "call_duration": 0, + "author": "John Doe (john@example.com')" + } mock_requests.register_uri( "PATCH", "https://api.chartmogul.com/v1/customer_notes/note_00000000-0000-0000-0000-000000000000", status_code=200, - json=note, + json=new_note, ) + new_text = {"text": "new text"} + config = Config("token") - CustomerNote.patch( - config, uuid="note_00000000-0000-0000-0000-000000000000", data=createNote + expected = CustomerNote.patch( + config, uuid="note_00000000-0000-0000-0000-000000000000", data=new_text ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) - self.assertEqual(mock_requests.last_request.json(), createNote) + self.assertEqual(mock_requests.last_request.json(), new_text) + self.assertTrue(isinstance(expected, CustomerNote)) @requests_mock.mock() def test_retrieve(self, mock_requests): @@ -116,12 +128,13 @@ def test_destroy(self, mock_requests): "DELETE", "https://api.chartmogul.com/v1/customer_notes/note_00000000-0000-0000-0000-000000000000", status_code=200, - json=note, + json={}, ) config = Config("token") - CustomerNote.destroy( + expected = CustomerNote.destroy( config, uuid="note_00000000-0000-0000-0000-000000000000" ).get() self.assertEqual(mock_requests.call_count, 1, "expected call") self.assertEqual(mock_requests.last_request.qs, {}) + self.assertTrue(expected, {}) From c74dfd7522446f44e2ba09e758b7e14e21e4a1fe Mon Sep 17 00:00:00 2001 From: SoeunSona <78331453+SoeunSona@users.noreply.github.com> Date: Wed, 20 Dec 2023 09:14:41 +0100 Subject: [PATCH 4/4] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 484f66c..442ac6b 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ chartmogul.Customer.connectSubscriptions(config, uuid='cus_5915ee5a-babd-406b-b8 chartmogul.Customer.contacts(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', cursor='aabbcc', per_page=20) chartmogul.Customer.createContact(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={}) chartmogul.Customer.notes(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', cursor='aabbcc', per_page=20) -chartmogul.Customer.createrNote(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={}) +chartmogul.Customer.createNote(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={}) ``` #### [Contacts](https://dev.chartmogul.com/docs/contacts)