Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: tests/xtm_mock_api.py

Issue 29886648: Issue #6942 - Add XTM integration in CMS (Closed)
Patch Set: Addressed comments from Patch Set #4 Created Oct. 5, 2018, 4:23 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(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.translations.xtm.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['Authorization']
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']]
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)
OLDNEW
« docs/usage/xml-sync.md ('K') | « tests/xtm_conftest.py ('k') | tox.ini » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld