Skip to content

Commit 4f5016b

Browse files
Made changes requested by Vova
1 parent d03ce8e commit 4f5016b

File tree

8 files changed

+105
-94
lines changed

8 files changed

+105
-94
lines changed

connect/resource/automation.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,17 @@ def process_request(self, request):
4343
@function_log
4444
def approve(self, pk, data):
4545
# type: (str, dict) -> str
46-
url = self.urljoin(self.url, pk, 'approve/')
47-
return self.api.post(url=url, data=data if data else {})
46+
return self.api.post(path=pk + '/approve/', data=data if data else {})
4847

4948
@function_log
5049
def inquire(self, pk):
5150
# type: (str) -> str
52-
url = self.urljoin(self.url, pk, 'inquire/')
53-
return self.api.post(url=url, data={})
51+
return self.api.post(path=pk + '/inquire/', data={})
5452

5553
@function_log
5654
def fail(self, pk, reason):
5755
# type: (str, str) -> str
58-
url = self.urljoin(self.url, pk, 'fail/')
59-
return self.api.post(url=url, data={'reason': reason})
56+
return self.api.post(path=pk + '/fail/', data={'reason': reason})
6057

6158
@function_log
6259
def render_template(self, pk, template_id):
@@ -71,6 +68,6 @@ def update_parameters(self, pk, params):
7168
list_dict.append(_.__dict__ if isinstance(_, Param) else _)
7269

7370
return self.api.put(
74-
url=self.urljoin(self.url, pk),
71+
path=pk,
7572
data=json.dumps({'asset': {'params': list_dict}}),
7673
)

connect/resource/base.py

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,19 @@
1818

1919

2020
class ApiClient(object):
21-
def __init__(self, config=None):
22-
# type: (Config) -> None
21+
def __init__(self, config, base_path):
22+
# type: (Config, str) -> None
23+
24+
# Assign base URL
25+
self._base_path = base_path
2326

2427
# Assign passed config or globally configured instance
2528
self._config = config or Config.get_instance()
2629

27-
# Assert data
28-
if not isinstance(self.config, Config):
29-
raise ValueError('A valid Config object is required to create an ApiClient')
30+
@property
31+
def base_path(self):
32+
# type: () -> str
33+
return self._base_path
3034

3135
@property
3236
def config(self):
@@ -43,8 +47,47 @@ def headers(self):
4347
'Content-Type': 'application/json',
4448
}
4549

