Merge lp:~matiasb/click-toolbelt/added-missing-params into lp:click-toolbelt
- added-missing-params
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Matias Bordese |
Approved revision: | 9 |
Merged at revision: | 7 |
Proposed branch: | lp:~matiasb/click-toolbelt/added-missing-params |
Merge into: | lp:click-toolbelt |
Diff against target: |
418 lines (+113/-81) 5 files modified
README (+3/-1) click/login.py (+4/-1) click/tests/test_login.py (+43/-19) click/tests/test_upload.py (+47/-54) click/upload.py (+16/-6) |
To merge this branch: | bzr merge lp:~matiasb/click-toolbelt/added-missing-params |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ricardo Kirkner (community) | Approve | ||
Review via email: mp+189962@code.launchpad.net |
Commit message
- Added missing parameters (architectures, changelog).
- Updated/fixed SSO login integration to work with updated ssoclient.
Description of the change
Added missing parameters (architectures, what's new).
Updated/fixed SSO login integration to work with updated ssoclient.
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~matiasb/click-toolbelt/added-missing-params into lp:click-toolbelt failed. Below is the output from the failed tests.
running test
Checking .pth file support in .
/usr/bin/python -E -c pass
Searching for ssoclient
Reading http://
Best match: ssoclient 2.0
Downloading https:/
Processing ssoclient-
Writing /tmp/easy_
Running ssoclient-
Installed /mnt/tarmac/
Searching for requests-oauthlib
Reading http://
Best match: requests-oauthlib 0.4.0
Downloading https:/
Processing requests-
Writing /tmp/easy_
Running requests-
Installed /mnt/tarmac/
Searching for cliff
Reading http://
Best match: cliff 1.4.5
Downloading https:/
Processing cliff-1.4.5.tar.gz
Writing /tmp/easy_
Running cliff-1.
Installed /mnt/tarmac/
zip_safe flag not set; analyzing archive contents...
warning: no files found matching '*.py' under directory 'tests'
Traceback (most recent call last):
File "setup.py", line 69, in <module>
tests_
File "/usr/lib/
dist.
File "/usr/lib/
self.
File "/usr/lib/
cmd_obj.run()
File "/usr/lib/
self.
File "/usr/lib/
parse_
File "/usr/lib/
raise VersionConflict
pkg_resources.
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~matiasb/click-toolbelt/added-missing-params into lp:click-toolbelt failed. Below is the output from the failed tests.
rm -rf .env
rm -rf cliff-*.egg
rm -rf cmd2-*.egg
rm -rf mock-*.egg
rm -rf oauthlib-*.egg
rm -rf prettytable-*.egg
rm -rf pyparsing-*.egg
rm -rf pyxdg-*.egg
rm -rf requests-*.egg
rm -rf requests_
rm -rf ssoclient-*.egg
rm -rf click.egg-info
virtualenv .env
make: virtualenv: Command not found
make: *** [env] Error 127
Preview Diff
1 | === modified file 'README' |
2 | --- README 2013-09-18 08:15:02 +0000 |
3 | +++ README 2013-10-09 14:25:04 +0000 |
4 | @@ -34,7 +34,7 @@ |
5 | |
6 | :: |
7 | |
8 | - $ click upload <namespace>.<app_name> <version> <filename> |
9 | + $ click upload <namespace>.<app_name> <version> <filename> <changelog> [architecture] |
10 | |
11 | |
12 | For this command to be successful, certain criteria must be met:: |
13 | @@ -42,3 +42,5 @@ |
14 | - user is registered as a developer on myapps.developer.ubuntu.com |
15 | - the application has been previously created on myapps.developer.ubuntu.com |
16 | - user owns the application for which he's upload the click package |
17 | + - if not specified, architecture will default to 'all' |
18 | + (possible values: 'armhf', 'i386', 'amd64', 'all'; you can specify more than one) |
19 | |
20 | === modified file 'click/login.py' |
21 | --- click/login.py 2013-09-18 12:00:15 +0000 |
22 | +++ click/login.py 2013-10-09 14:25:04 +0000 |
23 | @@ -7,6 +7,7 @@ |
24 | from cliff.command import Command |
25 | from ssoclient.v2 import ( |
26 | ApiException, |
27 | + UnexpectedApiError, |
28 | V2ApiClient, |
29 | ) |
30 | from xdg.BaseDirectory import save_config_path |
31 | @@ -59,10 +60,12 @@ |
32 | client = V2ApiClient(endpoint=api_endpoint) |
33 | try: |
34 | response = client.login(data=data) |
35 | + result['body'] = response |
36 | result['success'] = True |
37 | - result['body'] = response.json() |
38 | except ApiException as err: |
39 | result['body'] = err.body |
40 | + except UnexpectedApiError as err: |
41 | + result['body'] = err.json_body |
42 | return result |
43 | |
44 | def take_action(self, parsed_args): |
45 | |
46 | === modified file 'click/tests/test_login.py' |
47 | --- click/tests/test_login.py 2013-09-18 12:00:15 +0000 |
48 | +++ click/tests/test_login.py 2013-10-09 14:25:04 +0000 |
49 | @@ -112,8 +112,8 @@ |
50 | self.assertTrue(cfg.has_section('login.ubuntu.com')) |
51 | self.assertEqual(dict(cfg.items('login.ubuntu.com')), data) |
52 | |
53 | - @patch('click.login.V2ApiClient.login') |
54 | - def test_take_action_successful(self, mock_login): |
55 | + @patch('ssoclient.v2.http.requests.Session.request') |
56 | + def test_take_action_successful(self, mock_post): |
57 | token_data = { |
58 | 'consumer_key': 'consumer-key', |
59 | 'consumer_secret': 'consumer-secret', |
60 | @@ -123,32 +123,56 @@ |
61 | response = Response() |
62 | response.status_code = 201 |
63 | response._content = json.dumps(token_data).encode('utf-8') |
64 | - mock_login.return_value = response |
65 | + mock_post.return_value = response |
66 | |
67 | data = { |
68 | 'email': 'foo@foo.com', |
69 | 'password': 'password', |
70 | + 'token_name': 'consumer-key', |
71 | } |
72 | result = self.command.login(data) |
73 | expected = {'success': True, 'body': token_data} |
74 | self.assertEqual(result, expected) |
75 | |
76 | - @patch('click.login.V2ApiClient.login') |
77 | - def test_take_action_unsuccessful(self, mock_login): |
78 | - error_data = { |
79 | - 'message': 'Error during login.', |
80 | - 'code': 'UNAUTHORISED', |
81 | - 'extra': {}, |
82 | - } |
83 | - response = Response() |
84 | - response.status_code = 401 |
85 | - response.reason = 'UNAUTHORISED' |
86 | - response._content = json.dumps(error_data).encode('utf-8') |
87 | - mock_login.side_effect = ApiException(response, response.json()) |
88 | - |
89 | - data = { |
90 | - 'email': 'foo@foo.com', |
91 | - 'password': 'password', |
92 | + @patch('ssoclient.v2.http.requests.Session.request') |
93 | + def test_take_action_unsuccessful_api_exception(self, mock_post): |
94 | + error_data = { |
95 | + 'message': 'Error during login.', |
96 | + 'code': 'INVALID_CREDENTIALS', |
97 | + 'extra': {}, |
98 | + } |
99 | + response = Response() |
100 | + response.status_code = 401 |
101 | + response.reason = 'UNAUTHORISED' |
102 | + response._content = json.dumps(error_data).encode('utf-8') |
103 | + mock_post.return_value = response |
104 | + |
105 | + data = { |
106 | + 'email': 'foo@foo.com', |
107 | + 'password': 'password', |
108 | + 'token_name': 'consumer-key', |
109 | + } |
110 | + result = self.command.login(data) |
111 | + expected = {'success': False, 'body': error_data} |
112 | + self.assertEqual(result, expected) |
113 | + |
114 | + @patch('ssoclient.v2.http.requests.Session.request') |
115 | + def test_take_action_unsuccessful_unexpected_error(self, mock_post): |
116 | + error_data = { |
117 | + 'message': 'Error during login.', |
118 | + 'code': 'UNEXPECTED_ERROR_CODE', |
119 | + 'extra': {}, |
120 | + } |
121 | + response = Response() |
122 | + response.status_code = 401 |
123 | + response.reason = 'UNAUTHORISED' |
124 | + response._content = json.dumps(error_data).encode('utf-8') |
125 | + mock_post.return_value = response |
126 | + |
127 | + data = { |
128 | + 'email': 'foo@foo.com', |
129 | + 'password': 'password', |
130 | + 'token_name': 'consumer-key', |
131 | } |
132 | result = self.command.login(data) |
133 | expected = {'success': False, 'body': error_data} |
134 | |
135 | === modified file 'click/tests/test_upload.py' |
136 | --- click/tests/test_upload.py 2013-09-13 13:32:25 +0000 |
137 | +++ click/tests/test_upload.py 2013-10-09 14:25:04 +0000 |
138 | @@ -27,7 +27,9 @@ |
139 | args = None |
140 | self.command = Upload(app, args) |
141 | self.app_name = 'namespace.package_name' |
142 | + self.app_archs = ['all'] |
143 | self.app_version = '1' |
144 | + self.app_changelog = 'Initial version.' |
145 | self.uploaded_callback_url = MYAPPS_API_ROOT_URL + 'click-uploaded/1/' |
146 | |
147 | # setup patches |
148 | @@ -144,7 +146,9 @@ |
149 | self.mock_post.return_value = response |
150 | |
151 | data = self.command.get_upload_data(self.app_name, |
152 | - self.app_version) |
153 | + self.app_version, |
154 | + self.app_changelog, |
155 | + self.app_archs) |
156 | self.assertEqual(data, expected) |
157 | |
158 | def test_get_upload_data_for_unknown_app(self): |
159 | @@ -155,7 +159,9 @@ |
160 | self.mock_post.return_value = response |
161 | |
162 | data = self.command.get_upload_data(self.app_name, |
163 | - self.app_version) |
164 | + self.app_version, |
165 | + self.app_changelog, |
166 | + self.app_archs) |
167 | self.assertEqual(data, { |
168 | 'success': False, |
169 | 'message': 'The requested Click Package could not be found.'}) |
170 | @@ -164,23 +170,33 @@ |
171 | self.mock_post.side_effect = Exception('some http error') |
172 | |
173 | data = self.command.get_upload_data(self.app_name, |
174 | - self.app_version) |
175 | + self.app_version, |
176 | + self.app_changelog, |
177 | + self.app_archs) |
178 | self.assertEqual(data, {'success': False, |
179 | 'message': 'some http error'}) |
180 | |
181 | def test_get_upload_data_uses_default_myapps_url(self): |
182 | upload_url = MYAPPS_API_ROOT_URL + "click-upload/%s/" % ( |
183 | self.app_name,) |
184 | - self.command.get_upload_data(self.app_name, self.app_version) |
185 | + self.command.get_upload_data( |
186 | + self.app_name, self.app_version, self.app_changelog, |
187 | + self.app_archs) |
188 | self.mock_post.assert_called_once_with( |
189 | - upload_url, data={'version': self.app_version}) |
190 | + upload_url, data={'version': self.app_version, |
191 | + 'architectures': self.app_archs, |
192 | + 'changelog': self.app_changelog}) |
193 | |
194 | def test_get_upload_data_uses_environment_variable(self): |
195 | upload_url = MYAPPS_API_ROOT_URL + "click-upload/%s/" % ( |
196 | self.app_name,) |
197 | - self.command.get_upload_data(self.app_name, self.app_version) |
198 | + self.command.get_upload_data( |
199 | + self.app_name, self.app_version, self.app_changelog, |
200 | + self.app_archs) |
201 | self.mock_post.assert_called_once_with( |
202 | - upload_url, data={'version': self.app_version}) |
203 | + upload_url, data={'version': self.app_version, |
204 | + 'architectures': self.app_archs, |
205 | + 'changelog': self.app_changelog}) |
206 | |
207 | |
208 | class MarkUploadedTestCase(BaseUploadCommandTestCase): |
209 | @@ -286,21 +302,28 @@ |
210 | patcher.start() |
211 | self.addCleanup(patcher.stop) |
212 | |
213 | + parsed_args = namedtuple( |
214 | + 'parsed_args', |
215 | + 'application_name, version, filename, changelog, arch') |
216 | + |
217 | + filename = 'package.click' |
218 | + self.args = parsed_args( |
219 | + self.app_name, self.app_version, filename, self.app_changelog, |
220 | + self.app_archs) |
221 | + |
222 | def test_parser(self): |
223 | parser = self.command.get_parser(__namespace__) |
224 | - for i, name in enumerate(['application_name', 'version', 'filename']): |
225 | + cmd_args = ['application_name', 'version', 'filename', 'changelog'] |
226 | + for i, name in enumerate(cmd_args): |
227 | # argument 0 is builtin --help |
228 | # start comparing from first extra argument |
229 | self.assertEqual(parser._actions[i + 1].dest, name) |
230 | - self.assertFalse(parser._actions[i + 1].required) |
231 | + self.assertTrue(parser._actions[i + 1].required) |
232 | + # arch is optional, and last param |
233 | + self.assertEqual(parser._actions[-1].dest, 'arch') |
234 | + self.assertFalse(parser._actions[-1].required) |
235 | |
236 | def test_take_action(self): |
237 | - parsed_args = namedtuple('parsed_args', |
238 | - 'application_name, version, filename') |
239 | - |
240 | - filename = 'package.click' |
241 | - args = parsed_args(self.app_name, self.app_version, filename) |
242 | - |
243 | response = Response() |
244 | response.status_code = 200 |
245 | response._content = json.dumps({'successful': True}).encode( |
246 | @@ -309,12 +332,13 @@ |
247 | |
248 | with patch('click.upload.Upload.mark_uploaded') as mock_mark_uploaded: |
249 | mock_mark_uploaded.return_value = {'success': True} |
250 | - self.command.take_action(args) |
251 | + self.command.take_action(self.args) |
252 | |
253 | # assert request was made to get upload data |
254 | # followed by an updown upload request |
255 | self.mock_get_upload_data.assert_called_once_with( |
256 | - self.app_name, self.app_version) |
257 | + self.app_name, self.app_version, self.app_changelog, |
258 | + self.app_archs) |
259 | self.mock_post.assert_called_with( |
260 | CLICK_UPDOWN_UPLOAD_URL, |
261 | files={'binary': self.mock_open.return_value}, |
262 | @@ -323,54 +347,35 @@ |
263 | 'Click Package upload finished successfully.') |
264 | |
265 | def test_take_action_handles_error_while_getting_upload_data(self): |
266 | - parsed_args = namedtuple('parsed_args', |
267 | - 'application_name, version, filename') |
268 | - |
269 | - filename = 'package.click' |
270 | - args = parsed_args(self.app_name, self.app_version, filename) |
271 | - |
272 | response = Response() |
273 | response.status_code = 200 |
274 | self.mock_post.return_value = response |
275 | self.mock_get_upload_data.return_value = { |
276 | 'success': False, 'message': 'Some error'} |
277 | |
278 | - self.command.take_action(args) |
279 | + self.command.take_action(self.args) |
280 | |
281 | self.mock_log.error.assert_called_with( |
282 | 'Error verifying upload request. Uploading not possible.\n' |
283 | 'Reason: %s', 'Some error') |
284 | |
285 | def test_take_action_handles_error_while_uploading(self): |
286 | - parsed_args = namedtuple('parsed_args', |
287 | - 'application_name, version, filename') |
288 | - |
289 | - filename = 'package.click' |
290 | - args = parsed_args(self.app_name, self.app_version, filename) |
291 | - |
292 | self.mock_post.side_effect = Exception('foo') |
293 | |
294 | - self.command.take_action(args) |
295 | + self.command.take_action(self.args) |
296 | |
297 | self.mock_log.exception.assert_called_with( |
298 | 'There was an error uploading the click package.') |
299 | |
300 | def test_take_action_handles_unsuccessful_response(self): |
301 | - parsed_args = namedtuple('parsed_args', |
302 | - 'application_name, version, filename') |
303 | - |
304 | - filename = 'package.click' |
305 | - args = parsed_args(self.app_name, self.app_version, filename) |
306 | - |
307 | - upload_response = {'success': True, |
308 | - 'message': None} |
309 | + upload_response = {'success': True, 'message': None} |
310 | response = Response() |
311 | response.status_code = 200 |
312 | response.reason = 'OK' |
313 | response._content = json.dumps(upload_response).encode('utf-8') |
314 | self.mock_post.return_value = response |
315 | |
316 | - self.command.take_action(args) |
317 | + self.command.take_action(self.args) |
318 | |
319 | self.mock_log.error.assert_called_with( |
320 | 'There was an error uploading the click package.\n' |
321 | @@ -379,19 +384,13 @@ |
322 | 'OK', json.dumps(upload_response)) |
323 | |
324 | def test_take_action_handles_error_response(self): |
325 | - parsed_args = namedtuple('parsed_args', |
326 | - 'application_name, version, filename') |
327 | - |
328 | - filename = 'package.click' |
329 | - args = parsed_args(self.app_name, self.app_version, filename) |
330 | - |
331 | response = Response() |
332 | response.status_code = 500 |
333 | response.reason = 'INTERNAL SERVER ERROR' |
334 | response._content = 'This is a traceback.'.encode('utf-8') |
335 | self.mock_post.return_value = response |
336 | |
337 | - self.command.take_action(args) |
338 | + self.command.take_action(self.args) |
339 | |
340 | self.mock_log.error.assert_called_with( |
341 | 'There was an error uploading the click package.\n' |
342 | @@ -400,12 +399,6 @@ |
343 | 'INTERNAL SERVER ERROR', 'This is a traceback.') |
344 | |
345 | def test_take_action_handles_unsuccessful_mark_uploaded_response(self): |
346 | - parsed_args = namedtuple('parsed_args', |
347 | - 'application_name, version, filename') |
348 | - |
349 | - filename = 'package.click' |
350 | - args = parsed_args(self.app_name, self.app_version, filename) |
351 | - |
352 | upload_response = {'successful': True, 'message': None} |
353 | response = Response() |
354 | response.status_code = 200 |
355 | @@ -416,7 +409,7 @@ |
356 | with patch('click.upload.Upload.mark_uploaded') as mock_mark_uploaded: |
357 | mock_mark_uploaded.return_value = { |
358 | 'success': False, 'message': 'Please try again later'} |
359 | - self.command.take_action(args) |
360 | + self.command.take_action(self.args) |
361 | |
362 | self.mock_log.error.assert_called_with( |
363 | 'There was an error after uploading the click package.\n' |
364 | |
365 | === modified file 'click/upload.py' |
366 | --- click/upload.py 2013-09-13 13:32:25 +0000 |
367 | +++ click/upload.py 2013-10-09 14:25:04 +0000 |
368 | @@ -23,9 +23,13 @@ |
369 | |
370 | def get_parser(self, prog_name): |
371 | parser = super(Upload, self).get_parser(prog_name) |
372 | - parser.add_argument('application_name', nargs='?') |
373 | - parser.add_argument('version', nargs='?') |
374 | - parser.add_argument('filename', nargs='?') |
375 | + parser.add_argument('application_name') |
376 | + parser.add_argument('version') |
377 | + parser.add_argument('filename') |
378 | + parser.add_argument('changelog') |
379 | + parser.add_argument( |
380 | + 'arch', nargs='*', default='all', |
381 | + choices=['armhf', 'i386', 'amd64', 'all']) |
382 | return parser |
383 | |
384 | def get_config(self): |
385 | @@ -58,7 +62,7 @@ |
386 | session = None |
387 | return session |
388 | |
389 | - def get_upload_data(self, name, version): |
390 | + def get_upload_data(self, name, version, changelog, architectures): |
391 | myapps_url = os.environ.get('MYAPPS_API_ROOT_URL', MYAPPS_API_ROOT_URL) |
392 | myapps_url = urljoin(myapps_url, 'click-upload/%s/' % name) |
393 | |
394 | @@ -70,7 +74,10 @@ |
395 | return result |
396 | |
397 | try: |
398 | - response = session.post(myapps_url, data={'version': version}) |
399 | + response = session.post( |
400 | + myapps_url, |
401 | + data={'version': version, 'changelog': changelog, |
402 | + 'architectures': architectures}) |
403 | if response.ok: |
404 | result.update(response.json()) |
405 | message = result.get('message') |
406 | @@ -118,8 +125,11 @@ |
407 | application_name = parsed_args.application_name |
408 | version = parsed_args.version |
409 | filename = parsed_args.filename |
410 | + changelog = parsed_args.changelog |
411 | + architectures = parsed_args.arch |
412 | |
413 | - data = self.get_upload_data(application_name, version) |
414 | + data = self.get_upload_data( |
415 | + application_name, version, changelog, architectures) |
416 | if not data.get('success', False): |
417 | error = data.get('message', 'Unknown error') |
418 | self.log.error( |
LGTM