| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # This file is part of the Adblock Plus web scripts, | |
| 2 # Copyright (C) 2006-present eyeo GmbH | |
| 3 # | |
| 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 | |
| 6 # published by the Free Software Foundation. | |
| 7 # | |
| 8 # Adblock Plus is distributed in the hope that it will be useful, | |
| 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 11 # GNU General Public License for more details. | |
| 12 # | |
| 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/>. | |
| 15 | |
| 16 from flask import Flask, request, jsonify, Response, send_file | |
| 17 | |
| 18 import json | |
| 19 | |
| 20 from tests.utils import create_in_memory_zip | |
| 21 from cms.bin.xtm_translations.constants import SUPPORTED_LOCALES | |
| 22 | |
| 23 __all__ = [ | |
| 24 'xtm_mock', | |
| 25 'get_configured_app', | |
| 26 ] | |
| 27 | |
| 28 _CREDENTIALS = { | |
| 29 'token_gen_valid': {'client': 'admin', 'password': 'pass', 'userId': 20}, | |
| 30 'token_gen_invalid': {'client': 'user', 'password': 'pass', 'userId': 50}, | |
| 31 'token_valid': 'TheXTM-APIToken-VALID', | |
| 32 'token_invalid': 'TheXTM-APIToken-INVALID', | |
| 33 } | |
| 34 | |
| 35 _PROJECT_IDS = { | |
| 36 'valid': 1234, | |
| 37 'invalid': 1111, | |
| 38 'valid-no-targetfiles': 9999, | |
| 39 } | |
| 40 | |
| 41 _CREATION_MANDATORY_KEYS = {'workflowId', 'referenceId', 'name', | |
| 42 'targetLanguages', 'customerId', 'description'} | |
| 43 | |
| 44 _CREATION_OPTIONAL_KEYS = {r'translationFiles\[(?P<idx>\d+)\].file', | |
| 45 r'translationFiles\[(?P<idx>\d+)\].name'} | |
| 46 | |
| 47 xtm_mock = Flask('mocking-XTM') | |
| 48 | |
| 49 | |
| 50 def get_configured_app(**kwargs): | |
| 51 xtm_mock.config['rootdir'] = kwargs.pop('rootdir', '') | |
| 52 xtm_mock.config['target_languages'] = kwargs.pop('target_langs', ['de_DE']) | |
| 53 xtm_mock.config['files'] = kwargs.pop('files', []) | |
| 54 xtm_mock.config['source_language'] = kwargs.pop('source_lang', 'en_US') | |
| 55 return xtm_mock.wsgi_app | |
| 56 | |
| 57 | |
| 58 def _check_auth(): | |
| 59 try: | |
| 60 auth = request.headers.get('Authorization') | |
|
Vasily Kuznetsov
2018/09/26 15:45:27
Can this just be `request.headers['Authorization']
Tudor Avram
2018/10/04 06:48:15
Done.
| |
| 61 except Exception: | |
| 62 return False | |
| 63 | |
| 64 if auth != 'XTM-Basic ' + _CREDENTIALS['token_valid']: | |
| 65 return False | |
| 66 | |
| 67 return True | |
| 68 | |
| 69 | |
| 70 def _generate_jobs_list(): | |
| 71 jobs = [] | |
| 72 for lng in xtm_mock.config['target_languages']: | |
| 73 for f in xtm_mock.config['files']: | |
| 74 jobs.append({ | |
| 75 'fileName': f['name'], | |
| 76 'jobId': 1, | |
| 77 'sourceLanguage': xtm_mock.config['source_language'], | |
| 78 'targetLanguage': lng, | |
| 79 }) | |
| 80 | |
| 81 return jobs | |
| 82 | |
| 83 | |
| 84 @xtm_mock.route('/rest-api/auth/token', methods=['POST']) | |
| 85 def authenticate(): | |
| 86 if not request.is_json: | |
| 87 return Response( | |
| 88 status=400, | |
| 89 response=json.dumps({'reason': 'Request body not a JSON.'}), | |
| 90 ) | |
| 91 | |
| 92 credentials = request.json | |
| 93 missing_keys = \ | |
| 94 set(_CREDENTIALS['token_gen_valid'].keys()) - set(credentials.keys()) | |
| 95 | |
| 96 if len(missing_keys) > 0: | |
| 97 return Response( | |
| 98 status=400, | |
| 99 response=json.dumps({'reason': 'Missing keys: {}'.format( | |
| 100 missing_keys)}), | |
| 101 ) | |
| 102 | |
| 103 if credentials == _CREDENTIALS['token_gen_valid']: | |
| 104 return jsonify({'token': _CREDENTIALS['token_valid']}) | |
| 105 | |
| 106 if credentials == _CREDENTIALS['token_gen_invalid']: | |
| 107 return jsonify({'token': _CREDENTIALS['token_invalid']}) | |
| 108 | |
| 109 return Response( | |
| 110 status=400, | |
| 111 response=json.dumps({'reason': 'Invalid credentials.'}), | |
| 112 ) | |
| 113 | |
| 114 | |
| 115 @xtm_mock.route('/rest-api/projects', methods=['POST']) | |
| 116 def create_project(): | |
| 117 if not _check_auth(): | |
| 118 return Response( | |
| 119 status=401, | |
| 120 response=json.dumps({'reason': 'Authentication failed.'}), | |
| 121 ) | |
| 122 | |
| 123 form_data = request.form.to_dict(flat=False) | |
| 124 files = request.files.to_dict() | |
| 125 xtm_mock.config['target_languages'] = form_data['targetLanguages'] | |
| 126 keys_diff = _CREATION_MANDATORY_KEYS - set(form_data.keys()) | |
| 127 | |
| 128 if len(keys_diff) != 0: | |
| 129 return Response( | |
| 130 status=400, | |
| 131 response=json.dumps( | |
| 132 {'reason': 'Keys missing: {}'.format(keys_diff)}, | |
| 133 ), | |
| 134 ) | |
| 135 | |
| 136 # To make everything easier, just add the 1st file to the class | |
| 137 if 'translationFiles[0].name' in form_data.keys(): | |
| 138 xtm_mock.config['files'].append({ | |
| 139 'name': form_data['translationFiles[0].name'][0], | |
| 140 'data': files['translationFiles[0].file'], | |
| 141 }) | |
| 142 | |
| 143 response_json = { | |
| 144 'name': form_data['name'][0], | |
| 145 'projectId': _PROJECT_IDS['valid'], | |
| 146 'jobs': _generate_jobs_list(), | |
| 147 } | |
| 148 | |
| 149 return Response(status=201, response=json.dumps(response_json)) | |
| 150 | |
| 151 | |
| 152 @xtm_mock.route('/rest-api/projects/<int:project_id>/metrics', methods=['GET']) | |
| 153 def get_metrics(project_id): | |
| 154 if not _check_auth(): | |
| 155 return Response( | |
| 156 status=401, | |
| 157 response=json.dumps({'reason': 'Authentication failed.'}), | |
| 158 ) | |
| 159 | |
| 160 reply = [{ | |
| 161 'coreMetrics': {}, | |
| 162 'jobsMetrics': [], | |
| 163 'metricsProgress': {}, | |
| 164 'targetLanguage': tl, | |
| 165 }for tl in xtm_mock.config['target_languages']] | |
|
Vasily Kuznetsov
2018/09/26 15:45:27
Nit: space before "for"?
Tudor Avram
2018/10/04 06:48:16
Done.
| |
| 166 | |
| 167 if project_id == _PROJECT_IDS['valid']: | |
| 168 return jsonify(reply) | |
| 169 | |
| 170 return Response(status=404, response=json.dumps({'reason': 'Project not ' | |
| 171 'found!'})) | |
| 172 | |
| 173 | |
| 174 @xtm_mock.route('/rest-api/projects/<int:project_id>/target-languages', | |
| 175 methods=['POST']) | |
| 176 def add_languages(project_id): | |
| 177 if not _check_auth(): | |
| 178 return Response( | |
| 179 status=401, | |
| 180 response=json.dumps({'reason': 'Authentication failed.'}), | |
| 181 ) | |
| 182 if not request.is_json: | |
| 183 return Response( | |
| 184 status=400, | |
| 185 response=json.dumps({'reason': 'Not a JSON.'}), | |
| 186 ) | |
| 187 | |
| 188 for language in request.json['targetLanguages']: | |
| 189 if language not in SUPPORTED_LOCALES: | |
| 190 return Response( | |
| 191 status=400, | |
| 192 response=json.dumps( | |
| 193 {'reason': 'Unsupported language: {''}'.format(language)}, | |
| 194 ), | |
| 195 ) | |
| 196 | |
| 197 xtm_mock.config['target_languages'] += request.json['targetLanguages'] | |
| 198 response = { | |
| 199 'jobs': _generate_jobs_list(), | |
| 200 'projectId': project_id, | |
| 201 'projectName': 'test-name', | |
| 202 } | |
| 203 | |
| 204 if project_id == _PROJECT_IDS['valid']: | |
| 205 return jsonify(response) | |
| 206 | |
| 207 return Response( | |
| 208 status=404, | |
| 209 response=json.dumps({'reason': 'Project not found!'}), | |
| 210 ) | |
| 211 | |
| 212 | |
| 213 @xtm_mock.route('/rest-api/projects/<int:project_id>/files/upload', | |
| 214 methods=['POST']) | |
| 215 def upload_and_update(project_id): | |
| 216 if not _check_auth(): | |
| 217 return Response( | |
| 218 status=401, | |
| 219 response='Authentication failed.', | |
| 220 ) | |
| 221 if project_id == _PROJECT_IDS['invalid']: | |
| 222 return Response( | |
| 223 status=404, | |
| 224 response='Project not found!', | |
| 225 ) | |
| 226 | |
| 227 data = request.form.to_dict() | |
| 228 files = request.files.to_dict() | |
| 229 | |
| 230 xtm_mock.config['files'].append({ | |
| 231 'name': data['files[0].name'], 'data': files['files[0].file'], | |
| 232 }) | |
| 233 | |
| 234 return jsonify({ | |
| 235 'project_id': 1234, | |
| 236 'jobs': _generate_jobs_list(), | |
| 237 }) | |
| 238 | |
| 239 | |
| 240 @xtm_mock.route('/rest-api/projects/<int:project_id>/files/download', | |
| 241 methods=['GET']) | |
| 242 def download(project_id): | |
| 243 if not _check_auth(): | |
| 244 return Response( | |
| 245 status=401, | |
| 246 response=json.dumps({'reason': 'Authentication failed.'}), | |
| 247 ) | |
| 248 if project_id == _PROJECT_IDS['invalid']: | |
| 249 return Response( | |
| 250 status=404, | |
| 251 response=json.dumps({'reason': 'Project not found.'}), | |
| 252 ) | |
| 253 if project_id == _PROJECT_IDS['valid-no-targetfiles']: | |
| 254 return Response(status=200) | |
| 255 | |
| 256 names = [ | |
| 257 '/'.join([target_lang, f['name']]) for f in xtm_mock.config['files'] | |
| 258 for target_lang in xtm_mock.config['target_languages'] | |
| 259 ] | |
| 260 data = [ | |
| 261 f['data'] for f in xtm_mock.config['files'] | |
| 262 for _ in xtm_mock.config['target_languages'] | |
| 263 ] | |
| 264 | |
| 265 zip_file = create_in_memory_zip(names, data) | |
| 266 return send_file( | |
| 267 zip_file, | |
| 268 mimetype='application/zip', | |
| 269 ) | |
| 270 | |
| 271 | |
| 272 if __name__ == '__main__': | |
| 273 xtm_mock.run(debug=True) | |
| OLD | NEW |