50+
def get_url(self, path=''):
51+
# type: (str) -> str
52+
return self._urljoin(self.config.api_url, self.base_path, path)
53+
54+
@function_log
55+
def get(self, path='', params=None, **kwargs):
56+
kwargs = self._fix_request_kwargs(kwargs, path, params=params)
57+
response = requests.get(**kwargs)
58+
return self._check_response(response)
59+
60+
@function_log
61+
def post(self, path='', data=None, json=None, **kwargs):
62+
kwargs = self._fix_request_kwargs(kwargs, path, data=data, json=json)
63+
response = requests.post(**kwargs)
64+
return self._check_response(response)
65+
66+
@function_log
67+
def put(self, path='', data=None, **kwargs):
68+
kwargs = self._fix_request_kwargs(kwargs, path, data=data)
69+
response = requests.put(**kwargs)
70+
return self._check_response(response)
71+
72+
def _fix_request_kwargs(self, prev_kwargs, path, **kwargs):
73+
# type: (Dict[str, Any], str, Dict[str, Any]) -> Dict[str, Any]
74+
""" Set correct kwargs for requests """
75+
fixed_kwargs = prev_kwargs.copy()
76+
fixed_kwargs.update(kwargs)
77+
if 'get_url' not in fixed_kwargs:
78+
fixed_kwargs['get_url'] = self.get_url(path)
79+
if 'headers' not in fixed_kwargs:
80+
fixed_kwargs['headers'] = self.headers
81+
return fixed_kwargs
82+
4683
@staticmethod
47-
def check_response(response):
84+
def _urljoin(*args):
85+
return functools.reduce(
86+
lambda a, b: compat.urljoin(a + ('' if a.endswith('/') else '/'), b),
87+
args)
88+
89+
@staticmethod
90+
def _check_response(response):
4891
# type: (requests.Response) -> str
4992
if not hasattr(response, 'content'):
5093
raise AttributeError(
@@ -59,47 +102,30 @@ def check_response(response):
59102

60103
return response.content
61104

62-
@function_log
63-
def get(self, url, params=None, **kwargs):
64-
if 'headers' not in kwargs:
65-
kwargs['headers'] = self.headers
66-
response = requests.get(url, params, **kwargs)
67-
return self.check_response(response)
68-
69-
@function_log
70-
def post(self, url, data=None, json=None, **kwargs):
71-
if 'headers' not in kwargs:
72-
kwargs['headers'] = self.headers
73-
response = requests.post(url, data, json, **kwargs)
74-
return self.check_response(response)
75-
76-
@function_log
77-
def put(self, url, data=None, **kwargs):
78-
if 'headers' not in kwargs:
79-
kwargs['headers'] = self.headers
80-
response = requests.put(url, data, **kwargs)
81-
return self.check_response(response)
82-
83105

84106
class BaseResource(object):
85107
resource = None # type: str
86108
limit = 100 # type: int
87-
api = None # type: ApiClient
88109
schema = BaseSchema() # type: marshmallow.Schema
89110

90111
def __init__(self, config=None):
91-
# Assign passed config or globally configured instance
92-
self._config = config or Config.get_instance()
93-
94-
# Assert data
112+
# Set api
95113
if not self.__class__.resource:
96-
raise AttributeError('Resource name not specified in class {}'.format(
97-
self.__class__.__name__) + '. Add an attribute `resource` name of the resource')
114+
raise AttributeError('Resource name not specified in class {}. '
115+
.format(self.__class__.__name__) +
116+
'Add an attribute `resource` name of the resource')
117+
self._api = ApiClient(config, self.__class__.resource)
118+
119+
# Set passed config or globally configured instance
120+
self._config = config or Config.get_instance()
98121
if not isinstance(self.config, Config):
99122
raise ValueError('A valid Config object must be passed or globally configured '
100123
'to create a ' + type(self).__name__)
101-
if not BaseResource.api:
102-
BaseResource.api = ApiClient(config)
124+
125+
@property
126+
def api(self):
127+
# type: () -> ApiClient
128+
return self._api
103129

104130
@property
105131
def config(self):
@@ -111,13 +137,9 @@ def list(self):
111137
# type: () -> List[Any]
112138
filters = self.build_filter()
113139
logger.info('Get list request by filter - {}'.format(filters))
114-
response = self.api.get(url=self.url, params=filters)
140+
response = self.api.get(params=filters)
115141
return self._load_schema(response)
116142

117-
@property
118-
def url(self):
119-
return self.urljoin(self.config.api_url, self.resource)
120-
121143
def build_filter(self):
122144
# type: () -> Dict[str, Any]
123145
res_filter = {}
@@ -127,17 +149,11 @@ def build_filter(self):
127149

128150
def get(self, pk):
129151
# type: (str) -> Any
130-
response = self.api.get(url=self.urljoin(self.url, pk))
152+
response = self.api.get(path=pk)
131153
objects = self._load_schema(response)
132154
if isinstance(objects, list) and len(objects) > 0:
133155
return objects[0]
134156

135-
@staticmethod
136-
def urljoin(*args):
137-
return functools.reduce(
138-
lambda a, b: compat.urljoin(a + ('' if a.endswith('/') else '/'), b),
139-
args)
140-
141157
def _load_schema(self, response, many=None):
142158
# type: (str, bool) -> Union[List[Any], Any]
143159
objects, error = self.schema.loads(response, many)

connect/resource/template.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ def render(self, pk, request_id):
2626
# type: (str, str) -> ActivationTileResponse
2727
if not all([pk, request_id]):
2828
raise ValueError('Invalid ids for render template')
29-
30-
url = self.urljoin(self.url, pk, 'render')
31-
response = self.api.get(url, params={'request_id': request_id})
32-
29+
response = self.api.get(path=pk + '/render', params={'request_id': request_id})
3330
return ActivationTileResponse(response)
3431

3532
def get(self, pk):

connect/resource/tier_config_automation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def get_tier_config(self, tier_id, product_id):
6262
'configuration__product__id': product_id,
6363
'configuration__account__id': tier_id,
6464
}
65-
response = self.api.get(url=self.url, params=params)
65+
response = self.api.get(params=params)
6666
objects = self._load_schema(response)
6767

