From 3e987818456378d4b8995bd167597a714f6957d5 Mon Sep 17 00:00:00 2001 From: Pierre Date: Mon, 14 Oct 2019 09:32:06 +0200 Subject: [PATCH 1/2] Fix few paths of the products API Fix few paths of the products API client. Some were incomplete such as: - [POST] `crm-objects/v1/{product_id}` -> `crm-objects/v1/objects/products/{product_id}` Cf: https://developers.hubspot.com/docs/methods/products/update-products Also add `test_products`. --- hubspot3/products.py | 20 +-- hubspot3/test/test_products.py | 239 +++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 8 deletions(-) create mode 100644 hubspot3/test/test_products.py diff --git a/hubspot3/products.py b/hubspot3/products.py index c700197..5afaa91 100644 --- a/hubspot3/products.py +++ b/hubspot3/products.py @@ -12,7 +12,6 @@ class ProductsClient(BaseClient): """ Products extension for products API endpoint - THIS API ENDPOINT IS ONLY A PREVIEW AND IS SUBJECT TO CHANGE :see: https://developers.hubspot.com/docs/methods/products/products-overview """ @@ -72,17 +71,22 @@ def _get_path(self, subpath: str): def create(self, data=None, **options): """Create a new product.""" data = data or {} - - # See: https://developers.hubspot.com/docs/methods/products/create-product - data = [{"name": name, "value": value} for name, value in data.items()] - - return self._call("", data=data, method="POST", **options) + return self._call("objects/products", data=data, method="POST", **options) def update(self, product_id, data=None, **options): """Update a product based on its product ID.""" data = data or {} - return self._call("{}".format(product_id), data=data, method="POST", **options) + return self._call( + "objects/products/{product_id}".format(product_id=product_id), + data=data, + method="PUT", + **options, + ) def delete(self, product_id, **options): """Delete a product based on its product ID.""" - return self._call("{}".format(product_id), method="DELETE", **options) + return self._call( + "objects/products/{product_id}".format(product_id=product_id), + method="DELETE", + **options, + ) diff --git a/hubspot3/test/test_products.py b/hubspot3/test/test_products.py new file mode 100644 index 0000000..e83143c --- /dev/null +++ b/hubspot3/test/test_products.py @@ -0,0 +1,239 @@ +""" +testing hubspot3.products +""" +import json +import warnings +from unittest.mock import Mock, patch + +import pytest + +from hubspot3 import products + + +@pytest.fixture +def products_client(mock_connection): + client = products.ProductsClient(disable_auth=True) + client.options['connection_type'] = Mock(return_value=mock_connection) + return client + + +# The product id used in our tests. +HUBSPOT_PRODUCT_ID = 1642767 + + +class TestProductsClient(object): + """Performs few tests on the Hubspot ProductsClient.""" + + def test_get_path(self): + client = products.ProductsClient(disable_auth=True) + assert client._get_path("objects/products/42") == "crm-objects/v1/objects/products/42" + assert client._get_path("objects/products/paged") == "crm-objects/v1/objects/products/paged" + + def test_get_product(self, products_client, mock_connection): + """Test to retrieve a product from Hubspot by using the ProductsClient.""" + response_body = {"objectType": "PRODUCT", "objectId": HUBSPOT_PRODUCT_ID} + + mock_connection.set_response(200, json.dumps(response_body)) + response = products_client.get_product(str(HUBSPOT_PRODUCT_ID)) + + mock_connection.assert_num_requests(1) + mock_connection.assert_has_request( + method='GET', + # Notes: name and description properties as the one returned by default. + url='/crm-objects/v1/objects/products/1642767?properties=name&properties=description', + data=None, + ) + assert response == response_body + + def test_get_product_with_extra_properties(self, products_client, mock_connection): + """ + Test to retrieve a product from Hubspot by using the ProductsClient. + + By default, only `name` and `description` properties will be returned if no `properties` is given to the method. + In this test, we will ask for both 'price' and 'duration' extra properties. + """ + response_body = {"objectType": "PRODUCT", "objectId": HUBSPOT_PRODUCT_ID} + + mock_connection.set_response(200, json.dumps(response_body)) + response = products_client.get_product( + str(HUBSPOT_PRODUCT_ID), + ['price', 'duration'], + ) + + mock_connection.assert_num_requests(1) + mock_connection.assert_has_request( + method='GET', + url='/crm-objects/v1/objects/products/1642767?properties=name&properties=description&properties=price&properties=duration', # noqa: E501 + data=None, + ) + assert response == response_body + + def test_get_all_products(self, products_client, mock_connection): + """Test to retrieve all the products from Hubspot with the default properties.""" + response_body = { + "objects": [ + { + "objectType": "PRODUCT", + "portalId": 62515, + "objectId": 1642736, + "properties": { + "name": { + "value": "An updated product", + "timestamp": 1525287810508, + "source": "API", + "sourceId": None, + }, + "description": { + "value": "This product has a name.", + "timestamp": 1525287810508, + "source": "API", + "sourceId": None, + }, + }, + "version": 2, + "isDeleted": False, + }, + { + "objectType": "PRODUCT", + "portalId": 62515, + "objectId": 1642767, + "properties": { + "description": { + "value": "This product don't have a name.", + "timestamp": 1525287810508, + "source": "API", + "sourceId": None, + }, + }, + "version": 1, + "isDeleted": False, + }, + { + "objectType": "PRODUCT", + "portalId": 62515, + "objectId": 1642796, + "properties": {}, + "version": 2, + "isDeleted": False, + }, + ], + "hasMore": False, + "offset": 1642796, + } + + mock_connection.set_response(200, json.dumps(response_body)) + response = products_client.get_all_products() + + mock_connection.assert_num_requests(1) + mock_connection.assert_has_request( + method='GET', + url='/crm-objects/v1/objects/products/paged?limit=100&offset=0&properties=name&properties=description', + data=None, + ) + assert response == [{ + 'id': 1642736, + 'name': 'An updated product', + 'description': "This product has a name.", + }, { + 'id': 1642767, + 'description': "This product don't have a name.", + }, { + 'id': 1642796, + }] + + def test_create_product(self, products_client, mock_connection): + """Test to create a new product on Hubspot.""" + product_name = "A new product" + product_description = "A product to be created" + + response_body = { + "objectType": "PRODUCT", + "objectId": HUBSPOT_PRODUCT_ID, + "properties": { + "name": { + "value": product_name, + "timestamp": 1525287096980, + "source": "API", + "sourceId": None, + }, + }, + "description": { + "value": product_description, + "timestamp": 1525287096980, + "source": "API", + "sourceId": None, + }, + } + + mock_connection.set_response(200, json.dumps(response_body)) + response = products_client.create(data=[{ + 'name': "name", + 'value': product_name, + }, { + 'name': "description", + 'value': product_description, + }]) + + mock_connection.assert_num_requests(1) + mock_connection.assert_has_request( + method='POST', + url='/crm-objects/v1/objects/products?', + data=[{ + 'name': "name", + 'value': product_name, + }, { + 'name': "description", + 'value': product_description, + }], + ) + assert response == response_body + + def test_update_product(self, products_client, mock_connection): + """Test to update an existing product on Hubspot.""" + product_name = "An updated product" + product_description = "A product to be updated" + + response_body = { + "objectType": "PRODUCT", + "objectId": HUBSPOT_PRODUCT_ID, + "properties": { + "name": { + "value": product_name, + "timestamp": 1525287096980, + "source": "API", + "sourceId": None, + }, + }, + "description": { + "value": product_description, + "timestamp": 1525287096980, + "source": "API", + "sourceId": None, + }, + } + + mock_connection.set_response(200, json.dumps(response_body)) + response = products_client.update( + str(HUBSPOT_PRODUCT_ID), + data=[{ + 'name': "name", + 'value': product_name, + }, { + 'name': "description", + 'value': product_description, + }] + ) + + mock_connection.assert_num_requests(1) + mock_connection.assert_has_request( + method='PUT', + url='/crm-objects/v1/objects/products/{product_id}?'.format(product_id=HUBSPOT_PRODUCT_ID), + data=[{ + 'name': "name", + 'value': product_name, + }, { + 'name': "description", + 'value': product_description, + }], + ) + assert response == response_body From 09299147928c9f799c364d1fced685ddd55adc08 Mon Sep 17 00:00:00 2001 From: Pierre Date: Mon, 14 Oct 2019 11:53:06 +0200 Subject: [PATCH 2/2] Fixup ! Fix black --- hubspot3/products.py | 4 +- hubspot3/test/test_products.py | 111 ++++++++++++++++----------------- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/hubspot3/products.py b/hubspot3/products.py index 5afaa91..287f810 100644 --- a/hubspot3/products.py +++ b/hubspot3/products.py @@ -28,7 +28,7 @@ def get_product(self, product_id: str, properties: List[str] = None, **options): method="GET", params={"properties": ["name", "description", *properties]}, doseq=True, - **options + **options, ) def get_all_products( @@ -49,7 +49,7 @@ def get_all_products( "properties": ["name", "description", *properties], }, doseq=True, - **options + **options, ) output.extend( [ diff --git a/hubspot3/test/test_products.py b/hubspot3/test/test_products.py index e83143c..ce34d3d 100644 --- a/hubspot3/test/test_products.py +++ b/hubspot3/test/test_products.py @@ -2,8 +2,7 @@ testing hubspot3.products """ import json -import warnings -from unittest.mock import Mock, patch +from unittest.mock import Mock import pytest @@ -13,7 +12,7 @@ @pytest.fixture def products_client(mock_connection): client = products.ProductsClient(disable_auth=True) - client.options['connection_type'] = Mock(return_value=mock_connection) + client.options["connection_type"] = Mock(return_value=mock_connection) return client @@ -26,8 +25,14 @@ class TestProductsClient(object): def test_get_path(self): client = products.ProductsClient(disable_auth=True) - assert client._get_path("objects/products/42") == "crm-objects/v1/objects/products/42" - assert client._get_path("objects/products/paged") == "crm-objects/v1/objects/products/paged" + assert ( + client._get_path("objects/products/42") + == "crm-objects/v1/objects/products/42" + ) + assert ( + client._get_path("objects/products/paged") + == "crm-objects/v1/objects/products/paged" + ) def test_get_product(self, products_client, mock_connection): """Test to retrieve a product from Hubspot by using the ProductsClient.""" @@ -38,9 +43,9 @@ def test_get_product(self, products_client, mock_connection): mock_connection.assert_num_requests(1) mock_connection.assert_has_request( - method='GET', + method="GET", # Notes: name and description properties as the one returned by default. - url='/crm-objects/v1/objects/products/1642767?properties=name&properties=description', + url="/crm-objects/v1/objects/products/1642767?properties=name&properties=description", data=None, ) assert response == response_body @@ -56,14 +61,13 @@ def test_get_product_with_extra_properties(self, products_client, mock_connectio mock_connection.set_response(200, json.dumps(response_body)) response = products_client.get_product( - str(HUBSPOT_PRODUCT_ID), - ['price', 'duration'], + str(HUBSPOT_PRODUCT_ID), ["price", "duration"] ) mock_connection.assert_num_requests(1) mock_connection.assert_has_request( - method='GET', - url='/crm-objects/v1/objects/products/1642767?properties=name&properties=description&properties=price&properties=duration', # noqa: E501 + method="GET", + url="/crm-objects/v1/objects/products/1642767?properties=name&properties=description&properties=price&properties=duration", # noqa: E501 data=None, ) assert response == response_body @@ -103,7 +107,7 @@ def test_get_all_products(self, products_client, mock_connection): "timestamp": 1525287810508, "source": "API", "sourceId": None, - }, + } }, "version": 1, "isDeleted": False, @@ -126,20 +130,19 @@ def test_get_all_products(self, products_client, mock_connection): mock_connection.assert_num_requests(1) mock_connection.assert_has_request( - method='GET', - url='/crm-objects/v1/objects/products/paged?limit=100&offset=0&properties=name&properties=description', + method="GET", + url="/crm-objects/v1/objects/products/paged?limit=100&offset=0&properties=name&properties=description", data=None, ) - assert response == [{ - 'id': 1642736, - 'name': 'An updated product', - 'description': "This product has a name.", - }, { - 'id': 1642767, - 'description': "This product don't have a name.", - }, { - 'id': 1642796, - }] + assert response == [ + { + "id": 1642736, + "name": "An updated product", + "description": "This product has a name.", + }, + {"id": 1642767, "description": "This product don't have a name."}, + {"id": 1642796}, + ] def test_create_product(self, products_client, mock_connection): """Test to create a new product on Hubspot.""" @@ -155,7 +158,7 @@ def test_create_product(self, products_client, mock_connection): "timestamp": 1525287096980, "source": "API", "sourceId": None, - }, + } }, "description": { "value": product_description, @@ -166,25 +169,21 @@ def test_create_product(self, products_client, mock_connection): } mock_connection.set_response(200, json.dumps(response_body)) - response = products_client.create(data=[{ - 'name': "name", - 'value': product_name, - }, { - 'name': "description", - 'value': product_description, - }]) + response = products_client.create( + data=[ + {"name": "name", "value": product_name}, + {"name": "description", "value": product_description}, + ] + ) mock_connection.assert_num_requests(1) mock_connection.assert_has_request( - method='POST', - url='/crm-objects/v1/objects/products?', - data=[{ - 'name': "name", - 'value': product_name, - }, { - 'name': "description", - 'value': product_description, - }], + method="POST", + url="/crm-objects/v1/objects/products?", + data=[ + {"name": "name", "value": product_name}, + {"name": "description", "value": product_description}, + ], ) assert response == response_body @@ -202,7 +201,7 @@ def test_update_product(self, products_client, mock_connection): "timestamp": 1525287096980, "source": "API", "sourceId": None, - }, + } }, "description": { "value": product_description, @@ -215,25 +214,21 @@ def test_update_product(self, products_client, mock_connection): mock_connection.set_response(200, json.dumps(response_body)) response = products_client.update( str(HUBSPOT_PRODUCT_ID), - data=[{ - 'name': "name", - 'value': product_name, - }, { - 'name': "description", - 'value': product_description, - }] + data=[ + {"name": "name", "value": product_name}, + {"name": "description", "value": product_description}, + ], ) mock_connection.assert_num_requests(1) mock_connection.assert_has_request( - method='PUT', - url='/crm-objects/v1/objects/products/{product_id}?'.format(product_id=HUBSPOT_PRODUCT_ID), - data=[{ - 'name': "name", - 'value': product_name, - }, { - 'name': "description", - 'value': product_description, - }], + method="PUT", + url="/crm-objects/v1/objects/products/{product_id}?".format( + product_id=HUBSPOT_PRODUCT_ID + ), + data=[ + {"name": "name", "value": product_name}, + {"name": "description", "value": product_description}, + ], ) assert response == response_body