diff --git a/CmixAPIClient/api.py b/CmixAPIClient/api.py index eee7e27..784dbd5 100644 --- a/CmixAPIClient/api.py +++ b/CmixAPIClient/api.py @@ -139,6 +139,38 @@ def fetch_raw_results(self, survey_id, payload): response = requests.post(url, headers=self._authentication_headers, json=payload) return response.json() + def api_get(self, endpoint, error=''): + self.check_auth_headers() + url = '{}/{}'.format(CMIX_SERVICES['survey'][self.url_type], endpoint) + response = requests.get(url, headers=self._authentication_headers) + if response.status_code != 200: + if '' == error: + error = 'CMIX returned a non-200 response code' + raise CmixError( + '{}: {} and error {}'.format( + error, + response.status_code, + response.text + ) + ) + return response.json() + + def api_delete(self, endpoint, error=''): + self.check_auth_headers() + url = '{}/{}'.format(CMIX_SERVICES['survey'][self.url_type], endpoint) + response = requests.delete(url, headers=self._authentication_headers) + if response.status_code != 200: + if '' == error: + error = 'CMIX returned a non-200 response code' + raise CmixError( + '{}: {} and error {}'.format( + error, + response.status_code, + response.text + ) + ) + return response.json() + def get_surveys(self, status, *args, **kwargs): '''kwargs: @@ -402,3 +434,9 @@ def get_survey_simulations(self, survey_id): ) ) return simulations_response.json() + + def get_projects(self): + project_endpoint = 'projects' + project_error = 'CMIX returned a non-200 response code while getting projects' + project_response = self.api_get(project_endpoint, project_error) + return project_response diff --git a/CmixAPIClient/project.py b/CmixAPIClient/project.py new file mode 100644 index 0000000..c8a3beb --- /dev/null +++ b/CmixAPIClient/project.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from .error import CmixError + + +class CmixProject(object): + def __init__(self, client, project_id): + if None in [client, project_id]: + raise CmixError("Client and project id are required.") + self.client = client + self.project_id = project_id + + def delete_project(self): + project_endpoint = 'projects/{}'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while deleting project' + project_response = self.client.api_delete(project_endpoint, project_error) + return project_response + + def delete_group(self, group_id): + project_endpoint = 'projects/{}/groups/{}'.format(self.project_id, group_id) + project_error = 'CMIX returned a non-200 response code while deleting group' + project_response = self.client.api_delete(project_endpoint, project_error) + return project_response + + def get_project(self): + project_endpoint = 'projects/{}'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_sources(self): + project_endpoint = 'projects/{}/sources'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project sources' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_groups(self): + project_endpoint = 'projects/{}/groups'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project groups' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_links(self): + project_endpoint = 'projects/{}/links'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project links' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_full_links(self): + project_endpoint = 'projects/{}/full-links'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project full links' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_locales(self): + project_endpoint = 'projects/{}/locales'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project locales' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_markup_files(self): + project_endpoint = 'projects/{}/markup-files'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project markup files' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_respondent_links(self): + project_endpoint = 'projects/{}/respondent-links'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project respondent links' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response + + def get_surveys(self): + project_endpoint = 'projects/{}/surveys'.format(self.project_id) + project_error = 'CMIX returned a non-200 response code while getting project surveys' + project_response = self.client.api_get(project_endpoint, project_error) + return project_response diff --git a/README.md b/README.md index 39b93bd..f585d86 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,11 @@ A Python client library for the [Dynata Cmix API](https://wiki2.criticalmix.net/ ## Supported API Functions +### CmixAPI authenticate(*args, **kwargs) fetch_banner_filter(survey_id, question_a, question_b, response_id) fetch_raw_results(survey_id, payload) + get_projects() get_surveys(status, *args, **kwargs) get_survey_data_layouts(survey_id) get_survey_definition(survey_id) @@ -46,6 +48,20 @@ A Python client library for the [Dynata Cmix API](https://wiki2.criticalmix.net/ update_project(project_id, status=None) create_survey(xml_string) +### CmixProject + + delete_group(group_id) + delete_project() + get_full_links() + get_groups() + get_links() + get_locales() + get_markup_files() + get_project() + get_respondent_links() + get_sources() + get_surveys() + ## Contributing Information on [contributing](https://github.com/dynata/python-cmixapi-client/blob/dev/CONTRIBUTING.md) to this python library. diff --git a/tests/test_api.py b/tests/test_api.py index 6dc59a6..db831a2 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -22,6 +22,33 @@ def setUp(self): self.cmix_api = default_cmix_api() self.cmix_api._authentication_headers = {'Authorization': 'Bearer test'} self.survey_id = 1337 + self.project_id = 1492 + + def helper_get(self, function_name, endpoint): + func = getattr(self.cmix_api, function_name) + + # success case + with mock.patch('CmixAPIClient.api.requests') as mock_request: + mock_get = mock.Mock() + mock_get.status_code = 200 + mock_get.json.return_value = {} + mock_request.get.return_value = mock_get + + func() + + base_url = CMIX_SERVICES['survey']['BASE_URL'] + project_url = '{}/{}'.format(base_url, endpoint) + mock_request.get.assert_any_call(project_url, headers=self.cmix_api._authentication_headers) + + # error case (survey not found) + with mock.patch('CmixAPIClient.api.requests') as mock_request: + mock_get = mock.Mock() + mock_get.status_code = 404 + mock_get.json.return_value = {} + mock_request.get.return_value = mock_get + + with self.assertRaises(CmixError): + func() def test_cmix_authentication_check(self): with self.assertRaises(CmixError): @@ -424,3 +451,6 @@ def test_get_survey_simulations(self): with self.assertRaises(CmixError): self.cmix_api.get_survey_simulations(self.survey_id) + + def test_get_projects(self): + self.helper_get('get_projects', 'projects') diff --git a/tests/test_project.py b/tests/test_project.py new file mode 100644 index 0000000..ca450ce --- /dev/null +++ b/tests/test_project.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function +from __future__ import unicode_literals +import mock + +from unittest import TestCase +from CmixAPIClient.api import CMIX_SERVICES +from CmixAPIClient.project import CmixProject +from CmixAPIClient.error import CmixError +from .test_api import default_cmix_api + + +class TestCmixProject(TestCase): + def setUp(self): + self.cmix_api = default_cmix_api() + self.cmix_api._authentication_headers = {'Authorization': 'Bearer test'} + self.project_id = 1492 + + def helper_get(self, function_name, endpoint): + project = CmixProject(self.cmix_api, self.project_id) + func = getattr(project, function_name) + + # success case + with mock.patch('CmixAPIClient.api.requests') as mock_request: + mock_get = mock.Mock() + mock_get.status_code = 200 + mock_get.json.return_value = {} + mock_request.get.return_value = mock_get + + func() + + base_url = CMIX_SERVICES['survey']['BASE_URL'] + project_url = '{}/projects{}'.format(base_url, endpoint) + mock_request.get.assert_any_call(project_url, headers=self.cmix_api._authentication_headers) + + # error case (survey not found) + with mock.patch('CmixAPIClient.api.requests') as mock_request: + mock_get = mock.Mock() + mock_get.status_code = 404 + mock_get.json.return_value = {} + mock_request.get.return_value = mock_get + + with self.assertRaises(CmixError): + func() + + def helper_delete(self, function_name, endpoint, group_id=None): + project = CmixProject(self.cmix_api, self.project_id) + func = getattr(project, function_name) + + # success case + with mock.patch('CmixAPIClient.api.requests') as mock_request: + mock_delete = mock.Mock() + mock_delete.status_code = 200 + mock_delete.json.return_value = {} + mock_request.delete.return_value = mock_delete + + if group_id is not None: + func(group_id) + else: + func() + + base_url = CMIX_SERVICES['survey']['BASE_URL'] + project_url = '{}/projects{}'.format(base_url, endpoint) + mock_request.delete.assert_any_call(project_url, headers=self.cmix_api._authentication_headers) + + # error case (survey not found) + with mock.patch('CmixAPIClient.api.requests') as mock_request: + mock_delete = mock.Mock() + mock_delete.status_code = 404 + mock_delete.json.return_value = {} + mock_request.delete.return_value = mock_delete + + with self.assertRaises(CmixError): + if group_id is not None: + func(group_id) + else: + func() + + def test_delete_group(self): + self.helper_delete('delete_group', '/{}/groups/{}'.format(self.project_id, 13), 13) + + def test_delete_project(self): + self.helper_delete('delete_project', '/{}'.format(self.project_id)) + + def test_get_project(self): + self.helper_get('get_project', '/{}'.format(self.project_id)) + + def test_get_sources(self): + self.helper_get('get_sources', '/{}/sources'.format(self.project_id)) + + def test_get_groups(self): + self.helper_get('get_groups', '/{}/groups'.format(self.project_id)) + + def test_get_links(self): + self.helper_get('get_links', '/{}/links'.format(self.project_id)) + + def test_get_full_links(self): + self.helper_get('get_full_links', '/{}/full-links'.format(self.project_id)) + + def test_get_locales(self): + self.helper_get('get_locales', '/{}/locales'.format(self.project_id)) + + def test_get_markup_files(self): + self.helper_get('get_markup_files', '/{}/markup-files'.format(self.project_id)) + + def test_get_respondent_links(self): + self.helper_get('get_respondent_links', '/{}/respondent-links'.format(self.project_id)) + + def test_get_surveys(self): + self.helper_get('get_surveys', '/{}/surveys'.format(self.project_id))