diff --git a/README.md b/README.md index 7b61138..a9e7510 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,9 @@ 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) +[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 diff --git a/dynatademand/api.py b/dynatademand/api.py index 8bb9547..939e90a 100644 --- a/dynatademand/api.py +++ b/dynatademand/api.py @@ -72,6 +72,23 @@ def _api_get(self, uri, query_params=None): 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. url = '{}/token/password'.format(self.auth_base_url) @@ -274,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('/user') + + 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 @@ -355,6 +410,27 @@ 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', @@ -464,3 +540,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/body/upsert_project_permissions.json b/dynatademand/schemas/request/body/upsert_project_permissions.json new file mode 100644 index 0000000..10416d2 --- /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" + } + } + } + } + } +} 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_project_permissions.json b/dynatademand/schemas/request/path/get_project_permissions.json new file mode 100644 index 0000000..42fe507 --- /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" + ] +} 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/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/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/path/upsert_project_permissions.json b/dynatademand/schemas/request/path/upsert_project_permissions.json new file mode 100644 index 0000000..42fe507 --- /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" + ] +} diff --git a/dynatademand/schemas/request/query/get_roles.json b/dynatademand/schemas/request/query/get_roles.json new file mode 100644 index 0000000..363c981 --- /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": [] +} 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..5070380 100644 --- a/dynatademand/validator.py +++ b/dynatademand/validator.py @@ -17,6 +17,11 @@ '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', ], @@ -32,6 +37,9 @@ 'pause_line_item': ['path', ], 'update_line_item': ['path', 'body'], + # quotaCells + 'set_quotacell_status': ['path', ], + # Events 'get_events': ['query', ], 'get_event': ['path', ], @@ -50,6 +58,12 @@ # Supplier Sources 'get_sources': [], + + # Template + 'create_template': ['path', 'body', ], + 'update_template': ['path', 'body', ], + 'delete_template': ['path', ], + 'get_templates': ['path', 'query', ], } 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"] 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/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_company_users.json b/tests/test_files/get_company_users.json new file mode 100644 index 0000000..f8fe240 --- /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" + } +] diff --git a/tests/test_files/get_project_permissions.json b/tests/test_files/get_project_permissions.json new file mode 100644 index 0000000..e9a6d36 --- /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" + } + ] +} diff --git a/tests/test_files/get_roles.json b/tests/test_files/get_roles.json new file mode 100644 index 0000000..dd41fa9 --- /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" + } +] diff --git a/tests/test_files/get_teams.json b/tests/test_files/get_teams.json new file mode 100644 index 0000000..5e3e868 --- /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" + } +] 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/get_user_info.json b/tests/test_files/get_user_info.json new file mode 100644 index 0000000..52e893f --- /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" +} 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_files/upsert_project_permissions.json b/tests/test_files/upsert_project_permissions.json new file mode 100644 index 0000000..e9a6d36 --- /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" + } + ] +} diff --git a/tests/test_line_items.py b/tests/test_line_items.py index ce96072..33e9d32 100644 --- a/tests/test_line_items.py +++ b/tests/test_line_items.py @@ -159,6 +159,55 @@ 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. diff --git a/tests/test_permissions.py b/tests/test_permissions.py new file mode 100644 index 0000000..60ff85f --- /dev/null +++ b/tests/test_permissions.py @@ -0,0 +1,59 @@ +# 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 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' + + @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) diff --git a/tests/test_roles.py b/tests/test_roles.py new file mode 100644 index 0000000..5bfc717 --- /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 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' + + @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) diff --git a/tests/test_teams.py b/tests/test_teams.py new file mode 100644 index 0000000..47b987e --- /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 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' + + @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) diff --git a/tests/test_template.py b/tests/test_template.py new file mode 100644 index 0000000..d56ef4a --- /dev/null +++ b/tests/test_template.py @@ -0,0 +1,90 @@ +# 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 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) + # 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') + 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) diff --git a/tests/test_users.py b/tests/test_users.py new file mode 100644 index 0000000..486ccd8 --- /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/user'.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)