| OLD | NEW |
| 1 # This file is part of the Adblock Plus web scripts, | 1 # This file is part of the Adblock Plus web scripts, |
| 2 # Copyright (C) 2006-present eyeo GmbH | 2 # Copyright (C) 2006-present eyeo GmbH |
| 3 # | 3 # |
| 4 # Adblock Plus is free software: you can redistribute it and/or modify | 4 # Adblock Plus is free software: you can redistribute it and/or modify |
| 5 # it under the terms of the GNU General Public License version 3 as | 5 # it under the terms of the GNU General Public License version 3 as |
| 6 # published by the Free Software Foundation. | 6 # published by the Free Software Foundation. |
| 7 # | 7 # |
| 8 # Adblock Plus is distributed in the hope that it will be useful, | 8 # Adblock Plus is distributed in the hope that it will be useful, |
| 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 # GNU General Public License for more details. | 11 # GNU General Public License for more details. |
| 12 # | 12 # |
| 13 # You should have received a copy of the GNU General Public License | 13 # You should have received a copy of the GNU General Public License |
| 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| 15 | 15 |
| 16 from __future__ import unicode_literals | 16 from __future__ import unicode_literals |
| 17 | 17 |
| 18 import json | 18 import json |
| 19 try: |
| 20 import urlparse |
| 21 except ImportError: |
| 22 # It's Python 3 |
| 23 import urllib.parse as urlparse |
| 19 | 24 |
| 20 import requests | 25 import requests |
| 21 | 26 |
| 22 __all__ = [ | 27 __all__ = [ |
| 23 'XTMCloudAPI', 'XTMCloudException', 'get_token', | 28 'XTMCloudAPI', 'XTMCloudException', 'get_token', |
| 24 ] | 29 ] |
| 25 | 30 |
| 26 _BASE_URL = 'https://wstest2.xtm-intl.com/rest-api/' | 31 _BASE_URL = 'https://wstest2.xtm-intl.com/rest-api/' |
| 27 | 32 |
| 28 | 33 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 } | 74 } |
| 70 | 75 |
| 71 class _SuccessCodes: | 76 class _SuccessCodes: |
| 72 CREATE = 201 | 77 CREATE = 201 |
| 73 UPLOAD = 200 | 78 UPLOAD = 200 |
| 74 DOWNLOAD = 200 | 79 DOWNLOAD = 200 |
| 75 GET_TARGET_LANGS = 200 | 80 GET_TARGET_LANGS = 200 |
| 76 ADD_TARGET_LANGS = 200 | 81 ADD_TARGET_LANGS = 200 |
| 77 GET_WORKFLOW_IDS = 200 | 82 GET_WORKFLOW_IDS = 200 |
| 78 | 83 |
| 79 def __init__(self, token): | 84 def __init__(self, token, base_url): |
| 80 """Constructor. | 85 """Constructor. |
| 81 | 86 |
| 82 Parameters | 87 Parameters |
| 83 ---------- | 88 ---------- |
| 84 token: str | 89 token: str |
| 85 Token used to authenticate with the API. | 90 Token used to authenticate with the API. |
| 91 base_url: str |
| 92 Url used to connect to the API. |
| 86 | 93 |
| 87 """ | 94 """ |
| 88 self._token = token | 95 self._token = token |
| 89 self.base_url = _BASE_URL | 96 self.base_url = base_url |
| 90 | 97 |
| 91 def _execute(self, url, data=None, files=None, stream=False, | 98 def _execute(self, url, data=None, files=None, stream=False, |
| 92 params=None, headers=None): | 99 params=None, headers=None): |
| 93 """Send request to the API and return the response. | 100 """Send request to the API and return the response. |
| 94 | 101 |
| 95 Parameters | 102 Parameters |
| 96 ---------- | 103 ---------- |
| 97 url: str | 104 url: str |
| 98 The url we're making the request to. | 105 The url we're making the request to. |
| 99 data: dict | 106 data: dict |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 } | 218 } |
| 212 | 219 |
| 213 if files: | 220 if files: |
| 214 file_names, files_to_upload = self._construct_files_dict( | 221 file_names, files_to_upload = self._construct_files_dict( |
| 215 files, 'translationFiles[{}]') | 222 files, 'translationFiles[{}]') |
| 216 data.update(file_names) | 223 data.update(file_names) |
| 217 else: | 224 else: |
| 218 # Hacky way to go around 415 error code | 225 # Hacky way to go around 415 error code |
| 219 files_to_upload = {'a': 'b'} | 226 files_to_upload = {'a': 'b'} |
| 220 | 227 |
| 221 url = self.base_url + self._UrlPaths.CREATE | 228 url = urlparse.urljoin(self.base_url, self._UrlPaths.CREATE) |
| 222 | 229 |
| 223 response = self._execute(url, data=data, files=files_to_upload) | 230 response = self._execute(url, data=data, files=files_to_upload) |
| 224 | 231 |
| 225 if response.status_code != self._SuccessCodes.CREATE: | 232 if response.status_code != self._SuccessCodes.CREATE: |
| 226 # The creation was not successful | 233 # The creation was not successful |
| 234 print('Raising exception') |
| 227 raise XTMCloudException(response.status_code, | 235 raise XTMCloudException(response.status_code, |
| 228 response.text.decode('utf-8'), | 236 response.text.decode('utf-8'), |
| 229 'creating job') | 237 'creating job') |
| 230 | 238 |
| 231 data = json.loads(response.text.encode('utf-8')) | 239 data = json.loads(response.text.encode('utf-8')) |
| 232 | 240 |
| 233 return data['projectId'], data['jobs'] | 241 return data['projectId'], data['jobs'] |
| 234 | 242 |
| 235 def upload_files(self, files, project_id, overwrite=True): | 243 def upload_files(self, files, project_id, overwrite=True): |
| 236 """Upload a set of files to a project. | 244 """Upload a set of files to a project. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 268 file_names, files_to_upload = self._construct_files_dict( | 276 file_names, files_to_upload = self._construct_files_dict( |
| 269 files, 'files[{}]', | 277 files, 'files[{}]', |
| 270 ) | 278 ) |
| 271 | 279 |
| 272 if len(files_to_upload.keys()) == 0: | 280 if len(files_to_upload.keys()) == 0: |
| 273 raise Exception('Error: No files provided for upload.') | 281 raise Exception('Error: No files provided for upload.') |
| 274 | 282 |
| 275 data = {'matchType': self._MATCH_TYPE[overwrite]} | 283 data = {'matchType': self._MATCH_TYPE[overwrite]} |
| 276 data.update(file_names) | 284 data.update(file_names) |
| 277 | 285 |
| 278 url = self.base_url + self._UrlPaths.UPLOAD.format(project_id) | 286 url = urlparse.urljoin( |
| 287 self.base_url, self._UrlPaths.UPLOAD.format(project_id), |
| 288 ) |
| 279 | 289 |
| 280 response = self._execute(url, data=data, files=files_to_upload) | 290 response = self._execute(url, data=data, files=files_to_upload) |
| 281 | 291 |
| 282 if response.status_code != self._SuccessCodes.UPLOAD: | 292 if response.status_code != self._SuccessCodes.UPLOAD: |
| 283 raise XTMCloudException(response.status_code, | 293 raise XTMCloudException(response.status_code, |
| 284 response.text.encode('utf-8'), | 294 response.text.encode('utf-8'), |
| 285 'uploading files') | 295 'uploading files') |
| 286 | 296 |
| 287 return json.loads(response.text.encode('utf-8'))['jobs'] | 297 return json.loads(response.text.encode('utf-8'))['jobs'] |
| 288 | 298 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 300 ------- | 310 ------- |
| 301 bytes | 311 bytes |
| 302 The contents of the zip file returned by the API | 312 The contents of the zip file returned by the API |
| 303 | 313 |
| 304 Raises | 314 Raises |
| 305 ------ | 315 ------ |
| 306 XTMCloudException | 316 XTMCloudException |
| 307 If the request is flawed in any way. | 317 If the request is flawed in any way. |
| 308 | 318 |
| 309 """ | 319 """ |
| 310 url = (self.base_url + self._UrlPaths.DOWNLOAD).format(project_id) | 320 url = urlparse.urljoin( |
| 321 self.base_url, self._UrlPaths.DOWNLOAD.format(project_id), |
| 322 ) |
| 311 | 323 |
| 312 exception_msg = { | 324 exception_msg = { |
| 313 400: 'Invalid request', | 325 400: 'Invalid request', |
| 314 401: 'Authentication failed', | 326 401: 'Authentication failed', |
| 315 500: 'Internal server error', | 327 500: 'Internal server error', |
| 316 404: 'Project not found.', | 328 404: 'Project not found.', |
| 317 } | 329 } |
| 318 | 330 |
| 319 response = self._execute(url, params={'fileType': files_type}, | 331 response = self._execute(url, params={'fileType': files_type}, |
| 320 stream=True) | 332 stream=True) |
| (...skipping 19 matching lines...) Expand all Loading... |
| 340 ------- | 352 ------- |
| 341 iterable | 353 iterable |
| 342 With the target languages for this project. | 354 With the target languages for this project. |
| 343 | 355 |
| 344 Raises | 356 Raises |
| 345 ------ | 357 ------ |
| 346 XTMCloudException | 358 XTMCloudException |
| 347 If the request is unsuccessful. | 359 If the request is unsuccessful. |
| 348 | 360 |
| 349 """ | 361 """ |
| 350 url = (self.base_url + self._UrlPaths.GET_TARGET_LANG).format( | 362 url = urlparse.urljoin( |
| 351 project_id, | 363 self.base_url, self._UrlPaths.GET_TARGET_LANG.format(project_id), |
| 352 ) | 364 ) |
| 353 | 365 |
| 354 response = self._execute(url, stream=True) | 366 response = self._execute(url, stream=True) |
| 355 | 367 |
| 356 if response.status_code != self._SuccessCodes.GET_TARGET_LANGS: | 368 if response.status_code != self._SuccessCodes.GET_TARGET_LANGS: |
| 357 raise XTMCloudException(response.status_code, response.content, | 369 raise XTMCloudException(response.status_code, response.content, |
| 358 'extracting target languages') | 370 'extracting target languages') |
| 359 | 371 |
| 360 data = json.loads(response.content.encode('utf-8')) | 372 data = json.loads(response.content.encode('utf-8')) |
| 361 return {item['targetLanguage'] for item in data} | 373 return {item['targetLanguage'] for item in data} |
| (...skipping 10 matching lines...) Expand all Loading... |
| 372 | 384 |
| 373 Raises | 385 Raises |
| 374 ------- | 386 ------- |
| 375 XTMCloudException | 387 XTMCloudException |
| 376 If the request to the API was not successful. | 388 If the request to the API was not successful. |
| 377 | 389 |
| 378 """ | 390 """ |
| 379 data = json.dumps({ | 391 data = json.dumps({ |
| 380 'targetLanguages': target_languages, | 392 'targetLanguages': target_languages, |
| 381 }) | 393 }) |
| 382 url = (self.base_url + self._UrlPaths.ADD_TARGET_LANG).format( | 394 |
| 383 project_id, | 395 url = urlparse.urljoin( |
| 396 self.base_url, self._UrlPaths.ADD_TARGET_LANG.format(project_id), |
| 384 ) | 397 ) |
| 398 |
| 385 headers = {'content-type': 'application/json'} | 399 headers = {'content-type': 'application/json'} |
| 386 | 400 |
| 387 response = self._execute(url, data=data, headers=headers) | 401 response = self._execute(url, data=data, headers=headers) |
| 388 | 402 |
| 389 if response.status_code != self._SuccessCodes.ADD_TARGET_LANGS: | 403 if response.status_code != self._SuccessCodes.ADD_TARGET_LANGS: |
| 390 raise XTMCloudException(response.status_code, response.content, | 404 raise XTMCloudException(response.status_code, response.content, |
| 391 'adding target languages to project') | 405 'adding target languages to project') |
| 392 | 406 |
| 393 def get_workflows_by_name(self, name): | 407 def get_workflows_by_name(self, name): |
| 394 """Get workflows with a specific name. | 408 """Get workflows with a specific name. |
| 395 | 409 |
| 396 Parameters | 410 Parameters |
| 397 ---------- | 411 ---------- |
| 398 name: str | 412 name: str |
| 399 The name of the workflow we're looking for. | 413 The name of the workflow we're looking for. |
| 400 | 414 |
| 401 Returns | 415 Returns |
| 402 ------- | 416 ------- |
| 403 iterable | 417 iterable |
| 404 Of workflow ids that match the name provided. | 418 Of workflow ids that match the name provided. |
| 405 | 419 |
| 406 """ | 420 """ |
| 407 url = self.base_url + self._UrlPaths.GET_WORKFLOW_IDS | 421 url = urlparse.urljoin(self.base_url, self._UrlPaths.GET_WORKFLOW_IDS) |
| 408 | 422 |
| 409 response = self._execute(url, params={'name': name}) | 423 response = self._execute(url, params={'name': name}) |
| 410 | 424 |
| 411 if response.status_code != self._SuccessCodes.GET_WORKFLOW_IDS: | 425 if response.status_code != self._SuccessCodes.GET_WORKFLOW_IDS: |
| 412 raise XTMCloudException(response.status_code, response.content, | 426 raise XTMCloudException(response.status_code, response.content, |
| 413 'extracting workflow ids') | 427 'extracting workflow ids') |
| 414 | 428 |
| 415 valid_ids = [] | 429 valid_ids = [] |
| 416 for item in json.loads(response.content.encode('utf-8')): | 430 for item in json.loads(response.content.encode('utf-8')): |
| 417 if name.lower().replace(' ', '') == \ | 431 if name.lower().replace(' ', '') == \ |
| 418 item['name'].lower().replace(' ', ''): | 432 item['name'].lower().replace(' ', ''): |
| 419 valid_ids.append(item['id']) | 433 valid_ids.append(item['id']) |
| 420 return valid_ids | 434 return valid_ids |
| 421 | 435 |
| 422 | 436 |
| 423 def get_token(username, password, user_id): | 437 def get_token(username, password, user_id, base_url): |
| 424 """Generate an API token from username and password. | 438 """Generate an API token from username and password. |
| 425 | 439 |
| 426 Parameters | 440 Parameters |
| 427 ---------- | 441 ---------- |
| 428 username: str | 442 username: str |
| 429 The username used to generate the token. | 443 The username used to generate the token. |
| 430 password: str | 444 password: str |
| 431 The password used to generate the token. | 445 The password used to generate the token. |
| 432 user_id: int | 446 user_id: int |
| 433 The user ID used to generate the token. | 447 The user ID used to generate the token. |
| 448 base_url: str |
| 449 The url used to connect to the API |
| 434 | 450 |
| 435 Returns | 451 Returns |
| 436 ------- | 452 ------- |
| 437 str | 453 str |
| 438 The resulting token generated by the API. | 454 The resulting token generated by the API. |
| 439 | 455 |
| 440 Raises | 456 Raises |
| 441 ------ | 457 ------ |
| 442 XTMCloudException | 458 XTMCloudException |
| 443 If the credentials provided were invalid. | 459 If the credentials provided were invalid. |
| 444 | 460 |
| 445 """ | 461 """ |
| 446 request_body = json.dumps({ | 462 request_body = json.dumps({ |
| 447 'client': username, | 463 'client': username, |
| 448 'password': password, | 464 'password': password, |
| 449 'userId': user_id, | 465 'userId': user_id, |
| 450 }) | 466 }) |
| 451 | 467 |
| 452 url = _BASE_URL + 'auth/token' | 468 url = urlparse.urljoin(base_url, 'auth/token') |
| 453 | 469 |
| 454 headers = {'content-type': 'application/json'} | 470 headers = {'content-type': 'application/json'} |
| 455 | 471 |
| 456 response = requests.post(url, data=request_body, headers=headers) | 472 response = requests.post(url, data=request_body, headers=headers) |
| 457 | 473 |
| 458 if response.status_code == 200: | 474 if response.status_code == 200: |
| 459 return json.loads(response.text)['token'].encode() | 475 return json.loads(response.text)['token'].encode() |
| 460 | 476 |
| 461 raise XTMCloudException(response.status_code, | 477 raise XTMCloudException(response.status_code, |
| 462 response.text.encode('utf-8'), | 478 response.text.encode('utf-8'), |
| 463 'generating token') | 479 'generating token') |
| OLD | NEW |