|
6 | 6 | from coreapi.document import Document, Object, Link, Array, Error |
7 | 7 | from coreapi.exceptions import ErrorMessage |
8 | 8 | from coreapi.transports.base import BaseTransport |
| 9 | +import collections |
9 | 10 | import requests |
10 | 11 | import itypes |
11 | | -import json |
| 12 | +import mimetypes |
12 | 13 | import uritemplate |
13 | 14 |
|
14 | 15 |
|
15 | | -def _get_http_method(action): |
| 16 | +Params = collections.namedtuple('Params', ['path', 'query', 'headers', 'body', 'data', 'files']) |
| 17 | +empty_params = Params({}, {}, {}, None, {}, {}) |
| 18 | + |
| 19 | + |
| 20 | +def _get_method(action): |
16 | 21 | if not action: |
17 | 22 | return 'GET' |
18 | 23 | return action.upper() |
19 | 24 |
|
20 | 25 |
|
21 | | -def _separate_params(method, fields, params=None): |
| 26 | +def _get_params(method, fields, params=None): |
22 | 27 | """ |
23 | 28 | Separate the params into their location types: path, query, or form. |
24 | 29 | """ |
25 | 30 | if params is None: |
26 | | - return ({}, {}, {}, {}) |
| 31 | + return empty_params |
27 | 32 |
|
28 | 33 | field_map = {field.name: field for field in fields} |
29 | | - path_params = {} |
30 | | - query_params = {} |
31 | | - body_params = {} |
32 | | - header_params = {} |
| 34 | + |
| 35 | + path = {} |
| 36 | + query = {} |
| 37 | + headers = {} |
| 38 | + body = None |
| 39 | + data = {} |
| 40 | + files = {} |
| 41 | + |
33 | 42 | for key, value in params.items(): |
34 | 43 | if key not in field_map or not field_map[key].location: |
35 | | - # Default is 'query' for 'GET'/'DELETE', and 'form' others. |
36 | | - location = 'query' if method in ('GET', 'DELETE') else 'form' |
| 44 | + # Default is 'query' for 'GET', and 'form' for others. |
| 45 | + location = 'query' if method == 'GET' else 'form' |
37 | 46 | else: |
38 | 47 | location = field_map[key].location |
39 | 48 |
|
40 | 49 | if location == 'path': |
41 | | - path_params[key] = value |
| 50 | + path[key] = value |
42 | 51 | elif location == 'query': |
43 | | - query_params[key] = value |
| 52 | + query[key] = value |
44 | 53 | elif location == 'header': |
45 | | - header_params[key] = value |
| 54 | + headers[key] = value |
46 | 55 | elif location == 'body': |
47 | | - body_params = value |
48 | | - else: |
49 | | - body_params[key] = value |
| 56 | + body = value |
| 57 | + elif location == 'form': |
| 58 | + if isinstance(value, file): |
| 59 | + files[key] = value |
| 60 | + else: |
| 61 | + data[key] = value |
| 62 | + |
| 63 | + return Params(path, query, headers, body, data, files) |
| 64 | + |
| 65 | + |
| 66 | +def _get_encoding(encoding, params): |
| 67 | + if encoding: |
| 68 | + return encoding |
| 69 | + |
| 70 | + if params.body is not None: |
| 71 | + if isinstance(params.body, file): |
| 72 | + return 'application/octet-stream' |
| 73 | + return 'application/json' |
| 74 | + elif params.files: |
| 75 | + return 'multipart/form-data' |
| 76 | + elif params.data: |
| 77 | + return 'application/json' |
50 | 78 |
|
51 | | - return path_params, query_params, body_params, header_params |
| 79 | + return '' |
52 | 80 |
|
53 | 81 |
|
54 | | -def _expand_path_params(url, path_params): |
| 82 | +def _get_url(url, path_params): |
55 | 83 | """ |
56 | 84 | Given a templated URL and some parameters that have been provided, |
57 | 85 | expand the URL. |
@@ -85,19 +113,38 @@ def _get_headers(url, decoders=None, credentials=None): |
85 | 113 | return headers |
86 | 114 |
|
87 | 115 |
|
88 | | -def _make_http_request(url, method, headers=None, query_params=None, form_params=None): |
| 116 | +def _make_http_request(url, method, headers=None, encoding=None, params=empty_params): |
89 | 117 | """ |
90 | 118 | Make an HTTP request and return an HTTP response. |
91 | 119 | """ |
92 | 120 | opts = { |
93 | 121 | "headers": headers or {} |
94 | 122 | } |
95 | 123 |
|
96 | | - if query_params: |
97 | | - opts['params'] = query_params |
98 | | - elif form_params: |
99 | | - opts['data'] = json.dumps(form_params) |
100 | | - opts['headers']['content-type'] = 'application/json' |
| 124 | + if params.query: |
| 125 | + opts['params'] = params.query |
| 126 | + |
| 127 | + if (params.body is not None) or params.data or params.files: |
| 128 | + if encoding == 'application/json': |
| 129 | + if params.body is not None: |
| 130 | + opts['json'] = params.body |
| 131 | + else: |
| 132 | + opts['json'] = params.data |
| 133 | + elif encoding == 'multipart/form-data': |
| 134 | + opts['data'] = params.data |
| 135 | + opts['files'] = params.file |
| 136 | + elif encoding == 'application/x-www-form-urlencoded': |
| 137 | + opts['data'] = params.data |
| 138 | + elif encoding == 'application/octet-stream': |
| 139 | + opts['data'] = params.body |
| 140 | + # Determine a Content-Type header to add, if possible. |
| 141 | + content_type = getattr(params.body, 'content_type') |
| 142 | + if content_type is None: |
| 143 | + name = getattr(params.body, 'name') |
| 144 | + if name is not None: |
| 145 | + content_type, encoding = mimetypes.guess_type(name) |
| 146 | + if content_type: |
| 147 | + opts['headers']['content-type'] = content_type |
101 | 148 |
|
102 | 149 | return requests.request(method, url, **opts) |
103 | 150 |
|
@@ -207,13 +254,14 @@ def headers(self): |
207 | 254 | return self._headers |
208 | 255 |
|
209 | 256 | def transition(self, link, params=None, decoders=None, link_ancestors=None): |
210 | | - method = _get_http_method(link.action) |
211 | | - path_params, query_params, body_params, header_params = _separate_params(method, link.fields, params) |
212 | | - url = _expand_path_params(link.url, path_params) |
| 257 | + method = _get_method(link.action) |
| 258 | + params = _get_params(method, link.fields, params) |
| 259 | + encoding = _get_encoding(link.encoding, params) |
| 260 | + url = _get_url(link.url, params.path) |
213 | 261 | headers = _get_headers(url, decoders, self.credentials) |
214 | 262 | headers.update(self.headers) |
215 | | - headers.update(header_params) |
216 | | - response = _make_http_request(url, method, headers, query_params, body_params) |
| 263 | + headers.update(params.headers) |
| 264 | + response = _make_http_request(url, method, headers, encoding, params) |
217 | 265 | result = _decode_result(response, decoders) |
218 | 266 |
|
219 | 267 | if isinstance(result, Document) and link_ancestors: |
|
0 commit comments