6868
if isinstance(objects, list) and len(objects) > 0:

connect/resource/usage_automation.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def create_usage_file(self, usage_file):
6666
if not usage_file.description:
6767
# Could be because description is empty or None, so make sure it is empty
6868
usage_file.description = ''
69-
response = self.api.post(self.url, data=usage_file)
69+
response = self.api.post(data=usage_file)
7070
return self._load_schema(response, many=False)
7171

7272
@staticmethod
@@ -105,7 +105,7 @@ def upload_spreadsheet(self, usage_file, spreadsheet):
105105
file_contents = tmp.read()
106106

107107
# Setup request
108-
url = self.urljoin(self.url, usage_file.id, 'upload/')
108+
url = self.api.get_url(usage_file.id + '/upload/')
109109
headers = self.api.headers
110110
headers['Accept'] = 'application/json'
111111
del headers['Content-Type'] # This must NOT be set for multipart post requests
@@ -130,10 +130,15 @@ def upload_usage_records(self, usage_file, usage_records):
130130
self.upload_spreadsheet(usage_file, book)
131131

132132
def get_usage_template(self, product):
133-
# type: (Product) -> str
133+
# type: (Product) -> bytes
134134
location = self._get_usage_template_download_location(product.id)
135+
if not location:
136+
msg = 'Error obtaining template usage file location'
137+
logger.error(msg)
138+
raise FileRetrievalError(msg)
139+
135140
contents = self._retrieve_usage_template(location) if location else None
136-
if not location or contents is None:
141+
if not contents:
137142
msg = 'Error obtaining template usage file from `{}`'.format(location)
138143
logger.error(msg)
139144
raise FileRetrievalError(msg)
@@ -148,25 +153,17 @@ def submit_usage(self, usage_file, usage_records):
148153
def _get_usage_template_download_location(self, product_id):
149154
# type: (str) -> str
150155
try:
151-
response = self.api.get(self.urljoin(self.config.api_url,
152-
'/usage/products/' + product_id + ''))
156+
response = self.api.get(path='/usage/products/' + product_id + '/template/')
153157
response_dict = json.loads(response)
154158
return response_dict['template_link']
155159
except (requests.exceptions.RequestException, KeyError, TypeError, ValueError):
156160
return ''
157161

158162
@staticmethod
159163
def _retrieve_usage_template(location):
160-
# type: (str) -> Optional[str]
164+
# type: (str) -> Optional[bytes]
161165
try:
162-
# Try loading from file
163-
with open(location) as file_handle:
164-
return file_handle.read()
165-
except IOError:
166-
# Try loading from URL
167-
try:
168-
file_handle = requests.get(location)
169-
return file_handle.text
170-
except requests.exceptions.RequestException:
171-
# Fail
172-
return None
166+
response = requests.get(location)
167+
return response.content
168+
except requests.exceptions.RequestException:
169+
return None

connect/resource/usage_file_automation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def dispatch(self, request):
3939

4040
# Catch action
4141
except UsageFileAction as usage:
42-
self.api.post(self.urljoin(self.url, request.id, usage.code), data=usage.obj)
42+
self.api.post(path='{}/{}'.format(request.id, usage.code), data=usage.obj)
4343
processing_result = usage.code
4444

