Skip to content

Commit f357567

Browse files
committed
Added optional auth params to DemandAPIClient. Added Get Project endpoint and test module.
1 parent 8a9b112 commit f357567

File tree

4 files changed

+200
-5
lines changed

4 files changed

+200
-5
lines changed

dynatademand/api.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
import os
22
import requests
3+
34
from .errors import DemandAPIError
45

56

67
class DemandAPIClient(object):
7-
def __init__(self):
8-
self.client_id = os.getenv('DYNATA_DEMAND_CLIENT_ID', None)
9-
self.username = os.getenv('DYNATA_DEMAND_USERNAME', None)
10-
self.password = os.getenv('DYNATA_DEMAND_PASSWORD', None)
8+
def __init__(self, client_id=None, username=None, password=None, base_host=None):
9+
if client_id is not None:
10+
self.client_id = client_id
11+
else:
12+
self.client_id = os.getenv('DYNATA_DEMAND_CLIENT_ID', None)
13+
if username is not None:
14+
self.username = username
15+
else:
16+
self.username = os.getenv('DYNATA_DEMAND_USERNAME', None)
17+
if password is not None:
18+
self.password = password
19+
else:
20+
self.password = os.getenv('DYNATA_DEMAND_PASSWORD', None)
21+
if base_host is not None:
22+
self.base_host = base_host
23+
else:
24+
self.base_host = os.getenv('DYNATA_DEMAND_BASE_URL', default='https://api.researchnow.com')
25+
1126
if None in [self.client_id, self.username, self.password]:
1227
raise DemandAPIError("All authentication data is required.")
28+
1329
self._access_token = None
1430
self._refresh_token = None
15-
self.base_host = os.getenv('DYNATA_DEMAND_BASE_URL', default='https://api.researchnow.com')
1631
self.auth_base_url = '{}/auth/v1'.format(self.base_host)
1732
self.base_url = '{}/sample/v1'.format(self.base_host)
1833

@@ -35,6 +50,21 @@ def _api_post(self, uri, payload):
3550
))
3651
return response.json()
3752

53+
def _api_get(self, uri, query_params=None):
54+
# Send an authenticated POST request to an API endpoint.
55+
self._check_authentication()
56+
url = '{}{}'.format(self.base_url, uri)
57+
request_headers = {
58+
'oauth_access_token': self._access_token,
59+
'Content-Type': "application/json",
60+
}
61+
response = requests.get(url=url, params=query_params, headers=request_headers)
62+
if response.status_code > 399:
63+
raise DemandAPIError('Demand API request to {} failed with status {}. Response: {}'.format(
64+
url, response.status_code, response.content
65+
))
66+
return response.json()
67+
3868
def authenticate(self):
3969
url = '{}/token/password'.format(self.auth_base_url)
4070
auth_response = requests.post(url, json={
@@ -79,3 +109,6 @@ def logout(self):
79109
logout_response.status_code, logout_response.content
80110
))
81111
return logout_response.json()
112+
113+
def get_project(self, project_id):
114+
return self._api_get('/projects/{}'.format(project_id))

tests/test_authentication.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@
1616

1717

