-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathcustom_response.py
More file actions
125 lines (88 loc) · 3.93 KB
/
custom_response.py
File metadata and controls
125 lines (88 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import json
from syncano.exceptions import SyncanoException
class CustomResponseHandler(object):
"""
A helper class which allows to define and maintain custom response handlers.
Consider an example:
Script code::
set_response(HttpResponse(status_code=200, content='{"one": 1}', content_type='application/json'))
When suitable ScriptTrace is used::
trace = ScriptTrace.please.get(id=<code_box_trace_id>, script=<script_id>)
Then trace object will have a content attribute, which will be a dict created from json (simple: json.loads under
the hood);
So this is possible::
trace.content['one']
And the trace.content is equal to::
{'one': 1}
The handler can be easily overwrite::
def custom_handler(response):
return json.loads(response['response']['content'])['one']
trace.response_handler.overwrite_handler('application/json', custom_handler)
or globally::
ScriptTrace.response_handler.overwrite_handler('application/json', custom_handler)
Then trace.content is equal to::
1
Currently supported content_types (but any handler can be defined):
* application/json
* text/plain
"""
def __init__(self):
self.handlers = {}
self.register_handler('application/json', self.json_handler)
self.register_handler('plain/text', self.plain_handler)
def register_handler(self, content_type, handler):
if content_type in self.handlers:
raise SyncanoException('Handler "{}" already defined. User overwrite_handler instead.'.format(content_type))
self.handlers[content_type] = handler
def overwrite_handler(self, content_type, handler):
if content_type not in self.handlers:
raise SyncanoException('Handler "{}" not defined. User register_handler instead.'.format(content_type))
self.handlers[content_type] = handler
def process_response(self, response):
content_type = self._find_content_type(response)
try:
return self.handlers[content_type](response)
except KeyError:
return self._default_handler(response)
@staticmethod
def _find_content_type(response):
if not response:
return None
return response.get('response', {}).get('content_type')
@staticmethod
def _default_handler(response):
if not response:
return None
if 'response' in response:
return response['response']
if 'stdout' in response:
return response['stdout']
return response
@staticmethod
def json_handler(response):
return json.loads(response['response']['content'])
@staticmethod
def plain_handler(response):
return response['response']['content']
class CustomResponseMixin(object):
"""
A mixin which extends the Script and ScriptEndpoint traces (and any other Model - if used) with following fields:
* content - This is the response data if set_response is used in Script code, otherwise it is the 'stdout' field;
* content_type - The content_type specified by the user in Script code;
* status_code - The status_code specified by the user in Script code;
* error - An error which can occur when code is executed: the stderr response field;
To process the content based on content_type this Mixin uses the CustomResponseHandler - see the docs there.
"""
response_handler = CustomResponseHandler()
@property
def content(self):
return self.response_handler.process_response(self.result)
@property
def status_code(self):
return self.result.get('response', {}).get('status') if self.result else None
@property
def error(self):
return self.result.get('stderr') if self.result else None
@property
def content_type(self):
return self.result.get('response', {}).get('content_type') if self.result else None