4545
# Catch skip

tests/test_models.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,13 @@ def _get_response2_ok():
3535

3636
def test_resource_url():
3737
resource = FulfillmentAutomation()
38-
assert resource.url == '{}{}'.format(resource.config.api_url, resource.resource)
38+
assert resource.api.get_url() == resource.config.api_url + resource.resource + '/'
3939

4040

4141
def test_resource_urljoin():
4242
resource = FulfillmentAutomation()
43-
assert resource.urljoin('hello', 'world') == 'hello/world'
44-
assert resource.urljoin(resource.url, 'hello', 'world') == '{}{}/hello/world' \
45-
.format(resource.config.api_url, resource.resource)
43+
assert resource.api.base_path == resource.resource
44+
assert resource.api.get_url('hello/world') == '{}{}/hello/world'.format(resource.config.api_url, resource.resource)
4645

4746

4847
@patch('requests.get', MagicMock(return_value=_get_response_ok()))

tests/test_usage.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@
1818
from connect.models.product import Product
1919
from connect.resource import UsageAutomation
2020

21-
Response = namedtuple('Response', ('ok', 'content', 'text', 'status_code'))
21+
Response = namedtuple('Response', ('ok', 'content', 'status_code'))
2222

2323

2424
def _get_response_ok():
2525
with open(os.path.join(os.path.dirname(__file__), 'response_usage.json')) as file_handle:
2626
content = file_handle.read()
27-
return Response(ok=True, content=content, text='', status_code=200)
27+
return Response(ok=True, content=content, status_code=200)
2828

2929

3030
def _get_response_ok2():
3131
with open(os.path.join(os.path.dirname(__file__), 'response_usage2.json')) as file_handle:
3232
content = file_handle.read()
33-
return Response(ok=True, content=content, text='', status_code=201)
33+
return Response(ok=True, content=content, status_code=201)
3434

3535

3636
@patch('requests.get', MagicMock(return_value=_get_response_ok()))
@@ -47,24 +47,29 @@ def test_process():
4747
resource.process()
4848

4949

50-
@patch('requests.get', MagicMock(return_value=Response(
51-
ok=True, content='{"template_link": "..."}', text='test', status_code=200)))
50+
@patch('requests.get', MagicMock(side_effect=[
51+
Response(ok=True, content='{"template_link": "..."}', status_code=200),
52+
Response(ok=True, content='template_contents', status_code=200)]))
5253
def test_get_usage_template_ok():
53-
assert UsageAutomation().get_usage_template(Product(id='PRD-638-321-603')) == 'test'
54+
resource = UsageAutomation()
55+
assert resource.get_usage_template(Product(id='PRD-638-321-603')) == 'template_contents'
5456

5557

5658
@patch('requests.get', MagicMock(return_value=Response(
57-
ok=True, content='{}', text='', status_code=200)))
59+
ok=True, content='{}', status_code=200)))
5860
def test_get_usage_template_no_link():
61+
resource = UsageAutomation()
5962
with pytest.raises(FileRetrievalError):
60-
UsageAutomation().get_usage_template(Product(id='PRD-638-321-603'))
63+
resource.get_usage_template(Product(id='PRD-638-321-603'))
6164

6265

63-
@patch('requests.get', MagicMock(return_value=Response(
64-
ok=True, content='{"template_link": "..."}', text=None, status_code=200)))
66+
@patch('requests.get', MagicMock(side_effect=[
67+
Response(ok=True, content='{"template_link": "..."}', status_code=200),
68+
Response(ok=True, content=None, status_code=200)]))
6569
def test_get_usage_template_no_file():
70+
resource = UsageAutomation()
6671
with pytest.raises(FileRetrievalError):
67-
UsageAutomation().get_usage_template(Product(id='PRD-638-321-603'))
72+
resource.get_usage_template(Product(id='PRD-638-321-603'))
6873

6974

7075
class UsageAutomationTester(UsageAutomation):

0 commit comments

Comments
 (0)