1818
class AuthenticationTestMissingCredentials(unittest.TestCase):
19+
def test_authentication_params(self):
20+
DemandAPIClient(client_id="test", username="test", password="test", base_host=BASE_URL)
21+
22+
with self.assertRaises(DemandAPIError):
23+
DemandAPIClient(username="test", password="test", base_host=BASE_URL)
24+
25+
with self.assertRaises(DemandAPIError):
26+
DemandAPIClient(client_id="test", password="test", base_host=BASE_URL)
27+
28+
with self.assertRaises(DemandAPIError):
29+
DemandAPIClient(client_id="test", username="test", base_host=BASE_URL)
30+
31+
@patch('os.getenv')
32+
def test_authentication_params_with_env(self, mock_getenv):
33+
mock_getenv.side_effect = [
34+
"test_client_id",
35+
"test_username",
36+
"test_password",
37+
BASE_URL
38+
]
39+
40+
# None of these should raise an error if the appropriate missing data is available in the environment.
41+
DemandAPIClient(client_id="test", username="test", password="test", base_host=BASE_URL)
42+
DemandAPIClient(username="test", password="test", base_host=BASE_URL)
43+
DemandAPIClient(client_id="test", password="test", base_host=BASE_URL)
44+
DemandAPIClient(client_id="test", username="test", base_host=BASE_URL)
45+
1946
@patch('os.getenv')
2047
def test_missing_client_id(self, mock_getenv):
2148
mock_getenv.side_effect = [

tests/test_files/get_project.json

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{
2+
"extProjectId": "project001",
3+
"title": "Test Survey",
4+
"jobNumber": "PO-1234",
5+
"notificationEmails": [
6+
"api-test@dynata.com"
7+
],
8+
"devices": [
9+
"mobile",
10+
"desktop",
11+
"tablet"
12+
],
13+
"category": {
14+
"surveyTopic": [
15+
"AUTOMOTIVE",
16+
"BUSINESS"
17+
]
18+
},
19+
"lineItems": [
20+
{
21+
"extLineItemId": "lineItem001",
22+
"title": "US College",
23+
"countryISOCode": "US",
24+
"languageISOCode": "en",
25+
"surveyURL": "www.mysurvey.com/live/survey?pid=<#DubKnowledge[1500/Entity id]>&k2=<#Project[Secure Key 2]>&psid=<#IdParameter[Value]>",
26+
"surveyTestURL": "www.mysurvey.com/test/survey?pid=<#DubKnowledge[1500/Entity id]>&k2=<#Project[Secure Key 2]>&psid=<#IdParameter[Value]>",
27+
"indicativeIncidence": 20,
28+
"daysInField": 20,
29+
"lengthOfInterview": 10,
30+
"deliveryType": "BALANCED",
31+
"sources": [
32+
{
33+
"id": 100
34+
}
35+
],
36+
"targets": [
37+
{
38+
"count": 200,
39+
"dailyLimit": 0,
40+
"type": "COMPLETE"
41+
}
42+
],
43+
"quotaPlan": {
44+
"filters": [
45+
{
46+
"attributeId": "61961",
47+
"options": [
48+
"3",
49+
"4"
50+
],
51+
"operator": "include"
52+
}
53+
],
54+
"quotaGroups": [
55+
{
56+
"name": "Gender Distribution",
57+
"quotaCells": [
58+
{
59+
"quotaNodes": [
60+
{
61+
"attributeId": "11",
62+
"options": [
63+
"1"
64+
],
65+
"operator": "include"
66+
}
67+
],
68+
"count": 130
69+
},
70+
{
71+
"quotaNodes": [
72+
{
73+
"attributeId": "11",
74+
"options": [
75+
"2"
76+
],
77+
"operator": "include"
78+
}
79+
],
80+
"count": 70
81+
}
82+
]
83+
}
84+
]
85+
},
86+
"state": "PROVISIONED",
87+
"stateReason": "Created by Client",
88+
"stateLastUpdatedAt": "04/01/2018 00:00:00",
89+
"createdAt": "04/01/2018 00:00:00",
90+
"updatedAt": "04/01/2018 00:00:00",
91+
"launchedAt": null,
92+
"endLinks": {
93+
"complete": "https://api.researchnow.com/respondent/exit?rst=1&psid={psid}&med={calculatedSecurityCode}",
94+
"screenout": "https://api.researchnow.com/respondent/exit?rst=2&psid={psid}",
95+
"overquota": "https://api.researchnow.com/respondent/exit?rst=3&psid={psid}",
96+
"securityKey1": "35040",
97+
"securityLevel": "MEDIUM"
98+
}
99+
}
100+
],
101+
"exclusions": {
102+
"type": "PROJECT",
103+
"list": []
104+
}
105+
}

tests/test_projects.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# encoding: utf-8
2+
from __future__ import unicode_literals, print_function
3+
4+
import json
5+
import unittest
6+
import responses
7+
8+
try:
9+
from unittest.mock import patch
10+
except ImportError:
11+
from mock import patch
12+
13+
from dynatademand.api import DemandAPIClient
14+
15+
BASE_HOST = "http://test-url.example"
16+
17+
18+
class TestProjectEndpoints(unittest.TestCase):
19+
def setUp(self):
20+
self.api = DemandAPIClient(client_id='test', username='testuser', password='testpass', base_host=BASE_HOST)
21+
self.api._access_token = 'Bearer testtoken'
22+
23+
@responses.activate
24+
def test_get_project(self):
25+
with open('./tests/test_files/get_project.json', 'r') as project_file:
26+
project_json = json.load(project_file)
27+
responses.add(responses.GET, '{}/sample/v1/projects/1'.format(BASE_HOST), json=project_json, status=200)
28+
self.api.get_project(1)
29+
self.assertEqual(len(responses.calls), 1)
30+
self.assertEqual(responses.calls[0].response.json(), project_json)

0 commit comments

Comments
 (0)