From bcee977aa33d2c2c62b3602abb1c5ec2f5b670a4 Mon Sep 17 00:00:00 2001 From: Sneha Waddankeri Date: Thu, 2 Apr 2020 15:44:10 -0700 Subject: [PATCH 01/17] updating for set quotacell status endpoint --- README.md | 5 +- dynatademand/api.py | 20 ++++++++ .../request/path/set_quotacell_status.json | 27 ++++++++++ dynatademand/validator.py | 3 ++ tests/test_line_items.py | 51 +++++++++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 dynatademand/schemas/request/path/set_quotacell_status.json diff --git a/README.md b/README.md index 7b61138..447be6e 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,10 @@ Links to the Demand API documentation are included for each function. [Launch Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem-launch): launch_line_item(project_id, line_item_id) [Pause Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem-pause): pause_line_item(project_id, line_item_id) [Update Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem): update_line_item(project_id, line_item_id, line_item_data) -[Get Line Item Detailed Report](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/get-detailed-line-item): get_line_item_detailed_report(project_id, line_item_id) +[Get Line Item Detailed Report](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/get-detailed-line-item): get_line_item_detailed_report(project_id, line_item_id) + +### todo: will update this once it is published in developers.dynata.com +[Set QuotaCell Status](): set_quotacell_status(project_id, line_item_id, quota_cell_id, pause) ### Misc Functions diff --git a/dynatademand/api.py b/dynatademand/api.py index 58cbb71..b456213 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -352,6 +352,26 @@ def pause_line_item(self, project_id, line_item_id): ) return response_data + def set_quotacell_status(self, project_id, line_item_id, quota_cell_id, action): + # Stops traffic to a line item. + self.validator.validate_request( + 'set_quotacell_status', + path_data={ + 'extProjectId': '{}'.format(project_id), + 'extLineItemId': '{}'.format(line_item_id), + 'quotaCellId': '{}'.format(quota_cell_id), + 'action': '{}'.format(action), + }, + ) + response_data = self._api_post('/projects/{}/lineItems/{}/quotaCells/{}/{}'.format(project_id, line_item_id, quota_cell_id, action), {}) + if response_data.get('status').get('message') != 'success': + raise DemandAPIError( + "Could not {} quotacell. Demand API responded with: {}".format(action, + response_data + ) + ) + return response_data + def get_line_item(self, project_id, line_item_id): self.validator.validate_request( 'get_line_item', diff --git a/dynatademand/schemas/request/path/set_quotacell_status.json b/dynatademand/schemas/request/path/set_quotacell_status.json new file mode 100644 index 0000000..e4b1d83 --- /dev/null +++ b/dynatademand/schemas/request/path/set_quotacell_status.json @@ -0,0 +1,27 @@ +{ + "type": "object", + "properties": { + "extProjectId": { + "type": "string", + "required": true + }, + "extLineItemId": { + "type": "string", + "required": true + }, + "quotaCellId": { + "type": "string", + "required": true + }, + "action": { + "type": "string", + "required": true + } + }, + "required": [ + "extProjectId", + "extLineItemId", + "quotaCellId", + "action" + ] +} \ No newline at end of file diff --git a/dynatademand/validator.py b/dynatademand/validator.py index fef0ab0..c562a77 100644 --- a/dynatademand/validator.py +++ b/dynatademand/validator.py @@ -32,6 +32,9 @@ 'pause_line_item': ['path', ], 'update_line_item': ['path', 'body'], + # quotaCells + 'set_quotacell_status': ['path', ], + # Events 'get_events': ['query', ], 'get_event': ['path', ], diff --git a/tests/test_line_items.py b/tests/test_line_items.py index ce96072..3a8b7c7 100644 --- a/tests/test_line_items.py +++ b/tests/test_line_items.py @@ -159,6 +159,57 @@ def test_pause_line_item(self): self.api.pause_line_item(24, 180) self.assertEqual(len(responses.calls), 2) + @responses.activate + def test_set_quotacell_status(self): + # Tests launching a quotacell. + responses.add( + responses.POST, + '{}/sample/v1/projects/24/lineItems/180/quotaCells/1/launch'.format(BASE_HOST), + json={'status': {'message': 'success'}}, + status=200 + ) + # Response with error launching a quotacell. + responses.add( + responses.POST, + '{}/sample/v1/projects/24/lineItems/180/quotaCells/1/launch'.format(BASE_HOST), + json={'status': {'message': 'error'}}, + status=200 + ) + + # Tests pausing a quotacell. + responses.add( + responses.POST, + '{}/sample/v1/projects/24/lineItems/180/quotaCells/1/pause'.format(BASE_HOST), + json={'status': {'message': 'success'}}, + status=200 + ) + # Response with error for pausing a quotacell + responses.add( + responses.POST, + '{}/sample/v1/projects/24/lineItems/180/quotaCells/1/pause'.format(BASE_HOST), + json={'status': {'message': 'error'}}, + status=200 + ) + + # Test successful response for launch quotacell. + self.api.set_quotacell_status(24, 180, 1, "launch") + self.assertEqual(len(responses.calls), 1) + + + # Test error response for launch quotacell. + with self.assertRaises(DemandAPIError): + self.api.set_quotacell_status(24, 180, 1, "launch") + self.assertEqual(len(responses.calls), 2) + + # Test successful response for pause quotacell. + self.api.set_quotacell_status(24, 180, 1, "pause") + self.assertEqual(len(responses.calls), 3) + + # Test error response for pause quotacell. + with self.assertRaises(DemandAPIError): + self.api.set_quotacell_status(24, 180, 1, "pause") + self.assertEqual(len(responses.calls), 4) + @responses.activate def test_update_line_item(self): # Tests updating a line item. From 5e322bcae84e1074cf18f79277c4beff5ae21455 Mon Sep 17 00:00:00 2001 From: Sneha Waddankeri Date: Thu, 2 Apr 2020 15:52:55 -0700 Subject: [PATCH 02/17] fixing whitespaces and indentation --- dynatademand/api.py | 9 +++++---- tests/test_line_items.py | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dynatademand/api.py b/dynatademand/api.py index b456213..b210a66 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -363,11 +363,12 @@ def set_quotacell_status(self, project_id, line_item_id, quota_cell_id, action): 'action': '{}'.format(action), }, ) - response_data = self._api_post('/projects/{}/lineItems/{}/quotaCells/{}/{}'.format(project_id, line_item_id, quota_cell_id, action), {}) + response_data = self._api_post('/projects/{}/lineItems/{}/quotaCells/{}/{}'.format( + project_id, line_item_id, quota_cell_id, action), {}) if response_data.get('status').get('message') != 'success': - raise DemandAPIError( - "Could not {} quotacell. Demand API responded with: {}".format(action, - response_data + raise DemandAPIError( + "Could not {} quotacell. Demand API responded with: {}".format( + action,response_data ) ) return response_data diff --git a/tests/test_line_items.py b/tests/test_line_items.py index 3a8b7c7..3d7d575 100644 --- a/tests/test_line_items.py +++ b/tests/test_line_items.py @@ -175,7 +175,6 @@ def test_set_quotacell_status(self): json={'status': {'message': 'error'}}, status=200 ) - # Tests pausing a quotacell. responses.add( responses.POST, @@ -195,7 +194,6 @@ def test_set_quotacell_status(self): self.api.set_quotacell_status(24, 180, 1, "launch") self.assertEqual(len(responses.calls), 1) - # Test error response for launch quotacell. with self.assertRaises(DemandAPIError): self.api.set_quotacell_status(24, 180, 1, "launch") From a19115f076b4b882b5b3f9ea4ae5c273d3942fdc Mon Sep 17 00:00:00 2001 From: Sneha Waddankeri Date: Thu, 2 Apr 2020 16:08:18 -0700 Subject: [PATCH 03/17] indentation fix --- dynatademand/api.py | 4 ++-- tests/test_line_items.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dynatademand/api.py b/dynatademand/api.py index b210a66..0a468b7 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -366,9 +366,9 @@ def set_quotacell_status(self, project_id, line_item_id, quota_cell_id, action): response_data = self._api_post('/projects/{}/lineItems/{}/quotaCells/{}/{}'.format( project_id, line_item_id, quota_cell_id, action), {}) if response_data.get('status').get('message') != 'success': - raise DemandAPIError( + raise DemandAPIError( "Could not {} quotacell. Demand API responded with: {}".format( - action,response_data + action, response_data ) ) return response_data diff --git a/tests/test_line_items.py b/tests/test_line_items.py index 3d7d575..33e9d32 100644 --- a/tests/test_line_items.py +++ b/tests/test_line_items.py @@ -193,7 +193,7 @@ def test_set_quotacell_status(self): # Test successful response for launch quotacell. self.api.set_quotacell_status(24, 180, 1, "launch") self.assertEqual(len(responses.calls), 1) - + # Test error response for launch quotacell. with self.assertRaises(DemandAPIError): self.api.set_quotacell_status(24, 180, 1, "launch") From 288ee1f28cbaf0a65e14a3da93d9f82ea6ac0afc Mon Sep 17 00:00:00 2001 From: Uthayakumar Kumarasamy Date: Tue, 14 Apr 2020 08:27:51 -0700 Subject: [PATCH 04/17] added targeting template APIs --- dynatademand/api.py | 52 +++++++ .../schemas/request/body/create_template.json | 131 ++++++++++++++++++ .../schemas/request/body/update_template.json | 131 ++++++++++++++++++ .../schemas/request/path/create_template.json | 12 ++ .../schemas/request/path/delete_template.json | 12 ++ .../schemas/request/path/get_templates.json | 17 +++ .../schemas/request/path/update_template.json | 12 ++ .../schemas/request/query/get_templates.json | 41 ++++++ dynatademand/validator.py | 6 + tests/test_files/create_template.json | 58 ++++++++ tests/test_files/get_templates.json | 4 + tests/test_files/update_template.json | 58 ++++++++ tests/test_template.py | 88 ++++++++++++ 13 files changed, 622 insertions(+) create mode 100644 dynatademand/schemas/request/body/create_template.json create mode 100644 dynatademand/schemas/request/body/update_template.json create mode 100644 dynatademand/schemas/request/path/create_template.json create mode 100644 dynatademand/schemas/request/path/delete_template.json create mode 100644 dynatademand/schemas/request/path/get_templates.json create mode 100644 dynatademand/schemas/request/path/update_template.json create mode 100644 dynatademand/schemas/request/query/get_templates.json create mode 100644 tests/test_files/create_template.json create mode 100644 tests/test_files/get_templates.json create mode 100644 tests/test_files/update_template.json create mode 100644 tests/test_template.py diff --git a/dynatademand/api.py b/dynatademand/api.py index 58cbb71..0599ef9 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -71,6 +71,23 @@ def _api_get(self, uri, query_params=None): if response.headers['content-type'] == 'application/pdf': return response.content return response.json() + + def _api_delete(self, uri): + # Send an authenticated DELETE request to an API endpoint. + self._check_authentication() + url = '{}{}'.format(self.base_url, uri) + request_headers = { + 'Authorization': 'Bearer {}'.format(self._access_token), + 'Content-Type': "application/json", + } + response = requests.delete(url=url, headers=request_headers) + if response.status_code > 399: + raise DemandAPIError('Demand API request to {} failed with status {}. Response: {}'.format( + url, response.status_code, response.content + )) + if response.headers['content-type'] == 'application/pdf': + return response.content + return response.json() def authenticate(self): # Sends the authentication data to the access token endpoint. @@ -461,3 +478,38 @@ def reconcile_project(self, project_id, file, message): url, response.status_code, response.content )) return response.json() + + def create_template(self, template): + # TODO: Waiting on a valid path and request body schema. + # self.validator.validate_request( + # 'create_template', + # request_body=template, + # ) + return self._api_post('/templates/quotaplan', template) + + def update_template(self, id, template): + # TODO: Waiting on a valid path and request body schema. + # self.validator.validate_request( + # 'update_template', + # path_data={'id': '{}'.format(id)}, + # request_body=template, + # ) + return self._api_post('/templates/quotaplan/{}'.format(id), template) + + def delete_template(self, id): + self.validator.validate_request( + 'delete_template', + path_data={'id': '{}'.format(id)}, + ) + return self._api_delete('/templates/quotaplan/{}'.format(id)) + + def get_templates(self, country, lang, **kwargs): + self.validator.validate_request( + 'get_templates', + path_data={ + 'countryCode': '{}'.format(country), + 'languageCode': '{}'.format(lang) + }, + query_params=kwargs, + ) + return self._api_get('/templates/quotaplan/{}/{}'.format(country, lang), kwargs) diff --git a/dynatademand/schemas/request/body/create_template.json b/dynatademand/schemas/request/body/create_template.json new file mode 100644 index 0000000..1d0f011 --- /dev/null +++ b/dynatademand/schemas/request/body/create_template.json @@ -0,0 +1,131 @@ +{ + "type": "object", + "title": "New Template", + "properties": { + "name": { + "type": "string", + "description": "Name of the template" + }, + "description": { + "type": "string", + "description": "template description" + }, + "countryISOCode": { + "type": "string", + "description": "2 letter ISO Country Code", + "minLength": 2, + "maxLength": 2 + }, + "languageISOCode": { + "type": "string", + "description": "2 letter ISO Language Code", + "minLength": 2, + "maxLength": 2 + }, + "tags": { + "type": "array", + "description": "keyword tags" + }, + "quotaPlan": { + "type": "object", + "title": "Quota Plan", + "description": "Defines the type of respondents you want to invite for the survey", + "properties": { + "filters": { + "type": "array", + "description": "Filters are minimum set of targeting that every respondent must have in order to qualify for the study. Only attributes that have `isAllowedInFilters = true` is allowed to be used in `filters`", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "description": "The attribute you want to target respondents on" + }, + "options": { + "type": "array", + "description": "The options of the attribute you want to target respondents on", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "operator": { + "type": "string", + "enum": [ + "exclude", + "include" + ], + "default": "include", + "description": "The operator to use for the attribute options." + } + } + } + }, + "quotaGroups": { + "type": "array", + "description": "Quota groups define the allocated targeting attributes for panelists within this line item. Only attributes that have `isAllowedInQuotas = true` is allowed in `quotaGroups`.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A quota group name of your choosing" + }, + "quotaCells": { + "type": "array", + "description": "Quota Cells define the percentage allocation for the required targeting. A quota cell is made up of a collection of quota Nodes", + "items": { + "type": "object", + "properties": { + "quotaNodes": { + "type": "array", + "description": "Quota Nodes define the collection of attributes and options being targeted.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "description": "The attribute you want to target respondents on" + }, + "options": { + "type": "array", + "description": "The options of the attribute you want to target respondents on", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "operator": { + "type": "string", + "enum": [ + "exclude", + "include" + ], + "default": "include", + "description": "**Deprecated field** The operator to use for the attribute options." + } + } + } + }, + "count": { + "type": "integer", + "description": "The count of respondents you want to qualify for the defined quota cell" + } + } + } + } + } + } + } + } + } + }, + "required": [ + "name", + "countryISOCode", + "languageISOCode", + "description", + "quotaPlan" + ] + } + \ No newline at end of file diff --git a/dynatademand/schemas/request/body/update_template.json b/dynatademand/schemas/request/body/update_template.json new file mode 100644 index 0000000..1d0f011 --- /dev/null +++ b/dynatademand/schemas/request/body/update_template.json @@ -0,0 +1,131 @@ +{ + "type": "object", + "title": "New Template", + "properties": { + "name": { + "type": "string", + "description": "Name of the template" + }, + "description": { + "type": "string", + "description": "template description" + }, + "countryISOCode": { + "type": "string", + "description": "2 letter ISO Country Code", + "minLength": 2, + "maxLength": 2 + }, + "languageISOCode": { + "type": "string", + "description": "2 letter ISO Language Code", + "minLength": 2, + "maxLength": 2 + }, + "tags": { + "type": "array", + "description": "keyword tags" + }, + "quotaPlan": { + "type": "object", + "title": "Quota Plan", + "description": "Defines the type of respondents you want to invite for the survey", + "properties": { + "filters": { + "type": "array", + "description": "Filters are minimum set of targeting that every respondent must have in order to qualify for the study. Only attributes that have `isAllowedInFilters = true` is allowed to be used in `filters`", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "description": "The attribute you want to target respondents on" + }, + "options": { + "type": "array", + "description": "The options of the attribute you want to target respondents on", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "operator": { + "type": "string", + "enum": [ + "exclude", + "include" + ], + "default": "include", + "description": "The operator to use for the attribute options." + } + } + } + }, + "quotaGroups": { + "type": "array", + "description": "Quota groups define the allocated targeting attributes for panelists within this line item. Only attributes that have `isAllowedInQuotas = true` is allowed in `quotaGroups`.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A quota group name of your choosing" + }, + "quotaCells": { + "type": "array", + "description": "Quota Cells define the percentage allocation for the required targeting. A quota cell is made up of a collection of quota Nodes", + "items": { + "type": "object", + "properties": { + "quotaNodes": { + "type": "array", + "description": "Quota Nodes define the collection of attributes and options being targeted.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "description": "The attribute you want to target respondents on" + }, + "options": { + "type": "array", + "description": "The options of the attribute you want to target respondents on", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "operator": { + "type": "string", + "enum": [ + "exclude", + "include" + ], + "default": "include", + "description": "**Deprecated field** The operator to use for the attribute options." + } + } + } + }, + "count": { + "type": "integer", + "description": "The count of respondents you want to qualify for the defined quota cell" + } + } + } + } + } + } + } + } + } + }, + "required": [ + "name", + "countryISOCode", + "languageISOCode", + "description", + "quotaPlan" + ] + } + \ No newline at end of file diff --git a/dynatademand/schemas/request/path/create_template.json b/dynatademand/schemas/request/path/create_template.json new file mode 100644 index 0000000..ee03c17 --- /dev/null +++ b/dynatademand/schemas/request/path/create_template.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string", + "required": true + } + }, + "required": [ + "name" + ] + } \ No newline at end of file diff --git a/dynatademand/schemas/request/path/delete_template.json b/dynatademand/schemas/request/path/delete_template.json new file mode 100644 index 0000000..70f4d06 --- /dev/null +++ b/dynatademand/schemas/request/path/delete_template.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "id": { + "type": "integer", + "required": true + } + }, + "required": [ + "id" + ] +} \ No newline at end of file diff --git a/dynatademand/schemas/request/path/get_templates.json b/dynatademand/schemas/request/path/get_templates.json new file mode 100644 index 0000000..f9bd6f9 --- /dev/null +++ b/dynatademand/schemas/request/path/get_templates.json @@ -0,0 +1,17 @@ +{ + "type": "object", + "properties": { + "countryCode": { + "type": "string", + "required": true + }, + "languageCode": { + "type": "string", + "required": true + } + }, + "required": [ + "countryCode", + "languageCode" + ] +} \ No newline at end of file diff --git a/dynatademand/schemas/request/path/update_template.json b/dynatademand/schemas/request/path/update_template.json new file mode 100644 index 0000000..ee03c17 --- /dev/null +++ b/dynatademand/schemas/request/path/update_template.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string", + "required": true + } + }, + "required": [ + "name" + ] + } \ No newline at end of file diff --git a/dynatademand/schemas/request/query/get_templates.json b/dynatademand/schemas/request/query/get_templates.json new file mode 100644 index 0000000..cf0b7aa --- /dev/null +++ b/dynatademand/schemas/request/query/get_templates.json @@ -0,0 +1,41 @@ +{ + "type": "object", + "properties": { + "limit": { + "type": "integer", + "format": "int32", + "default": 10, + "description": "The number of results to return when viewing a page. For example, setting limit to 20 means that, at most, 20 results will be returned in the request.", + "maximum": 100 + }, + "offset": { + "type": "integer", + "format": "int32", + "default": 0, + "description": "The number of results to skip before returning results. For example, setting an offset of 20 means that the API will discard the first 20 results." + }, + "created_at": { + "type": "string", + "format": "date" + }, + "sort": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "tags": { + "type": "array" + } + }, + "required": [] +} \ No newline at end of file diff --git a/dynatademand/validator.py b/dynatademand/validator.py index fef0ab0..a560f04 100644 --- a/dynatademand/validator.py +++ b/dynatademand/validator.py @@ -50,6 +50,12 @@ # Supplier Sources 'get_sources': [], + + # Template + 'create_template': ['path', 'body', ], + 'update_template': ['path', 'body', ], + 'delete_template': ['path', ], + 'get_templates': ['path', 'query', ], } diff --git a/tests/test_files/create_template.json b/tests/test_files/create_template.json new file mode 100644 index 0000000..49f18f3 --- /dev/null +++ b/tests/test_files/create_template.json @@ -0,0 +1,58 @@ +{ + "countryISOCode": "US", + "languageISOCode": "en", + "description": "Test Quota Template", + "name": "Test Template", + "quotaPlan": { + "filters": [ + { + "attributeId": "4091", + "options": [ + "3", + "4" + ], + "operator": null + } + ], + "quotaGroups": [ + { + "name": "Gender distribution", + "quotaCells": [ + { + "quotaNodes": [ + { + "attributeId": "11", + "options": [ + "1" + ], + "operator": null + } + ], + "count": 0, + "perc": 30, + "quotaCellId": null + }, + { + "quotaNodes": [ + { + "attributeId": "11", + "options": [ + "2" + ], + "operator": null + } + ], + "count": 0, + "perc": 70, + "quotaCellId": null + } + ], + "quotaGroupId": null + } + ] + }, + "tags": [ + "ABC Ltd", + "Shopping" + ] +} \ No newline at end of file diff --git a/tests/test_files/get_templates.json b/tests/test_files/get_templates.json new file mode 100644 index 0000000..00ec81e --- /dev/null +++ b/tests/test_files/get_templates.json @@ -0,0 +1,4 @@ +{ + "limit": 100 +} + \ No newline at end of file diff --git a/tests/test_files/update_template.json b/tests/test_files/update_template.json new file mode 100644 index 0000000..07e2e79 --- /dev/null +++ b/tests/test_files/update_template.json @@ -0,0 +1,58 @@ +{ + "countryISOCode": "US", + "languageISOCode": "en", + "description": "Test Quota Template", + "name": "Test Template Update", + "quotaPlan": { + "filters": [ + { + "attributeId": "4091", + "options": [ + "3", + "4" + ], + "operator": null + } + ], + "quotaGroups": [ + { + "name": "Gender distribution", + "quotaCells": [ + { + "quotaNodes": [ + { + "attributeId": "11", + "options": [ + "1" + ], + "operator": null + } + ], + "count": 0, + "perc": 40, + "quotaCellId": null + }, + { + "quotaNodes": [ + { + "attributeId": "11", + "options": [ + "2" + ], + "operator": null + } + ], + "count": 0, + "perc": 60, + "quotaCellId": null + } + ], + "quotaGroupId": null + } + ] + }, + "tags": [ + "ABC Ltd", + "Shopping" + ] +} \ No newline at end of file diff --git a/tests/test_template.py b/tests/test_template.py new file mode 100644 index 0000000..42a3b1d --- /dev/null +++ b/tests/test_template.py @@ -0,0 +1,88 @@ +# encoding: utf-8 +from __future__ import unicode_literals, print_function + +import json +import unittest +import responses + +from dynatademand.api import DemandAPIClient +from dynatademand.errors import DemandAPIError + +BASE_HOST = "http://test-url.example" + +class TestTemplateEndpoints(unittest.TestCase): + def setUp(self): + self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) + self.api._access_token = 'Bearer testtoken' + + @responses.activate + def test_get_templates(self): + # Tests getting all templates. + with open('./tests/test_files/get_templates.json', 'r') as options: + options_json = json.load(options) + responses.add(responses.GET, '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST, 'US', 'en'), json=options_json, status=200) + self.api.get_templates('US', 'en') + self.assertEqual(len(responses.calls), 1) + + @responses.activate + def test_create_template(self): + # Tests creating a template. + with open('./tests/test_files/create_template.json', 'r') as new_template_file: + new_template_data = json.load(new_template_file) + # Success response + responses.add( + responses.POST, + '{}/sample/v1/templates/quotaplan'.format(BASE_HOST), + json={'status': {'message': 'success'}}, + status=200) + # Response with error status + responses.add( + responses.POST, + '{}/sample/v1/templates/quotaplan'.format(BASE_HOST), + json={'status': {'message': 'error'}}, + status=200) + # Test success response + self.api.create_template(new_template_data) + self.assertEqual(len(responses.calls), 1) + + @responses.activate + def test_update_template(self): + # Tests creating a template. + with open('./tests/test_files/update_template.json', 'r') as new_template_file: + new_template_data = json.load(new_template_file) + # Success response + responses.add( + responses.POST, + '{}/sample/v1/templates/quotaplan/{}'.format(BASE_HOST, 1), + json={'status': {'message': 'success'}}, + status=200) + # Response with error status + responses.add( + responses.POST, + '{}/sample/v1/templates/quotaplan/{}'.format(BASE_HOST, 1), + json={'status': {'message': 'error'}}, + status=200) + # Test success response + self.api.update_template(1, new_template_data) + self.assertEqual(len(responses.calls), 1) + + + @responses.activate + def test_delete_template(self): + # Tests deleteing templates + responses.add( + responses.DELETE, + '{}/sample/v1/templates/quotaplan/1'.format(BASE_HOST), + json={'status': {'message': 'success'}}, + status=200 + ) + # Response with error status + responses.add( + responses.DELETE, + '{}/sample/v1/templates/quotaplan/1'.format(BASE_HOST), + json={'status': {'message': 'error'}}, + status=200 + ) + # Test successful response + self.api.delete_template(1) + self.assertEqual(len(responses.calls), 1) From c87f8d920602b5a6bf4445423614bd7d6d41033a Mon Sep 17 00:00:00 2001 From: Uthayakumar Kumarasamy Date: Tue, 14 Apr 2020 14:28:11 -0700 Subject: [PATCH 05/17] fix lint --- dynatademand/api.py | 8 ++++---- tests/test_template.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dynatademand/api.py b/dynatademand/api.py index 0599ef9..5787409 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -71,7 +71,7 @@ def _api_get(self, uri, query_params=None): if response.headers['content-type'] == 'application/pdf': return response.content return response.json() - + def _api_delete(self, uri): # Send an authenticated DELETE request to an API endpoint. self._check_authentication() @@ -486,7 +486,7 @@ def create_template(self, template): # request_body=template, # ) return self._api_post('/templates/quotaplan', template) - + def update_template(self, id, template): # TODO: Waiting on a valid path and request body schema. # self.validator.validate_request( @@ -495,7 +495,7 @@ def update_template(self, id, template): # request_body=template, # ) return self._api_post('/templates/quotaplan/{}'.format(id), template) - + def delete_template(self, id): self.validator.validate_request( 'delete_template', @@ -512,4 +512,4 @@ def get_templates(self, country, lang, **kwargs): }, query_params=kwargs, ) - return self._api_get('/templates/quotaplan/{}/{}'.format(country, lang), kwargs) + return self._api_get('/templates/quotaplan/{}/{}'.format(country, lang), kwargs) \ No newline at end of file diff --git a/tests/test_template.py b/tests/test_template.py index 42a3b1d..3926a9c 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -6,7 +6,6 @@ import responses from dynatademand.api import DemandAPIClient -from dynatademand.errors import DemandAPIError BASE_HOST = "http://test-url.example" @@ -20,7 +19,9 @@ def test_get_templates(self): # Tests getting all templates. with open('./tests/test_files/get_templates.json', 'r') as options: options_json = json.load(options) - responses.add(responses.GET, '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST, 'US', 'en'), json=options_json, status=200) + responses.add(responses.GET, '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST, 'US', 'en'), + json=options_json, + status=200) self.api.get_templates('US', 'en') self.assertEqual(len(responses.calls), 1) @@ -65,8 +66,7 @@ def test_update_template(self): # Test success response self.api.update_template(1, new_template_data) self.assertEqual(len(responses.calls), 1) - - + @responses.activate def test_delete_template(self): # Tests deleteing templates @@ -85,4 +85,4 @@ def test_delete_template(self): ) # Test successful response self.api.delete_template(1) - self.assertEqual(len(responses.calls), 1) + self.assertEqual(len(responses.calls), 1) \ No newline at end of file From 060827aec7d4458f7d62c390603e31ccdc0dc934 Mon Sep 17 00:00:00 2001 From: Uthayakumar Kumarasamy Date: Tue, 14 Apr 2020 15:15:03 -0700 Subject: [PATCH 06/17] fix lint --- dynatademand/api.py | 6 +++--- tests/test_template.py | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/dynatademand/api.py b/dynatademand/api.py index 5787409..a5a1267 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -498,8 +498,8 @@ def update_template(self, id, template): def delete_template(self, id): self.validator.validate_request( - 'delete_template', - path_data={'id': '{}'.format(id)}, + 'delete_template', + path_data={'id': '{}'.format(id)}, ) return self._api_delete('/templates/quotaplan/{}'.format(id)) @@ -512,4 +512,4 @@ def get_templates(self, country, lang, **kwargs): }, query_params=kwargs, ) - return self._api_get('/templates/quotaplan/{}/{}'.format(country, lang), kwargs) \ No newline at end of file + return self._api_get('/templates/quotaplan/{}/{}'.format(country, lang), kwargs) diff --git a/tests/test_template.py b/tests/test_template.py index 3926a9c..1273fdf 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -9,6 +9,7 @@ BASE_HOST = "http://test-url.example" + class TestTemplateEndpoints(unittest.TestCase): def setUp(self): self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) @@ -19,7 +20,10 @@ def test_get_templates(self): # Tests getting all templates. with open('./tests/test_files/get_templates.json', 'r') as options: options_json = json.load(options) - responses.add(responses.GET, '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST, 'US', 'en'), + # Success response + responses.add( + responses.GET, + '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST,'US','en'), json=options_json, status=200) self.api.get_templates('US', 'en') @@ -74,15 +78,13 @@ def test_delete_template(self): responses.DELETE, '{}/sample/v1/templates/quotaplan/1'.format(BASE_HOST), json={'status': {'message': 'success'}}, - status=200 - ) + status=200) # Response with error status responses.add( responses.DELETE, '{}/sample/v1/templates/quotaplan/1'.format(BASE_HOST), json={'status': {'message': 'error'}}, - status=200 - ) + status=200) # Test successful response self.api.delete_template(1) - self.assertEqual(len(responses.calls), 1) \ No newline at end of file + self.assertEqual(len(responses.calls), 1) From c3301def6d1a0ef0a59b16389cccd5275e312bc6 Mon Sep 17 00:00:00 2001 From: Uthayakumar Kumarasamy Date: Tue, 14 Apr 2020 15:30:38 -0700 Subject: [PATCH 07/17] fix lint --- tests/test_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_template.py b/tests/test_template.py index 1273fdf..c598d08 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -22,10 +22,10 @@ def test_get_templates(self): options_json = json.load(options) # Success response responses.add( - responses.GET, - '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST,'US','en'), - json=options_json, - status=200) + responses.GET, + '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST,'US','en'), + json=options_json, + status=200) self.api.get_templates('US', 'en') self.assertEqual(len(responses.calls), 1) From f0420cfb62291d091816151cb6a32a6208be08a7 Mon Sep 17 00:00:00 2001 From: Uthayakumar Kumarasamy Date: Tue, 14 Apr 2020 15:36:27 -0700 Subject: [PATCH 08/17] fix lint --- tests/test_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_template.py b/tests/test_template.py index c598d08..d56ef4a 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -23,7 +23,7 @@ def test_get_templates(self): # Success response responses.add( responses.GET, - '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST,'US','en'), + '{}/sample/v1/templates/quotaplan/{}/{}'.format(BASE_HOST, 'US', 'en'), json=options_json, status=200) self.api.get_templates('US', 'en') From a95856d76afffc34401cbd6129c85503912687dd Mon Sep 17 00:00:00 2001 From: Sneha Waddankeri Date: Fri, 17 Apr 2020 11:04:51 -0700 Subject: [PATCH 09/17] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 447be6e..a9e7510 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,8 @@ Links to the Demand API documentation are included for each function. [Pause Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem-pause): pause_line_item(project_id, line_item_id) [Update Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem): update_line_item(project_id, line_item_id, line_item_data) [Get Line Item Detailed Report](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/get-detailed-line-item): get_line_item_detailed_report(project_id, line_item_id) - -### todo: will update this once it is published in developers.dynata.com -[Set QuotaCell Status](): set_quotacell_status(project_id, line_item_id, quota_cell_id, pause) +[Launch Quota cell](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-quota-cell-launch): set_quotacell_status(project_id, line_item_id, quota_cell_id, launch) +[Pause Quota cell](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-quota-cell-pause): set_quotacell_status(project_id, line_item_id, quota_cell_id, pause) ### Misc Functions From 8485b1ec868b0eddacdd60c03aeb33ba072b40ed Mon Sep 17 00:00:00 2001 From: Yugma Moradia Date: Thu, 3 Sep 2020 18:02:12 -0700 Subject: [PATCH 10/17] Adding python sdks for user management --- dynatademand/api.py | 38 +++++++++++++++++++ .../body/upsert_project_permissions.json | 30 +++++++++++++++ .../request/path/get_project_permissions.json | 12 ++++++ .../path/upsert_project_permissions.json | 12 ++++++ .../schemas/request/query/get_roles.json | 16 ++++++++ dynatademand/validator.py | 5 +++ 6 files changed, 113 insertions(+) create mode 100644 dynatademand/schemas/request/body/upsert_project_permissions.json create mode 100644 dynatademand/schemas/request/path/get_project_permissions.json create mode 100644 dynatademand/schemas/request/path/upsert_project_permissions.json create mode 100644 dynatademand/schemas/request/query/get_roles.json diff --git a/dynatademand/api.py b/dynatademand/api.py index da50910..db554f7 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -291,6 +291,44 @@ def get_project_detailed_report(self, project_id): ) return self._api_get('/projects/{}/detailedReport'.format(project_id)) + def get_user_info(self): + return self._api_get('/users/info') + + def get_company_users(self): + return self._api_get('/users') + + def get_company_teams(self): + return self._api_get('/teams') + + def get_roles(self, **kwargs): + self.validator.validate_request( + 'get_roles', + query_params=kwargs + ) + return self._api_get('/roles', kwargs) + + def get_project_permissions(self, project_id): + self.validator.validate_request( + 'get_project_permissions', + path_data={'extProjectId': '{}'.format(project_id)}, + ) + return self._api_get('/projects/{}/permissions'.format(project_id)) + + def upsert_project_permissions(self, project_id, upsert_permissions_data): + self.validator.validate_request( + 'upsert_project_permissions', + path_data={'extProjectId': '{}'.format(project_id)}, + request_body=upsert_permissions_data, + ) + response_data = self._api_post('/projects/{}/permissions'.format(project_id), upsert_permissions_data) + if response_data.get('status').get('message') != 'success': + raise DemandAPIError( + "Could not upsert project permissions. Demand API responded with: {}".format( + response_data + ) + ) + return response_data + def add_line_item(self, project_id, lineitem_data): ''' A line item is a project entity that exist for a specific market and diff --git a/dynatademand/schemas/request/body/upsert_project_permissions.json b/dynatademand/schemas/request/body/upsert_project_permissions.json new file mode 100644 index 0000000..0a13463 --- /dev/null +++ b/dynatademand/schemas/request/body/upsert_project_permissions.json @@ -0,0 +1,30 @@ +{ + "type": "object", + "properties": { + "users": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + } + }, + "teams": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + } + } + } +} \ No newline at end of file diff --git a/dynatademand/schemas/request/path/get_project_permissions.json b/dynatademand/schemas/request/path/get_project_permissions.json new file mode 100644 index 0000000..fc6287c --- /dev/null +++ b/dynatademand/schemas/request/path/get_project_permissions.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "extProjectId": { + "type": "string", + "required": true + } + }, + "required": [ + "extProjectId" + ] +} \ No newline at end of file diff --git a/dynatademand/schemas/request/path/upsert_project_permissions.json b/dynatademand/schemas/request/path/upsert_project_permissions.json new file mode 100644 index 0000000..fc6287c --- /dev/null +++ b/dynatademand/schemas/request/path/upsert_project_permissions.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "extProjectId": { + "type": "string", + "required": true + } + }, + "required": [ + "extProjectId" + ] +} \ No newline at end of file diff --git a/dynatademand/schemas/request/query/get_roles.json b/dynatademand/schemas/request/query/get_roles.json new file mode 100644 index 0000000..d4eab67 --- /dev/null +++ b/dynatademand/schemas/request/query/get_roles.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "This is the name of the role. eg: manager" + }, + "id": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [] +} \ No newline at end of file diff --git a/dynatademand/validator.py b/dynatademand/validator.py index 7edddff..55f2fe4 100644 --- a/dynatademand/validator.py +++ b/dynatademand/validator.py @@ -17,7 +17,12 @@ 'buy_project': ['path', 'body', ], 'get_project_detailed_report': ['path', ], 'reconcile_project': ['path', ], + 'get_project_permissions':['path', ], + 'upsert_project_permissions':['path', 'body', ], + # Roles + 'get_roles': ['query', ] + # Invoices 'get_invoice': ['path', ], 'get_invoices_summary': ['query', ], From fc6d11362e4baa20dc46e9c2215ac9ffd372623a Mon Sep 17 00:00:00 2001 From: Yugma Moradia Date: Fri, 4 Sep 2020 12:38:12 -0700 Subject: [PATCH 11/17] Adding missing requirements file and adding test cases --- dynatademand/validator.py | 4 +- requirements.txt | 2 + tests/test_files/get_company_users.json | 14 +++++ tests/test_files/get_project_permissions.json | 21 ++++++++ tests/test_files/get_roles.json | 22 ++++++++ tests/test_files/get_teams.json | 11 ++++ tests/test_files/get_user_info.json | 23 ++++++++ .../upsert_project_permissions.json | 21 ++++++++ tests/test_permissions.py | 54 +++++++++++++++++++ tests/test_roles.py | 26 +++++++++ tests/test_teams.py | 26 +++++++++ tests/test_users.py | 36 +++++++++++++ 12 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 requirements.txt create mode 100644 tests/test_files/get_company_users.json create mode 100644 tests/test_files/get_project_permissions.json create mode 100644 tests/test_files/get_roles.json create mode 100644 tests/test_files/get_teams.json create mode 100644 tests/test_files/get_user_info.json create mode 100644 tests/test_files/upsert_project_permissions.json create mode 100644 tests/test_permissions.py create mode 100644 tests/test_roles.py create mode 100644 tests/test_teams.py create mode 100644 tests/test_users.py diff --git a/dynatademand/validator.py b/dynatademand/validator.py index 55f2fe4..b7a0601 100644 --- a/dynatademand/validator.py +++ b/dynatademand/validator.py @@ -21,8 +21,8 @@ 'upsert_project_permissions':['path', 'body', ], # Roles - 'get_roles': ['query', ] - + 'get_roles': ['query', ], + # Invoices 'get_invoice': ['path', ], 'get_invoices_summary': ['query', ], diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..87608f6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +responses>=0.10.0 +jsonschema>=2.5.0 diff --git a/tests/test_files/get_company_users.json b/tests/test_files/get_company_users.json new file mode 100644 index 0000000..91e9dcc --- /dev/null +++ b/tests/test_files/get_company_users.json @@ -0,0 +1,14 @@ +[ + { + "email": "samplifyweb@dynata.com", + "id": 71, + "name": "Bears Beets", + "userName": "battlestargalactica" + }, + { + "email": "samplify@dynata.com", + "id": 102, + "name": "Michael Scott", + "userName": "papercompany" + } +] \ No newline at end of file diff --git a/tests/test_files/get_project_permissions.json b/tests/test_files/get_project_permissions.json new file mode 100644 index 0000000..3ab73ba --- /dev/null +++ b/tests/test_files/get_project_permissions.json @@ -0,0 +1,21 @@ +{ + "currentUser": { + "roles": [ + "PROJECT_MANAGER" + ] + }, + "extProjectId": "1910a9fe-59de-4796-aac6-8b7356a7d340", + "teams": [ + { + "id": 139, + "name": "All Users" + } + ], + "users": [ + { + "id": 71, + "role": "PROJECT_MANAGER", + "username": "samplifyweb" + } + ] +} \ No newline at end of file diff --git a/tests/test_files/get_roles.json b/tests/test_files/get_roles.json new file mode 100644 index 0000000..3c372ff --- /dev/null +++ b/tests/test_files/get_roles.json @@ -0,0 +1,22 @@ +[ + { + "allowedActions": [ + { + "action": "GET PROJECT", + "description": "Ability to view all the project details", + "id": "GET_PROJECT" + }, + { + "action": "LIST ALL PROJECTS", + "description": "Ability to view a list of projects on samplify", + "id": "LIST_ALL_PROJECTS" + } + ], + "assignableRoles": [ + "SURVEY_AUTHOR" + ], + "description": "", + "id": "SURVEY_AUTHOR", + "name": "Survey Author" + } +] \ No newline at end of file diff --git a/tests/test_files/get_teams.json b/tests/test_files/get_teams.json new file mode 100644 index 0000000..1fe435d --- /dev/null +++ b/tests/test_files/get_teams.json @@ -0,0 +1,11 @@ +[ + { + "createdAt": "2020/05/06 15:39:03", + "default": true, + "description": "", + "id": 139, + "name": "That's what she said", + "status": "ACTIVE", + "updatedAt": "2020/05/06 15:39:03" + } +] \ No newline at end of file diff --git a/tests/test_files/get_user_info.json b/tests/test_files/get_user_info.json new file mode 100644 index 0000000..60159c5 --- /dev/null +++ b/tests/test_files/get_user_info.json @@ -0,0 +1,23 @@ +{ + "companies": [ + { + "default": true, + "defaultRole": "PROJECT_MANAGER", + "id": 51, + "name": "Samplify Web", + "teams": [ + { + "default": true, + "id": 139, + "name": "All Users", + "role": "PROJECT_MANAGER", + "status": "ACTIVE" + } + ] + } + ], + "email": "samplifyweb@dynata.com", + "fullName": "Monica Faloola Gellar", + "id": 71, + "userName": "monicagellarrr" +} \ No newline at end of file diff --git a/tests/test_files/upsert_project_permissions.json b/tests/test_files/upsert_project_permissions.json new file mode 100644 index 0000000..3ab73ba --- /dev/null +++ b/tests/test_files/upsert_project_permissions.json @@ -0,0 +1,21 @@ +{ + "currentUser": { + "roles": [ + "PROJECT_MANAGER" + ] + }, + "extProjectId": "1910a9fe-59de-4796-aac6-8b7356a7d340", + "teams": [ + { + "id": 139, + "name": "All Users" + } + ], + "users": [ + { + "id": 71, + "role": "PROJECT_MANAGER", + "username": "samplifyweb" + } + ] +} \ No newline at end of file diff --git a/tests/test_permissions.py b/tests/test_permissions.py new file mode 100644 index 0000000..f2c48c3 --- /dev/null +++ b/tests/test_permissions.py @@ -0,0 +1,54 @@ +# encoding: utf-8 +from __future__ import unicode_literals, print_function + +import json +import unittest +import responses + +from dynatademand.api import DemandAPIClient +from dynatademand.errors import DemandAPIError + +BASE_HOST = "http://test-url.example" + + +class TestUsersEndpoints(unittest.TestCase): + def setUp(self): + self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) + self.api._access_token = 'Bearer testtoken' + + @responses.activate + def test_get_project_permissions(self): + with open('./tests/test_files/get_project_permissions.json', 'r') as get_permissions_file: + permissions_json = json.load(get_permissions_file) + responses.add(responses.GET, '{}/sample/v1/projects/1/permissions'.format(BASE_HOST), json=permissions_json, status=200) + self.api.get_project_permissions(1) + self.assertEqual(len(responses.calls), 1) + self.assertEqual(responses.calls[0].response.json(), permissions_json) + + @responses.activate + def test_upsert_project_permissions(self): + # Tests updating a project. + with open('./tests/test_files/upsert_project_permissions.json', 'r') as upsert_project_file: + upsert_project_data = json.load(upsert_project_file) + + # Success response + responses.add( + responses.POST, + '{}/sample/v1/projects/1/permissions'.format(BASE_HOST), + json={'status': {'message': 'success'}}, + status=200) + # Error message included + responses.add( + responses.POST, + '{}/sample/v1/projects/1/permissions'.format(BASE_HOST), + json={'status': {'message': 'error'}}, + status=200) + + # Test successful response. + self.api.upsert_project_permissions(1, upsert_project_data) + self.assertEqual(len(responses.calls), 1) + + # Test response with error included. + with self.assertRaises(DemandAPIError): + self.api.upsert_project_permissions(1, upsert_project_data) + self.assertEqual(len(responses.calls), 2) \ No newline at end of file diff --git a/tests/test_roles.py b/tests/test_roles.py new file mode 100644 index 0000000..20b13b1 --- /dev/null +++ b/tests/test_roles.py @@ -0,0 +1,26 @@ +# encoding: utf-8 +from __future__ import unicode_literals, print_function + +import json +import unittest +import responses + +from dynatademand.api import DemandAPIClient + +BASE_HOST = "http://test-url.example" + + +class TestUsersEndpoints(unittest.TestCase): + def setUp(self): + self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) + self.api._access_token = 'Bearer testtoken' + + @responses.activate + def test_get_roles(self): + # Tests getting all roles. + with open('./tests/test_files/get_roles.json', 'r') as roles: + roles_json = json.load(roles) + # Success response + responses.add(responses.GET,'{}/sample/v1/roles'.format(BASE_HOST), json=roles_json, status=200) + self.api.get_roles() + self.assertEqual(len(responses.calls), 1) \ No newline at end of file diff --git a/tests/test_teams.py b/tests/test_teams.py new file mode 100644 index 0000000..7b48db7 --- /dev/null +++ b/tests/test_teams.py @@ -0,0 +1,26 @@ +# encoding: utf-8 +from __future__ import unicode_literals, print_function + +import json +import unittest +import responses + +from dynatademand.api import DemandAPIClient + +BASE_HOST = "http://test-url.example" + + +class TestUsersEndpoints(unittest.TestCase): + def setUp(self): + self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) + self.api._access_token = 'Bearer testtoken' + + @responses.activate + def test_get_teams(self): + # Tests getting all teams. + with open('./tests/test_files/get_teams.json', 'r') as teams: + teams_json = json.load(teams) + # Success response + responses.add(responses.GET,'{}/sample/v1/teams'.format(BASE_HOST), json=teams_json, status=200) + self.api.get_company_teams() + self.assertEqual(len(responses.calls), 1) \ No newline at end of file diff --git a/tests/test_users.py b/tests/test_users.py new file mode 100644 index 0000000..e689ae4 --- /dev/null +++ b/tests/test_users.py @@ -0,0 +1,36 @@ +# encoding: utf-8 +from __future__ import unicode_literals, print_function + +import json +import unittest +import responses + +from dynatademand.api import DemandAPIClient + +BASE_HOST = "http://test-url.example" + + +class TestUsersEndpoints(unittest.TestCase): + def setUp(self): + self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) + self.api._access_token = 'Bearer testtoken' + + @responses.activate + def test_get_user_info(self): + # Tests getting currently logged in user info. + with open('./tests/test_files/get_user_info.json', 'r') as user_info: + user_info_json = json.load(user_info) + # Success response + responses.add(responses.GET,'{}/sample/v1/users/info'.format(BASE_HOST), json=user_info_json, status=200) + self.api.get_user_info() + self.assertEqual(len(responses.calls), 1) + + @responses.activate + def test_get_company_users(self): + # Tests getting all company users. + with open('./tests/test_files/get_company_users.json', 'r') as company_users: + company_users_json = json.load(company_users) + # Success response + responses.add(responses.GET,'{}/sample/v1/users'.format(BASE_HOST), json=company_users_json, status=200) + self.api.get_company_users() + self.assertEqual(len(responses.calls), 1) \ No newline at end of file From c0b60c3fbcbe75e57663d7b519c5f6073d422c4d Mon Sep 17 00:00:00 2001 From: Yugma Moradia Date: Fri, 4 Sep 2020 12:54:08 -0700 Subject: [PATCH 12/17] Fixing the indentations --- .../schemas/request/body/upsert_project_permissions.json | 2 +- .../schemas/request/path/get_project_permissions.json | 2 +- .../schemas/request/path/upsert_project_permissions.json | 2 +- dynatademand/schemas/request/query/get_roles.json | 2 +- tests/test_files/get_company_users.json | 2 +- tests/test_files/get_project_permissions.json | 2 +- tests/test_files/get_roles.json | 2 +- tests/test_files/get_teams.json | 2 +- tests/test_files/get_user_info.json | 2 +- tests/test_files/upsert_project_permissions.json | 2 +- tests/test_permissions.py | 4 ++-- tests/test_roles.py | 4 ++-- tests/test_teams.py | 4 ++-- tests/test_users.py | 2 +- 14 files changed, 17 insertions(+), 17 deletions(-) diff --git a/dynatademand/schemas/request/body/upsert_project_permissions.json b/dynatademand/schemas/request/body/upsert_project_permissions.json index 0a13463..10416d2 100644 --- a/dynatademand/schemas/request/body/upsert_project_permissions.json +++ b/dynatademand/schemas/request/body/upsert_project_permissions.json @@ -27,4 +27,4 @@ } } } -} \ No newline at end of file +} diff --git a/dynatademand/schemas/request/path/get_project_permissions.json b/dynatademand/schemas/request/path/get_project_permissions.json index fc6287c..42fe507 100644 --- a/dynatademand/schemas/request/path/get_project_permissions.json +++ b/dynatademand/schemas/request/path/get_project_permissions.json @@ -9,4 +9,4 @@ "required": [ "extProjectId" ] -} \ No newline at end of file +} diff --git a/dynatademand/schemas/request/path/upsert_project_permissions.json b/dynatademand/schemas/request/path/upsert_project_permissions.json index fc6287c..42fe507 100644 --- a/dynatademand/schemas/request/path/upsert_project_permissions.json +++ b/dynatademand/schemas/request/path/upsert_project_permissions.json @@ -9,4 +9,4 @@ "required": [ "extProjectId" ] -} \ No newline at end of file +} diff --git a/dynatademand/schemas/request/query/get_roles.json b/dynatademand/schemas/request/query/get_roles.json index d4eab67..363c981 100644 --- a/dynatademand/schemas/request/query/get_roles.json +++ b/dynatademand/schemas/request/query/get_roles.json @@ -13,4 +13,4 @@ } }, "required": [] -} \ No newline at end of file +} diff --git a/tests/test_files/get_company_users.json b/tests/test_files/get_company_users.json index 91e9dcc..f8fe240 100644 --- a/tests/test_files/get_company_users.json +++ b/tests/test_files/get_company_users.json @@ -11,4 +11,4 @@ "name": "Michael Scott", "userName": "papercompany" } -] \ No newline at end of file +] diff --git a/tests/test_files/get_project_permissions.json b/tests/test_files/get_project_permissions.json index 3ab73ba..e9a6d36 100644 --- a/tests/test_files/get_project_permissions.json +++ b/tests/test_files/get_project_permissions.json @@ -18,4 +18,4 @@ "username": "samplifyweb" } ] -} \ No newline at end of file +} diff --git a/tests/test_files/get_roles.json b/tests/test_files/get_roles.json index 3c372ff..dd41fa9 100644 --- a/tests/test_files/get_roles.json +++ b/tests/test_files/get_roles.json @@ -19,4 +19,4 @@ "id": "SURVEY_AUTHOR", "name": "Survey Author" } -] \ No newline at end of file +] diff --git a/tests/test_files/get_teams.json b/tests/test_files/get_teams.json index 1fe435d..5e3e868 100644 --- a/tests/test_files/get_teams.json +++ b/tests/test_files/get_teams.json @@ -8,4 +8,4 @@ "status": "ACTIVE", "updatedAt": "2020/05/06 15:39:03" } -] \ No newline at end of file +] diff --git a/tests/test_files/get_user_info.json b/tests/test_files/get_user_info.json index 60159c5..52e893f 100644 --- a/tests/test_files/get_user_info.json +++ b/tests/test_files/get_user_info.json @@ -20,4 +20,4 @@ "fullName": "Monica Faloola Gellar", "id": 71, "userName": "monicagellarrr" -} \ No newline at end of file +} diff --git a/tests/test_files/upsert_project_permissions.json b/tests/test_files/upsert_project_permissions.json index 3ab73ba..e9a6d36 100644 --- a/tests/test_files/upsert_project_permissions.json +++ b/tests/test_files/upsert_project_permissions.json @@ -18,4 +18,4 @@ "username": "samplifyweb" } ] -} \ No newline at end of file +} diff --git a/tests/test_permissions.py b/tests/test_permissions.py index f2c48c3..6cea3d1 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -11,7 +11,7 @@ BASE_HOST = "http://test-url.example" -class TestUsersEndpoints(unittest.TestCase): +class TestProjectPermissionsEndpoints(unittest.TestCase): def setUp(self): self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) self.api._access_token = 'Bearer testtoken' @@ -51,4 +51,4 @@ def test_upsert_project_permissions(self): # Test response with error included. with self.assertRaises(DemandAPIError): self.api.upsert_project_permissions(1, upsert_project_data) - self.assertEqual(len(responses.calls), 2) \ No newline at end of file + self.assertEqual(len(responses.calls), 2) diff --git a/tests/test_roles.py b/tests/test_roles.py index 20b13b1..4bb3c45 100644 --- a/tests/test_roles.py +++ b/tests/test_roles.py @@ -10,7 +10,7 @@ BASE_HOST = "http://test-url.example" -class TestUsersEndpoints(unittest.TestCase): +class TestRolesEndpoints(unittest.TestCase): def setUp(self): self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) self.api._access_token = 'Bearer testtoken' @@ -23,4 +23,4 @@ def test_get_roles(self): # Success response responses.add(responses.GET,'{}/sample/v1/roles'.format(BASE_HOST), json=roles_json, status=200) self.api.get_roles() - self.assertEqual(len(responses.calls), 1) \ No newline at end of file + self.assertEqual(len(responses.calls), 1) diff --git a/tests/test_teams.py b/tests/test_teams.py index 7b48db7..cb6ff24 100644 --- a/tests/test_teams.py +++ b/tests/test_teams.py @@ -10,7 +10,7 @@ BASE_HOST = "http://test-url.example" -class TestUsersEndpoints(unittest.TestCase): +class TestTeamsEndpoints(unittest.TestCase): def setUp(self): self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST) self.api._access_token = 'Bearer testtoken' @@ -23,4 +23,4 @@ def test_get_teams(self): # Success response responses.add(responses.GET,'{}/sample/v1/teams'.format(BASE_HOST), json=teams_json, status=200) self.api.get_company_teams() - self.assertEqual(len(responses.calls), 1) \ No newline at end of file + self.assertEqual(len(responses.calls), 1) diff --git a/tests/test_users.py b/tests/test_users.py index e689ae4..34da080 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -33,4 +33,4 @@ def test_get_company_users(self): # Success response responses.add(responses.GET,'{}/sample/v1/users'.format(BASE_HOST), json=company_users_json, status=200) self.api.get_company_users() - self.assertEqual(len(responses.calls), 1) \ No newline at end of file + self.assertEqual(len(responses.calls), 1) From da585d9aafce661aa7af3e6943350b6aed2051b3 Mon Sep 17 00:00:00 2001 From: Yugma Moradia Date: Fri, 4 Sep 2020 12:58:48 -0700 Subject: [PATCH 13/17] Fixing the missing spaces --- dynatademand/validator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dynatademand/validator.py b/dynatademand/validator.py index b7a0601..b1886ea 100644 --- a/dynatademand/validator.py +++ b/dynatademand/validator.py @@ -17,8 +17,8 @@ 'buy_project': ['path', 'body', ], 'get_project_detailed_report': ['path', ], 'reconcile_project': ['path', ], - 'get_project_permissions':['path', ], - 'upsert_project_permissions':['path', 'body', ], + 'get_project_permissions': ['path', ], + 'upsert_project_permissions': ['path', 'body', ], # Roles 'get_roles': ['query', ], From a470b85ec154802685ca0aa0290b42ecde8a7e81 Mon Sep 17 00:00:00 2001 From: Yugma Moradia Date: Fri, 4 Sep 2020 13:05:17 -0700 Subject: [PATCH 14/17] Fixing length of line --- dynatademand/validator.py | 2 +- tests/test_permissions.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dynatademand/validator.py b/dynatademand/validator.py index b1886ea..5070380 100644 --- a/dynatademand/validator.py +++ b/dynatademand/validator.py @@ -20,7 +20,7 @@ 'get_project_permissions': ['path', ], 'upsert_project_permissions': ['path', 'body', ], - # Roles + # Roles 'get_roles': ['query', ], # Invoices diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 6cea3d1..8355317 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -20,7 +20,8 @@ def setUp(self): def test_get_project_permissions(self): with open('./tests/test_files/get_project_permissions.json', 'r') as get_permissions_file: permissions_json = json.load(get_permissions_file) - responses.add(responses.GET, '{}/sample/v1/projects/1/permissions'.format(BASE_HOST), json=permissions_json, status=200) + responses.add(responses.GET, '{}/sample/v1/projects/1/permissions'.format(BASE_HOST), + json=permissions_json, status=200) self.api.get_project_permissions(1) self.assertEqual(len(responses.calls), 1) self.assertEqual(responses.calls[0].response.json(), permissions_json) From d6607e2f8455765efc7a74531c65d9bd944fe225 Mon Sep 17 00:00:00 2001 From: Yugma Moradia Date: Fri, 18 Sep 2020 11:18:19 -0700 Subject: [PATCH 15/17] Changing the userinfo path --- dynatademand/api.py | 2 +- tests/test_users.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dynatademand/api.py b/dynatademand/api.py index db554f7..bcbe088 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -292,7 +292,7 @@ def get_project_detailed_report(self, project_id): return self._api_get('/projects/{}/detailedReport'.format(project_id)) def get_user_info(self): - return self._api_get('/users/info') + return self._api_get('/user') def get_company_users(self): return self._api_get('/users') diff --git a/tests/test_users.py b/tests/test_users.py index 34da080..b443249 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -21,7 +21,7 @@ def test_get_user_info(self): with open('./tests/test_files/get_user_info.json', 'r') as user_info: user_info_json = json.load(user_info) # Success response - responses.add(responses.GET,'{}/sample/v1/users/info'.format(BASE_HOST), json=user_info_json, status=200) + responses.add(responses.GET,'{}/sample/v1/user'.format(BASE_HOST), json=user_info_json, status=200) self.api.get_user_info() self.assertEqual(len(responses.calls), 1) From 7bc9f8e7ab42f2423bf14fa47b52631fdc4214f1 Mon Sep 17 00:00:00 2001 From: Ridley Larsen Date: Wed, 23 Sep 2020 17:27:00 -0600 Subject: [PATCH 16/17] Fixed linting errors. --- dynatademand/api.py | 4 ++-- tests/test_permissions.py | 8 ++++++-- tests/test_roles.py | 2 +- tests/test_teams.py | 2 +- tests/test_users.py | 4 ++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/dynatademand/api.py b/dynatademand/api.py index bcbe088..939e90a 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -316,7 +316,7 @@ def get_project_permissions(self, project_id): def upsert_project_permissions(self, project_id, upsert_permissions_data): self.validator.validate_request( - 'upsert_project_permissions', + 'upsert_project_permissions', path_data={'extProjectId': '{}'.format(project_id)}, request_body=upsert_permissions_data, ) @@ -562,7 +562,7 @@ def delete_template(self, id): self.validator.validate_request( 'delete_template', path_data={'id': '{}'.format(id)}, - ) + ) return self._api_delete('/templates/quotaplan/{}'.format(id)) def get_templates(self, country, lang, **kwargs): diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 8355317..60ff85f 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -20,8 +20,12 @@ def setUp(self): def test_get_project_permissions(self): with open('./tests/test_files/get_project_permissions.json', 'r') as get_permissions_file: permissions_json = json.load(get_permissions_file) - responses.add(responses.GET, '{}/sample/v1/projects/1/permissions'.format(BASE_HOST), - json=permissions_json, status=200) + responses.add( + responses.GET, + '{}/sample/v1/projects/1/permissions'.format(BASE_HOST), + json=permissions_json, + status=200 + ) self.api.get_project_permissions(1) self.assertEqual(len(responses.calls), 1) self.assertEqual(responses.calls[0].response.json(), permissions_json) diff --git a/tests/test_roles.py b/tests/test_roles.py index 4bb3c45..5bfc717 100644 --- a/tests/test_roles.py +++ b/tests/test_roles.py @@ -21,6 +21,6 @@ def test_get_roles(self): with open('./tests/test_files/get_roles.json', 'r') as roles: roles_json = json.load(roles) # Success response - responses.add(responses.GET,'{}/sample/v1/roles'.format(BASE_HOST), json=roles_json, status=200) + responses.add(responses.GET, '{}/sample/v1/roles'.format(BASE_HOST), json=roles_json, status=200) self.api.get_roles() self.assertEqual(len(responses.calls), 1) diff --git a/tests/test_teams.py b/tests/test_teams.py index cb6ff24..47b987e 100644 --- a/tests/test_teams.py +++ b/tests/test_teams.py @@ -21,6 +21,6 @@ def test_get_teams(self): with open('./tests/test_files/get_teams.json', 'r') as teams: teams_json = json.load(teams) # Success response - responses.add(responses.GET,'{}/sample/v1/teams'.format(BASE_HOST), json=teams_json, status=200) + responses.add(responses.GET, '{}/sample/v1/teams'.format(BASE_HOST), json=teams_json, status=200) self.api.get_company_teams() self.assertEqual(len(responses.calls), 1) diff --git a/tests/test_users.py b/tests/test_users.py index b443249..486ccd8 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -21,7 +21,7 @@ def test_get_user_info(self): with open('./tests/test_files/get_user_info.json', 'r') as user_info: user_info_json = json.load(user_info) # Success response - responses.add(responses.GET,'{}/sample/v1/user'.format(BASE_HOST), json=user_info_json, status=200) + responses.add(responses.GET, '{}/sample/v1/user'.format(BASE_HOST), json=user_info_json, status=200) self.api.get_user_info() self.assertEqual(len(responses.calls), 1) @@ -31,6 +31,6 @@ def test_get_company_users(self): with open('./tests/test_files/get_company_users.json', 'r') as company_users: company_users_json = json.load(company_users) # Success response - responses.add(responses.GET,'{}/sample/v1/users'.format(BASE_HOST), json=company_users_json, status=200) + responses.add(responses.GET, '{}/sample/v1/users'.format(BASE_HOST), json=company_users_json, status=200) self.api.get_company_users() self.assertEqual(len(responses.calls), 1) From bb1b277197616d54cacf03a4a2ca0fa61005721c Mon Sep 17 00:00:00 2001 From: Ridley Larsen Date: Wed, 23 Sep 2020 17:46:30 -0600 Subject: [PATCH 17/17] Added correct version of pyrsistent to pyproject.toml for 2.7.18 compatibility. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 81303fd..d02265e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ pytest = "4.6.6" jsonschema = "3.2.0" pytest-runner = "5.2" flake8 = "^3.7.9" +pyrsistent = "0.14.11" [build-system] requires = ["poetry>=0.12"]