0002-misc-split-some-tests.patch
tests/api/test_access.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import base64 |
|
4 |
import datetime |
|
5 |
import hashlib |
|
6 |
import hmac |
|
7 |
import os |
|
8 |
import shutil |
|
9 | ||
10 |
import pytest |
|
11 |
from django.utils.encoding import force_bytes |
|
12 |
from django.utils.six.moves.urllib import parse as urllib |
|
13 |
from quixote import get_publisher |
|
14 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app, login |
|
15 | ||
16 |
from wcs.api_utils import get_secret_and_orig, is_url_signed, sign_url |
|
17 |
from wcs.qommon.errors import AccessForbiddenError |
|
18 |
from wcs.qommon.http_request import HTTPRequest |
|
19 |
from wcs.qommon.ident.password_accounts import PasswordAccount |
|
20 |
from wcs.roles import Role |
|
21 | ||
22 | ||
23 |
def pytest_generate_tests(metafunc): |
|
24 |
if 'pub' in metafunc.fixturenames: |
|
25 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
26 | ||
27 | ||
28 |
@pytest.fixture |
|
29 |
def pub(request, emails): |
|
30 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
31 | ||
32 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
33 |
pub.set_app_dir(req) |
|
34 |
pub.cfg['identification'] = {'methods': ['password']} |
|
35 |
pub.cfg['language'] = {'language': 'en'} |
|
36 |
pub.write_cfg() |
|
37 | ||
38 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
39 |
'''\ |
|
40 |
[api-secrets] |
|
41 |
coucou = 1234 |
|
42 |
''' |
|
43 |
) |
|
44 | ||
45 |
return pub |
|
46 | ||
47 | ||
48 |
def teardown_module(module): |
|
49 |
clean_temporary_pub() |
|
50 | ||
51 | ||
52 |
@pytest.fixture |
|
53 |
def local_user(): |
|
54 |
get_publisher().user_class.wipe() |
|
55 |
user = get_publisher().user_class() |
|
56 |
user.name = 'Jean Darmette' |
|
57 |
user.email = 'jean.darmette@triffouilis.fr' |
|
58 |
user.name_identifiers = ['0123456789'] |
|
59 |
user.store() |
|
60 |
return user |
|
61 | ||
62 | ||
63 |
@pytest.fixture |
|
64 |
def admin_user(): |
|
65 |
get_publisher().user_class.wipe() |
|
66 |
user = get_publisher().user_class() |
|
67 |
user.name = 'John Doe Admin' |
|
68 |
user.email = 'john.doe@example.com' |
|
69 |
user.name_identifiers = ['0123456789'] |
|
70 |
user.is_admin = True |
|
71 |
user.store() |
|
72 | ||
73 |
account = PasswordAccount(id='admin') |
|
74 |
account.set_password('admin') |
|
75 |
account.user_id = user.id |
|
76 |
account.store() |
|
77 | ||
78 |
return user |
|
79 | ||
80 | ||
81 |
@pytest.fixture(params=['sql', 'pickle']) |
|
82 |
def no_request_pub(request): |
|
83 |
pub = create_temporary_pub(sql_mode=bool(request.param == 'sql')) |
|
84 |
pub.app_dir = os.path.join(pub.APP_DIR, 'example.net') |
|
85 |
pub.set_config() |
|
86 | ||
87 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
88 |
''' |
|
89 |
[wscall-secrets] |
|
90 |
api.example.com = 1234 |
|
91 |
''' |
|
92 |
) |
|
93 |
return pub |
|
94 | ||
95 | ||
96 |
def test_get_secret_and_orig(no_request_pub): |
|
97 |
secret, orig = get_secret_and_orig('https://api.example.com/endpoint/') |
|
98 |
assert secret == '1234' |
|
99 |
assert orig == 'example.net' |
|
100 | ||
101 | ||
102 |
def test_user_page_redirect(pub): |
|
103 |
output = get_app(pub).get('/user') |
|
104 |
assert output.headers.get('location') == 'http://example.net/myspace/' |
|
105 | ||
106 | ||
107 |
def test_user_page_error(pub): |
|
108 |
# check we get json as output for errors |
|
109 |
output = get_app(pub).get('/api/user/', status=403) |
|
110 |
assert output.json['err_desc'] == 'no user specified' |
|
111 | ||
112 | ||
113 |
def test_user_page_error_when_json_and_no_user(pub): |
|
114 |
output = get_app(pub).get('/api/user/?format=json', status=403) |
|
115 |
assert output.json['err_desc'] == 'no user specified' |
|
116 | ||
117 | ||
118 |
def test_get_user_from_api_query_string_error_missing_orig(pub): |
|
119 |
output = get_app(pub).get('/api/user/?format=json&signature=xxx', status=403) |
|
120 |
assert output.json['err_desc'] == 'missing/multiple orig field' |
|
121 | ||
122 | ||
123 |
def test_get_user_from_api_query_string_error_invalid_orig(pub): |
|
124 |
output = get_app(pub).get('/api/user/?format=json&orig=coin&signature=xxx', status=403) |
|
125 |
assert output.json['err_desc'] == 'invalid orig' |
|
126 | ||
127 | ||
128 |
def test_get_user_from_api_query_string_error_missing_algo(pub): |
|
129 |
output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx', status=403) |
|
130 |
assert output.json['err_desc'] == 'missing/multiple algo field' |
|
131 | ||
132 | ||
133 |
def test_get_user_from_api_query_string_error_invalid_algo(pub): |
|
134 |
output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx&algo=coin', status=403) |
|
135 |
assert output.json['err_desc'] == 'invalid algo' |
|
136 |
output = get_app(pub).get( |
|
137 |
'/api/user/?format=json&orig=coucou&signature=xxx&algo=__getattribute__', status=403 |
|
138 |
) |
|
139 |
assert output.json['err_desc'] == 'invalid algo' |
|
140 | ||
141 | ||
142 |
def test_get_user_from_api_query_string_error_invalid_signature(pub): |
|
143 |
output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx&algo=sha1', status=403) |
|
144 |
assert output.json['err_desc'] == 'invalid signature' |
|
145 | ||
146 | ||
147 |
def test_get_user_from_api_query_string_error_missing_timestamp(pub): |
|
148 |
signature = urllib.quote( |
|
149 |
base64.b64encode(hmac.new(b'1234', b'format=json&orig=coucou&algo=sha1', hashlib.sha1).digest()) |
|
150 |
) |
|
151 |
output = get_app(pub).get( |
|
152 |
'/api/user/?format=json&orig=coucou&algo=sha1&signature=%s' % signature, status=403 |
|
153 |
) |
|
154 |
assert output.json['err_desc'] == 'missing/multiple timestamp field' |
|
155 | ||
156 | ||
157 |
def test_get_user_from_api_query_string_error_missing_email(pub): |
|
158 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
159 |
query = 'format=json&orig=coucou&algo=sha1×tamp=' + timestamp |
|
160 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
161 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) |
|
162 |
assert output.json['err_desc'] == 'no user specified' |
|
163 | ||
164 | ||
165 |
def test_get_user_from_api_query_string_error_unknown_nameid(pub): |
|
166 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
167 |
query = 'format=json&orig=coucou&algo=sha1&NameID=xxx×tamp=' + timestamp |
|
168 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
169 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) |
|
170 |
assert output.json['err_desc'] == 'unknown NameID' |
|
171 | ||
172 | ||
173 |
def test_get_user_from_api_query_string_error_missing_email_valid_endpoint(pub): |
|
174 |
# check it's ok to sign an URL without specifiying an user if the endpoint |
|
175 |
# works fine without user. |
|
176 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
177 |
query = 'format=json&orig=coucou&algo=sha1×tamp=' + timestamp |
|
178 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
179 |
output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature)) |
|
180 |
assert output.json == {'data': []} |
|
181 |
output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) |
|
182 |
assert output.json == {'err': 0, 'data': []} |
|
183 | ||
184 | ||
185 |
def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub): |
|
186 |
# check the categories and forms endpoints accept an unknown NameID |
|
187 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
188 |
query = 'format=json&NameID=xxx&orig=coucou&algo=sha1×tamp=' + timestamp |
|
189 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
190 |
output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature)) |
|
191 |
assert output.json == {'data': []} |
|
192 |
output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) |
|
193 |
assert output.json == {'err': 0, 'data': []} |
|
194 | ||
195 | ||
196 |
def test_get_user_from_api_query_string_error_success_sha1(pub, local_user): |
|
197 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
198 |
query = ( |
|
199 |
'format=json&orig=coucou&algo=sha1&email=' |
|
200 |
+ urllib.quote(local_user.email) |
|
201 |
+ '×tamp=' |
|
202 |
+ timestamp |
|
203 |
) |
|
204 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
205 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature)) |
|
206 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
207 | ||
208 | ||
209 |
def test_get_user_from_api_query_string_error_invalid_signature_algo_mismatch(pub, local_user): |
|
210 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
211 |
query = ( |
|
212 |
'format=json&orig=coucou&algo=sha256&email=' |
|
213 |
+ urllib.quote(local_user.email) |
|
214 |
+ '×tamp=' |
|
215 |
+ timestamp |
|
216 |
) |
|
217 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
218 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) |
|
219 |
assert output.json['err_desc'] == 'invalid signature' |
|
220 | ||
221 | ||
222 |
def test_get_user_from_api_query_string_error_success_sha256(pub, local_user): |
|
223 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
224 |
query = ( |
|
225 |
'format=json&orig=coucou&algo=sha256&email=' |
|
226 |
+ urllib.quote(local_user.email) |
|
227 |
+ '×tamp=' |
|
228 |
+ timestamp |
|
229 |
) |
|
230 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha256).digest())) |
|
231 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature)) |
|
232 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
233 | ||
234 | ||
235 |
def test_sign_url(pub, local_user): |
|
236 |
signed_url = sign_url( |
|
237 |
'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
238 |
'1234', |
|
239 |
) |
|
240 |
url = signed_url[len('http://example.net') :] |
|
241 |
output = get_app(pub).get(url) |
|
242 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
243 | ||
244 |
# try to add something after signed url |
|
245 |
get_app(pub).get('%s&foo=bar' % url, status=403) |
|
246 | ||
247 |
signed_url = sign_url( |
|
248 |
'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
249 |
'12345', |
|
250 |
) |
|
251 |
url = signed_url[len('http://example.net') :] |
|
252 |
output = get_app(pub).get(url, status=403) |
|
253 | ||
254 | ||
255 |
def test_get_user(pub, local_user): |
|
256 |
Role.wipe() |
|
257 |
role = Role(name='Foo bar') |
|
258 |
role.store() |
|
259 |
local_user.roles = [role.id] |
|
260 |
local_user.store() |
|
261 |
signed_url = sign_url( |
|
262 |
'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
263 |
'1234', |
|
264 |
) |
|
265 |
url = signed_url[len('http://example.net') :] |
|
266 |
output = get_app(pub).get(url) |
|
267 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
268 |
assert [x['name'] for x in output.json['user_roles']] == ['Foo bar'] |
|
269 |
assert [x['slug'] for x in output.json['user_roles']] == ['foo-bar'] |
|
270 | ||
271 | ||
272 |
def test_api_access_from_xml_storable_object(pub, local_user, admin_user): |
|
273 |
app = login(get_app(pub)) |
|
274 |
resp = app.get('/backoffice/settings/api-access/new') |
|
275 |
resp.form['name'] = 'Salut API access key' |
|
276 |
resp.form['access_identifier'] = 'salut' |
|
277 |
resp.form['access_key'] = '5678' |
|
278 |
resp = resp.form.submit('submit') |
|
279 | ||
280 |
Role.wipe() |
|
281 |
role = Role(name='Foo bar') |
|
282 |
role.store() |
|
283 |
local_user.roles = [role.id] |
|
284 |
local_user.store() |
|
285 |
signed_url = sign_url( |
|
286 |
'http://example.net/api/user/?format=json&orig=UNKNOWN_ACCESS&email=%s' |
|
287 |
% (urllib.quote(local_user.email)), |
|
288 |
'5678', |
|
289 |
) |
|
290 |
url = signed_url[len('http://example.net') :] |
|
291 |
output = get_app(pub).get(url, status=403) |
|
292 |
assert output.json['err_desc'] == 'invalid orig' |
|
293 | ||
294 |
signed_url = sign_url( |
|
295 |
'http://example.net/api/user/?format=json&orig=salut&email=%s' % (urllib.quote(local_user.email)), |
|
296 |
'5678', |
|
297 |
) |
|
298 |
url = signed_url[len('http://example.net') :] |
|
299 |
output = get_app(pub).get(url) |
|
300 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
301 | ||
302 | ||
303 |
def test_is_url_signed_check_nonce(pub, local_user, freezer): |
|
304 |
ORIG = 'xxx' |
|
305 |
KEY = 'xxx' |
|
306 | ||
307 |
pub.site_options.add_section('api-secrets') |
|
308 |
pub.site_options.set('api-secrets', ORIG, KEY) |
|
309 |
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')) |
|
310 |
# test clean_nonces do not bark when nonces directory is empty |
|
311 |
if os.path.exists(os.path.join(pub.app_dir, 'nonces')): |
|
312 |
shutil.rmtree(os.path.join(pub.app_dir, 'nonces')) |
|
313 |
pub.clean_nonces(now=0) |
|
314 |
nonce_dir = os.path.join(pub.app_dir, 'nonces') |
|
315 |
assert not os.path.exists(nonce_dir) or not os.listdir(nonce_dir) |
|
316 |
signed_url = sign_url('?format=json&orig=%s&email=%s' % (ORIG, urllib.quote(local_user.email)), KEY) |
|
317 |
req = HTTPRequest( |
|
318 |
None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net', 'QUERY_STRING': signed_url[1:]} |
|
319 |
) |
|
320 |
req.process_inputs() |
|
321 |
pub.set_app_dir(req) |
|
322 |
pub._set_request(req) |
|
323 | ||
324 |
assert is_url_signed() |
|
325 |
with pytest.raises(AccessForbiddenError) as exc_info: |
|
326 |
req.signed = False |
|
327 |
is_url_signed() |
|
328 |
assert exc_info.value.public_msg == 'nonce already used' |
|
329 |
# test that clean nonces works |
|
330 |
pub.clean_nonces() |
|
331 |
assert os.listdir(nonce_dir) |
|
332 | ||
333 |
# 80 seconds in the future, nothing should be cleaned |
|
334 |
freezer.move_to(datetime.timedelta(seconds=80)) |
|
335 |
pub.clean_nonces() |
|
336 |
assert os.listdir(nonce_dir) |
|
337 | ||
338 |
# 90 seconds in the future, nonces should be removed |
|
339 |
freezer.move_to(datetime.timedelta(seconds=10)) |
|
340 |
pub.clean_nonces() |
|
341 |
assert not os.listdir(nonce_dir) |
|
342 | ||
343 | ||
344 |
def test_get_user_compat_endpoint(pub, local_user): |
|
345 |
signed_url = sign_url( |
|
346 |
'http://example.net/user?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), '1234' |
|
347 |
) |
|
348 |
url = signed_url[len('http://example.net') :] |
|
349 |
output = get_app(pub).get(url) |
|
350 |
assert output.json['user_display_name'] == u'Jean Darmette' |
tests/api/test_all.py | ||
---|---|---|
1 | 1 |
# -*- coding: utf-8 -*- |
2 | 2 | |
3 |
import pytest |
|
4 |
import json |
|
5 |
import shutil |
|
6 |
import os |
|
7 |
import hmac |
|
8 |
import base64 |
|
9 |
import hashlib |
|
10 |
import mock |
|
11 |
import re |
|
12 |
import datetime |
|
13 |
import time |
|
14 |
import json |
|
15 |
import sys |
|
16 |
import xml.etree.ElementTree as ET |
|
17 |
import zipfile |
|
18 | ||
19 |
from django.utils.encoding import force_bytes, force_text |
|
20 |
from django.utils.six import StringIO, BytesIO |
|
21 |
from django.utils.six.moves.urllib import parse as urllib |
|
22 |
from django.utils.six.moves.urllib import parse as urlparse |
|
23 | ||
24 |
from quixote import cleanup, get_publisher |
|
25 |
from wcs.qommon.http_request import HTTPRequest |
|
26 |
from wcs.qommon.form import PicklableUpload |
|
27 |
from wcs.qommon.ident.password_accounts import PasswordAccount |
|
28 |
from wcs.qommon import ods |
|
29 |
from wcs.users import User |
|
30 |
from wcs.roles import Role |
|
31 |
from wcs.blocks import BlockDef |
|
32 |
from wcs.carddef import CardDef |
|
33 |
from wcs.formdef import FormDef |
|
34 |
from wcs.formdata import Evolution |
|
35 |
from wcs.categories import Category, CardDefCategory |
|
36 |
from wcs.data_sources import NamedDataSource |
|
37 |
from wcs.workflows import ( |
|
38 |
Workflow, |
|
39 |
EditableWorkflowStatusItem, |
|
40 |
WorkflowBackofficeFieldsFormDef, |
|
41 |
WorkflowVariablesFieldsFormDef, |
|
42 |
) |
|
43 |
from wcs.wf.jump import JumpWorkflowStatusItem |
|
44 |
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem |
|
45 |
from wcs import fields, qommon |
|
46 |
from wcs.api_utils import sign_url, get_secret_and_orig, is_url_signed, DEFAULT_DURATION |
|
47 |
from wcs.qommon.errors import AccessForbiddenError |
|
48 | ||
49 |
from utilities import get_app, create_temporary_pub, clean_temporary_pub, login |
|
50 | ||
51 | ||
52 |
def pytest_generate_tests(metafunc): |
|
53 |
if 'pub' in metafunc.fixturenames: |
|
54 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
55 | ||
56 | ||
57 |
@pytest.fixture |
|
58 |
def pub(request, emails): |
|
59 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
60 | ||
61 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
62 |
pub.set_app_dir(req) |
|
63 |
pub.cfg['identification'] = {'methods': ['password']} |
|
64 |
pub.cfg['language'] = {'language': 'en'} |
|
65 |
pub.write_cfg() |
|
66 | ||
67 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
68 |
'''\ |
|
69 |
[api-secrets] |
|
70 |
coucou = 1234 |
|
71 |
''' |
|
72 |
) |
|
73 | ||
74 |
return pub |
|
75 | ||
76 | ||
77 |
def teardown_module(module): |
|
78 |
clean_temporary_pub() |
|
79 | ||
80 | ||
81 |
@pytest.fixture |
|
82 |
def local_user(): |
|
83 |
get_publisher().user_class.wipe() |
|
84 |
user = get_publisher().user_class() |
|
85 |
user.name = 'Jean Darmette' |
|
86 |
user.email = 'jean.darmette@triffouilis.fr' |
|
87 |
user.name_identifiers = ['0123456789'] |
|
88 |
user.store() |
|
89 |
return user |
|
90 | ||
91 | ||
92 |
@pytest.fixture |
|
93 |
def admin_user(): |
|
94 |
get_publisher().user_class.wipe() |
|
95 |
user = get_publisher().user_class() |
|
96 |
user.name = 'John Doe Admin' |
|
97 |
user.email = 'john.doe@example.com' |
|
98 |
user.name_identifiers = ['0123456789'] |
|
99 |
user.is_admin = True |
|
100 |
user.store() |
|
101 | ||
102 |
account = PasswordAccount(id='admin') |
|
103 |
account.set_password('admin') |
|
104 |
account.user_id = user.id |
|
105 |
account.store() |
|
106 | ||
107 |
return user |
|
108 | ||
109 | ||
110 |
def sign_uri(uri, user=None, format='json'): |
|
111 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
112 |
scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri) |
|
113 |
if query: |
|
114 |
query += '&' |
|
115 |
if format: |
|
116 |
query += 'format=%s&' % format |
|
117 |
query += 'orig=coucou&algo=sha256×tamp=' + timestamp |
|
118 |
if user: |
|
119 |
query += '&email=' + urllib.quote(user.email) |
|
120 |
query += '&signature=%s' % urllib.quote( |
|
121 |
base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha256).digest()) |
|
122 |
) |
|
123 |
return urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) |
|
124 | ||
125 | ||
126 |
def test_user_page_redirect(pub): |
|
127 |
output = get_app(pub).get('/user') |
|
128 |
assert output.headers.get('location') == 'http://example.net/myspace/' |
|
129 | ||
130 | ||
131 |
def test_user_page_error(pub): |
|
132 |
# check we get json as output for errors |
|
133 |
output = get_app(pub).get('/api/user/', status=403) |
|
134 |
assert output.json['err_desc'] == 'no user specified' |
|
135 | ||
136 | ||
137 |
def test_user_page_error_when_json_and_no_user(pub): |
|
138 |
output = get_app(pub).get('/api/user/?format=json', status=403) |
|
139 |
assert output.json['err_desc'] == 'no user specified' |
|
140 | ||
141 | ||
142 |
def test_get_user_from_api_query_string_error_missing_orig(pub): |
|
143 |
output = get_app(pub).get('/api/user/?format=json&signature=xxx', status=403) |
|
144 |
assert output.json['err_desc'] == 'missing/multiple orig field' |
|
145 | ||
146 | ||
147 |
def test_get_user_from_api_query_string_error_invalid_orig(pub): |
|
148 |
output = get_app(pub).get('/api/user/?format=json&orig=coin&signature=xxx', status=403) |
|
149 |
assert output.json['err_desc'] == 'invalid orig' |
|
150 | ||
151 | ||
152 |
def test_get_user_from_api_query_string_error_missing_algo(pub): |
|
153 |
output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx', status=403) |
|
154 |
assert output.json['err_desc'] == 'missing/multiple algo field' |
|
155 | ||
156 | ||
157 |
def test_get_user_from_api_query_string_error_invalid_algo(pub): |
|
158 |
output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx&algo=coin', status=403) |
|
159 |
assert output.json['err_desc'] == 'invalid algo' |
|
160 |
output = get_app(pub).get( |
|
161 |
'/api/user/?format=json&orig=coucou&signature=xxx&algo=__getattribute__', status=403 |
|
162 |
) |
|
163 |
assert output.json['err_desc'] == 'invalid algo' |
|
164 | ||
165 | ||
166 |
def test_get_user_from_api_query_string_error_invalid_signature(pub): |
|
167 |
output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx&algo=sha1', status=403) |
|
168 |
assert output.json['err_desc'] == 'invalid signature' |
|
169 | ||
170 | ||
171 |
def test_get_user_from_api_query_string_error_missing_timestamp(pub): |
|
172 |
signature = urllib.quote( |
|
173 |
base64.b64encode(hmac.new(b'1234', b'format=json&orig=coucou&algo=sha1', hashlib.sha1).digest()) |
|
174 |
) |
|
175 |
output = get_app(pub).get( |
|
176 |
'/api/user/?format=json&orig=coucou&algo=sha1&signature=%s' % signature, status=403 |
|
177 |
) |
|
178 |
assert output.json['err_desc'] == 'missing/multiple timestamp field' |
|
179 | ||
180 | ||
181 |
def test_get_user_from_api_query_string_error_missing_email(pub): |
|
182 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
183 |
query = 'format=json&orig=coucou&algo=sha1×tamp=' + timestamp |
|
184 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
185 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) |
|
186 |
assert output.json['err_desc'] == 'no user specified' |
|
187 | ||
188 | ||
189 |
def test_get_user_from_api_query_string_error_unknown_nameid(pub): |
|
190 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
191 |
query = 'format=json&orig=coucou&algo=sha1&NameID=xxx×tamp=' + timestamp |
|
192 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
193 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) |
|
194 |
assert output.json['err_desc'] == 'unknown NameID' |
|
195 | ||
196 | ||
197 |
def test_get_user_from_api_query_string_error_missing_email_valid_endpoint(pub): |
|
198 |
# check it's ok to sign an URL without specifiying an user if the endpoint |
|
199 |
# works fine without user. |
|
200 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
201 |
query = 'format=json&orig=coucou&algo=sha1×tamp=' + timestamp |
|
202 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
203 |
output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature)) |
|
204 |
assert output.json == {'data': []} |
|
205 |
output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) |
|
206 |
assert output.json == {'err': 0, 'data': []} |
|
207 | ||
208 | ||
209 |
def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub): |
|
210 |
# check the categories and forms endpoints accept an unknown NameID |
|
211 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
212 |
query = 'format=json&NameID=xxx&orig=coucou&algo=sha1×tamp=' + timestamp |
|
213 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
214 |
output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature)) |
|
215 |
assert output.json == {'data': []} |
|
216 |
output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) |
|
217 |
assert output.json == {'err': 0, 'data': []} |
|
218 | ||
219 | ||
220 |
def test_get_user_from_api_query_string_error_success_sha1(pub, local_user): |
|
221 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
222 |
query = ( |
|
223 |
'format=json&orig=coucou&algo=sha1&email=' |
|
224 |
+ urllib.quote(local_user.email) |
|
225 |
+ '×tamp=' |
|
226 |
+ timestamp |
|
227 |
) |
|
228 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
229 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature)) |
|
230 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
231 | ||
232 | ||
233 |
def test_get_user_from_api_query_string_error_invalid_signature_algo_mismatch(pub, local_user): |
|
234 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
235 |
query = ( |
|
236 |
'format=json&orig=coucou&algo=sha256&email=' |
|
237 |
+ urllib.quote(local_user.email) |
|
238 |
+ '×tamp=' |
|
239 |
+ timestamp |
|
240 |
) |
|
241 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha1).digest())) |
|
242 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) |
|
243 |
assert output.json['err_desc'] == 'invalid signature' |
|
244 | ||
245 | ||
246 |
def test_get_user_from_api_query_string_error_success_sha256(pub, local_user): |
|
247 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
248 |
query = ( |
|
249 |
'format=json&orig=coucou&algo=sha256&email=' |
|
250 |
+ urllib.quote(local_user.email) |
|
251 |
+ '×tamp=' |
|
252 |
+ timestamp |
|
253 |
) |
|
254 |
signature = urllib.quote(base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha256).digest())) |
|
255 |
output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature)) |
|
256 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
257 | ||
258 | ||
259 |
def test_sign_url(pub, local_user): |
|
260 |
signed_url = sign_url( |
|
261 |
'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
262 |
'1234', |
|
263 |
) |
|
264 |
url = signed_url[len('http://example.net') :] |
|
265 |
output = get_app(pub).get(url) |
|
266 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
267 | ||
268 |
# try to add something after signed url |
|
269 |
get_app(pub).get('%s&foo=bar' % url, status=403) |
|
270 | ||
271 |
signed_url = sign_url( |
|
272 |
'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
273 |
'12345', |
|
274 |
) |
|
275 |
url = signed_url[len('http://example.net') :] |
|
276 |
output = get_app(pub).get(url, status=403) |
|
277 | ||
278 | ||
279 |
def test_get_user(pub, local_user): |
|
280 |
Role.wipe() |
|
281 |
role = Role(name='Foo bar') |
|
282 |
role.store() |
|
283 |
local_user.roles = [role.id] |
|
284 |
local_user.store() |
|
285 |
signed_url = sign_url( |
|
286 |
'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
287 |
'1234', |
|
288 |
) |
|
289 |
url = signed_url[len('http://example.net') :] |
|
290 |
output = get_app(pub).get(url) |
|
291 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
292 |
assert [x['name'] for x in output.json['user_roles']] == ['Foo bar'] |
|
293 |
assert [x['slug'] for x in output.json['user_roles']] == ['foo-bar'] |
|
294 | ||
295 | ||
296 |
def test_api_access_from_xml_storable_object(pub, local_user, admin_user): |
|
297 |
app = login(get_app(pub)) |
|
298 |
resp = app.get('/backoffice/settings/api-access/new') |
|
299 |
resp.form['name'] = 'Salut API access key' |
|
300 |
resp.form['access_identifier'] = 'salut' |
|
301 |
resp.form['access_key'] = '5678' |
|
302 |
resp = resp.form.submit('submit') |
|
303 | ||
304 |
Role.wipe() |
|
305 |
role = Role(name='Foo bar') |
|
306 |
role.store() |
|
307 |
local_user.roles = [role.id] |
|
308 |
local_user.store() |
|
309 |
signed_url = sign_url( |
|
310 |
'http://example.net/api/user/?format=json&orig=UNKNOWN_ACCESS&email=%s' |
|
311 |
% (urllib.quote(local_user.email)), |
|
312 |
'5678', |
|
313 |
) |
|
314 |
url = signed_url[len('http://example.net') :] |
|
315 |
output = get_app(pub).get(url, status=403) |
|
316 |
assert output.json['err_desc'] == 'invalid orig' |
|
317 | ||
318 |
signed_url = sign_url( |
|
319 |
'http://example.net/api/user/?format=json&orig=salut&email=%s' % (urllib.quote(local_user.email)), |
|
320 |
'5678', |
|
321 |
) |
|
322 |
url = signed_url[len('http://example.net') :] |
|
323 |
output = get_app(pub).get(url) |
|
324 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
325 | ||
326 | ||
327 |
def test_is_url_signed_check_nonce(pub, local_user, freezer): |
|
328 |
ORIG = 'xxx' |
|
329 |
KEY = 'xxx' |
|
330 | ||
331 |
pub.site_options.add_section('api-secrets') |
|
332 |
pub.site_options.set('api-secrets', ORIG, KEY) |
|
333 |
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')) |
|
334 |
# test clean_nonces do not bark when nonces directory is empty |
|
335 |
if os.path.exists(os.path.join(pub.app_dir, 'nonces')): |
|
336 |
shutil.rmtree(os.path.join(pub.app_dir, 'nonces')) |
|
337 |
pub.clean_nonces(now=0) |
|
338 |
nonce_dir = os.path.join(pub.app_dir, 'nonces') |
|
339 |
assert not os.path.exists(nonce_dir) or not os.listdir(nonce_dir) |
|
340 |
signed_url = sign_url('?format=json&orig=%s&email=%s' % (ORIG, urllib.quote(local_user.email)), KEY) |
|
341 |
req = HTTPRequest( |
|
342 |
None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net', 'QUERY_STRING': signed_url[1:]} |
|
343 |
) |
|
344 |
req.process_inputs() |
|
345 |
pub.set_app_dir(req) |
|
346 |
pub._set_request(req) |
|
347 | ||
348 |
assert is_url_signed() |
|
349 |
with pytest.raises(AccessForbiddenError) as exc_info: |
|
350 |
req.signed = False |
|
351 |
is_url_signed() |
|
352 |
assert exc_info.value.public_msg == 'nonce already used' |
|
353 |
# test that clean nonces works |
|
354 |
pub.clean_nonces() |
|
355 |
assert os.listdir(nonce_dir) |
|
356 | ||
357 |
# 80 seconds in the future, nothing should be cleaned |
|
358 |
freezer.move_to(datetime.timedelta(seconds=80)) |
|
359 |
pub.clean_nonces() |
|
360 |
assert os.listdir(nonce_dir) |
|
361 | ||
362 |
# 90 seconds in the future, nonces should be removed |
|
363 |
freezer.move_to(datetime.timedelta(seconds=10)) |
|
364 |
pub.clean_nonces() |
|
365 |
assert not os.listdir(nonce_dir) |
|
366 | ||
367 | ||
368 |
def test_get_user_compat_endpoint(pub, local_user): |
|
369 |
signed_url = sign_url( |
|
370 |
'http://example.net/user?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), '1234' |
|
371 |
) |
|
372 |
url = signed_url[len('http://example.net') :] |
|
373 |
output = get_app(pub).get(url) |
|
374 |
assert output.json['user_display_name'] == u'Jean Darmette' |
|
375 | ||
376 | ||
377 |
def test_formdef_list(pub): |
|
378 |
Role.wipe() |
|
379 |
role = Role(name='Foo bar') |
|
380 |
role.id = '14' |
|
381 |
role.store() |
|
382 | ||
383 |
FormDef.wipe() |
|
384 |
formdef = FormDef() |
|
385 |
formdef.name = 'test' |
|
386 |
formdef.description = 'plop' |
|
387 |
formdef.keywords = 'mobile, test' |
|
388 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
389 |
formdef.fields = [] |
|
390 |
formdef.store() |
|
391 | ||
392 |
# anonymous access -> 403 |
|
393 |
resp1 = get_app(pub).get('/json', status=403) |
|
394 |
resp2 = get_app(pub).get('/', headers={'Accept': 'application/json'}, status=403) |
|
395 |
resp3 = get_app(pub).get('/api/formdefs/', status=403) |
|
396 | ||
397 |
# signed request |
|
398 |
resp1 = get_app(pub).get(sign_uri('/json')) |
|
399 |
resp2 = get_app(pub).get(sign_uri('/'), headers={'Accept': 'application/json'}) |
|
400 |
resp3 = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
401 |
assert resp1.json == resp2.json == resp3.json |
|
402 |
assert resp1.json['data'][0]['title'] == 'test' |
|
403 |
assert resp1.json['data'][0]['url'] == 'http://example.net/test/' |
|
404 |
assert resp1.json['data'][0]['redirection'] is False |
|
405 |
assert resp1.json['data'][0]['always_advertise'] is False |
|
406 |
assert resp1.json['data'][0]['description'] == 'plop' |
|
407 |
assert resp1.json['data'][0]['keywords'] == ['mobile', 'test'] |
|
408 |
assert list(resp1.json['data'][0]['functions'].keys()) == ['_receiver'] |
|
409 |
assert resp1.json['data'][0]['functions']['_receiver']['label'] == 'Recipient' |
|
410 |
assert resp1.json['data'][0]['functions']['_receiver']['role']['slug'] == role.slug |
|
411 |
assert resp1.json['data'][0]['functions']['_receiver']['role']['name'] == role.name |
|
412 |
assert 'count' not in resp1.json['data'][0] |
|
413 | ||
414 |
# backoffice_submission formdef : none |
|
415 |
resp1 = get_app(pub).get('/api/formdefs/?backoffice-submission=on', status=403) |
|
416 |
resp1 = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
417 |
assert resp1.json['err'] == 0 |
|
418 |
assert len(resp1.json['data']) == 0 |
|
419 | ||
420 |
formdef.data_class().wipe() |
|
421 | ||
422 |
# a draft |
|
423 |
formdata = formdef.data_class()() |
|
424 |
formdata.data = {} |
|
425 |
formdata.just_created() |
|
426 |
formdata.status = 'draft' |
|
427 |
formdata.store() |
|
428 | ||
429 |
other_formdef = FormDef() |
|
430 |
other_formdef.name = 'test 2' |
|
431 |
other_formdef.fields = [] |
|
432 |
other_formdef.store() |
|
433 |
other_formdata = other_formdef.data_class()() |
|
434 |
other_formdata.data = {} |
|
435 |
other_formdata.just_created() |
|
436 |
other_formdata.store() |
|
437 | ||
438 |
# formdata created: |
|
439 |
# - 1 day ago (=3*4) |
|
440 |
# - 7 days ago (=2*2) |
|
441 |
# - 29 days ago (=1*1) |
|
442 |
# - 31 days ago (=0) |
|
443 |
for days in [1, 1, 1, 7, 7, 29, 31]: |
|
444 |
formdata = formdef.data_class()() |
|
445 |
formdata.data = {} |
|
446 |
formdata.just_created() |
|
447 |
formdata.receipt_time = (datetime.datetime.now() - datetime.timedelta(days=days)).timetuple() |
|
448 |
formdata.store() |
|
449 | ||
450 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?include-count=on')) |
|
451 |
if not pub.is_using_postgresql(): |
|
452 |
assert resp.json['data'][0]['count'] == 8 |
|
453 |
else: |
|
454 |
# 3*4 + 2*2 + 1*1 |
|
455 |
assert resp.json['data'][0]['count'] == 17 |
|
456 | ||
457 | ||
458 |
def test_limited_formdef_list(pub, local_user): |
|
459 |
Role.wipe() |
|
460 |
role = Role(name='Foo bar') |
|
461 |
role.id = '14' |
|
462 |
role.store() |
|
463 | ||
464 |
FormDef.wipe() |
|
465 |
formdef = FormDef() |
|
466 |
formdef.name = 'test' |
|
467 |
formdef.description = 'plop' |
|
468 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
469 |
formdef.fields = [] |
|
470 |
formdef.store() |
|
471 | ||
472 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
473 |
assert resp.json['err'] == 0 |
|
474 |
assert len(resp.json['data']) == 1 |
|
475 |
assert resp.json['data'][0]['authentication_required'] is False |
|
476 |
# not present in backoffice-submission formdefs |
|
477 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
478 |
assert resp.json['err'] == 0 |
|
479 |
assert len(resp.json['data']) == 0 |
|
480 | ||
481 |
# check it's not advertised |
|
482 |
formdef.roles = [role.id] |
|
483 |
formdef.store() |
|
484 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
485 |
resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=')) |
|
486 |
resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX')) |
|
487 |
resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) |
|
488 |
assert resp.json['err'] == 0 |
|
489 |
assert len(resp.json['data']) == 1 # advertised in naked calls (as done from combo) |
|
490 |
assert len(resp2.json['data']) == 0 # not advertised otherwise |
|
491 |
assert resp2.json == resp3.json == resp4.json |
|
492 |
# still not present in backoffice-submission formdefs |
|
493 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
494 |
assert resp.json['err'] == 0 |
|
495 |
assert len(resp.json['data']) == 0 |
|
496 | ||
497 |
# unless user has correct roles |
|
498 |
local_user.roles = [role.id] |
|
499 |
local_user.store() |
|
500 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) |
|
501 |
assert resp.json['err'] == 0 |
|
502 |
assert len(resp.json['data']) == 1 |
|
503 | ||
504 |
local_user.roles = [] |
|
505 |
local_user.store() |
|
506 | ||
507 |
# check it's also included in anonymous/signed calls, but marked for |
|
508 |
# authentication |
|
509 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
510 |
assert resp.json['data'][0] |
|
511 |
assert resp.json['data'][0]['authentication_required'] is True |
|
512 | ||
513 |
# check it's advertised |
|
514 |
formdef.always_advertise = True |
|
515 |
formdef.store() |
|
516 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
517 |
resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=')) |
|
518 |
resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX')) |
|
519 |
resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) |
|
520 |
assert resp.json['err'] == 0 |
|
521 |
assert len(resp.json['data']) == 1 |
|
522 |
assert resp.json['data'][0]['authentication_required'] |
|
523 |
assert resp.json == resp2.json == resp3.json == resp4.json |
|
524 | ||
525 |
formdef.required_authentication_contexts = ['fedict'] |
|
526 |
formdef.store() |
|
527 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
528 |
assert resp.json['data'][0]['required_authentication_contexts'] == ['fedict'] |
|
529 | ||
530 | ||
531 |
def test_formdef_list_redirection(pub): |
|
532 |
FormDef.wipe() |
|
533 |
formdef = FormDef() |
|
534 |
formdef.name = 'test' |
|
535 |
formdef.disabled = True |
|
536 |
formdef.disabled_redirection = 'http://example.net' |
|
537 |
formdef.fields = [] |
|
538 |
formdef.store() |
|
539 | ||
540 |
resp1 = get_app(pub).get(sign_uri('/json')) |
|
541 |
assert resp1.json['err'] == 0 |
|
542 |
assert resp1.json['data'][0]['title'] == 'test' |
|
543 |
assert resp1.json['data'][0]['url'] == 'http://example.net/test/' |
|
544 |
assert resp1.json['data'][0]['redirection'] == True |
|
545 |
assert 'count' not in resp1.json['data'][0] |
|
546 | ||
547 | ||
548 |
def test_backoffice_submission_formdef_list(pub, local_user): |
|
549 |
Role.wipe() |
|
550 |
role = Role(name='Foo bar') |
|
551 |
role.id = '14' |
|
552 |
role.store() |
|
553 | ||
554 |
FormDef.wipe() |
|
555 |
formdef = FormDef() |
|
556 |
formdef.name = 'test' |
|
557 |
formdef.description = 'plop' |
|
558 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
559 |
formdef.fields = [] |
|
560 |
formdef.store() |
|
561 | ||
562 |
formdef2 = FormDef() |
|
563 |
formdef2.name = 'ignore me' |
|
564 |
formdef2.fields = [] |
|
565 |
formdef2.store() |
|
566 | ||
567 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
568 |
assert resp.json['err'] == 0 |
|
569 |
assert len(resp.json['data']) == 0 |
|
570 | ||
571 |
# check it's not advertised ... |
|
572 |
formdef.backoffice_submission_roles = [role.id] |
|
573 |
formdef.store() |
|
574 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
575 |
assert resp.json['err'] == 0 |
|
576 |
assert len(resp.json['data']) == 0 |
|
577 | ||
578 |
# even if it's advertised on frontoffice |
|
579 |
formdef.always_advertise = True |
|
580 |
formdef.store() |
|
581 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
582 |
assert resp.json['err'] == 0 |
|
583 |
assert len(resp.json['data']) == 0 |
|
584 | ||
585 |
# even if user is admin |
|
586 |
local_user.is_admin = True |
|
587 |
local_user.store() |
|
588 |
resp = get_app(pub).get( |
|
589 |
sign_uri('/api/formdefs/?backoffice-submission=on&NameID=%s' % local_user.name_identifiers[0]) |
|
590 |
) |
|
591 |
assert resp.json['err'] == 0 |
|
592 |
assert len(resp.json['data']) == 0 |
|
593 |
local_user.is_admin = False |
|
594 |
local_user.store() |
|
595 | ||
596 |
# ... unless user has correct roles |
|
597 |
local_user.roles = [role.id] |
|
598 |
local_user.store() |
|
599 |
resp = get_app(pub).get( |
|
600 |
sign_uri('/api/formdefs/?backoffice-submission=on&NameID=%s' % local_user.name_identifiers[0]) |
|
601 |
) |
|
602 |
assert resp.json['err'] == 0 |
|
603 |
assert len(resp.json['data']) == 1 |
|
604 |
assert 'backoffice_submission_url' in resp.json['data'][0] |
|
605 | ||
606 |
# but not advertised if it's a redirection |
|
607 |
formdef.disabled = True |
|
608 |
formdef.disabled_redirection = 'http://example.net' |
|
609 |
formdef.store() |
|
610 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
611 |
assert resp.json['err'] == 0 |
|
612 |
assert len(resp.json['data']) == 0 |
|
613 | ||
614 | ||
615 |
def test_formdef_schema(pub): |
|
616 |
Workflow.wipe() |
|
617 |
workflow = Workflow(name='test') |
|
618 |
st1 = workflow.add_status('Status1', 'st1') |
|
619 |
jump = JumpWorkflowStatusItem() |
|
620 |
jump.status = 'st2' |
|
621 |
jump.timeout = 100 |
|
622 |
st1.items.append(jump) |
|
623 |
st2 = workflow.add_status('Status2', 'st2') |
|
624 |
jump = JumpWorkflowStatusItem() |
|
625 |
jump.status = 'st3' |
|
626 |
st2.items.append(jump) |
|
627 |
st2 = workflow.add_status('Status3', 'st3') |
|
628 |
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) |
|
629 |
workflow.backoffice_fields_formdef.fields = [ |
|
630 |
fields.StringField(id='bo1', label='1st backoffice field', type='string', varname='backoffice_blah'), |
|
631 |
] |
|
632 |
workflow.store() |
|
633 |
FormDef.wipe() |
|
634 |
formdef = FormDef() |
|
635 |
formdef.name = 'test' |
|
636 |
formdef.fields = [ |
|
637 |
fields.StringField(id='0', label='foobar'), |
|
638 |
fields.ItemField( |
|
639 |
id='1', |
|
640 |
label='foobar1', |
|
641 |
varname='foobar1', |
|
642 |
data_source={ |
|
643 |
'type': 'json', |
|
644 |
'value': 'http://datasource.com', |
|
645 |
}, |
|
646 |
), |
|
647 |
fields.ItemsField( |
|
648 |
id='2', |
|
649 |
label='foobar2', |
|
650 |
varname='foobar2', |
|
651 |
data_source={ |
|
652 |
'type': 'formula', |
|
653 |
'value': '[dict(id=i, text=\'label %s\' % i, foo=i) for i in range(10)]', |
|
654 |
}, |
|
655 |
), |
|
656 |
] |
|
657 | ||
658 |
formdef.workflow_id = workflow.id |
|
659 |
formdef.store() |
|
660 | ||
661 |
with mock.patch('wcs.qommon.misc.urlopen') as urlopen: |
|
662 |
urlopen.side_effect = lambda *args: StringIO( |
|
663 |
'''\ |
|
664 |
{"data": [{"id": 0, "text": "zéro", "foo": "bar"}, \ |
|
665 |
{"id": 1, "text": "uné", "foo": "bar1"}, \ |
|
666 |
{"id": 2, "text": "deux", "foo": "bar2"}]}''' |
|
667 |
) |
|
668 |
resp = get_app(pub).get('/api/formdefs/test/schema') |
|
669 |
resp2 = get_app(pub).get('/test/schema') |
|
670 |
resp3 = get_app(pub).get(sign_url('/api/formdefs/test/schema?orig=coucou', '1234')) |
|
671 |
resp4 = get_app(pub).get(sign_url('/api/formdefs/test/schema?orig=coucou', '1234')) |
|
672 | ||
673 |
# check schema |
|
674 |
assert resp.json == resp2.json |
|
675 |
assert set(resp.json.keys()) >= set( |
|
676 |
[ |
|
677 |
'enable_tracking_codes', |
|
678 |
'url_name', |
|
679 |
'description', |
|
680 |
'workflow', |
|
681 |
'expiration_date', |
|
682 |
'discussion', |
|
683 |
'has_captcha', |
|
684 |
'always_advertise', |
|
685 |
'name', |
|
686 |
'disabled', |
|
687 |
'only_allow_one', |
|
688 |
'fields', |
|
689 |
'keywords', |
|
690 |
'publication_date', |
|
691 |
'detailed_emails', |
|
692 |
'disabled_redirection', |
|
693 |
] |
|
694 |
) |
|
695 |
assert resp.json['name'] == 'test' |
|
696 | ||
697 |
# fields checks |
|
698 |
assert resp.json['fields'][0]['label'] == 'foobar' |
|
699 |
assert resp.json['fields'][0]['type'] == 'string' |
|
700 | ||
701 |
assert resp.json['fields'][1]['label'] == 'foobar1' |
|
702 |
assert resp.json['fields'][1]['type'] == 'item' |
|
703 | ||
704 |
# check structured items are only exported for authenticated callers |
|
705 |
assert resp.json['fields'][1]['items'] == [] |
|
706 |
assert resp.json['fields'][2]['items'] == [] |
|
707 |
assert 'structured_items' not in resp.json['fields'][1] |
|
708 |
assert 'structured_items' not in resp.json['fields'][2] |
|
709 | ||
710 |
assert len(resp3.json['fields'][1]['structured_items']) == 3 |
|
711 |
assert resp3.json['fields'][1]['structured_items'][0]['id'] == 0 |
|
712 |
assert resp3.json['fields'][1]['structured_items'][0]['text'] == u'zéro' |
|
713 |
assert resp3.json['fields'][1]['structured_items'][0]['foo'] == 'bar' |
|
714 |
assert resp3.json['fields'][1]['items'][0] == u'zéro' |
|
715 | ||
716 |
assert resp3.json['fields'][2]['label'] == 'foobar2' |
|
717 |
assert resp3.json['fields'][2]['type'] == 'items' |
|
718 |
assert len(resp3.json['fields'][2]['structured_items']) == 10 |
|
719 |
assert resp3.json['fields'][2]['structured_items'][0]['id'] == 0 |
|
720 |
assert resp3.json['fields'][2]['structured_items'][0]['text'] == 'label 0' |
|
721 |
assert resp3.json['fields'][2]['structured_items'][0]['foo'] == 0 |
|
722 |
assert resp3.json['fields'][2]['items'][0] == 'label 0' |
|
723 | ||
724 |
# if structured_items fails no values |
|
725 |
assert 'structured_items' not in resp4.json['fields'][1] |
|
726 |
assert resp4.json['fields'][1]['items'] == [] |
|
727 | ||
728 |
# workflow checks |
|
729 |
assert len(resp.json['workflow']['statuses']) == 3 |
|
730 |
assert resp.json['workflow']['statuses'][0]['id'] == 'st1' |
|
731 |
assert resp.json['workflow']['statuses'][0]['endpoint'] is False |
|
732 |
assert resp.json['workflow']['statuses'][0]['waitpoint'] is True |
|
733 |
assert resp.json['workflow']['statuses'][1]['id'] == 'st2' |
|
734 |
assert resp.json['workflow']['statuses'][1]['endpoint'] is False |
|
735 |
assert resp.json['workflow']['statuses'][1]['waitpoint'] is False |
|
736 |
assert resp.json['workflow']['statuses'][2]['id'] == 'st3' |
|
737 |
assert resp.json['workflow']['statuses'][2]['endpoint'] is True |
|
738 |
assert resp.json['workflow']['statuses'][2]['waitpoint'] is True |
|
739 |
assert len(resp.json['workflow']['fields']) == 1 |
|
740 | ||
741 |
assert resp.json['workflow']['fields'][0]['label'] == '1st backoffice field' |
|
742 | ||
743 |
get_app(pub).get('/api/formdefs/xxx/schema', status=404) |
|
744 | ||
745 | ||
746 |
def test_post_invalid_json(pub, local_user): |
|
747 |
resp = get_app(pub).post( |
|
748 |
'/api/formdefs/test/submit', params='not a json payload', content_type='application/json', status=400 |
|
749 |
) |
|
750 |
assert resp.json['err'] == 1 |
|
751 |
assert resp.json['err_class'] == 'Invalid request' |
|
752 | ||
753 | ||
754 |
def test_formdef_submit(pub, local_user): |
|
755 |
Role.wipe() |
|
756 |
role = Role(name='test') |
|
757 |
role.store() |
|
758 |
local_user.roles = [role.id] |
|
759 |
local_user.store() |
|
760 | ||
761 |
FormDef.wipe() |
|
762 |
formdef = FormDef() |
|
763 |
formdef.name = 'test' |
|
764 |
formdef.fields = [fields.StringField(id='0', label='foobar')] |
|
765 |
formdef.store() |
|
766 |
data_class = formdef.data_class() |
|
767 | ||
768 |
resp = get_app(pub).post_json('/api/formdefs/test/submit', {'data': {}}, status=403) |
|
769 |
assert resp.json['err'] == 1 |
|
770 |
assert resp.json['err_desc'] == 'unsigned API call' |
|
771 | ||
772 |
def url(): |
|
773 |
signed_url = sign_url( |
|
774 |
'http://example.net/api/formdefs/test/submit' |
|
775 |
+ '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
776 |
'1234', |
|
777 |
) |
|
778 |
return signed_url[len('http://example.net') :] |
|
779 | ||
780 |
resp = get_app(pub).post_json(url(), {'data': {}}) |
|
781 |
assert resp.json['err'] == 0 |
|
782 |
assert resp.json['data']['url'] == ('http://example.net/test/%s/' % resp.json['data']['id']) |
|
783 |
assert resp.json['data']['backoffice_url'] == ( |
|
784 |
'http://example.net/backoffice/management/test/%s/' % resp.json['data']['id'] |
|
785 |
) |
|
786 |
assert resp.json['data']['api_url'] == ('http://example.net/api/forms/test/%s/' % resp.json['data']['id']) |
|
787 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
788 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
789 |
assert data_class.get(resp.json['data']['id']).tracking_code is None |
|
790 | ||
791 |
local_user2 = get_publisher().user_class() |
|
792 |
local_user2.name = 'Test' |
|
793 |
local_user2.email = 'foo@localhost' |
|
794 |
local_user2.store() |
|
795 |
resp = get_app(pub).post_json(url(), {'data': {}, 'user': {'NameID': [], 'email': local_user2.email}}) |
|
796 |
assert data_class.get(resp.json['data']['id']).user.email == local_user2.email |
|
797 | ||
798 |
resp = get_app(pub).post( |
|
799 |
url(), json.dumps({'data': {}}), status=400 |
|
800 |
) # missing Content-Type: application/json header |
|
801 |
assert resp.json['err_desc'] == 'expected JSON but missing appropriate content-type' |
|
802 | ||
803 |
# check qualified content type are recognized |
|
804 |
resp = get_app(pub).post(url(), json.dumps({'data': {}}), content_type='application/json; charset=utf-8') |
|
805 |
assert resp.json['data']['url'] |
|
806 | ||
807 |
formdef.disabled = True |
|
808 |
formdef.store() |
|
809 |
resp = get_app(pub).post_json(url(), {'data': {}}, status=403) |
|
810 |
assert resp.json['err'] == 1 |
|
811 |
assert resp.json['err_desc'] == 'disabled form' |
|
812 | ||
813 |
formdef.disabled = False |
|
814 |
formdef.store() |
|
815 |
resp = get_app(pub).post_json(url(), {'meta': {'backoffice-submission': True}, 'data': {}}, status=403) |
|
816 |
formdef.backoffice_submission_roles = ['xx'] |
|
817 |
formdef.store() |
|
818 |
resp = get_app(pub).post_json(url(), {'meta': {'backoffice-submission': True}, 'data': {}}, status=403) |
|
819 |
formdef.backoffice_submission_roles = [role.id] |
|
820 |
formdef.store() |
|
821 |
resp = get_app(pub).post_json(url(), {'meta': {'backoffice-submission': True}, 'data': {}}) |
|
822 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
823 |
assert data_class.get(resp.json['data']['id']).backoffice_submission is True |
|
824 |
assert data_class.get(resp.json['data']['id']).user_id is None |
|
825 |
assert data_class.get(resp.json['data']['id']).submission_agent_id == str(local_user.id) |
|
826 | ||
827 |
formdef.enable_tracking_codes = True |
|
828 |
formdef.store() |
|
829 |
resp = get_app(pub).post_json(url(), {'data': {}}) |
|
830 |
assert data_class.get(resp.json['data']['id']).tracking_code |
|
831 | ||
832 |
resp = get_app(pub).post_json(url(), {'meta': {'draft': True}, 'data': {}}) |
|
833 |
assert data_class.get(resp.json['data']['id']).status == 'draft' |
|
834 | ||
835 |
resp = get_app(pub).post_json( |
|
836 |
url(), |
|
837 |
{ |
|
838 |
'meta': {'backoffice-submission': True}, |
|
839 |
'data': {}, |
|
840 |
'context': {'channel': 'mail', 'comments': 'blah'}, |
|
841 |
}, |
|
842 |
) |
|
843 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
844 |
assert data_class.get(resp.json['data']['id']).backoffice_submission is True |
|
845 |
assert data_class.get(resp.json['data']['id']).user_id is None |
|
846 |
assert data_class.get(resp.json['data']['id']).submission_context == {'comments': 'blah'} |
|
847 |
assert data_class.get(resp.json['data']['id']).submission_channel == 'mail' |
|
848 | ||
849 |
data_class.wipe() |
|
850 | ||
851 | ||
852 |
def test_formdef_submit_only_one(pub, local_user): |
|
853 |
Role.wipe() |
|
854 |
role = Role(name='test') |
|
855 |
role.store() |
|
856 |
local_user.roles = [role.id] |
|
857 |
local_user.store() |
|
858 | ||
859 |
FormDef.wipe() |
|
860 |
formdef = FormDef() |
|
861 |
formdef.name = 'test' |
|
862 |
formdef.only_allow_one = True |
|
863 |
formdef.fields = [fields.StringField(id='0', label='foobar')] |
|
864 |
formdef.store() |
|
865 |
data_class = formdef.data_class() |
|
866 | ||
867 |
def url(): |
|
868 |
signed_url = sign_url( |
|
869 |
'http://example.net/api/formdefs/test/submit' |
|
870 |
+ '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
871 |
'1234', |
|
872 |
) |
|
873 |
return signed_url[len('http://example.net') :] |
|
874 | ||
875 |
resp = get_app(pub).post_json(url(), {'data': {}}) |
|
876 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
877 | ||
878 |
assert data_class.count() == 1 |
|
879 | ||
880 |
resp = get_app(pub).post_json(url(), {'data': {}}, status=403) |
|
881 |
assert resp.json['err'] == 1 |
|
882 |
assert resp.json['err_desc'] == 'only one formdata by user is allowed' |
|
883 | ||
884 |
formdata = data_class.select()[0] |
|
885 |
formdata.user_id = '1000' # change owner |
|
886 |
formdata.store() |
|
887 | ||
888 |
resp = get_app(pub).post_json(url(), {'data': {}}, status=200) |
|
889 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
890 |
assert data_class.count() == 2 |
|
891 | ||
892 | ||
893 |
def test_formdef_submit_with_varname(pub, local_user): |
|
894 |
NamedDataSource.wipe() |
|
895 |
data_source = NamedDataSource(name='foobar') |
|
896 |
source = [{'id': '1', 'text': 'foo', 'more': 'XXX'}, {'id': '2', 'text': 'bar', 'more': 'YYY'}] |
|
897 |
data_source.data_source = {'type': 'formula', 'value': repr(source)} |
|
898 |
data_source.store() |
|
899 | ||
900 |
data_source = NamedDataSource(name='foobar_jsonp') |
|
901 |
data_source.data_source = {'type': 'formula', 'value': 'http://example.com/jsonp'} |
|
902 |
data_source.store() |
|
903 | ||
904 |
Role.wipe() |
|
905 |
role = Role(name='test') |
|
906 |
role.store() |
|
907 |
local_user.roles = [role.id] |
|
908 |
local_user.store() |
|
909 | ||
910 |
FormDef.wipe() |
|
911 |
formdef = FormDef() |
|
912 |
formdef.name = 'test' |
|
913 |
formdef.fields = [ |
|
914 |
fields.StringField(id='0', label='foobar0', varname='foobar0'), |
|
915 |
fields.ItemField(id='1', label='foobar1', varname='foobar1', data_source={'type': 'foobar'}), |
|
916 |
fields.ItemField(id='2', label='foobar2', varname='foobar2', data_source={'type': 'foobar_jsonp'}), |
|
917 |
fields.DateField(id='3', label='foobar3', varname='date'), |
|
918 |
fields.FileField(id='4', label='foobar4', varname='file'), |
|
919 |
fields.MapField(id='5', label='foobar5', varname='map'), |
|
920 |
fields.StringField(id='6', label='foobar6', varname='foobar6'), |
|
921 |
] |
|
922 |
formdef.store() |
|
923 |
data_class = formdef.data_class() |
|
924 | ||
925 |
resp = get_app(pub).post_json('/api/formdefs/test/submit', {'data': {}}, status=403) |
|
926 |
assert resp.json['err'] == 1 |
|
927 |
assert resp.json['err_desc'] == 'unsigned API call' |
|
928 | ||
929 |
signed_url = sign_url( |
|
930 |
'http://example.net/api/formdefs/test/submit' |
|
931 |
+ '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
932 |
'1234', |
|
933 |
) |
|
934 |
url = signed_url[len('http://example.net') :] |
|
935 |
payload = { |
|
936 |
'data': { |
|
937 |
'foobar0': 'xxx', |
|
938 |
'foobar1': '1', |
|
939 |
'foobar1_structured': { |
|
940 |
'id': '1', |
|
941 |
'text': 'foo', |
|
942 |
'more': 'XXX', |
|
943 |
}, |
|
944 |
'foobar2': 'bar', |
|
945 |
'foobar2_raw': '10', |
|
946 |
'date': '1970-01-01', |
|
947 |
'file': { |
|
948 |
'filename': 'test.txt', |
|
949 |
'content': force_text(base64.b64encode(b'test')), |
|
950 |
}, |
|
951 |
'map': { |
|
952 |
'lat': 1.5, |
|
953 |
'lon': 2.25, |
|
954 |
}, |
|
955 |
} |
|
956 |
} |
|
957 |
resp = get_app(pub).post_json(url, payload) |
|
958 |
assert resp.json['err'] == 0 |
|
959 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
960 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
961 |
assert data_class.get(resp.json['data']['id']).tracking_code is None |
|
962 |
assert data_class.get(resp.json['data']['id']).data['0'] == 'xxx' |
|
963 |
assert data_class.get(resp.json['data']['id']).data['1'] == '1' |
|
964 |
assert data_class.get(resp.json['data']['id']).data['1_structured'] == source[0] |
|
965 |
assert data_class.get(resp.json['data']['id']).data['2'] == '10' |
|
966 |
assert data_class.get(resp.json['data']['id']).data['2_display'] == 'bar' |
|
967 |
assert data_class.get(resp.json['data']['id']).data['3'] == time.struct_time( |
|
968 |
(1970, 1, 1, 0, 0, 0, 3, 1, -1) |
|
969 |
) |
|
970 | ||
971 |
assert data_class.get(resp.json['data']['id']).data['4'].orig_filename == 'test.txt' |
|
972 |
assert data_class.get(resp.json['data']['id']).data['4'].get_content() == b'test' |
|
973 |
assert data_class.get(resp.json['data']['id']).data['5'] == '1.5;2.25' |
|
974 |
# test bijectivity |
|
975 |
assert ( |
|
976 |
formdef.fields[3].get_json_value(data_class.get(resp.json['data']['id']).data['3']) |
|
977 |
== payload['data']['date'] |
|
978 |
) |
|
979 |
for k in payload['data']['file']: |
|
980 |
data = data_class.get(resp.json['data']['id']).data['4'] |
|
981 |
assert formdef.fields[4].get_json_value(data)[k] == payload['data']['file'][k] |
|
982 |
assert ( |
|
983 |
formdef.fields[5].get_json_value(data_class.get(resp.json['data']['id']).data['5']) |
|
984 |
== payload['data']['map'] |
|
985 |
) |
|
986 | ||
987 |
data_class.wipe() |
|
988 | ||
989 | ||
990 |
def test_formdef_submit_from_wscall(pub, local_user): |
|
991 |
test_formdef_submit_with_varname(pub, local_user) |
|
992 |
formdef = FormDef.select()[0] |
|
993 |
workflow = Workflow.get_default_workflow() |
|
994 |
workflow.id = '2' |
|
995 |
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) |
|
996 |
workflow.backoffice_fields_formdef.fields = [ |
|
997 |
fields.StringField(id='bo1', label='1st backoffice field', type='string', varname='backoffice_blah'), |
|
998 |
] |
|
999 |
workflow.store() |
|
1000 |
formdef.workflow = workflow |
|
1001 |
formdef.store() |
|
1002 | ||
1003 |
formdata = formdef.data_class()() |
|
1004 |
formdata.just_created() |
|
1005 | ||
1006 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
1007 |
upload.receive([b'test']) |
|
1008 | ||
1009 |
formdata.data = { |
|
1010 |
'0': 'xxx', |
|
1011 |
'1': '1', |
|
1012 |
'1_display': '1', |
|
1013 |
'1_structured': { |
|
1014 |
'id': '1', |
|
1015 |
'text': 'foo', |
|
1016 |
'more': 'XXX', |
|
1017 |
}, |
|
1018 |
'2': '10', |
|
1019 |
'2_display': 'bar', |
|
1020 |
'3': time.strptime('1970-01-01', '%Y-%m-%d'), |
|
1021 |
'4': upload, |
|
1022 |
'5': '1.5;2.25', |
|
1023 |
'bo1': 'backoffice field', |
|
1024 |
} |
|
1025 |
formdata.just_created() |
|
1026 |
formdata.evolution[-1].status = 'wf-new' |
|
1027 |
formdata.store() |
|
1028 | ||
1029 |
payload = json.loads(json.dumps(formdata.get_json_export_dict(), cls=qommon.misc.JSONEncoder)) |
|
1030 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
1031 |
url = signed_url[len('http://example.net') :] |
|
1032 | ||
1033 |
resp = get_app(pub).post_json(url, payload) |
|
1034 |
assert resp.json['err'] == 0 |
|
1035 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
1036 |
assert new_formdata.data['0'] == formdata.data['0'] |
|
1037 |
assert new_formdata.data['1'] == formdata.data['1'] |
|
1038 |
assert new_formdata.data['1_display'] == formdata.data['1_display'] |
|
1039 |
assert new_formdata.data['1_structured'] == formdata.data['1_structured'] |
|
1040 |
assert new_formdata.data['2'] == formdata.data['2'] |
|
1041 |
assert new_formdata.data['2_display'] == formdata.data['2_display'] |
|
1042 |
assert new_formdata.data['3'] == formdata.data['3'] |
|
1043 |
assert new_formdata.data['4'].get_content() == formdata.data['4'].get_content() |
|
1044 |
assert new_formdata.data['5'] == formdata.data['5'] |
|
1045 |
assert new_formdata.data['bo1'] == formdata.data['bo1'] |
|
1046 |
assert not new_formdata.data.get('6') |
|
1047 |
assert new_formdata.user_id is None |
|
1048 | ||
1049 |
# add an extra attribute |
|
1050 |
payload['extra'] = {'foobar6': 'YYY'} |
|
1051 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
1052 |
url = signed_url[len('http://example.net') :] |
|
1053 |
resp = get_app(pub).post_json(url, payload) |
|
1054 |
assert resp.json['err'] == 0 |
|
1055 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
1056 |
assert new_formdata.data['0'] == formdata.data['0'] |
|
1057 |
assert new_formdata.data['6'] == 'YYY' |
|
1058 | ||
1059 |
# add user |
|
1060 |
formdata.user_id = local_user.id |
|
1061 |
formdata.store() |
|
1062 | ||
1063 |
payload = json.loads(json.dumps(formdata.get_json_export_dict(), cls=qommon.misc.JSONEncoder)) |
|
1064 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
1065 |
url = signed_url[len('http://example.net') :] |
|
1066 | ||
1067 |
resp = get_app(pub).post_json(url, payload) |
|
1068 |
assert resp.json['err'] == 0 |
|
1069 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
1070 |
assert str(new_formdata.user_id) == str(local_user.id) |
|
1071 | ||
1072 |
# test missing map data |
|
1073 |
del formdata.data['5'] |
|
1074 | ||
1075 |
payload = json.loads(json.dumps(formdata.get_json_export_dict(), cls=qommon.misc.JSONEncoder)) |
|
1076 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
1077 |
url = signed_url[len('http://example.net') :] |
|
1078 | ||
1079 |
resp = get_app(pub).post_json(url, payload) |
|
1080 |
assert resp.json['err'] == 0 |
|
1081 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
1082 |
assert new_formdata.data.get('5') is None |
|
1083 | ||
1084 | ||
1085 |
def test_categories(pub): |
|
1086 |
FormDef.wipe() |
|
1087 |
Category.wipe() |
|
1088 |
category = Category() |
|
1089 |
category.name = 'Category' |
|
1090 |
category.description = 'hello world' |
|
1091 |
category.store() |
|
1092 | ||
1093 |
resp = get_app(pub).get('/api/categories/', headers={'Accept': 'application/json'}) |
|
1094 |
assert resp.json['data'] == [] # no advertised forms |
|
1095 | ||
1096 |
formdef = FormDef() |
|
1097 |
formdef.name = 'test' |
|
1098 |
formdef.category_id = category.id |
|
1099 |
formdef.fields = [] |
|
1100 |
formdef.keywords = 'mobile, test' |
|
1101 |
formdef.store() |
|
1102 |
formdef.data_class().wipe() |
|
1103 | ||
1104 |
formdef = FormDef() |
|
1105 |
formdef.name = 'test 2' |
|
1106 |
formdef.category_id = category.id |
|
1107 |
formdef.fields = [] |
|
1108 |
formdef.keywords = 'foobar' |
|
1109 |
formdef.store() |
|
1110 |
formdef.data_class().wipe() |
|
1111 | ||
1112 |
resp = get_app(pub).get('/api/categories/') |
|
1113 |
resp2 = get_app(pub).get('/categories', headers={'Accept': 'application/json'}) |
|
1114 |
assert resp.json == resp2.json |
|
1115 |
assert resp.json['data'][0]['title'] == 'Category' |
|
1116 |
assert resp.json['data'][0]['url'] == 'http://example.net/category/' |
|
1117 |
assert resp.json['data'][0]['description'] == '<p>hello world</p>' |
|
1118 |
assert set(resp.json['data'][0]['keywords']) == set(['foobar', 'mobile', 'test']) |
|
1119 |
assert not 'forms' in resp.json['data'][0] |
|
1120 | ||
1121 |
# check HTML description |
|
1122 |
category.description = '<p><strong>hello world</strong></p>' |
|
1123 |
category.store() |
|
1124 |
resp = get_app(pub).get('/api/categories/') |
|
1125 |
assert resp.json['data'][0]['description'] == category.description |
|
1126 | ||
1127 | ||
1128 |
def test_categories_private(pub, local_user): |
|
1129 |
FormDef.wipe() |
|
1130 |
Category.wipe() |
|
1131 |
category = Category() |
|
1132 |
category.name = 'Category' |
|
1133 |
category.description = 'hello world' |
|
1134 |
category.store() |
|
1135 | ||
1136 |
formdef = FormDef() |
|
1137 |
formdef.name = 'test' |
|
1138 |
formdef.category_id = category.id |
|
1139 |
formdef.fields = [] |
|
1140 |
formdef.store() |
|
1141 |
formdef.data_class().wipe() |
|
1142 | ||
1143 |
# open form |
|
1144 |
resp = get_app(pub).get('/api/categories/') |
|
1145 |
assert len(resp.json['data']) == 1 |
|
1146 | ||
1147 |
# private form, the category doesn't appear anymore |
|
1148 |
formdef.roles = ['plop'] |
|
1149 |
formdef.store() |
|
1150 |
resp = get_app(pub).get('/api/categories/') |
|
1151 |
assert len(resp.json['data']) == 0 |
|
1152 | ||
1153 |
# not even for a signed request specifying an user |
|
1154 |
resp = get_app(pub).get(sign_uri('http://example.net/api/categories/', local_user)) |
|
1155 |
assert len(resp.json['data']) == 0 |
|
1156 | ||
1157 |
# but it appears if this is a signed request without user |
|
1158 |
resp = get_app(pub).get(sign_uri('http://example.net/api/categories/')) |
|
1159 |
assert len(resp.json['data']) == 1 |
|
1160 | ||
1161 |
# or signed with an authorised user |
|
1162 |
local_user.roles = ['plop'] |
|
1163 |
local_user.store() |
|
1164 |
resp = get_app(pub).get(sign_uri('http://example.net/api/categories/', local_user)) |
|
1165 |
assert len(resp.json['data']) == 1 |
|
1166 | ||
1167 | ||
1168 |
def test_categories_formdefs(pub, local_user): |
|
1169 |
FormDef.wipe() |
|
1170 |
Category.wipe() |
|
1171 |
category = Category() |
|
1172 |
category.name = 'Category' |
|
1173 |
category.description = 'hello world' |
|
1174 |
category.store() |
|
1175 | ||
1176 |
formdef = FormDef() |
|
1177 |
formdef.name = 'test' |
|
1178 |
formdef.category_id = category.id |
|
1179 |
formdef.fields = [] |
|
1180 |
formdef.keywords = 'mobile, test' |
|
1181 |
formdef.store() |
|
1182 |
formdef.data_class().wipe() |
|
1183 | ||
1184 |
formdef = FormDef() |
|
1185 |
formdef.name = 'test 2' |
|
1186 |
formdef.category_id = category.id |
|
1187 |
formdef.fields = [] |
|
1188 |
formdef.keywords = 'foobar' |
|
1189 |
formdef.store() |
|
1190 |
formdef.data_class().wipe() |
|
1191 | ||
1192 |
formdef2 = FormDef() |
|
1193 |
formdef2.name = 'other test' |
|
1194 |
formdef2.category_id = None |
|
1195 |
formdef2.fields = [] |
|
1196 |
formdef2.store() |
|
1197 |
formdef2.data_class().wipe() |
|
1198 | ||
1199 |
formdef2 = FormDef() |
|
1200 |
formdef2.name = 'test disabled' |
|
1201 |
formdef2.category_id = category.id |
|
1202 |
formdef2.fields = [] |
|
1203 |
formdef2.disabled = True |
|
1204 |
formdef2.store() |
|
1205 |
formdef2.data_class().wipe() |
|
1206 | ||
1207 |
resp = get_app(pub).get('/api/categories/category/formdefs/', status=403) |
|
1208 |
resp2 = get_app(pub).get('/category/json', status=403) |
|
1209 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/')) |
|
1210 |
resp2 = get_app(pub).get(sign_uri('/category/json')) |
|
1211 |
assert resp.json == resp2.json |
|
1212 |
assert resp.json['err'] == 0 |
|
1213 |
assert len(resp.json['data']) == 2 |
|
1214 |
assert resp.json['data'][0]['title'] == 'test' |
|
1215 |
assert resp.json['data'][0]['url'] == 'http://example.net/test/' |
|
1216 |
assert resp.json['data'][0]['redirection'] == False |
|
1217 |
assert resp.json['data'][0]['category'] == 'Category' |
|
1218 |
assert resp.json['data'][0]['category_slug'] == 'category' |
|
1219 |
assert 'count' not in resp.json['data'][0] |
|
1220 | ||
1221 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?include-count=on')) |
|
1222 |
assert resp.json['data'][0]['title'] == 'test' |
|
1223 |
assert resp.json['data'][0]['url'] == 'http://example.net/test/' |
|
1224 |
assert resp.json['data'][0]['count'] == 0 |
|
1225 | ||
1226 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?include-disabled=on')) |
|
1227 |
assert len(resp.json['data']) == 3 |
|
1228 |
assert resp.json['data'][2]['title'] == 'test disabled' |
|
1229 | ||
1230 |
get_app(pub).get('/api/categories/XXX/formdefs/', status=404) |
|
1231 | ||
1232 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?backoffice-submission=on')) |
|
1233 |
assert resp.json['err'] == 0 |
|
1234 |
assert len(resp.json['data']) == 0 |
|
1235 | ||
1236 |
Role.wipe() |
|
1237 |
role = Role(name='test') |
|
1238 |
role.store() |
|
1239 |
local_user.roles = [] |
|
1240 |
local_user.store() |
|
1241 |
# check it's not advertised ... |
|
1242 |
formdef.backoffice_submission_roles = [role.id] |
|
1243 |
formdef.store() |
|
1244 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?backoffice-submission=on')) |
|
1245 |
assert resp.json['err'] == 0 |
|
1246 |
assert len(resp.json['data']) == 0 |
|
1247 |
resp = get_app(pub).get( |
|
1248 |
sign_uri( |
|
1249 |
'/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' |
|
1250 |
% local_user.name_identifiers[0] |
|
1251 |
) |
|
1252 |
) |
|
1253 |
assert resp.json['err'] == 0 |
|
1254 |
assert len(resp.json['data']) == 0 |
|
1255 |
# ... unless user has correct roles |
|
1256 |
local_user.roles = [role.id] |
|
1257 |
local_user.store() |
|
1258 |
resp = get_app(pub).get( |
|
1259 |
sign_uri( |
|
1260 |
'/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' |
|
1261 |
% local_user.name_identifiers[0] |
|
1262 |
) |
|
1263 |
) |
|
1264 |
assert resp.json['err'] == 0 |
|
1265 |
assert len(resp.json['data']) == 1 |
|
1266 | ||
1267 | ||
1268 |
def test_categories_full(pub): |
|
1269 |
test_categories(pub) |
|
1270 |
resp = get_app(pub).get('/api/categories/?full=on') |
|
1271 |
assert len(resp.json['data'][0]['forms']) == 2 |
|
1272 |
assert resp.json['data'][0]['forms'][0]['title'] == 'test' |
|
1273 |
assert resp.json['data'][0]['forms'][1]['title'] == 'test 2' |
|
1274 | ||
1275 | ||
1276 |
def test_formdata(pub, local_user): |
|
1277 |
NamedDataSource.wipe() |
|
1278 |
data_source = NamedDataSource(name='foobar') |
|
1279 |
data_source.data_source = { |
|
1280 |
'type': 'formula', |
|
1281 |
'value': repr([{'id': '1', 'text': 'foo', 'more': 'XXX'}, {'id': '2', 'text': 'bar', 'more': 'YYY'}]), |
|
1282 |
} |
|
1283 |
data_source.store() |
|
1284 | ||
1285 |
BlockDef.wipe() |
|
1286 |
block = BlockDef() |
|
1287 |
block.name = 'foobar' |
|
1288 |
block.fields = [ |
|
1289 |
fields.StringField(id='abc', label='Foo', varname='foo'), |
|
1290 |
fields.ItemField(id='xyz', label='Test', type='item', data_source={'type': 'foobar'}, varname='bar'), |
|
1291 |
] |
|
1292 |
block.store() |
|
1293 | ||
1294 |
Role.wipe() |
|
1295 |
role = Role(name='test') |
|
1296 |
role.id = '123' |
|
1297 |
role.store() |
|
1298 |
another_role = Role(name='another') |
|
1299 |
another_role.id = '321' |
|
1300 |
another_role.store() |
|
1301 |
FormDef.wipe() |
|
1302 |
formdef = FormDef() |
|
1303 |
formdef.geolocations = {'base': 'blah'} |
|
1304 |
formdef.name = 'test' |
|
1305 |
formdef.fields = [ |
|
1306 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1307 |
fields.StringField(id='1', label='foobar2'), |
|
1308 |
fields.DateField(id='2', label='foobar3', varname='date'), |
|
1309 |
fields.FileField(id='3', label='foobar4', varname='file'), |
|
1310 |
fields.ItemField(id='4', label='foobar5', varname='item', data_source={'type': 'foobar'}), |
|
1311 |
fields.BlockField(id='5', label='test', varname='blockdata', type='block:foobar', max_items=3), |
|
1312 |
] |
|
1313 |
Workflow.wipe() |
|
1314 |
workflow = Workflow(name='foo') |
|
1315 |
workflow.possible_status = Workflow.get_default_workflow().possible_status[:] |
|
1316 |
workflow.roles['_foobar'] = 'Foobar' |
|
1317 |
workflow.store() |
|
1318 |
formdef.workflow_id = workflow.id |
|
1319 |
formdef.workflow_roles = {'_receiver': role.id, '_foobar': another_role.id} |
|
1320 |
formdef.store() |
|
1321 |
item_field = formdef.fields[4] |
|
1322 | ||
1323 |
formdef.data_class().wipe() |
|
1324 |
formdata = formdef.data_class()() |
|
1325 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
1326 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
1327 |
upload.receive([b'base64me']) |
|
1328 |
formdata.data = { |
|
1329 |
'0': 'foo@localhost', |
|
1330 |
'1': 'xxx', |
|
1331 |
'2': date, |
|
1332 |
'3': upload, |
|
1333 |
'4': '1', |
|
1334 |
'5': { |
|
1335 |
'data': [ |
|
1336 |
{'abc': 'plop', 'xyz': '1', 'xyz_display': 'foo', 'xyz_structured': 'XXX'}, |
|
1337 |
], |
|
1338 |
'schema': {}, # not important here |
|
1339 |
}, |
|
1340 |
'5_display': 'hello', |
|
1341 |
} |
|
1342 |
formdata.data['4_display'] = item_field.store_display_value(formdata.data, item_field.id) |
|
1343 |
formdata.data['4_structured'] = item_field.store_structured_value(formdata.data, item_field.id) |
|
1344 |
formdata.user_id = local_user.id |
|
1345 |
formdata.just_created() |
|
1346 |
formdata.status = 'wf-new' |
|
1347 |
formdata.evolution[-1].status = 'wf-new' |
|
1348 |
formdata.geolocations = {'base': {'lon': 10, 'lat': -12}} |
|
1349 |
formdata.store() |
|
1350 | ||
1351 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=403) |
|
1352 | ||
1353 |
local_user.roles = [role.id] |
|
1354 |
local_user.store() |
|
1355 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
1356 | ||
1357 |
assert datetime.datetime.strptime(resp.json['last_update_time'], '%Y-%m-%dT%H:%M:%S') |
|
1358 |
assert datetime.datetime.strptime(resp.json['receipt_time'], '%Y-%m-%dT%H:%M:%S') |
|
1359 |
assert len(resp.json['fields']) == 8 |
|
1360 |
assert 'foobar' in resp.json['fields'] |
|
1361 |
assert 'foobar2' not in resp.json['fields'] # foobar2 has no varname, not in json |
|
1362 |
assert resp.json['user']['name'] == local_user.name |
|
1363 |
assert resp.json['fields']['foobar'] == 'foo@localhost' |
|
1364 |
assert resp.json['fields']['date'] == '2014-01-20' |
|
1365 |
assert resp.json['fields']['file']['content'] == 'YmFzZTY0bWU=' # base64('base64me') |
|
1366 |
assert resp.json['fields']['file']['filename'] == 'test.txt' |
|
1367 |
assert resp.json['fields']['file']['content_type'] == 'text/plain' |
|
1368 |
assert resp.json['fields']['item'] == 'foo' |
|
1369 |
assert resp.json['fields']['item_raw'] == '1' |
|
1370 |
assert resp.json['fields']['item_structured'] == {'id': '1', 'text': 'foo', 'more': 'XXX'} |
|
1371 |
assert resp.json['fields']['blockdata'] == 'hello' |
|
1372 |
assert resp.json['fields']['blockdata_raw'] == [ |
|
1373 |
{'foo': 'plop', 'bar': 'foo', 'bar_raw': '1', 'bar_structured': 'XXX'} |
|
1374 |
] |
|
1375 |
assert resp.json['workflow']['status']['name'] == 'New' |
|
1376 |
assert resp.json['workflow']['real_status']['name'] == 'New' |
|
1377 |
assert resp.json['submission']['channel'] == 'web' |
|
1378 |
assert resp.json['geolocations']['base']['lon'] == 10 |
|
1379 |
assert resp.json['geolocations']['base']['lat'] == -12 |
|
1380 | ||
1381 |
assert [x.get('id') for x in resp.json['roles']['_receiver']] == [str(role.id)] |
|
1382 |
assert [x.get('id') for x in resp.json['roles']['_foobar']] == [str(another_role.id)] |
|
1383 |
assert set([x.get('id') for x in resp.json['roles']['concerned']]) == set( |
|
1384 |
[str(role.id), str(another_role.id)] |
|
1385 |
) |
|
1386 |
assert [x.get('id') for x in resp.json['roles']['actions']] == [str(role.id)] |
|
1387 | ||
1388 |
# check the ?format=json endpoint returns 403 |
|
1389 |
resp = get_app(pub).get('/test/%s/?format=json' % formdata.id, status=403) |
|
1390 |
resp2 = get_app(pub).get(sign_uri('/test/%s/' % formdata.id, user=local_user), status=403) |
|
1391 | ||
1392 |
# check status visibility |
|
1393 |
workflow.add_status('Status1', 'st1') |
|
1394 |
workflow.possible_status[-1].visibility = ['unknown'] |
|
1395 |
workflow.store() |
|
1396 |
formdata.jump_status('st1') |
|
1397 |
assert formdata.status == 'wf-st1' |
|
1398 | ||
1399 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
1400 |
assert resp.json['workflow']['status'] == {'id': 'new', 'name': 'New'} |
|
1401 |
assert resp.json['workflow']['real_status'] == {'id': 'st1', 'name': 'Status1'} |
|
1402 | ||
1403 | ||
1404 |
def test_formdata_backoffice_fields(pub, local_user): |
|
1405 |
test_formdata(pub, local_user) |
|
1406 |
Workflow.wipe() |
|
1407 |
workflow = Workflow(name='foo') |
|
1408 |
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) |
|
1409 |
workflow.backoffice_fields_formdef.fields = [ |
|
1410 |
fields.StringField(id='bo1', label='1st backoffice field', type='string', varname='backoffice_blah'), |
|
1411 |
] |
|
1412 |
workflow.store() |
|
1413 | ||
1414 |
formdef = FormDef.select()[0] |
|
1415 |
formdata = formdef.data_class().select()[0] |
|
1416 |
formdata.data['bo1'] = 'Hello world' |
|
1417 |
formdata.store() |
|
1418 | ||
1419 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user)) |
|
1420 |
assert resp.json['workflow']['fields']['backoffice_blah'] == 'Hello world' |
|
1421 | ||
1422 | ||
1423 |
def test_formdata_duplicated_varnames(pub, local_user): |
|
1424 |
Role.wipe() |
|
1425 |
role = Role(name='test') |
|
1426 |
role.id = '123' |
|
1427 |
role.store() |
|
1428 |
another_role = Role(name='another') |
|
1429 |
another_role.id = '321' |
|
1430 |
another_role.store() |
|
1431 |
FormDef.wipe() |
|
1432 |
formdef = FormDef() |
|
1433 |
formdef.geolocations = {'base': 'blah'} |
|
1434 |
formdef.name = 'test' |
|
1435 |
formdef.fields = [ |
|
1436 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1437 |
fields.StringField(id='1', label='foobar2', varname='foobar'), |
|
1438 |
] |
|
1439 |
workflow = Workflow.get_default_workflow() |
|
1440 |
workflow.roles['_foobar'] = 'Foobar' |
|
1441 |
workflow.id = '2' |
|
1442 |
workflow.store() |
|
1443 |
formdef.workflow_id = workflow.id |
|
1444 |
formdef.workflow_roles = {'_receiver': role.id, '_foobar': another_role.id} |
|
1445 |
formdef.store() |
|
1446 | ||
1447 |
formdef.data_class().wipe() |
|
1448 |
formdata = formdef.data_class()() |
|
1449 |
formdata.data = { |
|
1450 |
'0': 'foo', |
|
1451 |
'1': 'bar', |
|
1452 |
} |
|
1453 |
formdata.user_id = local_user.id |
|
1454 |
formdata.just_created() |
|
1455 |
formdata.status = 'wf-new' |
|
1456 |
formdata.evolution[-1].status = 'wf-new' |
|
1457 |
formdata.store() |
|
1458 | ||
1459 |
local_user.roles = [role.id] |
|
1460 |
local_user.store() |
|
1461 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
1462 |
assert resp.json['fields'] == {'foobar': 'foo'} |
|
1463 | ||
1464 |
formdata.data = { |
|
1465 |
'0': 'foo', |
|
1466 |
'1': '', |
|
1467 |
} |
|
1468 |
formdata.store() |
|
1469 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
1470 |
assert resp.json['fields'] == {'foobar': 'foo'} |
|
1471 | ||
1472 |
formdata.data = { |
|
1473 |
'0': '', |
|
1474 |
'1': 'foo', |
|
1475 |
} |
|
1476 |
formdata.store() |
|
1477 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
1478 |
assert resp.json['fields'] == {'foobar': 'foo'} |
|
1479 | ||
1480 | ||
1481 |
def test_formdata_edit(pub, local_user): |
|
1482 |
Role.wipe() |
|
1483 |
role = Role(name='test') |
|
1484 |
role.id = '123' |
|
1485 |
role.store() |
|
1486 |
another_role = Role(name='another') |
|
1487 |
another_role.id = '321' |
|
1488 |
another_role.store() |
|
1489 |
local_user.roles = [role.id] |
|
1490 |
local_user.store() |
|
1491 |
FormDef.wipe() |
|
1492 |
formdef = FormDef() |
|
1493 |
formdef.name = 'test' |
|
1494 |
formdef.fields = [ |
|
1495 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1496 |
] |
|
1497 |
Workflow.wipe() |
|
1498 |
workflow = Workflow(name='foo') |
|
1499 |
workflow.possible_status = Workflow.get_default_workflow().possible_status[:] |
|
1500 |
workflow.roles['_foobar'] = 'Foobar' |
|
1501 |
workflow.store() |
|
1502 |
formdef.workflow_id = workflow.id |
|
1503 |
formdef.workflow_roles = {'_receiver': role.id, '_foobar': another_role.id} |
|
1504 |
formdef.store() |
|
1505 |
formdef.data_class().wipe() |
|
1506 |
formdata = formdef.data_class()() |
|
1507 |
formdata.data = { |
|
1508 |
'0': 'foo@localhost', |
|
1509 |
} |
|
1510 |
formdata.user_id = local_user.id |
|
1511 |
formdata.just_created() |
|
1512 |
formdata.status = 'wf-new' |
|
1513 |
formdata.evolution[-1].status = 'wf-new' |
|
1514 |
formdata.store() |
|
1515 | ||
1516 |
# not user |
|
1517 |
get_app(pub).post_json( |
|
1518 |
sign_uri('/api/forms/test/%s/' % formdata.id), {'data': {'0': 'bar@localhost'}}, status=403 |
|
1519 |
) |
|
1520 | ||
1521 |
# no editable action |
|
1522 |
get_app(pub).post_json( |
|
1523 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
1524 |
{'data': {'0': 'bar@localhost'}}, |
|
1525 |
status=403, |
|
1526 |
) |
|
1527 | ||
1528 |
wfedit = EditableWorkflowStatusItem() |
|
1529 |
wfedit.id = '_wfedit' |
|
1530 |
wfedit.by = [local_user.roles[0]] |
|
1531 |
workflow.possible_status[1].items.append(wfedit) |
|
1532 |
wfedit.parent = workflow.possible_status[1] |
|
1533 |
workflow.store() |
|
1534 | ||
1535 |
get_app(pub).post_json( |
|
1536 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
1537 |
{'data': {'0': 'bar@localhost'}}, |
|
1538 |
status=200, |
|
1539 |
) |
|
1540 |
assert formdef.data_class().select()[0].data['0'] == 'bar@localhost' |
|
1541 | ||
1542 |
# not editable by user role |
|
1543 |
wfedit.by = ['XX'] |
|
1544 |
workflow.store() |
|
1545 |
get_app(pub).post_json( |
|
1546 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
1547 |
{'data': {'0': 'bar@localhost'}}, |
|
1548 |
status=403, |
|
1549 |
) |
|
1550 | ||
1551 |
# edit + jump |
|
1552 |
wfedit.status = 'rejected' |
|
1553 |
wfedit.by = [local_user.roles[0]] |
|
1554 |
workflow.store() |
|
1555 | ||
1556 |
get_app(pub).post_json( |
|
1557 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
1558 |
{'data': {'0': 'bar2@localhost'}}, |
|
1559 |
status=200, |
|
1560 |
) |
|
1561 |
assert formdef.data_class().select()[0].data['0'] == 'bar2@localhost' |
|
1562 |
assert formdef.data_class().select()[0].status == 'wf-rejected' |
|
1563 | ||
1564 | ||
1565 |
def test_formdata_with_workflow_data(pub, local_user): |
|
1566 |
Role.wipe() |
|
1567 |
role = Role(name='test') |
|
1568 |
role.id = '123' |
|
1569 |
role.store() |
|
1570 | ||
1571 |
local_user.roles = [role.id] |
|
1572 |
local_user.store() |
|
1573 | ||
1574 |
FormDef.wipe() |
|
1575 |
formdef = FormDef() |
|
1576 |
formdef.name = 'test' |
|
1577 |
formdef.fields = [] |
|
1578 |
workflow = Workflow.get_default_workflow() |
|
1579 |
workflow.id = '2' |
|
1580 |
workflow.store() |
|
1581 |
formdef.workflow_id = workflow.id |
|
1582 |
formdef.workflow_roles = {'_receiver': role.id} |
|
1583 |
formdef.store() |
|
1584 | ||
1585 |
formdef.data_class().wipe() |
|
1586 |
formdata = formdef.data_class()() |
|
1587 |
formdata.just_created() |
|
1588 |
formdata.status = 'wf-new' |
|
1589 |
formdata.evolution[-1].status = 'wf-new' |
|
1590 | ||
1591 |
from wcs.qommon.form import PicklableUpload as PicklableUpload3 |
|
1592 | ||
1593 |
upload = PicklableUpload3('test.txt', 'text/plain', 'ascii') |
|
1594 |
upload.receive([b'test']) |
|
1595 |
upload2 = PicklableUpload3('test.txt', 'text/plain', 'ascii') |
|
1596 |
upload2.receive([b'test']) |
|
1597 |
formdata.workflow_data = {'blah': upload, 'blah2': upload2, 'xxx': 23} |
|
1598 |
formdata.store() |
|
1599 | ||
1600 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user)) |
|
1601 |
assert resp.json['workflow']['data']['xxx'] == 23 |
|
1602 |
assert resp.json['workflow']['data']['blah']['filename'] == 'test.txt' |
|
1603 |
assert resp.json['workflow']['data']['blah']['content_type'] == 'text/plain' |
|
1604 |
assert base64.decodebytes(force_bytes(resp.json['workflow']['data']['blah']['content'])) == b'test' |
|
1605 |
assert base64.decodebytes(force_bytes(resp.json['workflow']['data']['blah2']['content'])) == b'test' |
|
1606 | ||
1607 | ||
1608 |
def test_user_by_nameid(pub, local_user): |
|
1609 |
resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user), status=404) |
|
1610 |
local_user.name_identifiers = ['xyz'] |
|
1611 |
local_user.store() |
|
1612 |
resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user)) |
|
1613 |
assert str(resp.json['id']) == str(local_user.id) |
|
1614 | ||
1615 | ||
1616 |
def test_user_roles(pub, local_user): |
|
1617 |
local_user.name_identifiers = ['xyz'] |
|
1618 |
local_user.store() |
|
1619 |
role = Role(name='Foo bar') |
|
1620 |
role.store() |
|
1621 |
local_user.roles = [role.id] |
|
1622 |
local_user.store() |
|
1623 |
resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user)) |
|
1624 |
assert len(resp.json['user_roles']) == 1 |
|
1625 |
assert resp.json['user_roles'][0]['name'] == 'Foo bar' |
|
1626 | ||
1627 | ||
1628 |
def test_user_forms(pub, local_user): |
|
1629 |
Workflow.wipe() |
|
1630 |
workflow = Workflow.get_default_workflow() |
|
1631 |
workflow.id = '2' |
|
1632 |
workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow) |
|
1633 |
workflow.variables_formdef.fields.append( |
|
1634 |
fields.DateField(label='Test', type='date', varname='option_date') |
|
1635 |
) |
|
1636 |
workflow.store() |
|
1637 | ||
1638 |
FormDef.wipe() |
|
1639 |
formdef = FormDef() |
|
1640 |
formdef.name = 'test' |
|
1641 |
formdef.fields = [ |
|
1642 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1643 |
fields.StringField(id='1', label='foobar2'), |
|
1644 |
fields.DateField(id='2', label='date', type='date', varname='date'), |
|
1645 |
] |
|
1646 |
formdef.keywords = 'hello, world' |
|
1647 |
formdef.disabled = False |
|
1648 |
formdef.enable_tracking_codes = True |
|
1649 |
formdef.workflow = workflow |
|
1650 |
formdef.workflow_options = {'option_date': datetime.date(2020, 1, 15).timetuple()} |
|
1651 |
formdef.store() |
|
1652 |
formdef.data_class().wipe() |
|
1653 | ||
1654 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
1655 |
assert resp.json['err'] == 0 |
|
1656 |
assert len(resp.json['data']) == 0 |
|
1657 | ||
1658 |
formdata = formdef.data_class()() |
|
1659 |
formdata.data = { |
|
1660 |
'0': 'foo@localhost', |
|
1661 |
'1': 'xxx', |
|
1662 |
'2': datetime.date(2020, 1, 15).timetuple(), |
|
1663 |
} |
|
1664 |
formdata.user_id = local_user.id |
|
1665 |
formdata.just_created() |
|
1666 |
formdata.jump_status('new') |
|
1667 |
formdata.store() |
|
1668 | ||
1669 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
1670 |
resp2 = get_app(pub).get(sign_uri('/myspace/forms', user=local_user)) |
|
1671 |
resp3 = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id)) |
|
1672 |
assert resp.json['err'] == 0 |
|
1673 |
assert len(resp.json['data']) == 1 |
|
1674 |
assert resp.json['data'][0]['form_name'] == 'test' |
|
1675 |
assert resp.json['data'][0]['form_slug'] == 'test' |
|
1676 |
assert resp.json['data'][0]['form_status'] == 'New' |
|
1677 |
assert datetime.datetime.strptime(resp.json['data'][0]['form_receipt_datetime'], '%Y-%m-%dT%H:%M:%S') |
|
1678 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
1679 |
assert resp.json == resp2.json == resp3.json |
|
1680 | ||
1681 |
resp = get_app(pub).get(sign_uri('/api/user/forms?full=on', user=local_user)) |
|
1682 |
assert resp.json['err'] == 0 |
|
1683 |
assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost' |
|
1684 |
assert resp.json['data'][0]['fields']['date'] == '2020-01-15' |
|
1685 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
1686 |
assert resp.json['data'][0]['form_option_option_date'] == '2020-01-15' |
|
1687 |
resp2 = get_app(pub).get(sign_uri('/api/user/forms?&full=on', user=local_user)) |
|
1688 |
assert resp.json == resp2.json |
|
1689 | ||
1690 |
formdef.disabled = True |
|
1691 |
formdef.store() |
|
1692 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
1693 |
assert resp.json['err'] == 0 |
|
1694 |
assert len(resp.json['data']) == 1 |
|
1695 | ||
1696 |
# check digest is part of contents |
|
1697 |
formdef.digest_template = 'XYZ' |
|
1698 |
formdef.data_class().get(formdata.id).store() |
|
1699 |
assert formdef.data_class().get(formdata.id).digest == 'XYZ' |
|
1700 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
1701 |
assert resp.json['data'][0]['form_digest'] == 'XYZ' |
|
1702 | ||
1703 |
resp = get_app(pub).get(sign_uri('/api/user/forms?NameID=xxx')) |
|
1704 |
assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []} |
|
1705 |
resp2 = get_app(pub).get(sign_uri('/api/user/forms?&NameID=xxx')) |
|
1706 |
assert resp.json == resp2.json |
|
1707 | ||
1708 |
formdata = formdef.data_class()() |
|
1709 |
formdata.user_id = local_user.id |
|
1710 |
formdata.status = 'draft' |
|
1711 |
formdata.receipt_time = datetime.datetime(2015, 1, 1).timetuple() |
|
1712 |
formdata.store() |
|
1713 | ||
1714 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
1715 |
assert resp.json['err'] == 0 |
|
1716 |
assert len(resp.json['data']) == 1 |
|
1717 | ||
1718 |
resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user)) |
|
1719 |
assert resp.json['err'] == 0 |
|
1720 |
assert len(resp.json['data']) == 1 |
|
1721 | ||
1722 |
formdef.disabled = False |
|
1723 |
formdef.store() |
|
1724 | ||
1725 |
resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user)) |
|
1726 |
assert resp.json['err'] == 0 |
|
1727 |
assert len(resp.json['data']) == 2 |
|
1728 | ||
1729 |
draft_formdata = [x for x in resp.json['data'] if x['status'] == 'Draft'][0] |
|
1730 | ||
1731 |
formdata = formdef.data_class()() |
|
1732 |
formdata.data = {'0': 'foo@localhost', '1': 'xyy'} |
|
1733 |
formdata.user_id = local_user.id |
|
1734 |
formdata.just_created() |
|
1735 |
formdata.receipt_time = (datetime.datetime.now() + datetime.timedelta(days=1)).timetuple() |
|
1736 |
formdata.jump_status('new') |
|
1737 |
formdata.store() |
|
1738 | ||
1739 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
1740 |
assert len(resp.json['data']) == 2 |
|
1741 |
resp2 = get_app(pub).get(sign_uri('/api/user/forms?sort=desc', user=local_user)) |
|
1742 |
assert len(resp2.json['data']) == 2 |
|
1743 |
assert resp2.json['data'][0] == resp.json['data'][1] |
|
1744 |
assert resp2.json['data'][1] == resp.json['data'][0] |
|
1745 | ||
1746 | ||
1747 |
def test_user_forms_limit_offset(pub, local_user): |
|
1748 |
if not pub.is_using_postgresql(): |
|
1749 |
pytest.skip('this requires SQL') |
|
1750 |
return |
|
1751 | ||
1752 |
FormDef.wipe() |
|
1753 |
formdef = FormDef() |
|
1754 |
formdef.name = 'test limit offset' |
|
1755 |
formdef.fields = [ |
|
1756 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1757 |
fields.StringField(id='1', label='foobar2'), |
|
1758 |
] |
|
1759 |
formdef.keywords = 'hello, world' |
|
1760 |
formdef.disabled = False |
|
1761 |
formdef.enable_tracking_codes = False |
|
1762 |
formdef.store() |
|
1763 |
formdef.data_class().wipe() |
|
1764 | ||
1765 |
for i in range(50): |
|
1766 |
formdata = formdef.data_class()() |
|
1767 |
formdata.data = {'0': 'foo@localhost', '1': str(i)} |
|
1768 |
formdata.user_id = local_user.id |
|
1769 |
formdata.just_created() |
|
1770 |
formdata.receipt_time = (datetime.datetime.now() + datetime.timedelta(days=i)).timetuple() |
|
1771 |
formdata.jump_status('new') |
|
1772 |
formdata.store() |
|
1773 | ||
1774 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id)) |
|
1775 |
assert resp.json['err'] == 0 |
|
1776 |
assert len(resp.json['data']) == 50 |
|
1777 | ||
1778 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms?limit=10' % local_user.id)) |
|
1779 |
assert resp.json['err'] == 0 |
|
1780 |
assert len(resp.json['data']) == 10 |
|
1781 |
assert [x['form_number_raw'] for x in resp.json['data']] == [str(x) for x in range(1, 11)] |
|
1782 | ||
1783 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms?limit=10&offset=45' % local_user.id)) |
|
1784 |
assert resp.json['err'] == 0 |
|
1785 |
assert len(resp.json['data']) == 5 |
|
1786 |
assert [x['form_number_raw'] for x in resp.json['data']] == [str(x) for x in range(46, 51)] |
|
1787 | ||
1788 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms?limit=10&sort=desc' % local_user.id)) |
|
1789 |
assert resp.json['err'] == 0 |
|
1790 |
assert len(resp.json['data']) == 10 |
|
1791 |
assert [x['form_number_raw'] for x in resp.json['data']] == [str(x) for x in range(50, 40, -1)] |
|
1792 | ||
1793 | ||
1794 |
def test_user_forms_from_agent(pub, local_user): |
|
1795 |
Role.wipe() |
|
1796 |
role = Role(name='Foo bar') |
|
1797 |
role.store() |
|
1798 | ||
1799 |
agent_user = get_publisher().user_class() |
|
1800 |
agent_user.name = 'Agent' |
|
1801 |
agent_user.email = 'agent@example.com' |
|
1802 |
agent_user.name_identifiers = ['ABCDE'] |
|
1803 |
agent_user.roles = [role.id] |
|
1804 |
agent_user.store() |
|
1805 | ||
1806 |
FormDef.wipe() |
|
1807 |
formdef = FormDef() |
|
1808 |
formdef.name = 'test' |
|
1809 |
formdef.fields = [ |
|
1810 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1811 |
fields.StringField(id='1', label='foobar2'), |
|
1812 |
] |
|
1813 |
formdef.store() |
|
1814 |
formdef.data_class().wipe() |
|
1815 | ||
1816 |
formdata = formdef.data_class()() |
|
1817 |
formdata.data = {'0': 'foo@localhost', '1': 'xxx'} |
|
1818 |
formdata.user_id = local_user.id |
|
1819 |
formdata.just_created() |
|
1820 |
formdata.jump_status('new') |
|
1821 |
formdata.store() |
|
1822 | ||
1823 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user)) |
|
1824 |
assert resp.json['err'] == 0 |
|
1825 |
assert len(resp.json['data']) == 1 |
|
1826 |
assert resp.json['data'][0]['form_name'] == 'test' |
|
1827 |
assert resp.json['data'][0]['form_slug'] == 'test' |
|
1828 |
assert resp.json['data'][0]['form_status'] == 'New' |
|
1829 |
assert resp.json['data'][0]['readable'] is False |
|
1830 | ||
1831 |
formdef.skip_from_360_view = True |
|
1832 |
formdef.store() |
|
1833 | ||
1834 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user)) |
|
1835 |
assert len(resp.json['data']) == 0 |
|
1836 | ||
1837 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
1838 |
formdef.store() |
|
1839 |
formdef.data_class().rebuild_security() |
|
1840 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user)) |
|
1841 |
assert len(resp.json['data']) == 1 |
|
1842 | ||
1843 |
agent_user.roles = [] |
|
1844 |
agent_user.store() |
|
1845 |
get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user), status=403) |
|
1846 | ||
1847 | ||
1848 |
def test_user_drafts(pub, local_user): |
|
1849 |
FormDef.wipe() |
|
1850 |
formdef = FormDef() |
|
1851 |
formdef.name = 'test' |
|
1852 |
formdef.fields = [ |
|
1853 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1854 |
fields.StringField(id='1', label='foobar2'), |
|
1855 |
fields.FileField(id='2', label='foobar3', varname='file'), |
|
1856 |
] |
|
1857 |
formdef.keywords = 'hello, world' |
|
1858 |
formdef.disabled = False |
|
1859 |
formdef.enable_tracking_codes = True |
|
1860 |
formdef.store() |
|
1861 | ||
1862 |
formdef.data_class().wipe() |
|
1863 | ||
1864 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
1865 |
assert resp.json['err'] == 0 |
|
1866 |
assert len(resp.json['data']) == 0 |
|
1867 | ||
1868 |
formdata = formdef.data_class()() |
|
1869 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
1870 |
upload.receive([b'base64me']) |
|
1871 |
formdata.data = {'0': 'foo@localhost', '1': 'xxx', '2': upload} |
|
1872 |
formdata.user_id = local_user.id |
|
1873 |
formdata.page_no = 1 |
|
1874 |
formdata.status = 'draft' |
|
1875 |
formdata.receipt_time = datetime.datetime(2015, 1, 1).timetuple() |
|
1876 |
formdata.store() |
|
1877 | ||
1878 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
1879 |
resp2 = get_app(pub).get(sign_uri('/myspace/drafts', user=local_user)) |
|
1880 |
assert resp.json['err'] == 0 |
|
1881 |
assert len(resp.json['data']) == 1 |
|
1882 |
assert resp.json == resp2.json |
|
1883 |
assert not 'fields' in resp.json['data'][0] |
|
1884 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
1885 | ||
1886 |
resp = get_app(pub).get(sign_uri('/api/user/drafts?full=on', user=local_user)) |
|
1887 |
assert resp.json['err'] == 0 |
|
1888 |
assert 'fields' in resp.json['data'][0] |
|
1889 |
assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost' |
|
1890 |
assert 'url' in resp.json['data'][0]['fields']['file'] |
|
1891 |
assert 'content' not in resp.json['data'][0]['fields']['file'] # no file content in full lists |
|
1892 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
1893 | ||
1894 |
formdef.enable_tracking_codes = False |
|
1895 |
formdef.store() |
|
1896 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
1897 |
assert resp.json['err'] == 0 |
|
1898 |
assert len(resp.json['data']) == 1 |
|
1899 | ||
1900 |
formdef.enable_tracking_codes = True |
|
1901 |
formdef.disabled = True |
|
1902 |
formdef.store() |
|
1903 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
1904 |
assert resp.json['err'] == 0 |
|
1905 |
assert len(resp.json['data']) == 0 |
|
1906 | ||
1907 |
resp = get_app(pub).get(sign_uri('/api/user/drafts?NameID=xxx')) |
|
1908 |
assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []} |
|
1909 |
resp2 = get_app(pub).get(sign_uri('/api/user/drafts?&NameID=xxx')) |
|
1910 |
assert resp.json == resp2.json |
|
1911 | ||
1912 | ||
1913 |
def test_api_list_formdata(pub, local_user): |
|
1914 |
Role.wipe() |
|
1915 |
role = Role(name='test') |
|
1916 |
role.store() |
|
1917 | ||
1918 |
FormDef.wipe() |
|
1919 |
formdef = FormDef() |
|
1920 |
formdef.name = 'test' |
|
1921 |
formdef.workflow_roles = {'_receiver': role.id} |
|
1922 |
formdef.fields = [ |
|
1923 |
fields.StringField(id='0', label='foobar', varname='foobar', type='string'), |
|
1924 |
fields.ItemField( |
|
1925 |
id='1', label='foobar3', varname='foobar3', type='item', items=['foo', 'bar', 'baz'] |
|
1926 |
), |
|
1927 |
fields.FileField(id='2', label='foobar4', varname='file', type='file'), |
|
1928 |
] |
|
1929 |
formdef.store() |
|
1930 | ||
1931 |
data_class = formdef.data_class() |
|
1932 |
data_class.wipe() |
|
1933 | ||
1934 |
for i in range(30): |
|
1935 |
formdata = data_class() |
|
1936 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
1937 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
1938 |
upload.receive([b'base64me']) |
|
1939 |
formdata.data = {'0': 'FOO BAR %d' % i, '2': upload} |
|
1940 |
formdata.user_id = local_user.id |
|
1941 |
if i % 4 == 0: |
|
1942 |
formdata.data['1'] = 'foo' |
|
1943 |
formdata.data['1_display'] = 'foo' |
|
1944 |
elif i % 4 == 1: |
|
1945 |
formdata.data['1'] = 'bar' |
|
1946 |
formdata.data['1_display'] = 'bar' |
|
1947 |
else: |
|
1948 |
formdata.data['1'] = 'baz' |
|
1949 |
formdata.data['1_display'] = 'baz' |
|
1950 | ||
1951 |
formdata.just_created() |
|
1952 |
if i % 3 == 0: |
|
1953 |
formdata.jump_status('new') |
|
1954 |
elif i % 3 == 1: |
|
1955 |
formdata.jump_status('just_submitted') |
|
1956 |
else: |
|
1957 |
formdata.jump_status('finished') |
|
1958 |
if i % 7 == 0: |
|
1959 |
formdata.backoffice_submission = True |
|
1960 |
formdata.submission_channel = 'mail' |
|
1961 |
formdata.evolution[-1].time = ( |
|
1962 |
datetime.datetime(2020, 1, 2, 3, 4) + datetime.timedelta(hours=i) |
|
1963 |
).timetuple() |
|
1964 |
formdata.store() |
|
1965 | ||
1966 |
# check access is denied if the user has not the appropriate role |
|
1967 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user), status=403) |
|
1968 | ||
1969 |
# add proper role to user |
|
1970 |
local_user.roles = [role.id] |
|
1971 |
local_user.store() |
|
1972 | ||
1973 |
# check it now gets the data |
|
1974 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user)) |
|
1975 |
assert len(resp.json) == 30 |
|
1976 |
assert datetime.datetime.strptime(resp.json[0]['receipt_time'], '%Y-%m-%dT%H:%M:%S') |
|
1977 |
assert not 'fields' in resp.json[0] |
|
1978 | ||
1979 |
# check getting full formdata |
|
1980 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on', user=local_user)) |
|
1981 |
assert len(resp.json) == 30 |
|
1982 |
assert 'receipt_time' in resp.json[0] |
|
1983 |
assert 'fields' in resp.json[0] |
|
1984 |
assert 'url' in resp.json[0]['fields']['file'] |
|
1985 |
assert 'content' not in resp.json[0]['fields']['file'] # no file content in full lists |
|
1986 |
assert 'user' in resp.json[0] |
|
1987 |
assert 'evolution' in resp.json[0] |
|
1988 |
assert len(resp.json[0]['evolution']) == 2 |
|
1989 |
assert 'status' in resp.json[0]['evolution'][0] |
|
1990 |
assert 'who' in resp.json[0]['evolution'][0] |
|
1991 |
assert 'time' in resp.json[0]['evolution'][0] |
|
1992 |
assert resp.json[0]['evolution'][0]['who']['id'] == local_user.id |
|
1993 | ||
1994 |
assert all('status' in x['workflow'] for x in resp.json) |
|
1995 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 0'][0]['submission'][ |
|
1996 |
'backoffice' |
|
1997 |
] is True |
|
1998 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 0'][0]['submission'][ |
|
1999 |
'channel' |
|
2000 |
] == 'mail' |
|
2001 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 1'][0]['submission'][ |
|
2002 |
'backoffice' |
|
2003 |
] is False |
|
2004 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 1'][0]['submission']['channel'] == 'web' |
|
2005 | ||
2006 |
# check filtered results |
|
2007 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=foo', user=local_user)) |
|
2008 |
assert len(resp.json) == 8 |
|
2009 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=bar', user=local_user)) |
|
2010 |
assert len(resp.json) == 8 |
|
2011 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=baz', user=local_user)) |
|
2012 |
assert len(resp.json) == 14 |
|
2013 | ||
2014 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar=FOO BAR 3', user=local_user)) |
|
2015 |
assert len(resp.json) == 1 |
|
2016 | ||
2017 |
# check filter on status |
|
2018 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter=pending', user=local_user)) |
|
2019 |
assert len(resp.json) == 20 |
|
2020 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter=done', user=local_user)) |
|
2021 |
assert len(resp.json) == 10 |
|
2022 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter=all', user=local_user)) |
|
2023 |
assert len(resp.json) == 30 |
|
2024 | ||
2025 |
# check filter on last update time |
|
2026 |
resp = get_app(pub).get( |
|
2027 |
sign_uri( |
|
2028 |
'/api/forms/test/list?filter-start-mtime=on&filter-start-mtime-value=2020-01-03', user=local_user |
|
2029 |
) |
|
2030 |
) |
|
2031 |
assert len(resp.json) == 16 |
|
2032 |
resp = get_app(pub).get( |
|
2033 |
sign_uri( |
|
2034 |
'/api/forms/test/list?filter-start-mtime=on&filter-start-mtime-value=2020-01-03 10:00', |
|
2035 |
user=local_user, |
|
2036 |
) |
|
2037 |
) |
|
2038 |
assert len(resp.json) == 10 |
|
2039 |
resp = get_app(pub).get( |
|
2040 |
sign_uri( |
|
2041 |
'/api/forms/test/list?filter-end-mtime=on&filter-end-mtime-value=2020-01-03', user=local_user |
|
2042 |
) |
|
2043 |
) |
|
2044 |
assert len(resp.json) == 14 |
|
2045 |
resp = get_app(pub).get( |
|
2046 |
sign_uri( |
|
2047 |
'/api/forms/test/list?filter-end-mtime=on&filter-end-mtime-value=2020-01-03 10:00', |
|
2048 |
user=local_user, |
|
2049 |
) |
|
2050 |
) |
|
2051 |
assert len(resp.json) == 20 |
|
2052 | ||
2053 |
# check limit and offset |
|
2054 |
resp_all = get_app(pub).get(sign_uri('/api/forms/test/list?filter=all', user=local_user)) |
|
2055 |
assert len(resp_all.json) == 30 |
|
2056 |
partial_resps = [] |
|
2057 |
for i in range(0, 48, 12): |
|
2058 |
partial_resps.append( |
|
2059 |
get_app(pub).get( |
|
2060 |
sign_uri('/api/forms/test/list?filter=all&offset=%s&limit=12' % i, user=local_user) |
|
2061 |
) |
|
2062 |
) |
|
2063 |
assert len(partial_resps[0].json) == 12 |
|
2064 |
assert len(partial_resps[1].json) == 12 |
|
2065 |
assert len(partial_resps[2].json) == 6 |
|
2066 |
assert len(partial_resps[3].json) == 0 |
|
2067 |
resp_all_ids = [x.get('id') for x in resp_all.json] |
|
2068 |
resp_partial_ids = [] |
|
2069 |
for resp in partial_resps: |
|
2070 |
resp_partial_ids.extend([x.get('id') for x in resp.json]) |
|
2071 |
assert resp_all_ids == resp_partial_ids |
|
2072 | ||
2073 |
# check error handling |
|
2074 |
get_app(pub).get(sign_uri('/api/forms/test/list?filter=all&offset=plop', user=local_user), status=400) |
|
2075 |
get_app(pub).get(sign_uri('/api/forms/test/list?filter=all&limit=plop', user=local_user), status=400) |
|
2076 | ||
2077 | ||
2078 |
def test_api_anonymized_formdata(pub, local_user, admin_user): |
|
2079 |
Role.wipe() |
|
2080 |
role = Role(name='test') |
|
2081 |
role.store() |
|
2082 | ||
2083 |
FormDef.wipe() |
|
2084 |
formdef = FormDef() |
|
2085 |
formdef.name = 'test' |
|
2086 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2087 |
formdef.fields = [ |
|
2088 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2089 |
fields.ItemField( |
|
2090 |
id='1', label='foobar3', varname='foobar3', type='item', items=['foo', 'bar', 'baz'] |
|
2091 |
), |
|
2092 |
fields.FileField(id='2', label='foobar4', varname='file'), |
|
2093 |
] |
|
2094 |
formdef.store() |
|
2095 | ||
2096 |
data_class = formdef.data_class() |
|
2097 |
data_class.wipe() |
|
2098 | ||
2099 |
for i in range(30): |
|
2100 |
formdata = data_class() |
|
2101 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
2102 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
2103 |
upload.receive([b'base64me']) |
|
2104 |
formdata.data = {'0': 'FOO BAR %d' % i, '2': upload} |
|
2105 |
formdata.user_id = local_user.id |
|
2106 |
if i % 4 == 0: |
|
2107 |
formdata.data['1'] = 'foo' |
|
2108 |
formdata.data['1_display'] = 'foo' |
|
2109 |
elif i % 4 == 1: |
|
2110 |
formdata.data['1'] = 'bar' |
|
2111 |
formdata.data['1_display'] = 'bar' |
|
2112 |
else: |
|
2113 |
formdata.data['1'] = 'baz' |
|
2114 |
formdata.data['1_display'] = 'baz' |
|
2115 | ||
2116 |
formdata.just_created() |
|
2117 |
if i % 3 == 0: |
|
2118 |
formdata.jump_status('new') |
|
2119 |
else: |
|
2120 |
evo = Evolution() |
|
2121 |
evo.who = admin_user.id |
|
2122 |
evo.time = time.localtime() |
|
2123 |
evo.status = 'wf-%s' % 'finished' |
|
2124 |
formdata.evolution.append(evo) |
|
2125 |
formdata.status = evo.status |
|
2126 |
formdata.store() |
|
2127 | ||
2128 |
# check access is granted even if the user has not the appropriate role |
|
2129 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?anonymise&full=on', user=local_user)) |
|
2130 |
assert len(resp.json) == 30 |
|
2131 |
assert 'receipt_time' in resp.json[0] |
|
2132 |
assert 'fields' in resp.json[0] |
|
2133 |
assert 'user' not in resp.json[0] |
|
2134 |
assert 'file' not in resp.json[0]['fields'] # no file export in full lists |
|
2135 |
assert 'foobar3' in resp.json[0]['fields'] |
|
2136 |
assert 'foobar' not in resp.json[0]['fields'] |
|
2137 |
assert 'evolution' in resp.json[0] |
|
2138 |
assert len(resp.json[0]['evolution']) == 2 |
|
2139 |
assert 'status' in resp.json[0]['evolution'][0] |
|
2140 |
assert not 'who' in resp.json[0]['evolution'][0] |
|
2141 |
assert 'time' in resp.json[0]['evolution'][0] |
|
2142 |
# check evolution made by other than _submitter are exported |
|
2143 |
assert 'who' in resp.json[1]['evolution'][1] |
|
2144 |
assert 'id' in resp.json[1]['evolution'][1]['who'] |
|
2145 |
assert 'email' in resp.json[1]['evolution'][1]['who'] |
|
2146 |
assert 'NameID' in resp.json[1]['evolution'][1]['who'] |
|
2147 |
assert 'name' in resp.json[1]['evolution'][1]['who'] |
|
2148 | ||
2149 |
# check access is granted event if there is no user |
|
2150 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?anonymise&full=on')) |
|
2151 |
assert len(resp.json) == 30 |
|
2152 |
assert 'receipt_time' in resp.json[0] |
|
2153 |
assert 'fields' in resp.json[0] |
|
2154 |
assert 'user' not in resp.json[0] |
|
2155 |
assert 'file' not in resp.json[0]['fields'] # no file export in full lists |
|
2156 |
assert 'foobar3' in resp.json[0]['fields'] |
|
2157 |
assert 'foobar' not in resp.json[0]['fields'] |
|
2158 |
assert 'evolution' in resp.json[0] |
|
2159 |
assert len(resp.json[0]['evolution']) == 2 |
|
2160 |
assert 'status' in resp.json[0]['evolution'][0] |
|
2161 |
assert not 'who' in resp.json[0]['evolution'][0] |
|
2162 |
assert 'time' in resp.json[0]['evolution'][0] |
|
2163 |
# check anonymise is enforced on detail view |
|
2164 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/?anonymise&full=on' % resp.json[1]['id'])) |
|
2165 |
assert 'receipt_time' in resp.json |
|
2166 |
assert 'fields' in resp.json |
|
2167 |
assert 'user' not in resp.json |
|
2168 |
assert 'file' not in resp.json['fields'] # no file export in detail |
|
2169 |
assert 'foobar3' in resp.json['fields'] |
|
2170 |
assert 'foobar' not in resp.json['fields'] |
|
2171 |
assert 'evolution' in resp.json |
|
2172 |
assert len(resp.json['evolution']) == 2 |
|
2173 |
assert 'status' in resp.json['evolution'][0] |
|
2174 |
assert not 'who' in resp.json['evolution'][0] |
|
2175 |
assert 'time' in resp.json['evolution'][0] |
|
2176 |
# check evolution made by other than _submitter are exported |
|
2177 |
assert 'who' in resp.json['evolution'][1] |
|
2178 |
assert 'id' in resp.json['evolution'][1]['who'] |
|
2179 |
assert 'email' in resp.json['evolution'][1]['who'] |
|
2180 |
assert 'NameID' in resp.json['evolution'][1]['who'] |
|
2181 |
assert 'name' in resp.json['evolution'][1]['who'] |
|
2182 | ||
2183 | ||
2184 |
def test_api_geojson_formdata(pub, local_user): |
|
2185 |
Role.wipe() |
|
2186 |
role = Role(name='test') |
|
2187 |
role.store() |
|
2188 | ||
2189 |
FormDef.wipe() |
|
2190 |
formdef = FormDef() |
|
2191 |
formdef.name = 'test' |
|
2192 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2193 |
formdef.fields = [ |
|
2194 |
fields.StringField(id='0', label='foobar', varname='foobar', type='string'), |
|
2195 |
fields.FileField(id='1', label='foobar1', type='file'), |
|
2196 |
] |
|
2197 |
formdef.store() |
|
2198 | ||
2199 |
data_class = formdef.data_class() |
|
2200 |
data_class.wipe() |
|
2201 | ||
2202 |
formdef.geolocations = {'base': 'Location'} |
|
2203 |
formdef.store() |
|
2204 | ||
2205 |
# check access is denied if the user has not the appropriate role |
|
2206 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user), status=403) |
|
2207 |
# even if there's an anonymse parameter |
|
2208 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?anonymise', user=local_user), status=403) |
|
2209 | ||
2210 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
2211 |
upload.receive([b'base64me']) |
|
2212 | ||
2213 |
foobar = '<font color="red">FOO BAR</font>' |
|
2214 |
username = '<font color="red">Jean Darmette</font>' |
|
2215 | ||
2216 |
data = {'0': foobar, '1': upload} |
|
2217 |
local_user.name = username |
|
2218 |
local_user.store() |
|
2219 |
for i in range(30): |
|
2220 |
formdata = data_class() |
|
2221 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
2222 |
formdata.geolocations = {'base': {'lat': 48, 'lon': 2}} |
|
2223 |
formdata.data = data |
|
2224 |
formdata.user_id = local_user.id |
|
2225 |
formdata.just_created() |
|
2226 |
if i % 3 == 0: |
|
2227 |
formdata.jump_status('new') |
|
2228 |
else: |
|
2229 |
formdata.jump_status('finished') |
|
2230 |
formdata.store() |
|
2231 | ||
2232 |
# add proper role to user |
|
2233 |
local_user.roles = [role.id] |
|
2234 |
local_user.store() |
|
2235 | ||
2236 |
# check it gets the data |
|
2237 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user)) |
|
2238 |
assert 'features' in resp.json |
|
2239 |
assert len(resp.json['features']) == 10 |
|
2240 |
display_fields = resp.json['features'][0]['properties']['display_fields'] |
|
2241 |
assert len(display_fields) == 5 |
|
2242 |
for field in display_fields: |
|
2243 |
if field['label'] == 'Number': |
|
2244 |
assert field['varname'] == 'id' |
|
2245 |
assert field['html_value'] == '1-28' |
|
2246 |
assert field['value'] == '1-28' |
|
2247 |
if field['label'] == 'User Label': |
|
2248 |
assert field['varname'] == 'user_label' |
|
2249 |
assert field['value'] == username |
|
2250 |
assert field['html_value'] == "<font color="red">Jean Darmette</font>" |
|
2251 |
if field['label'] == 'foobar': |
|
2252 |
assert field['varname'] == 'foobar' |
|
2253 |
assert field['value'] == foobar |
|
2254 |
assert field['html_value'] == "<font color="red">FOO BAR</font>" |
|
2255 |
if field['label'] == 'foobar1': |
|
2256 |
assert field['varname'] is None |
|
2257 |
assert field['value'] == "test.txt" |
|
2258 |
assert ( |
|
2259 |
field['html_value'] |
|
2260 |
== '<div class="file-field"><a download="test.txt" href="http://example.net/backoffice/management/test/28/download?f=1"><span>test.txt</span></a></div>' |
|
2261 |
) |
|
2262 |
field_varnames = [f['varname'] for f in display_fields] |
|
2263 |
assert 'foobar' not in field_varnames |
|
2264 | ||
2265 |
# check full=on |
|
2266 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?full=on', user=local_user)) |
|
2267 |
assert len(resp.json['features']) == 10 |
|
2268 |
display_fields = resp.json['features'][0]['properties']['display_fields'] |
|
2269 |
assert len(display_fields) == 8 |
|
2270 |
field_varnames = [f['varname'] for f in display_fields] |
|
2271 |
assert 'foobar' in field_varnames |
|
2272 | ||
2273 |
# check with a filter |
|
2274 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?filter=done', user=local_user)) |
|
2275 |
assert 'features' in resp.json |
|
2276 |
assert len(resp.json['features']) == 20 |
|
2277 | ||
2278 |
# check with http basic auth |
|
2279 |
app = get_app(pub) |
|
2280 |
app.authorization = ('Basic', ('user', 'password')) |
|
2281 |
resp = app.get('/api/forms/test/geojson?email=%s' % local_user.email, status=401) |
|
2282 | ||
2283 |
# add authentication info |
|
2284 |
pub.load_site_options() |
|
2285 |
pub.site_options.add_section('api-http-auth-geojson') |
|
2286 |
pub.site_options.set('api-http-auth-geojson', 'user', 'password') |
|
2287 |
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')) |
|
2288 | ||
2289 |
resp = app.get('/api/forms/test/geojson?email=%s' % local_user.email) |
|
2290 |
assert 'features' in resp.json |
|
2291 |
assert len(resp.json['features']) == 10 |
|
2292 | ||
2293 |
# check 404 if the formdef doesn't have geolocation support |
|
2294 |
formdef.geolocations = {} |
|
2295 |
formdef.store() |
|
2296 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user), status=404) |
|
2297 | ||
2298 | ||
2299 |
def test_api_ods_formdata(pub, local_user): |
|
2300 |
Role.wipe() |
|
2301 |
role = Role(name='test') |
|
2302 |
role.store() |
|
2303 | ||
2304 |
FormDef.wipe() |
|
2305 |
formdef = FormDef() |
|
2306 |
formdef.name = 'test' |
|
2307 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2308 |
formdef.fields = [ |
|
2309 |
fields.StringField(id='0', label='foobar', varname='foobar', type='string'), |
|
2310 |
] |
|
2311 |
formdef.store() |
|
2312 | ||
2313 |
data_class = formdef.data_class() |
|
2314 |
data_class.wipe() |
|
2315 | ||
2316 |
# check access is denied if the user has not the appropriate role |
|
2317 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user), status=403) |
|
2318 |
# even if there's an anonymise parameter |
|
2319 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods?anonymise', user=local_user), status=403) |
|
2320 | ||
2321 |
data = {'0': 'foobar'} |
|
2322 |
for i in range(30): |
|
2323 |
formdata = data_class() |
|
2324 |
formdata.data = data |
|
2325 |
formdata.user_id = local_user.id |
|
2326 |
formdata.just_created() |
|
2327 |
if i % 3 == 0: |
|
2328 |
formdata.jump_status('new') |
|
2329 |
else: |
|
2330 |
formdata.jump_status('finished') |
|
2331 |
formdata.store() |
|
2332 | ||
2333 |
# add proper role to user |
|
2334 |
local_user.roles = [role.id] |
|
2335 |
local_user.store() |
|
2336 | ||
2337 |
# check it gets the data |
|
2338 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user)) |
|
2339 |
assert resp.content_type == 'application/vnd.oasis.opendocument.spreadsheet' |
|
2340 | ||
2341 |
# check it still gives a ods file when there is more data |
|
2342 |
for i in range(300): |
|
2343 |
formdata = data_class() |
|
2344 |
formdata.data = data |
|
2345 |
formdata.user_id = local_user.id |
|
2346 |
formdata.just_created() |
|
2347 |
formdata.jump_status('new') |
|
2348 |
formdata.store() |
|
2349 | ||
2350 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user)) |
|
2351 |
assert resp.content_type == 'application/vnd.oasis.opendocument.spreadsheet' |
|
2352 |
zipf = zipfile.ZipFile(BytesIO(resp.body)) |
|
2353 |
ods_sheet = ET.parse(zipf.open('content.xml')) |
|
2354 |
assert len(ods_sheet.findall('.//{%s}table-row' % ods.NS['table'])) == 311 |
|
2355 | ||
2356 | ||
2357 |
def test_api_custom_view_access(pub, local_user): |
|
2358 |
Role.wipe() |
|
2359 |
role = Role(name='test') |
|
2360 |
role.store() |
|
2361 |
local_user.roles = [role.id] |
|
2362 |
local_user.store() |
|
2363 | ||
2364 |
FormDef.wipe() |
|
2365 |
formdef = FormDef() |
|
2366 |
formdef.name = 'test' |
|
2367 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2368 |
formdef.fields = [fields.StringField(id='0', label='foobar', varname='foobar')] |
|
2369 |
formdef.geolocations = {'base': 'Location'} |
|
2370 |
formdef.store() |
|
2371 | ||
2372 |
carddef = CardDef() |
|
2373 |
carddef.name = 'test' |
|
2374 |
carddef.fields = [fields.StringField(id='0', label='foobar', varname='foo')] |
|
2375 |
carddef.workflow_roles = {'_viewer': role.id} |
|
2376 |
carddef.digest_template = 'bla {{ form_var_foo }} xxx' |
|
2377 |
carddef.geolocations = {'base': 'Location'} |
|
2378 |
carddef.store() |
|
2379 | ||
2380 |
pub.custom_view_class.wipe() |
|
2381 |
custom_view = pub.custom_view_class() |
|
2382 |
custom_view.title = 'shared formdef custom view' |
|
2383 |
custom_view.formdef = formdef |
|
2384 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2385 |
custom_view.filters = {} |
|
2386 |
custom_view.visibility = 'any' |
|
2387 |
custom_view.store() |
|
2388 | ||
2389 |
custom_view = pub.custom_view_class() |
|
2390 |
custom_view.title = 'private formdef custom view' |
|
2391 |
custom_view.formdef = formdef |
|
2392 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2393 |
custom_view.filters = {} |
|
2394 |
custom_view.visibility = 'owner' |
|
2395 |
custom_view.user = local_user |
|
2396 |
custom_view.store() |
|
2397 | ||
2398 |
custom_view = pub.custom_view_class() |
|
2399 |
custom_view.title = 'shared carddef custom view' |
|
2400 |
custom_view.formdef = carddef |
|
2401 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2402 |
custom_view.filters = {} |
|
2403 |
custom_view.visibility = 'any' |
|
2404 |
custom_view.store() |
|
2405 | ||
2406 |
custom_view = pub.custom_view_class() |
|
2407 |
custom_view.title = 'private carddef custom view' |
|
2408 |
custom_view.formdef = carddef |
|
2409 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2410 |
custom_view.filters = {} |
|
2411 |
custom_view.visibility = 'owner' |
|
2412 |
custom_view.user = local_user |
|
2413 |
custom_view.store() |
|
2414 | ||
2415 |
custom_view = pub.custom_view_class() |
|
2416 |
custom_view.title = 'datasource carddef custom view' |
|
2417 |
custom_view.formdef = carddef |
|
2418 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2419 |
custom_view.filters = {} |
|
2420 |
custom_view.visibility = 'datasource' |
|
2421 |
custom_view.store() |
|
2422 | ||
2423 |
get_app(pub).get(sign_uri('/api/forms/test/list/shared-formdef-custom-view', user=local_user), status=200) |
|
2424 |
get_app(pub).get(sign_uri('/api/forms/test/ods/shared-formdef-custom-view', user=local_user), status=200) |
|
2425 |
get_app(pub).get( |
|
2426 |
sign_uri('/api/forms/test/geojson/shared-formdef-custom-view', user=local_user), status=200 |
|
2427 |
) |
|
2428 |
get_app(pub).get( |
|
2429 |
sign_uri('/api/forms/test/list/private-formdef-custom-view', user=local_user), status=404 |
|
2430 |
) |
|
2431 |
get_app(pub).get(sign_uri('/api/forms/test/ods/private-formdef-custom-view', user=local_user), status=404) |
|
2432 |
get_app(pub).get( |
|
2433 |
sign_uri('/api/forms/test/geojson/private-formdef-custom-view', user=local_user), status=404 |
|
2434 |
) |
|
2435 | ||
2436 |
get_app(pub).get(sign_uri('/api/cards/test/list/shared-carddef-custom-view', user=local_user), status=200) |
|
2437 |
get_app(pub).get(sign_uri('/api/cards/test/ods/shared-carddef-custom-view', user=local_user), status=200) |
|
2438 |
get_app(pub).get( |
|
2439 |
sign_uri('/api/cards/test/geojson/shared-carddef-custom-view', user=local_user), status=200 |
|
2440 |
) |
|
2441 |
get_app(pub).get( |
|
2442 |
sign_uri('/api/cards/test/list/private-carddef-custom-view', user=local_user), status=404 |
|
2443 |
) |
|
2444 |
get_app(pub).get(sign_uri('/api/cards/test/ods/private-carddef-custom-view', user=local_user), status=404) |
|
2445 |
get_app(pub).get( |
|
2446 |
sign_uri('/api/cards/test/geojson/private-carddef-custom-view', user=local_user), status=404 |
|
2447 |
) |
|
2448 |
get_app(pub).get( |
|
2449 |
sign_uri('/api/cards/test/list/datasource-carddef-custom-view', user=local_user), status=200 |
|
2450 |
) |
|
2451 |
get_app(pub).get( |
|
2452 |
sign_uri('/api/cards/test/ods/datasource-carddef-custom-view', user=local_user), status=200 |
|
2453 |
) |
|
2454 |
get_app(pub).get( |
|
2455 |
sign_uri('/api/cards/test/geojson/datasource-carddef-custom-view', user=local_user), status=200 |
|
2456 |
) |
|
2457 | ||
2458 | ||
2459 |
def test_api_list_formdata_custom_view(pub, local_user): |
|
2460 |
Role.wipe() |
|
2461 |
role = Role(name='test') |
|
2462 |
role.store() |
|
2463 | ||
2464 |
FormDef.wipe() |
|
2465 |
formdef = FormDef() |
|
2466 |
formdef.name = 'test' |
|
2467 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2468 |
formdef.fields = [ |
|
2469 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2470 |
] |
|
2471 |
formdef.store() |
|
2472 | ||
2473 |
data_class = formdef.data_class() |
|
2474 |
data_class.wipe() |
|
2475 | ||
2476 |
for i in range(30): |
|
2477 |
formdata = data_class() |
|
2478 |
formdata.data = {'0': 'FOO BAR %d' % i} |
|
2479 |
formdata.user_id = local_user.id |
|
2480 |
formdata.just_created() |
|
2481 |
if i % 3 == 0: |
|
2482 |
formdata.jump_status('new') |
|
2483 |
else: |
|
2484 |
formdata.jump_status('finished') |
|
2485 |
formdata.store() |
|
2486 | ||
2487 |
# add proper role to user |
|
2488 |
local_user.roles = [role.id] |
|
2489 |
local_user.store() |
|
2490 | ||
2491 |
# check it now gets the data |
|
2492 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user)) |
|
2493 |
assert len(resp.json) == 30 |
|
2494 | ||
2495 |
pub.custom_view_class.wipe() |
|
2496 |
custom_view = pub.custom_view_class() |
|
2497 |
custom_view.title = 'custom view' |
|
2498 |
custom_view.formdef = formdef |
|
2499 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2500 |
custom_view.filters = {"filter": "done", "filter-status": "on"} |
|
2501 |
custom_view.visibility = 'any' |
|
2502 |
custom_view.store() |
|
2503 | ||
2504 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list/custom-view', user=local_user)) |
|
2505 |
assert len(resp.json['data']) == 20 |
|
2506 | ||
2507 | ||
2508 |
def test_api_ods_formdata_custom_view(pub, local_user): |
|
2509 |
Role.wipe() |
|
2510 |
role = Role(name='test') |
|
2511 |
role.store() |
|
2512 | ||
2513 |
FormDef.wipe() |
|
2514 |
formdef = FormDef() |
|
2515 |
formdef.name = 'test' |
|
2516 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2517 |
formdef.fields = [ |
|
2518 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2519 |
] |
|
2520 |
formdef.store() |
|
2521 | ||
2522 |
data_class = formdef.data_class() |
|
2523 |
data_class.wipe() |
|
2524 | ||
2525 |
for i in range(30): |
|
2526 |
formdata = data_class() |
|
2527 |
formdata.data = {'0': 'FOO BAR %d' % i} |
|
2528 |
formdata.user_id = local_user.id |
|
2529 |
formdata.just_created() |
|
2530 |
if i % 3 == 0: |
|
2531 |
formdata.jump_status('new') |
|
2532 |
else: |
|
2533 |
formdata.jump_status('finished') |
|
2534 |
formdata.store() |
|
2535 | ||
2536 |
# add proper role to user |
|
2537 |
local_user.roles = [role.id] |
|
2538 |
local_user.store() |
|
2539 | ||
2540 |
# check it now gets the data |
|
2541 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user)) |
|
2542 |
zipf = zipfile.ZipFile(BytesIO(resp.body)) |
|
2543 |
ods_sheet = ET.parse(zipf.open('content.xml')) |
|
2544 |
assert len(ods_sheet.findall('.//{%s}table-row' % ods.NS['table'])) == 11 |
|
2545 | ||
2546 |
pub.custom_view_class.wipe() |
|
2547 |
custom_view = pub.custom_view_class() |
|
2548 |
custom_view.title = 'custom view' |
|
2549 |
custom_view.formdef = formdef |
|
2550 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2551 |
custom_view.filters = {"filter": "done", "filter-status": "on"} |
|
2552 |
custom_view.visibility = 'any' |
|
2553 |
custom_view.store() |
|
2554 | ||
2555 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods/custom-view', user=local_user)) |
|
2556 |
zipf = zipfile.ZipFile(BytesIO(resp.body)) |
|
2557 |
ods_sheet = ET.parse(zipf.open('content.xml')) |
|
2558 |
assert len(ods_sheet.findall('.//{%s}table-row' % ods.NS['table'])) == 21 |
|
2559 | ||
2560 | ||
2561 |
def test_api_geojson_formdata_custom_view(pub, local_user): |
|
2562 |
Role.wipe() |
|
2563 |
role = Role(name='test') |
|
2564 |
role.store() |
|
2565 | ||
2566 |
FormDef.wipe() |
|
2567 |
formdef = FormDef() |
|
2568 |
formdef.name = 'test' |
|
2569 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2570 |
formdef.fields = [ |
|
2571 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2572 |
] |
|
2573 |
formdef.geolocations = {'base': 'Location'} |
|
2574 |
formdef.store() |
|
2575 | ||
2576 |
data_class = formdef.data_class() |
|
2577 |
data_class.wipe() |
|
2578 | ||
2579 |
for i in range(30): |
|
2580 |
formdata = data_class() |
|
2581 |
formdata.data = {'0': 'FOO BAR %d' % i} |
|
2582 |
formdata.geolocations = {'base': {'lat': 48, 'lon': 2}} |
|
2583 |
formdata.user_id = local_user.id |
|
2584 |
formdata.just_created() |
|
2585 |
if i % 3 == 0: |
|
2586 |
formdata.jump_status('new') |
|
2587 |
else: |
|
2588 |
formdata.jump_status('finished') |
|
2589 |
formdata.store() |
|
2590 | ||
2591 |
# add proper role to user |
|
2592 |
local_user.roles = [role.id] |
|
2593 |
local_user.store() |
|
2594 | ||
2595 |
# check it now gets the data |
|
2596 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user)) |
|
2597 |
assert len(resp.json['features']) == 10 |
|
2598 | ||
2599 |
pub.custom_view_class.wipe() |
|
2600 |
custom_view = pub.custom_view_class() |
|
2601 |
custom_view.title = 'custom view' |
|
2602 |
custom_view.formdef = formdef |
|
2603 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2604 |
custom_view.filters = {"filter": "done", "filter-status": "on"} |
|
2605 |
custom_view.visibility = 'any' |
|
2606 |
custom_view.store() |
|
2607 | ||
2608 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson/custom-view', user=local_user)) |
|
2609 |
assert len(resp.json['features']) == 20 |
|
2610 | ||
2611 | ||
2612 |
def test_api_global_geojson(pub, local_user): |
|
2613 |
Role.wipe() |
|
2614 |
role = Role(name='test') |
|
2615 |
role.store() |
|
2616 | ||
2617 |
FormDef.wipe() |
|
2618 |
formdef = FormDef() |
|
2619 |
formdef.name = 'test' |
|
2620 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2621 |
formdef.fields = [] |
|
2622 |
formdef.store() |
|
2623 | ||
2624 |
data_class = formdef.data_class() |
|
2625 |
data_class.wipe() |
|
2626 | ||
2627 |
formdef.geolocations = {'base': 'Location'} |
|
2628 |
formdef.store() |
|
2629 | ||
2630 |
for i in range(30): |
|
2631 |
formdata = data_class() |
|
2632 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
2633 |
formdata.geolocations = {'base': {'lat': 48, 'lon': 2}} |
|
2634 |
formdata.user_id = local_user.id |
|
2635 |
formdata.just_created() |
|
2636 |
if i % 3 == 0: |
|
2637 |
formdata.jump_status('new') |
|
2638 |
else: |
|
2639 |
formdata.jump_status('finished') |
|
2640 |
formdata.store() |
|
2641 | ||
2642 |
if not pub.is_using_postgresql(): |
|
2643 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user), status=404) |
|
2644 |
pytest.skip('this requires SQL') |
|
2645 |
return |
|
2646 | ||
2647 |
# check empty content if user doesn't have the appropriate role |
|
2648 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user)) |
|
2649 |
assert 'features' in resp.json |
|
2650 |
assert len(resp.json['features']) == 0 |
|
2651 | ||
2652 |
# add proper role to user |
|
2653 |
local_user.roles = [role.id] |
|
2654 |
local_user.store() |
|
2655 | ||
2656 |
# check it gets the data |
|
2657 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user)) |
|
2658 |
assert 'features' in resp.json |
|
2659 |
assert len(resp.json['features']) == 10 |
|
2660 | ||
2661 |
# check with a filter |
|
2662 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson?status=done', user=local_user)) |
|
2663 |
assert 'features' in resp.json |
|
2664 |
assert len(resp.json['features']) == 20 |
|
2665 | ||
2666 | ||
2667 |
def test_api_global_listing(pub, local_user): |
|
2668 |
if not pub.is_using_postgresql(): |
|
2669 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user), status=404) |
|
2670 |
pytest.skip('this requires SQL') |
|
2671 |
return |
|
2672 | ||
2673 |
Role.wipe() |
|
2674 |
role = Role(name='test') |
|
2675 |
role.store() |
|
2676 | ||
2677 |
# check there's no crash if there are no formdefs |
|
2678 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
2679 |
assert len(resp.json['data']) == 0 |
|
2680 | ||
2681 |
FormDef.wipe() |
|
2682 |
formdef = FormDef() |
|
2683 |
formdef.name = 'test' |
|
2684 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2685 |
formdef.fields = [ |
|
2686 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2687 |
] |
|
2688 |
formdef.store() |
|
2689 | ||
2690 |
data_class = formdef.data_class() |
|
2691 |
data_class.wipe() |
|
2692 | ||
2693 |
formdef.store() |
|
2694 | ||
2695 |
for i in range(30): |
|
2696 |
formdata = data_class() |
|
2697 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
2698 |
formdata.data = {'0': 'FOO BAR'} |
|
2699 |
formdata.user_id = local_user.id |
|
2700 |
formdata.just_created() |
|
2701 |
if i % 3 == 0: |
|
2702 |
formdata.jump_status('new') |
|
2703 |
else: |
|
2704 |
formdata.jump_status('finished') |
|
2705 |
formdata.store() |
|
2706 | ||
2707 |
# check empty content if user doesn't have the appropriate role |
|
2708 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
2709 |
assert len(resp.json['data']) == 0 |
|
2710 | ||
2711 |
# add proper role to user |
|
2712 |
local_user.roles = [role.id] |
|
2713 |
local_user.store() |
|
2714 | ||
2715 |
# check it gets the data |
|
2716 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
2717 |
assert len(resp.json['data']) == 10 |
|
2718 | ||
2719 |
# check with a filter |
|
2720 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done', user=local_user)) |
|
2721 |
assert len(resp.json['data']) == 20 |
|
2722 | ||
2723 |
# check limit/offset |
|
2724 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done&limit=5', user=local_user)) |
|
2725 |
assert len(resp.json['data']) == 5 |
|
2726 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done&offset=5&limit=5', user=local_user)) |
|
2727 |
assert len(resp.json['data']) == 5 |
|
2728 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done&offset=18&limit=5', user=local_user)) |
|
2729 |
assert len(resp.json['data']) == 2 |
|
2730 | ||
2731 |
# check error handling |
|
2732 |
get_app(pub).get(sign_uri('/api/forms/?status=done&limit=plop', user=local_user), status=400) |
|
2733 |
get_app(pub).get(sign_uri('/api/forms/?status=done&offset=plop', user=local_user), status=400) |
|
2734 |
get_app(pub).get(sign_uri('/api/forms/?category_id=plop', user=local_user), status=400) |
|
2735 | ||
2736 |
# check when there are missing statuses |
|
2737 |
for formdata in data_class.select(): |
|
2738 |
formdata.status = 'wf-missing' |
|
2739 |
formdata.store() |
|
2740 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all', user=local_user)) |
|
2741 |
assert resp.json['data'][0]['status'] is None |
|
2742 |
assert 'unknown' in resp.json['data'][0]['title'] |
|
2743 | ||
2744 | ||
2745 |
def test_api_global_listing_ignored_roles(pub, local_user): |
|
2746 |
test_api_global_listing(pub, local_user) |
|
2747 | ||
2748 |
role = Role(name='test2') |
|
2749 |
role.store() |
|
2750 | ||
2751 |
formdef = FormDef() |
|
2752 |
formdef.name = 'test2' |
|
2753 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2754 |
formdef.fields = [ |
|
2755 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2756 |
] |
|
2757 |
formdef.store() |
|
2758 | ||
2759 |
data_class = formdef.data_class() |
|
2760 |
data_class.wipe() |
|
2761 | ||
2762 |
for i in range(10): |
|
2763 |
formdata = data_class() |
|
2764 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
2765 |
formdata.data = {'0': 'FOO BAR'} |
|
2766 |
formdata.user_id = local_user.id |
|
2767 |
formdata.just_created() |
|
2768 |
formdata.jump_status('new') |
|
2769 |
formdata.store() |
|
2770 | ||
2771 |
# considering roles |
|
2772 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all&limit=100', user=local_user)) |
|
2773 |
assert len(resp.json['data']) == 30 |
|
2774 | ||
2775 |
# ignore roles |
|
2776 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all&limit=100&ignore-roles=on', user=local_user)) |
|
2777 |
assert len(resp.json['data']) == 40 |
|
2778 | ||
2779 |
# check sensitive forms are not exposed |
|
2780 |
formdef.skip_from_360_view = True |
|
2781 |
formdef.store() |
|
2782 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all&limit=100&ignore-roles=on', user=local_user)) |
|
2783 |
assert len(resp.json['data']) == 30 |
|
2784 | ||
2785 | ||
2786 |
def test_api_include_anonymised(pub, local_user): |
|
2787 |
if not pub.is_using_postgresql(): |
|
2788 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user), status=404) |
|
2789 |
pytest.skip('this requires SQL') |
|
2790 |
return |
|
2791 | ||
2792 |
Role.wipe() |
|
2793 |
role = Role(name='test') |
|
2794 |
role.store() |
|
2795 | ||
2796 |
# add proper role to user |
|
2797 |
local_user.roles = [role.id] |
|
2798 |
local_user.store() |
|
2799 | ||
2800 |
FormDef.wipe() |
|
2801 |
formdef = FormDef() |
|
2802 |
formdef.name = 'test' |
|
2803 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2804 |
formdef.fields = [ |
|
2805 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2806 |
] |
|
2807 |
formdef.store() |
|
2808 | ||
2809 |
data_class = formdef.data_class() |
|
2810 |
data_class.wipe() |
|
2811 | ||
2812 |
for i in range(10): |
|
2813 |
formdata = data_class() |
|
2814 |
formdata.data = {'0': 'FOO BAR'} |
|
2815 |
formdata.user_id = local_user.id |
|
2816 |
formdata.just_created() |
|
2817 |
formdata.jump_status('new') |
|
2818 |
formdata.store() |
|
3 |
import json |
|
4 |
import os |
|
2819 | 5 | |
2820 |
# anonymise the last one |
|
2821 |
formdata.anonymise() |
|
6 |
import mock |
|
7 |
import pytest |
|
8 |
from django.utils.six import StringIO |
|
9 |
from quixote import get_publisher |
|
10 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app |
|
2822 | 11 | |
2823 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
2824 |
assert len(resp.json['data']) == 10 |
|
12 |
from wcs.api_utils import sign_url |
|
13 |
from wcs.formdef import FormDef |
|
14 |
from wcs.qommon.http_request import HTTPRequest |
|
2825 | 15 | |
2826 |
resp = get_app(pub).get(sign_uri('/api/forms/?include-anonymised=on', user=local_user)) |
|
2827 |
assert len(resp.json['data']) == 10 |
|
2828 | 16 | |
2829 |
resp = get_app(pub).get(sign_uri('/api/forms/?include-anonymised=off', user=local_user)) |
|
2830 |
assert len(resp.json['data']) == 9 |
|
17 |
def pytest_generate_tests(metafunc): |
|
18 |
if 'pub' in metafunc.fixturenames: |
|
19 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
2831 | 20 | |
2832 | 21 | |
2833 | 22 |
@pytest.fixture |
2834 |
def ics_data(local_user): |
|
2835 |
Role.wipe() |
|
2836 |
role = Role(name='test') |
|
2837 |
role.store() |
|
2838 | ||
2839 |
FormDef.wipe() |
|
2840 |
formdef = FormDef() |
|
2841 |
formdef.url_name = 'test' |
|
2842 |
formdef.name = 'testé' |
|
2843 |
formdef.workflow_roles = {'_receiver': role.id} |
|
2844 |
formdef.fields = [ |
|
2845 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
2846 |
fields.StringField(id='1', label='foobar2', varname='foobar2'), |
|
2847 |
] |
|
2848 |
formdef.digest_template = 'plöp {{ form_var_foobar }} plÔp' |
|
2849 |
formdef.store() |
|
2850 | ||
2851 |
data_class = formdef.data_class() |
|
2852 |
data_class.wipe() |
|
2853 | ||
2854 |
date = datetime.datetime(2014, 1, 20, 12, 00) |
|
2855 |
for i in range(30): |
|
2856 |
formdata = data_class() |
|
2857 |
formdata.data = {'0': (date + datetime.timedelta(days=i)).strftime('%Y-%m-%d %H:%M')} |
|
2858 |
formdata.data['1'] = (date + datetime.timedelta(days=i, minutes=i + 1)).strftime('%Y-%m-%d %H:%M') |
|
2859 |
formdata.user_id = local_user.id |
|
2860 |
formdata.just_created() |
|
2861 |
if i % 3 == 0: |
|
2862 |
formdata.jump_status('new') |
|
2863 |
else: |
|
2864 |
formdata.jump_status('finished') |
|
2865 |
formdata.store() |
|
2866 | ||
2867 |
# not a datetime: ignored |
|
2868 |
date = datetime.date(2014, 1, 20) |
|
2869 |
formdata = data_class() |
|
2870 |
formdata.data = {'0': '12:00'} |
|
2871 |
formdata.data['1'] = '13:00' |
|
2872 |
formdata.user_id = local_user.id |
|
2873 |
formdata.just_created() |
|
2874 |
formdata.jump_status('new') |
|
2875 |
formdata.store() |
|
2876 | ||
2877 | ||
2878 |
def test_api_ics_formdata(pub, local_user, ics_data): |
|
2879 |
role = Role.select()[0] |
|
2880 | ||
2881 |
# check access is denied if the user has not the appropriate role |
|
2882 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar', user=local_user), status=403) |
|
2883 |
# even if there's an anonymse parameter |
|
2884 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar?anonymise', user=local_user), status=403) |
|
2885 | ||
2886 |
# add proper role to user |
|
2887 |
local_user.roles = [role.id] |
|
2888 |
local_user.store() |
|
2889 | ||
2890 |
def remove_dtstamp(body): |
|
2891 |
# remove dtstamp as the precise timing may vary between two consecutive |
|
2892 |
# calls and we shouldn't care. |
|
2893 |
return re.sub('DTSTAMP:.*', 'DTSTAMP:--', body) |
|
2894 | ||
2895 |
# check 404 on incomplete ics url access |
|
2896 |
assert get_app(pub).get(sign_uri('/api/forms/test/ics/', user=local_user), status=404) |
|
2897 | ||
2898 |
# check it gets the data |
|
2899 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar', user=local_user)) |
|
2900 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/', user=local_user)) |
|
2901 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
2902 |
assert resp.headers['content-type'] == 'text/calendar; charset=utf-8' |
|
2903 |
assert resp.text.count('BEGIN:VEVENT') == 10 |
|
2904 |
# check that description contains form name, display id, workflow status, |
|
2905 |
# backoffice url and attached user |
|
2906 |
pattern = re.compile(u'DESCRIPTION:testé \| 1-\d+ \| New', re.MULTILINE) |
|
2907 |
m = pattern.findall(resp.text) |
|
2908 |
assert len(m) == 10 |
|
2909 |
assert resp.text.count('Jean Darmette') == 10 |
|
2910 |
assert resp.text.count('DTSTART') == 10 |
|
2911 | ||
2912 |
# check formdata digest summary and description contains the formdata digest |
|
2913 |
pattern = re.compile(r'SUMMARY:testé #1-\d+ - plöp \d{4}-\d{2}-\d{2} \d{2}:\d{2} plÔp', re.MULTILINE) |
|
2914 |
m = pattern.findall(resp.text) |
|
2915 |
assert len(m) == 10 |
|
2916 |
assert resp.text.count(r'plöp') == 20 |
|
2917 | ||
2918 |
# check with a filter |
|
2919 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar?filter=done', user=local_user)) |
|
2920 |
assert resp.text.count('BEGIN:VEVENT') == 20 |
|
2921 |
pattern = re.compile(u'DESCRIPTION:testé \| 1-\d+ \| Finished', re.MULTILINE) |
|
2922 |
m = pattern.findall(resp.text) |
|
2923 |
assert len(m) == 20 |
|
2924 |
assert resp.text.count('Jean Darmette') == 20 |
|
2925 | ||
2926 |
# check 404 on erroneous field var |
|
2927 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/xxx', user=local_user), status=404) |
|
2928 | ||
2929 |
# check 404 on an erroneous field var for endtime |
|
2930 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/xxx', user=local_user), status=404) |
|
2931 | ||
2932 |
# check 404 on too many path elements |
|
2933 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/foobar2/xxx', user=local_user), status=404) |
|
2934 | ||
2935 |
# check ics data with start and end varnames |
|
2936 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/foobar2', user=local_user)) |
|
2937 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/foobar2/', user=local_user)) |
|
2938 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
2939 |
assert resp.text.count('DTSTART') == 10 |
|
2940 |
assert resp.text.count('DTEND') == 10 |
|
2941 | ||
2942 | ||
2943 |
def test_api_ics_formdata_http_auth(pub, local_user, admin_user, ics_data): |
|
2944 |
role = Role.select()[0] |
|
2945 | ||
2946 |
# check as admin |
|
2947 |
app = login(get_app(pub)) |
|
2948 |
resp = app.get('/api/forms/test/ics/foobar', status=200) |
|
2949 | ||
2950 |
# no access |
|
2951 |
app = get_app(pub) |
|
2952 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=401) |
|
2953 |
assert resp.headers['Www-Authenticate'] |
|
2954 | ||
2955 |
# auth but no access |
|
2956 |
app = get_app(pub) |
|
2957 |
app.authorization = ('Basic', ('user', 'password')) |
|
2958 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=401) |
|
2959 | ||
2960 |
# add authentication info |
|
2961 |
pub.load_site_options() |
|
2962 |
pub.site_options.add_section('api-http-auth-ics') |
|
2963 |
pub.site_options.set('api-http-auth-ics', 'user', 'password') |
|
2964 |
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')) |
|
2965 | ||
2966 |
# check access is denied if the user has not the appropriate role |
|
2967 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=403) |
|
2968 | ||
2969 |
# check access is denied if the user is not specified |
|
2970 |
resp = app.get('/api/forms/test/ics/foobar', status=403) |
|
2971 | ||
2972 |
# add proper role to user |
|
2973 |
local_user.roles = [role.id] |
|
2974 |
local_user.store() |
|
2975 | ||
2976 |
# check it gets the data |
|
2977 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=200) |
|
2978 |
assert resp.headers['content-type'] == 'text/calendar; charset=utf-8' |
|
2979 |
assert resp.text.count('BEGIN:VEVENT') == 10 |
|
2980 | ||
2981 |
# check it fails with a different password |
|
2982 |
app.authorization = ('Basic', ('user', 'password2')) |
|
2983 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=401) |
|
2984 | ||
2985 | ||
2986 |
def test_api_ics_formdata_custom_view(pub, local_user, ics_data): |
|
2987 |
role = Role.select()[0] |
|
2988 | ||
2989 |
formdef = FormDef.get_by_urlname('test') |
|
2990 | ||
2991 |
pub.custom_view_class.wipe() |
|
2992 |
custom_view = pub.custom_view_class() |
|
2993 |
custom_view.title = 'custom view' |
|
2994 |
custom_view.formdef = formdef |
|
2995 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
2996 |
custom_view.filters = {} |
|
2997 |
custom_view.visibility = 'any' |
|
2998 |
custom_view.store() |
|
2999 | ||
3000 |
# check access is denied if the user has not the appropriate role |
|
3001 |
resp = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar', user=local_user), status=403) |
|
3002 |
# even if there's an anonymise parameter |
|
3003 |
resp = get_app(pub).get( |
|
3004 |
sign_uri('/api/forms/test/custom-view/ics/foobar?anonymise', user=local_user), status=403 |
|
3005 |
) |
|
3006 | ||
3007 |
# add proper role to user |
|
3008 |
local_user.roles = [role.id] |
|
3009 |
local_user.store() |
|
3010 | ||
3011 |
def remove_dtstamp(body): |
|
3012 |
# remove dtstamp as the precise timing may vary between two consecutive |
|
3013 |
# calls and we shouldn't care. |
|
3014 |
return re.sub('DTSTAMP:.*', 'DTSTAMP:--', body) |
|
3015 | ||
3016 |
# check it gets the data |
|
3017 |
resp = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar', user=local_user)) |
|
3018 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar/', user=local_user)) |
|
3019 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
3020 |
assert resp.headers['content-type'] == 'text/calendar; charset=utf-8' |
|
3021 |
assert resp.text.count('BEGIN:VEVENT') == 10 |
|
3022 | ||
3023 |
# check ics data with start and end varnames |
|
3024 |
resp = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar/foobar2', user=local_user)) |
|
3025 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar/foobar2/', user=local_user)) |
|
3026 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
3027 |
assert resp.text.count('DTSTART') == 10 |
|
3028 |
assert resp.text.count('DTEND') == 10 |
|
3029 | ||
3030 | ||
3031 |
def test_roles(pub, local_user): |
|
3032 |
Role.wipe() |
|
3033 |
role = Role(name='Hello World') |
|
3034 |
role.emails = ['toto@example.com', 'zozo@example.com'] |
|
3035 |
role.details = 'kouign amann' |
|
3036 |
role.store() |
|
3037 | ||
3038 |
resp = get_app(pub).get('/api/roles', status=403) |
|
3039 | ||
3040 |
resp = get_app(pub).get(sign_uri('/api/roles')) |
|
3041 |
assert resp.json['data'][0]['text'] == 'Hello World' |
|
3042 |
assert resp.json['data'][0]['slug'] == 'hello-world' |
|
3043 |
assert resp.json['data'][0]['emails'] == ['toto@example.com', 'zozo@example.com'] |
|
3044 |
assert resp.json['data'][0]['emails_to_members'] == False |
|
3045 |
assert resp.json['data'][0]['details'] == 'kouign amann' |
|
3046 | ||
3047 |
# also check old endpoint, for compatibility |
|
3048 |
resp = get_app(pub).get(sign_uri('/roles'), headers={'Accept': 'application/json'}) |
|
3049 |
assert resp.json['data'][0]['text'] == 'Hello World' |
|
3050 |
assert resp.json['data'][0]['slug'] == 'hello-world' |
|
3051 |
assert resp.json['data'][0]['emails'] == ['toto@example.com', 'zozo@example.com'] |
|
3052 |
assert resp.json['data'][0]['emails_to_members'] == False |
|
3053 |
assert resp.json['data'][0]['details'] == 'kouign amann' |
|
3054 | ||
3055 | ||
3056 |
def test_users(pub, local_user): |
|
3057 |
resp = get_app(pub).get('/api/users/', status=403) |
|
3058 | ||
3059 |
resp = get_app(pub).get(sign_uri('/api/users/')) |
|
3060 |
assert resp.json['data'][0]['user_display_name'] == local_user.name |
|
3061 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
3062 |
assert resp.json['data'][0]['user_id'] == local_user.id |
|
3063 | ||
3064 |
role = Role(name='Foo bar') |
|
3065 |
role.store() |
|
3066 |
local_user.roles = [role.id] |
|
3067 |
local_user.store() |
|
3068 | ||
3069 |
resp = get_app(pub).get(sign_uri('/api/users/?q=jean')) |
|
3070 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
3071 |
assert len(resp.json['data'][0]['user_roles']) == 1 |
|
3072 |
assert resp.json['data'][0]['user_roles'][0]['name'] == 'Foo bar' |
|
3073 | ||
3074 |
resp = get_app(pub).get(sign_uri('/api/users/?q=foobar')) |
|
3075 |
assert len(resp.json['data']) == 0 |
|
3076 | ||
3077 |
from wcs.admin.settings import UserFieldsFormDef |
|
3078 | ||
3079 |
formdef = UserFieldsFormDef(pub) |
|
3080 |
formdef.fields.append(fields.StringField(id='3', label='test', type='string')) |
|
3081 |
formdef.store() |
|
3082 | ||
3083 |
local_user.form_data = {'3': 'HELLO'} |
|
3084 |
local_user.set_attributes_from_formdata(local_user.form_data) |
|
3085 |
local_user.store() |
|
3086 | ||
3087 |
resp = get_app(pub).get(sign_uri('/api/users/?q=HELLO')) |
|
3088 |
assert len(resp.json['data']) == 1 |
|
3089 |
resp = get_app(pub).get(sign_uri('/api/users/?q=foobar')) |
|
3090 |
assert len(resp.json['data']) == 0 |
|
3091 | ||
3092 | ||
3093 |
def test_users_unaccent(pub, local_user): |
|
3094 |
local_user.name = 'Jean Sénisme' |
|
3095 |
local_user.store() |
|
3096 |
resp = get_app(pub).get(sign_uri('/api/users/?q=jean')) |
|
3097 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
3098 | ||
3099 |
resp = get_app(pub).get(sign_uri('/api/users/?q=senisme')) |
|
3100 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
3101 | ||
3102 |
resp = get_app(pub).get(sign_uri('/api/users/?q=sénisme')) |
|
3103 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
3104 | ||
3105 |
resp = get_app(pub).get(sign_uri('/api/users/?q=blah')) |
|
3106 |
assert len(resp.json['data']) == 0 |
|
3107 | ||
3108 | ||
3109 |
def test_workflow_trigger(pub, local_user): |
|
3110 |
workflow = Workflow(name='test') |
|
3111 |
st1 = workflow.add_status('Status1', 'st1') |
|
3112 |
jump = JumpWorkflowStatusItem() |
|
3113 |
jump.trigger = 'XXX' |
|
3114 |
jump.status = 'st2' |
|
3115 |
st1.items.append(jump) |
|
3116 |
jump.parent = st1 |
|
3117 |
st2 = workflow.add_status('Status2', 'st2') |
|
3118 |
workflow.store() |
|
3119 | ||
3120 |
FormDef.wipe() |
|
3121 |
formdef = FormDef() |
|
3122 |
formdef.name = 'test' |
|
3123 |
formdef.fields = [] |
|
3124 |
formdef.workflow_id = workflow.id |
|
3125 |
formdef.store() |
|
3126 | ||
3127 |
formdef.data_class().wipe() |
|
3128 |
formdata = formdef.data_class()() |
|
3129 |
formdata.just_created() |
|
3130 |
formdata.store() |
|
3131 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
3132 | ||
3133 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200) |
|
3134 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
3135 | ||
3136 |
# check with trailing slash |
|
3137 |
formdata.store() # reset |
|
3138 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX/'), status=200) |
|
3139 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
3140 | ||
3141 |
Role.wipe() |
|
3142 |
role = Role(name='xxx') |
|
3143 |
role.store() |
|
3144 | ||
3145 |
jump.by = [role.id] |
|
3146 |
workflow.store() |
|
3147 | ||
3148 |
formdata.store() # (will get back to wf-st1) |
|
3149 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=403) |
|
3150 | ||
3151 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX', user=local_user), status=403) |
|
3152 | ||
3153 |
local_user.roles = [role.id] |
|
3154 |
local_user.store() |
|
3155 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX', user=local_user), status=200) |
|
3156 | ||
3157 | ||
3158 |
def test_workflow_trigger_with_data(pub, local_user): |
|
3159 |
workflow = Workflow(name='test') |
|
3160 |
st1 = workflow.add_status('Status1', 'st1') |
|
3161 |
jump = JumpWorkflowStatusItem() |
|
3162 |
jump.trigger = 'XXX' |
|
3163 |
jump.status = 'st2' |
|
3164 |
st1.items.append(jump) |
|
3165 |
jump.parent = st1 |
|
3166 |
st2 = workflow.add_status('Status2', 'st2') |
|
3167 |
workflow.store() |
|
3168 | ||
3169 |
FormDef.wipe() |
|
3170 |
formdef = FormDef() |
|
3171 |
formdef.name = 'test' |
|
3172 |
formdef.fields = [] |
|
3173 |
formdef.workflow_id = workflow.id |
|
3174 |
formdef.store() |
|
3175 | ||
3176 |
formdef.data_class().wipe() |
|
3177 |
formdata = formdef.data_class()() |
|
3178 |
formdata.just_created() |
|
3179 |
formdata.store() |
|
3180 | ||
3181 |
get_app(pub).post_json( |
|
3182 |
sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200, params={'test': 'data'} |
|
3183 |
) |
|
3184 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
3185 |
assert formdef.data_class().get(formdata.id).workflow_data == {'test': 'data'} |
|
3186 | ||
3187 |
# post with empty dictionary |
|
3188 |
formdata.store() # reset |
|
3189 |
get_app(pub).post_json(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200, params={}) |
|
3190 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
3191 |
assert not formdef.data_class().get(formdata.id).workflow_data |
|
3192 | ||
3193 |
# post with empty data |
|
3194 |
formdata.store() # reset |
|
3195 |
get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200) |
|
3196 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
3197 |
assert not formdef.data_class().get(formdata.id).workflow_data |
|
3198 | ||
3199 |
# post with empty data, but declare json content-type |
|
3200 |
formdata.store() # reset |
|
3201 |
get_app(pub).post( |
|
3202 |
sign_uri(formdata.get_url() + 'jump/trigger/XXX'), |
|
3203 |
status=200, |
|
3204 |
headers={'content-type': 'application/json'}, |
|
3205 |
) |
|
3206 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
3207 |
assert not formdef.data_class().get(formdata.id).workflow_data |
|
3208 | ||
3209 |
# post with invalid JSON data |
|
3210 |
formdata.store() # reset |
|
3211 |
get_app(pub).post( |
|
3212 |
sign_uri(formdata.get_url() + 'jump/trigger/XXX'), |
|
3213 |
status=400, |
|
3214 |
headers={'content-type': 'application/json'}, |
|
3215 |
params='ERROR', |
|
3216 |
) |
|
3217 | ||
3218 | ||
3219 |
def test_workflow_trigger_with_condition(pub, local_user): |
|
3220 |
workflow = Workflow(name='test') |
|
3221 |
st1 = workflow.add_status('Status1', 'st1') |
|
3222 |
jump = JumpWorkflowStatusItem() |
|
3223 |
jump.trigger = 'XXX' |
|
3224 |
jump.condition = {'type': 'django', 'value': 'form_var_foo == "bar"'} |
|
3225 |
jump.status = 'st2' |
|
3226 |
st1.items.append(jump) |
|
3227 |
jump.parent = st1 |
|
3228 |
st2 = workflow.add_status('Status2', 'st2') |
|
3229 |
workflow.store() |
|
3230 | ||
3231 |
FormDef.wipe() |
|
3232 |
formdef = FormDef() |
|
3233 |
formdef.name = 'test' |
|
3234 |
formdef.fields = [fields.StringField(id='0', label='foo', varname='foo')] |
|
3235 |
formdef.workflow_id = workflow.id |
|
3236 |
formdef.store() |
|
3237 | ||
3238 |
formdef.data_class().wipe() |
|
3239 |
formdata = formdef.data_class()() |
|
3240 |
formdata.data = {'0': 'foo'} |
|
3241 |
formdata.just_created() |
|
3242 |
formdata.store() |
|
3243 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
3244 | ||
3245 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=403) |
|
3246 |
assert resp.json == {'err_desc': 'unmet condition', 'err': 1} |
|
3247 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
3248 |
# check without json |
|
3249 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX', format=None), status=403) |
|
3250 |
assert resp.content_type == 'text/html' |
|
3251 | ||
3252 |
formdata.data['0'] = 'bar' |
|
3253 |
formdata.store() |
|
3254 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX')) |
|
3255 |
assert resp.json == {'err': 0, 'url': None} |
|
3256 | ||
3257 | ||
3258 |
def test_workflow_trigger_jump_once(pub, local_user): |
|
3259 |
workflow = Workflow(name='test') |
|
3260 |
st1 = workflow.add_status('Status1', 'st1') |
|
3261 |
st2 = workflow.add_status('Status2', 'st2') |
|
3262 |
st3 = workflow.add_status('Status3', 'st3') |
|
3263 |
jump = JumpWorkflowStatusItem() |
|
3264 |
jump.trigger = 'XXX' |
|
3265 |
jump.status = 'st2' |
|
3266 |
st1.items.append(jump) |
|
3267 |
jump.parent = st1 |
|
3268 |
jump = JumpWorkflowStatusItem() |
|
3269 |
jump.trigger = 'XXX' |
|
3270 |
jump.status = 'st3' |
|
3271 |
st2.items.append(jump) |
|
3272 |
jump.parent = st2 |
|
3273 |
workflow.store() |
|
3274 | ||
3275 |
FormDef.wipe() |
|
3276 |
formdef = FormDef() |
|
3277 |
formdef.name = 'test' |
|
3278 |
formdef.fields = [] |
|
3279 |
formdef.workflow_id = workflow.id |
|
3280 |
formdef.store() |
|
3281 | ||
3282 |
formdef.data_class().wipe() |
|
3283 |
formdata = formdef.data_class()() |
|
3284 |
formdata.just_created() |
|
3285 |
formdata.store() |
|
3286 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
3287 | ||
3288 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX')) |
|
3289 |
assert resp.json == {'err': 0, 'url': None} |
|
3290 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
3291 | ||
3292 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX')) |
|
3293 |
assert resp.json == {'err': 0, 'url': None} |
|
3294 |
assert formdef.data_class().get(formdata.id).status == 'wf-st3' |
|
3295 | ||
3296 | ||
3297 |
def test_workflow_global_webservice_trigger(pub, local_user): |
|
3298 |
workflow = Workflow(name='test') |
|
3299 |
st1 = workflow.add_status('Status1', 'st1') |
|
3300 | ||
3301 |
ac1 = workflow.add_global_action('Action', 'ac1') |
|
3302 |
trigger = ac1.append_trigger('webservice') |
|
3303 |
trigger.identifier = 'plop' |
|
3304 | ||
3305 |
add_to_journal = RegisterCommenterWorkflowStatusItem() |
|
3306 |
add_to_journal.id = '_add_to_journal' |
|
3307 |
add_to_journal.comment = 'HELLO WORLD' |
|
3308 |
ac1.items.append(add_to_journal) |
|
3309 |
add_to_journal.parent = ac1 |
|
3310 | ||
3311 |
workflow.store() |
|
3312 | ||
3313 |
FormDef.wipe() |
|
3314 |
formdef = FormDef() |
|
3315 |
formdef.name = 'test' |
|
3316 |
formdef.fields = [] |
|
3317 |
formdef.workflow_id = workflow.id |
|
3318 |
formdef.store() |
|
3319 | ||
3320 |
formdef.data_class().wipe() |
|
3321 |
formdata = formdef.data_class()() |
|
3322 |
formdata.just_created() |
|
3323 |
formdata.store() |
|
3324 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
3325 | ||
3326 |
# call to undefined hook |
|
3327 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'hooks/XXX/'), status=404) |
|
3328 |
resp = get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/XXX/'), status=404) |
|
3329 | ||
3330 |
# anonymous call |
|
3331 |
resp = get_app(pub).post(formdata.get_url() + 'hooks/plop/', status=200) |
|
3332 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD' |
|
3333 | ||
3334 |
add_to_journal.comment = 'HELLO WORLD 2' |
|
3335 |
workflow.store() |
|
3336 |
resp = get_app(pub).post(formdata.get_api_url() + 'hooks/plop/', status=200) |
|
3337 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD 2' |
|
3338 | ||
3339 |
# call requiring user |
|
3340 |
add_to_journal.comment = 'HELLO WORLD 3' |
|
3341 |
trigger.roles = ['logged-users'] |
|
3342 |
workflow.store() |
|
3343 |
resp = get_app(pub).post(formdata.get_api_url() + 'hooks/plop/', status=403) |
|
3344 |
resp = get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/'), status=200) |
|
3345 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD 3' |
|
3346 | ||
3347 |
# call requiring roles |
|
3348 |
add_to_journal.comment = 'HELLO WORLD 4' |
|
3349 |
trigger.roles = ['logged-users'] |
|
3350 |
workflow.store() |
|
3351 |
Role.wipe() |
|
3352 |
role = Role(name='xxx') |
|
3353 |
role.store() |
|
3354 |
trigger.roles = [role.id] |
|
3355 |
workflow.store() |
|
3356 |
resp = get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/'), status=403) |
|
3357 |
resp = get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), status=403) |
|
3358 | ||
3359 |
local_user.roles = [role.id] |
|
3360 |
local_user.store() |
|
3361 |
resp = get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), status=200) |
|
3362 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD 4' |
|
23 |
def pub(request, emails): |
|
24 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
3363 | 25 | |
3364 |
# call adding data |
|
3365 |
add_to_journal.comment = 'HELLO {{plop_test}}' |
|
3366 |
workflow.store() |
|
3367 |
resp = get_app(pub).post_json( |
|
3368 |
sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), {'test': 'foobar'}, status=200 |
|
3369 |
) |
|
3370 |
# (django templating make it turn into HTML) |
|
3371 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == '<div>HELLO foobar</div>' |
|
26 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
27 |
pub.set_app_dir(req) |
|
28 |
pub.cfg['identification'] = {'methods': ['password']} |
|
29 |
pub.cfg['language'] = {'language': 'en'} |
|
30 |
pub.write_cfg() |
|
3372 | 31 | |
3373 |
# call adding data but with no actions
|
|
3374 |
ac1.items = []
|
|
3375 |
workflow.store() |
|
3376 |
resp = get_app(pub).post_json( |
|
3377 |
sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), {'test': 'BAR'}, status=200 |
|
32 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write(
|
|
33 |
'''\
|
|
34 |
[api-secrets] |
|
35 |
coucou = 1234 |
|
36 |
''' |
|
3378 | 37 |
) |
3379 |
assert formdef.data_class().get(formdata.id).workflow_data == {'plop': {'test': 'BAR'}} |
|
3380 | ||
3381 | 38 | |
3382 |
def test_workflow_global_webservice_trigger_no_trailing_slash(pub, local_user): |
|
3383 |
workflow = Workflow(name='test') |
|
3384 |
st1 = workflow.add_status('Status1', 'st1') |
|
3385 | ||
3386 |
ac1 = workflow.add_global_action('Action', 'ac1') |
|
3387 |
trigger = ac1.append_trigger('webservice') |
|
3388 |
trigger.identifier = 'plop' |
|
3389 | ||
3390 |
add_to_journal = RegisterCommenterWorkflowStatusItem() |
|
3391 |
add_to_journal.id = '_add_to_journal' |
|
3392 |
add_to_journal.comment = 'HELLO WORLD' |
|
3393 |
ac1.items.append(add_to_journal) |
|
3394 |
add_to_journal.parent = ac1 |
|
3395 | ||
3396 |
workflow.store() |
|
3397 | ||
3398 |
FormDef.wipe() |
|
3399 |
formdef = FormDef() |
|
3400 |
formdef.name = 'test' |
|
3401 |
formdef.fields = [] |
|
3402 |
formdef.workflow_id = workflow.id |
|
3403 |
formdef.store() |
|
3404 | ||
3405 |
formdef.data_class().wipe() |
|
3406 |
formdata = formdef.data_class()() |
|
3407 |
formdata.just_created() |
|
3408 |
formdata.store() |
|
3409 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
39 |
return pub |
|
3410 | 40 | |
3411 |
# call to undefined hook |
|
3412 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'hooks/XXX'), status=404) |
|
3413 |
resp = get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/XXX'), status=404) |
|
3414 | 41 | |
3415 |
# anonymous call |
|
3416 |
resp = get_app(pub).post(formdata.get_url() + 'hooks/plop', status=200) |
|
3417 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD' |
|
42 |
def teardown_module(module): |
|
43 |
clean_temporary_pub() |
|
3418 | 44 | |
3419 | 45 | |
3420 | 46 |
def test_tracking_code(pub): |
... | ... | |
3502 | 128 |
assert resp.json['msg'] == 'unknown condition type' |
3503 | 129 | |
3504 | 130 | |
3505 |
@pytest.fixture(params=['sql', 'pickle']) |
|
3506 |
def no_request_pub(request): |
|
3507 |
pub = create_temporary_pub(sql_mode=bool(request.param == 'sql')) |
|
3508 |
pub.app_dir = os.path.join(pub.APP_DIR, 'example.net') |
|
3509 |
pub.set_config() |
|
3510 | ||
3511 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
3512 |
''' |
|
3513 |
[wscall-secrets] |
|
3514 |
api.example.com = 1234 |
|
3515 |
''' |
|
3516 |
) |
|
3517 |
return pub |
|
3518 | ||
3519 | ||
3520 |
def test_get_secret_and_orig(no_request_pub): |
|
3521 |
secret, orig = get_secret_and_orig('https://api.example.com/endpoint/') |
|
3522 |
assert secret == '1234' |
|
3523 |
assert orig == 'example.net' |
|
3524 | ||
3525 | ||
3526 | 131 |
def test_reverse_geocoding(pub): |
3527 | 132 |
with mock.patch('wcs.qommon.misc.urlopen') as urlopen: |
3528 | 133 |
urlopen.side_effect = lambda *args: StringIO(json.dumps({'address': 'xxx'})) |
... | ... | |
3563 | 168 |
) |
3564 | 169 | |
3565 | 170 | |
3566 |
def test_formdef_submit_structured(pub, local_user): |
|
3567 |
Role.wipe() |
|
3568 |
role = Role(name='test') |
|
3569 |
role.store() |
|
3570 |
local_user.roles = [role.id] |
|
3571 |
local_user.store() |
|
3572 | ||
3573 |
FormDef.wipe() |
|
3574 |
formdef = FormDef() |
|
3575 |
formdef.name = 'test' |
|
3576 |
formdef.fields = [ |
|
3577 |
fields.ItemField( |
|
3578 |
id='0', |
|
3579 |
label='foobar', |
|
3580 |
varname='foobar', |
|
3581 |
data_source={ |
|
3582 |
'type': 'json', |
|
3583 |
'value': 'http://datasource.com', |
|
3584 |
}, |
|
3585 |
), |
|
3586 |
fields.ItemField( |
|
3587 |
id='1', |
|
3588 |
label='foobar1', |
|
3589 |
varname='foobar1', |
|
3590 |
data_source={ |
|
3591 |
'type': 'formula', |
|
3592 |
'value': '[dict(id=i, text=\'label %s\' % i, foo=i) for i in range(10)]', |
|
3593 |
}, |
|
3594 |
), |
|
3595 |
] |
|
3596 |
formdef.store() |
|
3597 |
data_class = formdef.data_class() |
|
3598 | ||
3599 |
def url(): |
|
3600 |
signed_url = sign_url( |
|
3601 |
'http://example.net/api/formdefs/test/submit' |
|
3602 |
'?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
3603 |
'1234', |
|
3604 |
) |
|
3605 |
return signed_url[len('http://example.net') :] |
|
3606 | ||
3607 |
with mock.patch('wcs.qommon.misc.urlopen') as urlopen: |
|
3608 |
urlopen.side_effect = lambda *args: StringIO( |
|
3609 |
'''\ |
|
3610 |
{"data": [{"id": 0, "text": "zéro", "foo": "bar"}, \ |
|
3611 |
{"id": 1, "text": "uné", "foo": "bar1"}, \ |
|
3612 |
{"id": 2, "text": "deux", "foo": "bar2"}]}''' |
|
3613 |
) |
|
3614 |
resp = get_app(pub).post_json( |
|
3615 |
url(), |
|
3616 |
{ |
|
3617 |
'data': { |
|
3618 |
'0': '0', |
|
3619 |
"1": '3', |
|
3620 |
} |
|
3621 |
}, |
|
3622 |
) |
|
3623 | ||
3624 |
formdata = data_class.get(resp.json['data']['id']) |
|
3625 |
assert formdata.status == 'wf-new' |
|
3626 |
assert formdata.data['0'] == '0' |
|
3627 |
assert formdata.data['0_display'] == 'zéro' |
|
3628 |
assert formdata.data['0_structured'] == { |
|
3629 |
'id': 0, |
|
3630 |
'text': 'zéro', |
|
3631 |
'foo': 'bar', |
|
3632 |
} |
|
3633 |
assert formdata.data['1'] == '3' |
|
3634 |
assert formdata.data['1_display'] == 'label 3' |
|
3635 |
assert formdata.data['1_structured'] == { |
|
3636 |
'id': 3, |
|
3637 |
'text': 'label 3', |
|
3638 |
'foo': 3, |
|
3639 |
} |
|
3640 | ||
3641 |
data_class.wipe() |
|
3642 | ||
3643 | ||
3644 | 171 |
def test_geocoding(pub): |
3645 | 172 |
with mock.patch('wcs.qommon.misc.urlopen') as urlopen: |
3646 | 173 |
urlopen.side_effect = lambda *args: StringIO(json.dumps([{'lat': 0, 'lon': 0}])) |
... | ... | |
3689 | 216 |
urlopen.call_args[0][0] |
3690 | 217 |
== 'http://reverse.example.net/?param=value&format=json&q=test&accept-language=en' |
3691 | 218 |
) |
3692 | ||
3693 | ||
3694 |
def test_cards(pub, local_user): |
|
3695 |
Role.wipe() |
|
3696 |
role = Role(name='test') |
|
3697 |
role.store() |
|
3698 |
local_user.roles = [role.id] |
|
3699 |
local_user.store() |
|
3700 | ||
3701 |
CardDefCategory.wipe() |
|
3702 |
category = CardDefCategory() |
|
3703 |
category.name = 'Category A' |
|
3704 |
category.store() |
|
3705 | ||
3706 |
CardDef.wipe() |
|
3707 |
carddef = CardDef() |
|
3708 |
carddef.name = 'test' |
|
3709 |
carddef.fields = [fields.StringField(id='0', label='foobar', varname='foo')] |
|
3710 |
carddef.workflow_roles = {'_viewer': role.id} |
|
3711 |
carddef.digest_template = 'bla {{ form_var_foo }} xxx' |
|
3712 |
carddef.store() |
|
3713 | ||
3714 |
carddef.data_class().wipe() |
|
3715 |
formdata = carddef.data_class()() |
|
3716 |
formdata.data = {'0': 'blah'} |
|
3717 |
formdata.just_created() |
|
3718 |
formdata.store() |
|
3719 | ||
3720 |
custom_view = pub.custom_view_class() |
|
3721 |
custom_view.title = 'shared carddef custom view' |
|
3722 |
custom_view.formdef = carddef |
|
3723 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
3724 |
custom_view.filters = {} |
|
3725 |
custom_view.visibility = 'any' |
|
3726 |
custom_view.store() |
|
3727 | ||
3728 |
custom_view = pub.custom_view_class() |
|
3729 |
custom_view.title = 'private carddef custom view' |
|
3730 |
custom_view.formdef = carddef |
|
3731 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
3732 |
custom_view.filters = {} |
|
3733 |
custom_view.visibility = 'owner' |
|
3734 |
custom_view.user = local_user |
|
3735 |
custom_view.store() |
|
3736 | ||
3737 |
custom_view = pub.custom_view_class() |
|
3738 |
custom_view.title = 'datasource carddef custom view' |
|
3739 |
custom_view.formdef = carddef |
|
3740 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
3741 |
custom_view.filters = {} |
|
3742 |
custom_view.visibility = 'datasource' |
|
3743 |
custom_view.store() |
|
3744 | ||
3745 |
resp = get_app(pub).get('/api/cards/@list', status=403) |
|
3746 |
resp = get_app(pub).get(sign_uri('/api/cards/@list')) |
|
3747 |
assert len(resp.json['data']) == 1 |
|
3748 |
assert resp.json['data'][0]['slug'] == 'test' |
|
3749 |
assert resp.json['data'][0]['category_slug'] is None |
|
3750 |
assert resp.json['data'][0]['category_name'] is None |
|
3751 |
assert resp.json['data'][0]['custom_views'] == [ |
|
3752 |
{'id': 'datasource-carddef-custom-view', 'text': 'datasource carddef custom view'}, |
|
3753 |
{'id': 'shared-carddef-custom-view', 'text': 'shared carddef custom view'}, |
|
3754 |
] |
|
3755 | ||
3756 |
carddef.category = category |
|
3757 |
carddef.store() |
|
3758 |
resp = get_app(pub).get(sign_uri('/api/cards/@list')) |
|
3759 |
assert len(resp.json['data']) == 1 |
|
3760 |
assert resp.json['data'][0]['slug'] == 'test' |
|
3761 |
assert resp.json['data'][0]['category_slug'] == 'category-a' |
|
3762 |
assert resp.json['data'][0]['category_name'] == 'Category A' |
|
3763 | ||
3764 |
# signed but anonymous |
|
3765 |
resp = get_app(pub).get(sign_uri('/api/cards/test/list?NameID='), status=403) |
|
3766 | ||
3767 |
# signed without specifying any user -> get everything |
|
3768 |
resp = get_app(pub).get(sign_uri('/api/cards/test/list')) |
|
3769 |
assert len(resp.json['data']) == 1 |
|
3770 | ||
3771 |
resp = get_app(pub).get(sign_uri('/api/cards/test/list?NameID=%s' % local_user.name_identifiers[0])) |
|
3772 |
assert len(resp.json['data']) == 1 |
|
3773 |
assert resp.json['data'][0]['display_id'] == formdata.get_display_id() |
|
3774 |
assert resp.json['data'][0]['display_name'] == formdata.get_display_name() |
|
3775 |
assert resp.json['data'][0]['digest'] == formdata.digest |
|
3776 |
assert resp.json['data'][0]['text'] == formdata.digest |
|
3777 |
resp = get_app(pub).get( |
|
3778 |
sign_uri('/api/cards/test/list?NameID=%s&full=on' % local_user.name_identifiers[0]) |
|
3779 |
) |
|
3780 |
assert resp.json['data'][0]['fields']['foo'] == 'blah' |
|
3781 |
assert resp.json['data'][0]['digest'] == formdata.digest |
|
3782 |
assert resp.json['data'][0]['text'] == formdata.digest |
|
3783 | ||
3784 |
# get schema |
|
3785 |
resp = get_app(pub).get(sign_uri('/api/cards/test/@schema'), status=200) |
|
3786 |
assert len(resp.json['fields']) == 1 |
|
3787 |
assert resp.json['fields'][0]['label'] == 'foobar' |
|
3788 |
assert resp.json['fields'][0]['varname'] == 'foo' |
|
3789 | ||
3790 | ||
3791 |
def test_cards_import_csv(pub, local_user): |
|
3792 |
Role.wipe() |
|
3793 |
role = Role(name='test') |
|
3794 |
role.store() |
|
3795 |
local_user.roles = [role.id] |
|
3796 |
local_user.store() |
|
3797 | ||
3798 |
CardDef.wipe() |
|
3799 |
carddef = CardDef() |
|
3800 |
carddef.name = 'test' |
|
3801 |
carddef.fields = [ |
|
3802 |
fields.StringField(id='0', label='foobar', varname='foo'), |
|
3803 |
fields.StringField(id='1', label='foobar2', varname='foo2'), |
|
3804 |
] |
|
3805 |
carddef.workflow_roles = {'_viewer': role.id} |
|
3806 |
carddef.backoffice_submission_roles = [role.id] |
|
3807 |
carddef.digest_template = 'bla {{ form_var_foo }} xxx' |
|
3808 |
carddef.store() |
|
3809 | ||
3810 |
carddef.data_class().wipe() |
|
3811 | ||
3812 |
get_app(pub).get(sign_uri('/api/cards/test/import-csv'), status=405) |
|
3813 |
get_app(pub).put(sign_uri('/api/cards/test/import-csv'), status=403) |
|
3814 |
get_app(pub).put( |
|
3815 |
sign_uri('/api/cards/test/import-csv', user=local_user), |
|
3816 |
params=b'foobar;foobar2\nfirst entry;plop\nsecond entry;plop\n', |
|
3817 |
headers={'content-type': 'text/csv'}, |
|
3818 |
) |
|
3819 |
assert carddef.data_class().count() == 2 |
|
3820 |
assert set([x.data['0'] for x in carddef.data_class().select()]) == {'first entry', 'second entry'} |
|
3821 | ||
3822 | ||
3823 |
def test_api_invalid_http_basic_auth(pub, local_user, admin_user, ics_data): |
|
3824 |
app = get_app(pub) |
|
3825 |
app.get( |
|
3826 |
'/api/forms/test/ics/foobar?email=%s' % local_user.email, |
|
3827 |
headers={'Authorization': 'Basic garbage'}, |
|
3828 |
status=401, |
|
3829 |
) |
tests/api/test_carddef.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import os |
|
4 | ||
5 |
import pytest |
|
6 |
from quixote import get_publisher |
|
7 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app |
|
8 | ||
9 |
from wcs import fields |
|
10 |
from wcs.carddef import CardDef |
|
11 |
from wcs.categories import CardDefCategory |
|
12 |
from wcs.qommon.http_request import HTTPRequest |
|
13 |
from wcs.roles import Role |
|
14 | ||
15 |
from .utils import sign_uri |
|
16 | ||
17 | ||
18 |
def pytest_generate_tests(metafunc): |
|
19 |
if 'pub' in metafunc.fixturenames: |
|
20 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
21 | ||
22 | ||
23 |
@pytest.fixture |
|
24 |
def pub(request, emails): |
|
25 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
26 | ||
27 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
28 |
pub.set_app_dir(req) |
|
29 |
pub.cfg['identification'] = {'methods': ['password']} |
|
30 |
pub.cfg['language'] = {'language': 'en'} |
|
31 |
pub.write_cfg() |
|
32 | ||
33 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
34 |
'''\ |
|
35 |
[api-secrets] |
|
36 |
coucou = 1234 |
|
37 |
''' |
|
38 |
) |
|
39 | ||
40 |
return pub |
|
41 | ||
42 | ||
43 |
def teardown_module(module): |
|
44 |
clean_temporary_pub() |
|
45 | ||
46 | ||
47 |
@pytest.fixture |
|
48 |
def local_user(): |
|
49 |
get_publisher().user_class.wipe() |
|
50 |
user = get_publisher().user_class() |
|
51 |
user.name = 'Jean Darmette' |
|
52 |
user.email = 'jean.darmette@triffouilis.fr' |
|
53 |
user.name_identifiers = ['0123456789'] |
|
54 |
user.store() |
|
55 |
return user |
|
56 | ||
57 | ||
58 |
def test_cards(pub, local_user): |
|
59 |
Role.wipe() |
|
60 |
role = Role(name='test') |
|
61 |
role.store() |
|
62 |
local_user.roles = [role.id] |
|
63 |
local_user.store() |
|
64 | ||
65 |
CardDefCategory.wipe() |
|
66 |
category = CardDefCategory() |
|
67 |
category.name = 'Category A' |
|
68 |
category.store() |
|
69 | ||
70 |
CardDef.wipe() |
|
71 |
carddef = CardDef() |
|
72 |
carddef.name = 'test' |
|
73 |
carddef.fields = [fields.StringField(id='0', label='foobar', varname='foo')] |
|
74 |
carddef.workflow_roles = {'_viewer': role.id} |
|
75 |
carddef.digest_template = 'bla {{ form_var_foo }} xxx' |
|
76 |
carddef.store() |
|
77 | ||
78 |
carddef.data_class().wipe() |
|
79 |
formdata = carddef.data_class()() |
|
80 |
formdata.data = {'0': 'blah'} |
|
81 |
formdata.just_created() |
|
82 |
formdata.store() |
|
83 | ||
84 |
custom_view = pub.custom_view_class() |
|
85 |
custom_view.title = 'shared carddef custom view' |
|
86 |
custom_view.formdef = carddef |
|
87 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
88 |
custom_view.filters = {} |
|
89 |
custom_view.visibility = 'any' |
|
90 |
custom_view.store() |
|
91 | ||
92 |
custom_view = pub.custom_view_class() |
|
93 |
custom_view.title = 'private carddef custom view' |
|
94 |
custom_view.formdef = carddef |
|
95 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
96 |
custom_view.filters = {} |
|
97 |
custom_view.visibility = 'owner' |
|
98 |
custom_view.user = local_user |
|
99 |
custom_view.store() |
|
100 | ||
101 |
custom_view = pub.custom_view_class() |
|
102 |
custom_view.title = 'datasource carddef custom view' |
|
103 |
custom_view.formdef = carddef |
|
104 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
105 |
custom_view.filters = {} |
|
106 |
custom_view.visibility = 'datasource' |
|
107 |
custom_view.store() |
|
108 | ||
109 |
resp = get_app(pub).get('/api/cards/@list', status=403) |
|
110 |
resp = get_app(pub).get(sign_uri('/api/cards/@list')) |
|
111 |
assert len(resp.json['data']) == 1 |
|
112 |
assert resp.json['data'][0]['slug'] == 'test' |
|
113 |
assert resp.json['data'][0]['category_slug'] is None |
|
114 |
assert resp.json['data'][0]['category_name'] is None |
|
115 |
assert resp.json['data'][0]['custom_views'] == [ |
|
116 |
{'id': 'datasource-carddef-custom-view', 'text': 'datasource carddef custom view'}, |
|
117 |
{'id': 'shared-carddef-custom-view', 'text': 'shared carddef custom view'}, |
|
118 |
] |
|
119 | ||
120 |
carddef.category = category |
|
121 |
carddef.store() |
|
122 |
resp = get_app(pub).get(sign_uri('/api/cards/@list')) |
|
123 |
assert len(resp.json['data']) == 1 |
|
124 |
assert resp.json['data'][0]['slug'] == 'test' |
|
125 |
assert resp.json['data'][0]['category_slug'] == 'category-a' |
|
126 |
assert resp.json['data'][0]['category_name'] == 'Category A' |
|
127 | ||
128 |
# signed but anonymous |
|
129 |
resp = get_app(pub).get(sign_uri('/api/cards/test/list?NameID='), status=403) |
|
130 | ||
131 |
# signed without specifying any user -> get everything |
|
132 |
resp = get_app(pub).get(sign_uri('/api/cards/test/list')) |
|
133 |
assert len(resp.json['data']) == 1 |
|
134 | ||
135 |
resp = get_app(pub).get(sign_uri('/api/cards/test/list?NameID=%s' % local_user.name_identifiers[0])) |
|
136 |
assert len(resp.json['data']) == 1 |
|
137 |
assert resp.json['data'][0]['display_id'] == formdata.get_display_id() |
|
138 |
assert resp.json['data'][0]['display_name'] == formdata.get_display_name() |
|
139 |
assert resp.json['data'][0]['digest'] == formdata.digest |
|
140 |
assert resp.json['data'][0]['text'] == formdata.digest |
|
141 |
resp = get_app(pub).get( |
|
142 |
sign_uri('/api/cards/test/list?NameID=%s&full=on' % local_user.name_identifiers[0]) |
|
143 |
) |
|
144 |
assert resp.json['data'][0]['fields']['foo'] == 'blah' |
|
145 |
assert resp.json['data'][0]['digest'] == formdata.digest |
|
146 |
assert resp.json['data'][0]['text'] == formdata.digest |
|
147 | ||
148 |
# get schema |
|
149 |
resp = get_app(pub).get(sign_uri('/api/cards/test/@schema'), status=200) |
|
150 |
assert len(resp.json['fields']) == 1 |
|
151 |
assert resp.json['fields'][0]['label'] == 'foobar' |
|
152 |
assert resp.json['fields'][0]['varname'] == 'foo' |
|
153 | ||
154 | ||
155 |
def test_cards_import_csv(pub, local_user): |
|
156 |
Role.wipe() |
|
157 |
role = Role(name='test') |
|
158 |
role.store() |
|
159 |
local_user.roles = [role.id] |
|
160 |
local_user.store() |
|
161 | ||
162 |
CardDef.wipe() |
|
163 |
carddef = CardDef() |
|
164 |
carddef.name = 'test' |
|
165 |
carddef.fields = [ |
|
166 |
fields.StringField(id='0', label='foobar', varname='foo'), |
|
167 |
fields.StringField(id='1', label='foobar2', varname='foo2'), |
|
168 |
] |
|
169 |
carddef.workflow_roles = {'_viewer': role.id} |
|
170 |
carddef.backoffice_submission_roles = [role.id] |
|
171 |
carddef.digest_template = 'bla {{ form_var_foo }} xxx' |
|
172 |
carddef.store() |
|
173 | ||
174 |
carddef.data_class().wipe() |
|
175 | ||
176 |
get_app(pub).get(sign_uri('/api/cards/test/import-csv'), status=405) |
|
177 |
get_app(pub).put(sign_uri('/api/cards/test/import-csv'), status=403) |
|
178 |
get_app(pub).put( |
|
179 |
sign_uri('/api/cards/test/import-csv', user=local_user), |
|
180 |
params=b'foobar;foobar2\nfirst entry;plop\nsecond entry;plop\n', |
|
181 |
headers={'content-type': 'text/csv'}, |
|
182 |
) |
|
183 |
assert carddef.data_class().count() == 2 |
|
184 |
assert set([x.data['0'] for x in carddef.data_class().select()]) == {'first entry', 'second entry'} |
tests/api/test_category.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import os |
|
4 | ||
5 |
import pytest |
|
6 |
from quixote import get_publisher |
|
7 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app |
|
8 | ||
9 |
from wcs.categories import Category |
|
10 |
from wcs.formdef import FormDef |
|
11 |
from wcs.qommon.http_request import HTTPRequest |
|
12 |
from wcs.roles import Role |
|
13 | ||
14 |
from .utils import sign_uri |
|
15 | ||
16 | ||
17 |
def pytest_generate_tests(metafunc): |
|
18 |
if 'pub' in metafunc.fixturenames: |
|
19 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
20 | ||
21 | ||
22 |
@pytest.fixture |
|
23 |
def pub(request, emails): |
|
24 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
25 | ||
26 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
27 |
pub.set_app_dir(req) |
|
28 |
pub.cfg['identification'] = {'methods': ['password']} |
|
29 |
pub.cfg['language'] = {'language': 'en'} |
|
30 |
pub.write_cfg() |
|
31 | ||
32 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
33 |
'''\ |
|
34 |
[api-secrets] |
|
35 |
coucou = 1234 |
|
36 |
''' |
|
37 |
) |
|
38 | ||
39 |
return pub |
|
40 | ||
41 | ||
42 |
def teardown_module(module): |
|
43 |
clean_temporary_pub() |
|
44 | ||
45 | ||
46 |
@pytest.fixture |
|
47 |
def local_user(): |
|
48 |
get_publisher().user_class.wipe() |
|
49 |
user = get_publisher().user_class() |
|
50 |
user.name = 'Jean Darmette' |
|
51 |
user.email = 'jean.darmette@triffouilis.fr' |
|
52 |
user.name_identifiers = ['0123456789'] |
|
53 |
user.store() |
|
54 |
return user |
|
55 | ||
56 | ||
57 |
def test_categories(pub): |
|
58 |
FormDef.wipe() |
|
59 |
Category.wipe() |
|
60 |
category = Category() |
|
61 |
category.name = 'Category' |
|
62 |
category.description = 'hello world' |
|
63 |
category.store() |
|
64 | ||
65 |
resp = get_app(pub).get('/api/categories/', headers={'Accept': 'application/json'}) |
|
66 |
assert resp.json['data'] == [] # no advertised forms |
|
67 | ||
68 |
formdef = FormDef() |
|
69 |
formdef.name = 'test' |
|
70 |
formdef.category_id = category.id |
|
71 |
formdef.fields = [] |
|
72 |
formdef.keywords = 'mobile, test' |
|
73 |
formdef.store() |
|
74 |
formdef.data_class().wipe() |
|
75 | ||
76 |
formdef = FormDef() |
|
77 |
formdef.name = 'test 2' |
|
78 |
formdef.category_id = category.id |
|
79 |
formdef.fields = [] |
|
80 |
formdef.keywords = 'foobar' |
|
81 |
formdef.store() |
|
82 |
formdef.data_class().wipe() |
|
83 | ||
84 |
resp = get_app(pub).get('/api/categories/') |
|
85 |
resp2 = get_app(pub).get('/categories', headers={'Accept': 'application/json'}) |
|
86 |
assert resp.json == resp2.json |
|
87 |
assert resp.json['data'][0]['title'] == 'Category' |
|
88 |
assert resp.json['data'][0]['url'] == 'http://example.net/category/' |
|
89 |
assert resp.json['data'][0]['description'] == '<p>hello world</p>' |
|
90 |
assert set(resp.json['data'][0]['keywords']) == set(['foobar', 'mobile', 'test']) |
|
91 |
assert 'forms' not in resp.json['data'][0] |
|
92 | ||
93 |
# check HTML description |
|
94 |
category.description = '<p><strong>hello world</strong></p>' |
|
95 |
category.store() |
|
96 |
resp = get_app(pub).get('/api/categories/') |
|
97 |
assert resp.json['data'][0]['description'] == category.description |
|
98 | ||
99 | ||
100 |
def test_categories_private(pub, local_user): |
|
101 |
FormDef.wipe() |
|
102 |
Category.wipe() |
|
103 |
category = Category() |
|
104 |
category.name = 'Category' |
|
105 |
category.description = 'hello world' |
|
106 |
category.store() |
|
107 | ||
108 |
formdef = FormDef() |
|
109 |
formdef.name = 'test' |
|
110 |
formdef.category_id = category.id |
|
111 |
formdef.fields = [] |
|
112 |
formdef.store() |
|
113 |
formdef.data_class().wipe() |
|
114 | ||
115 |
# open form |
|
116 |
resp = get_app(pub).get('/api/categories/') |
|
117 |
assert len(resp.json['data']) == 1 |
|
118 | ||
119 |
# private form, the category doesn't appear anymore |
|
120 |
formdef.roles = ['plop'] |
|
121 |
formdef.store() |
|
122 |
resp = get_app(pub).get('/api/categories/') |
|
123 |
assert len(resp.json['data']) == 0 |
|
124 | ||
125 |
# not even for a signed request specifying an user |
|
126 |
resp = get_app(pub).get(sign_uri('http://example.net/api/categories/', local_user)) |
|
127 |
assert len(resp.json['data']) == 0 |
|
128 | ||
129 |
# but it appears if this is a signed request without user |
|
130 |
resp = get_app(pub).get(sign_uri('http://example.net/api/categories/')) |
|
131 |
assert len(resp.json['data']) == 1 |
|
132 | ||
133 |
# or signed with an authorised user |
|
134 |
local_user.roles = ['plop'] |
|
135 |
local_user.store() |
|
136 |
resp = get_app(pub).get(sign_uri('http://example.net/api/categories/', local_user)) |
|
137 |
assert len(resp.json['data']) == 1 |
|
138 | ||
139 | ||
140 |
def test_categories_formdefs(pub, local_user): |
|
141 |
FormDef.wipe() |
|
142 |
Category.wipe() |
|
143 |
category = Category() |
|
144 |
category.name = 'Category' |
|
145 |
category.description = 'hello world' |
|
146 |
category.store() |
|
147 | ||
148 |
formdef = FormDef() |
|
149 |
formdef.name = 'test' |
|
150 |
formdef.category_id = category.id |
|
151 |
formdef.fields = [] |
|
152 |
formdef.keywords = 'mobile, test' |
|
153 |
formdef.store() |
|
154 |
formdef.data_class().wipe() |
|
155 | ||
156 |
formdef = FormDef() |
|
157 |
formdef.name = 'test 2' |
|
158 |
formdef.category_id = category.id |
|
159 |
formdef.fields = [] |
|
160 |
formdef.keywords = 'foobar' |
|
161 |
formdef.store() |
|
162 |
formdef.data_class().wipe() |
|
163 | ||
164 |
formdef2 = FormDef() |
|
165 |
formdef2.name = 'other test' |
|
166 |
formdef2.category_id = None |
|
167 |
formdef2.fields = [] |
|
168 |
formdef2.store() |
|
169 |
formdef2.data_class().wipe() |
|
170 | ||
171 |
formdef2 = FormDef() |
|
172 |
formdef2.name = 'test disabled' |
|
173 |
formdef2.category_id = category.id |
|
174 |
formdef2.fields = [] |
|
175 |
formdef2.disabled = True |
|
176 |
formdef2.store() |
|
177 |
formdef2.data_class().wipe() |
|
178 | ||
179 |
resp = get_app(pub).get('/api/categories/category/formdefs/', status=403) |
|
180 |
resp2 = get_app(pub).get('/category/json', status=403) |
|
181 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/')) |
|
182 |
resp2 = get_app(pub).get(sign_uri('/category/json')) |
|
183 |
assert resp.json == resp2.json |
|
184 |
assert resp.json['err'] == 0 |
|
185 |
assert len(resp.json['data']) == 2 |
|
186 |
assert resp.json['data'][0]['title'] == 'test' |
|
187 |
assert resp.json['data'][0]['url'] == 'http://example.net/test/' |
|
188 |
assert resp.json['data'][0]['redirection'] is False |
|
189 |
assert resp.json['data'][0]['category'] == 'Category' |
|
190 |
assert resp.json['data'][0]['category_slug'] == 'category' |
|
191 |
assert 'count' not in resp.json['data'][0] |
|
192 | ||
193 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?include-count=on')) |
|
194 |
assert resp.json['data'][0]['title'] == 'test' |
|
195 |
assert resp.json['data'][0]['url'] == 'http://example.net/test/' |
|
196 |
assert resp.json['data'][0]['count'] == 0 |
|
197 | ||
198 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?include-disabled=on')) |
|
199 |
assert len(resp.json['data']) == 3 |
|
200 |
assert resp.json['data'][2]['title'] == 'test disabled' |
|
201 | ||
202 |
get_app(pub).get('/api/categories/XXX/formdefs/', status=404) |
|
203 | ||
204 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?backoffice-submission=on')) |
|
205 |
assert resp.json['err'] == 0 |
|
206 |
assert len(resp.json['data']) == 0 |
|
207 | ||
208 |
Role.wipe() |
|
209 |
role = Role(name='test') |
|
210 |
role.store() |
|
211 |
local_user.roles = [] |
|
212 |
local_user.store() |
|
213 |
# check it's not advertised ... |
|
214 |
formdef.backoffice_submission_roles = [role.id] |
|
215 |
formdef.store() |
|
216 |
resp = get_app(pub).get(sign_uri('/api/categories/category/formdefs/?backoffice-submission=on')) |
|
217 |
assert resp.json['err'] == 0 |
|
218 |
assert len(resp.json['data']) == 0 |
|
219 |
resp = get_app(pub).get( |
|
220 |
sign_uri( |
|
221 |
'/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' |
|
222 |
% local_user.name_identifiers[0] |
|
223 |
) |
|
224 |
) |
|
225 |
assert resp.json['err'] == 0 |
|
226 |
assert len(resp.json['data']) == 0 |
|
227 |
# ... unless user has correct roles |
|
228 |
local_user.roles = [role.id] |
|
229 |
local_user.store() |
|
230 |
resp = get_app(pub).get( |
|
231 |
sign_uri( |
|
232 |
'/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' |
|
233 |
% local_user.name_identifiers[0] |
|
234 |
) |
|
235 |
) |
|
236 |
assert resp.json['err'] == 0 |
|
237 |
assert len(resp.json['data']) == 1 |
|
238 | ||
239 | ||
240 |
def test_categories_full(pub): |
|
241 |
test_categories(pub) |
|
242 |
resp = get_app(pub).get('/api/categories/?full=on') |
|
243 |
assert len(resp.json['data'][0]['forms']) == 2 |
|
244 |
assert resp.json['data'][0]['forms'][0]['title'] == 'test' |
|
245 |
assert resp.json['data'][0]['forms'][1]['title'] == 'test 2' |
tests/api/test_custom_view.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import os |
|
4 |
import xml.etree.ElementTree as ET |
|
5 |
import zipfile |
|
6 | ||
7 |
import pytest |
|
8 |
from django.utils.six import BytesIO |
|
9 |
from quixote import get_publisher |
|
10 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app |
|
11 | ||
12 |
from wcs import fields |
|
13 |
from wcs.carddef import CardDef |
|
14 |
from wcs.formdef import FormDef |
|
15 |
from wcs.qommon import ods |
|
16 |
from wcs.qommon.http_request import HTTPRequest |
|
17 |
from wcs.roles import Role |
|
18 | ||
19 |
from .utils import sign_uri |
|
20 | ||
21 | ||
22 |
def pytest_generate_tests(metafunc): |
|
23 |
if 'pub' in metafunc.fixturenames: |
|
24 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
25 | ||
26 | ||
27 |
@pytest.fixture |
|
28 |
def pub(request, emails): |
|
29 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
30 | ||
31 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
32 |
pub.set_app_dir(req) |
|
33 |
pub.cfg['identification'] = {'methods': ['password']} |
|
34 |
pub.cfg['language'] = {'language': 'en'} |
|
35 |
pub.write_cfg() |
|
36 | ||
37 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
38 |
'''\ |
|
39 |
[api-secrets] |
|
40 |
coucou = 1234 |
|
41 |
''' |
|
42 |
) |
|
43 | ||
44 |
return pub |
|
45 | ||
46 | ||
47 |
def teardown_module(module): |
|
48 |
clean_temporary_pub() |
|
49 | ||
50 | ||
51 |
@pytest.fixture |
|
52 |
def local_user(): |
|
53 |
get_publisher().user_class.wipe() |
|
54 |
user = get_publisher().user_class() |
|
55 |
user.name = 'Jean Darmette' |
|
56 |
user.email = 'jean.darmette@triffouilis.fr' |
|
57 |
user.name_identifiers = ['0123456789'] |
|
58 |
user.store() |
|
59 |
return user |
|
60 | ||
61 | ||
62 |
@pytest.fixture |
|
63 |
def test_api_custom_view_access(pub, local_user): |
|
64 |
Role.wipe() |
|
65 |
role = Role(name='test') |
|
66 |
role.store() |
|
67 |
local_user.roles = [role.id] |
|
68 |
local_user.store() |
|
69 | ||
70 |
FormDef.wipe() |
|
71 |
formdef = FormDef() |
|
72 |
formdef.name = 'test' |
|
73 |
formdef.workflow_roles = {'_receiver': role.id} |
|
74 |
formdef.fields = [fields.StringField(id='0', label='foobar', varname='foobar')] |
|
75 |
formdef.geolocations = {'base': 'Location'} |
|
76 |
formdef.store() |
|
77 | ||
78 |
carddef = CardDef() |
|
79 |
carddef.name = 'test' |
|
80 |
carddef.fields = [fields.StringField(id='0', label='foobar', varname='foo')] |
|
81 |
carddef.workflow_roles = {'_viewer': role.id} |
|
82 |
carddef.digest_template = 'bla {{ form_var_foo }} xxx' |
|
83 |
carddef.geolocations = {'base': 'Location'} |
|
84 |
carddef.store() |
|
85 | ||
86 |
pub.custom_view_class.wipe() |
|
87 |
custom_view = pub.custom_view_class() |
|
88 |
custom_view.title = 'shared formdef custom view' |
|
89 |
custom_view.formdef = formdef |
|
90 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
91 |
custom_view.filters = {} |
|
92 |
custom_view.visibility = 'any' |
|
93 |
custom_view.store() |
|
94 | ||
95 |
custom_view = pub.custom_view_class() |
|
96 |
custom_view.title = 'private formdef custom view' |
|
97 |
custom_view.formdef = formdef |
|
98 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
99 |
custom_view.filters = {} |
|
100 |
custom_view.visibility = 'owner' |
|
101 |
custom_view.user = local_user |
|
102 |
custom_view.store() |
|
103 | ||
104 |
custom_view = pub.custom_view_class() |
|
105 |
custom_view.title = 'shared carddef custom view' |
|
106 |
custom_view.formdef = carddef |
|
107 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
108 |
custom_view.filters = {} |
|
109 |
custom_view.visibility = 'any' |
|
110 |
custom_view.store() |
|
111 | ||
112 |
custom_view = pub.custom_view_class() |
|
113 |
custom_view.title = 'private carddef custom view' |
|
114 |
custom_view.formdef = carddef |
|
115 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
116 |
custom_view.filters = {} |
|
117 |
custom_view.visibility = 'owner' |
|
118 |
custom_view.user = local_user |
|
119 |
custom_view.store() |
|
120 | ||
121 |
custom_view = pub.custom_view_class() |
|
122 |
custom_view.title = 'datasource carddef custom view' |
|
123 |
custom_view.formdef = carddef |
|
124 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
125 |
custom_view.filters = {} |
|
126 |
custom_view.visibility = 'datasource' |
|
127 |
custom_view.store() |
|
128 | ||
129 |
get_app(pub).get(sign_uri('/api/forms/test/list/shared-formdef-custom-view', user=local_user), status=200) |
|
130 |
get_app(pub).get(sign_uri('/api/forms/test/ods/shared-formdef-custom-view', user=local_user), status=200) |
|
131 |
get_app(pub).get( |
|
132 |
sign_uri('/api/forms/test/geojson/shared-formdef-custom-view', user=local_user), status=200 |
|
133 |
) |
|
134 |
get_app(pub).get( |
|
135 |
sign_uri('/api/forms/test/list/private-formdef-custom-view', user=local_user), status=404 |
|
136 |
) |
|
137 |
get_app(pub).get(sign_uri('/api/forms/test/ods/private-formdef-custom-view', user=local_user), status=404) |
|
138 |
get_app(pub).get( |
|
139 |
sign_uri('/api/forms/test/geojson/private-formdef-custom-view', user=local_user), status=404 |
|
140 |
) |
|
141 | ||
142 |
get_app(pub).get(sign_uri('/api/cards/test/list/shared-carddef-custom-view', user=local_user), status=200) |
|
143 |
get_app(pub).get(sign_uri('/api/cards/test/ods/shared-carddef-custom-view', user=local_user), status=200) |
|
144 |
get_app(pub).get( |
|
145 |
sign_uri('/api/cards/test/geojson/shared-carddef-custom-view', user=local_user), status=200 |
|
146 |
) |
|
147 |
get_app(pub).get( |
|
148 |
sign_uri('/api/cards/test/list/private-carddef-custom-view', user=local_user), status=404 |
|
149 |
) |
|
150 |
get_app(pub).get(sign_uri('/api/cards/test/ods/private-carddef-custom-view', user=local_user), status=404) |
|
151 |
get_app(pub).get( |
|
152 |
sign_uri('/api/cards/test/geojson/private-carddef-custom-view', user=local_user), status=404 |
|
153 |
) |
|
154 |
get_app(pub).get( |
|
155 |
sign_uri('/api/cards/test/list/datasource-carddef-custom-view', user=local_user), status=200 |
|
156 |
) |
|
157 |
get_app(pub).get( |
|
158 |
sign_uri('/api/cards/test/ods/datasource-carddef-custom-view', user=local_user), status=200 |
|
159 |
) |
|
160 |
get_app(pub).get( |
|
161 |
sign_uri('/api/cards/test/geojson/datasource-carddef-custom-view', user=local_user), status=200 |
|
162 |
) |
|
163 | ||
164 | ||
165 |
def test_api_list_formdata_custom_view(pub, local_user): |
|
166 |
Role.wipe() |
|
167 |
role = Role(name='test') |
|
168 |
role.store() |
|
169 | ||
170 |
FormDef.wipe() |
|
171 |
formdef = FormDef() |
|
172 |
formdef.name = 'test' |
|
173 |
formdef.workflow_roles = {'_receiver': role.id} |
|
174 |
formdef.fields = [ |
|
175 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
176 |
] |
|
177 |
formdef.store() |
|
178 | ||
179 |
data_class = formdef.data_class() |
|
180 |
data_class.wipe() |
|
181 | ||
182 |
for i in range(30): |
|
183 |
formdata = data_class() |
|
184 |
formdata.data = {'0': 'FOO BAR %d' % i} |
|
185 |
formdata.user_id = local_user.id |
|
186 |
formdata.just_created() |
|
187 |
if i % 3 == 0: |
|
188 |
formdata.jump_status('new') |
|
189 |
else: |
|
190 |
formdata.jump_status('finished') |
|
191 |
formdata.store() |
|
192 | ||
193 |
# add proper role to user |
|
194 |
local_user.roles = [role.id] |
|
195 |
local_user.store() |
|
196 | ||
197 |
# check it now gets the data |
|
198 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user)) |
|
199 |
assert len(resp.json) == 30 |
|
200 | ||
201 |
pub.custom_view_class.wipe() |
|
202 |
custom_view = pub.custom_view_class() |
|
203 |
custom_view.title = 'custom view' |
|
204 |
custom_view.formdef = formdef |
|
205 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
206 |
custom_view.filters = {"filter": "done", "filter-status": "on"} |
|
207 |
custom_view.visibility = 'any' |
|
208 |
custom_view.store() |
|
209 | ||
210 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list/custom-view', user=local_user)) |
|
211 |
assert len(resp.json['data']) == 20 |
|
212 | ||
213 | ||
214 |
def test_api_ods_formdata_custom_view(pub, local_user): |
|
215 |
Role.wipe() |
|
216 |
role = Role(name='test') |
|
217 |
role.store() |
|
218 | ||
219 |
FormDef.wipe() |
|
220 |
formdef = FormDef() |
|
221 |
formdef.name = 'test' |
|
222 |
formdef.workflow_roles = {'_receiver': role.id} |
|
223 |
formdef.fields = [ |
|
224 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
225 |
] |
|
226 |
formdef.store() |
|
227 | ||
228 |
data_class = formdef.data_class() |
|
229 |
data_class.wipe() |
|
230 | ||
231 |
for i in range(30): |
|
232 |
formdata = data_class() |
|
233 |
formdata.data = {'0': 'FOO BAR %d' % i} |
|
234 |
formdata.user_id = local_user.id |
|
235 |
formdata.just_created() |
|
236 |
if i % 3 == 0: |
|
237 |
formdata.jump_status('new') |
|
238 |
else: |
|
239 |
formdata.jump_status('finished') |
|
240 |
formdata.store() |
|
241 | ||
242 |
# add proper role to user |
|
243 |
local_user.roles = [role.id] |
|
244 |
local_user.store() |
|
245 | ||
246 |
# check it now gets the data |
|
247 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user)) |
|
248 |
zipf = zipfile.ZipFile(BytesIO(resp.body)) |
|
249 |
ods_sheet = ET.parse(zipf.open('content.xml')) |
|
250 |
assert len(ods_sheet.findall('.//{%s}table-row' % ods.NS['table'])) == 11 |
|
251 | ||
252 |
pub.custom_view_class.wipe() |
|
253 |
custom_view = pub.custom_view_class() |
|
254 |
custom_view.title = 'custom view' |
|
255 |
custom_view.formdef = formdef |
|
256 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
257 |
custom_view.filters = {"filter": "done", "filter-status": "on"} |
|
258 |
custom_view.visibility = 'any' |
|
259 |
custom_view.store() |
|
260 | ||
261 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods/custom-view', user=local_user)) |
|
262 |
zipf = zipfile.ZipFile(BytesIO(resp.body)) |
|
263 |
ods_sheet = ET.parse(zipf.open('content.xml')) |
|
264 |
assert len(ods_sheet.findall('.//{%s}table-row' % ods.NS['table'])) == 21 |
|
265 | ||
266 | ||
267 |
def test_api_geojson_formdata_custom_view(pub, local_user): |
|
268 |
Role.wipe() |
|
269 |
role = Role(name='test') |
|
270 |
role.store() |
|
271 | ||
272 |
FormDef.wipe() |
|
273 |
formdef = FormDef() |
|
274 |
formdef.name = 'test' |
|
275 |
formdef.workflow_roles = {'_receiver': role.id} |
|
276 |
formdef.fields = [ |
|
277 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
278 |
] |
|
279 |
formdef.geolocations = {'base': 'Location'} |
|
280 |
formdef.store() |
|
281 | ||
282 |
data_class = formdef.data_class() |
|
283 |
data_class.wipe() |
|
284 | ||
285 |
for i in range(30): |
|
286 |
formdata = data_class() |
|
287 |
formdata.data = {'0': 'FOO BAR %d' % i} |
|
288 |
formdata.geolocations = {'base': {'lat': 48, 'lon': 2}} |
|
289 |
formdata.user_id = local_user.id |
|
290 |
formdata.just_created() |
|
291 |
if i % 3 == 0: |
|
292 |
formdata.jump_status('new') |
|
293 |
else: |
|
294 |
formdata.jump_status('finished') |
|
295 |
formdata.store() |
|
296 | ||
297 |
# add proper role to user |
|
298 |
local_user.roles = [role.id] |
|
299 |
local_user.store() |
|
300 | ||
301 |
# check it now gets the data |
|
302 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user)) |
|
303 |
assert len(resp.json['features']) == 10 |
|
304 | ||
305 |
pub.custom_view_class.wipe() |
|
306 |
custom_view = pub.custom_view_class() |
|
307 |
custom_view.title = 'custom view' |
|
308 |
custom_view.formdef = formdef |
|
309 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
310 |
custom_view.filters = {"filter": "done", "filter-status": "on"} |
|
311 |
custom_view.visibility = 'any' |
|
312 |
custom_view.store() |
|
313 | ||
314 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson/custom-view', user=local_user)) |
|
315 |
assert len(resp.json['features']) == 20 |
tests/api/test_formdata.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import base64 |
|
4 |
import datetime |
|
5 |
import os |
|
6 |
import re |
|
7 |
import time |
|
8 |
import xml.etree.ElementTree as ET |
|
9 |
import zipfile |
|
10 | ||
11 |
import pytest |
|
12 |
from django.utils.encoding import force_bytes |
|
13 |
from django.utils.six import BytesIO |
|
14 |
from quixote import get_publisher |
|
15 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app, login |
|
16 | ||
17 |
from wcs import fields |
|
18 |
from wcs.blocks import BlockDef |
|
19 |
from wcs.data_sources import NamedDataSource |
|
20 |
from wcs.formdata import Evolution |
|
21 |
from wcs.formdef import FormDef |
|
22 |
from wcs.qommon import ods |
|
23 |
from wcs.qommon.form import PicklableUpload |
|
24 |
from wcs.qommon.http_request import HTTPRequest |
|
25 |
from wcs.qommon.ident.password_accounts import PasswordAccount |
|
26 |
from wcs.roles import Role |
|
27 |
from wcs.workflows import EditableWorkflowStatusItem, Workflow, WorkflowBackofficeFieldsFormDef |
|
28 | ||
29 |
from .utils import sign_uri |
|
30 | ||
31 | ||
32 |
def pytest_generate_tests(metafunc): |
|
33 |
if 'pub' in metafunc.fixturenames: |
|
34 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
35 | ||
36 | ||
37 |
@pytest.fixture |
|
38 |
def pub(request, emails): |
|
39 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
40 | ||
41 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
42 |
pub.set_app_dir(req) |
|
43 |
pub.cfg['identification'] = {'methods': ['password']} |
|
44 |
pub.cfg['language'] = {'language': 'en'} |
|
45 |
pub.write_cfg() |
|
46 | ||
47 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
48 |
'''\ |
|
49 |
[api-secrets] |
|
50 |
coucou = 1234 |
|
51 |
''' |
|
52 |
) |
|
53 | ||
54 |
return pub |
|
55 | ||
56 | ||
57 |
def teardown_module(module): |
|
58 |
clean_temporary_pub() |
|
59 | ||
60 | ||
61 |
@pytest.fixture |
|
62 |
def local_user(): |
|
63 |
get_publisher().user_class.wipe() |
|
64 |
user = get_publisher().user_class() |
|
65 |
user.name = 'Jean Darmette' |
|
66 |
user.email = 'jean.darmette@triffouilis.fr' |
|
67 |
user.name_identifiers = ['0123456789'] |
|
68 |
user.store() |
|
69 |
return user |
|
70 | ||
71 | ||
72 |
@pytest.fixture |
|
73 |
def admin_user(): |
|
74 |
get_publisher().user_class.wipe() |
|
75 |
user = get_publisher().user_class() |
|
76 |
user.name = 'John Doe Admin' |
|
77 |
user.email = 'john.doe@example.com' |
|
78 |
user.name_identifiers = ['0123456789'] |
|
79 |
user.is_admin = True |
|
80 |
user.store() |
|
81 | ||
82 |
account = PasswordAccount(id='admin') |
|
83 |
account.set_password('admin') |
|
84 |
account.user_id = user.id |
|
85 |
account.store() |
|
86 | ||
87 |
return user |
|
88 | ||
89 | ||
90 |
@pytest.fixture |
|
91 |
def ics_data(local_user): |
|
92 |
Role.wipe() |
|
93 |
role = Role(name='test') |
|
94 |
role.store() |
|
95 | ||
96 |
FormDef.wipe() |
|
97 |
formdef = FormDef() |
|
98 |
formdef.url_name = 'test' |
|
99 |
formdef.name = 'testé' |
|
100 |
formdef.workflow_roles = {'_receiver': role.id} |
|
101 |
formdef.fields = [ |
|
102 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
103 |
fields.StringField(id='1', label='foobar2', varname='foobar2'), |
|
104 |
] |
|
105 |
formdef.digest_template = 'plöp {{ form_var_foobar }} plÔp' |
|
106 |
formdef.store() |
|
107 | ||
108 |
data_class = formdef.data_class() |
|
109 |
data_class.wipe() |
|
110 | ||
111 |
date = datetime.datetime(2014, 1, 20, 12, 00) |
|
112 |
for i in range(30): |
|
113 |
formdata = data_class() |
|
114 |
formdata.data = {'0': (date + datetime.timedelta(days=i)).strftime('%Y-%m-%d %H:%M')} |
|
115 |
formdata.data['1'] = (date + datetime.timedelta(days=i, minutes=i + 1)).strftime('%Y-%m-%d %H:%M') |
|
116 |
formdata.user_id = local_user.id |
|
117 |
formdata.just_created() |
|
118 |
if i % 3 == 0: |
|
119 |
formdata.jump_status('new') |
|
120 |
else: |
|
121 |
formdata.jump_status('finished') |
|
122 |
formdata.store() |
|
123 | ||
124 |
# not a datetime: ignored |
|
125 |
date = datetime.date(2014, 1, 20) |
|
126 |
formdata = data_class() |
|
127 |
formdata.data = {'0': '12:00'} |
|
128 |
formdata.data['1'] = '13:00' |
|
129 |
formdata.user_id = local_user.id |
|
130 |
formdata.just_created() |
|
131 |
formdata.jump_status('new') |
|
132 |
formdata.store() |
|
133 | ||
134 | ||
135 |
def test_formdata(pub, local_user): |
|
136 |
NamedDataSource.wipe() |
|
137 |
data_source = NamedDataSource(name='foobar') |
|
138 |
data_source.data_source = { |
|
139 |
'type': 'formula', |
|
140 |
'value': repr([{'id': '1', 'text': 'foo', 'more': 'XXX'}, {'id': '2', 'text': 'bar', 'more': 'YYY'}]), |
|
141 |
} |
|
142 |
data_source.store() |
|
143 | ||
144 |
BlockDef.wipe() |
|
145 |
block = BlockDef() |
|
146 |
block.name = 'foobar' |
|
147 |
block.fields = [ |
|
148 |
fields.StringField(id='abc', label='Foo', varname='foo'), |
|
149 |
fields.ItemField(id='xyz', label='Test', type='item', data_source={'type': 'foobar'}, varname='bar'), |
|
150 |
] |
|
151 |
block.store() |
|
152 | ||
153 |
Role.wipe() |
|
154 |
role = Role(name='test') |
|
155 |
role.id = '123' |
|
156 |
role.store() |
|
157 |
another_role = Role(name='another') |
|
158 |
another_role.id = '321' |
|
159 |
another_role.store() |
|
160 |
FormDef.wipe() |
|
161 |
formdef = FormDef() |
|
162 |
formdef.geolocations = {'base': 'blah'} |
|
163 |
formdef.name = 'test' |
|
164 |
formdef.fields = [ |
|
165 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
166 |
fields.StringField(id='1', label='foobar2'), |
|
167 |
fields.DateField(id='2', label='foobar3', varname='date'), |
|
168 |
fields.FileField(id='3', label='foobar4', varname='file'), |
|
169 |
fields.ItemField(id='4', label='foobar5', varname='item', data_source={'type': 'foobar'}), |
|
170 |
fields.BlockField(id='5', label='test', varname='blockdata', type='block:foobar', max_items=3), |
|
171 |
] |
|
172 |
Workflow.wipe() |
|
173 |
workflow = Workflow(name='foo') |
|
174 |
workflow.possible_status = Workflow.get_default_workflow().possible_status[:] |
|
175 |
workflow.roles['_foobar'] = 'Foobar' |
|
176 |
workflow.store() |
|
177 |
formdef.workflow_id = workflow.id |
|
178 |
formdef.workflow_roles = {'_receiver': role.id, '_foobar': another_role.id} |
|
179 |
formdef.store() |
|
180 |
item_field = formdef.fields[4] |
|
181 | ||
182 |
formdef.data_class().wipe() |
|
183 |
formdata = formdef.data_class()() |
|
184 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
|
185 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
186 |
upload.receive([b'base64me']) |
|
187 |
formdata.data = { |
|
188 |
'0': 'foo@localhost', |
|
189 |
'1': 'xxx', |
|
190 |
'2': date, |
|
191 |
'3': upload, |
|
192 |
'4': '1', |
|
193 |
'5': { |
|
194 |
'data': [ |
|
195 |
{'abc': 'plop', 'xyz': '1', 'xyz_display': 'foo', 'xyz_structured': 'XXX'}, |
|
196 |
], |
|
197 |
'schema': {}, # not important here |
|
198 |
}, |
|
199 |
'5_display': 'hello', |
|
200 |
} |
|
201 |
formdata.data['4_display'] = item_field.store_display_value(formdata.data, item_field.id) |
|
202 |
formdata.data['4_structured'] = item_field.store_structured_value(formdata.data, item_field.id) |
|
203 |
formdata.user_id = local_user.id |
|
204 |
formdata.just_created() |
|
205 |
formdata.status = 'wf-new' |
|
206 |
formdata.evolution[-1].status = 'wf-new' |
|
207 |
formdata.geolocations = {'base': {'lon': 10, 'lat': -12}} |
|
208 |
formdata.store() |
|
209 | ||
210 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=403) |
|
211 | ||
212 |
local_user.roles = [role.id] |
|
213 |
local_user.store() |
|
214 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
215 | ||
216 |
assert datetime.datetime.strptime(resp.json['last_update_time'], '%Y-%m-%dT%H:%M:%S') |
|
217 |
assert datetime.datetime.strptime(resp.json['receipt_time'], '%Y-%m-%dT%H:%M:%S') |
|
218 |
assert len(resp.json['fields']) == 8 |
|
219 |
assert 'foobar' in resp.json['fields'] |
|
220 |
assert 'foobar2' not in resp.json['fields'] # foobar2 has no varname, not in json |
|
221 |
assert resp.json['user']['name'] == local_user.name |
|
222 |
assert resp.json['fields']['foobar'] == 'foo@localhost' |
|
223 |
assert resp.json['fields']['date'] == '2014-01-20' |
|
224 |
assert resp.json['fields']['file']['content'] == 'YmFzZTY0bWU=' # base64('base64me') |
|
225 |
assert resp.json['fields']['file']['filename'] == 'test.txt' |
|
226 |
assert resp.json['fields']['file']['content_type'] == 'text/plain' |
|
227 |
assert resp.json['fields']['item'] == 'foo' |
|
228 |
assert resp.json['fields']['item_raw'] == '1' |
|
229 |
assert resp.json['fields']['item_structured'] == {'id': '1', 'text': 'foo', 'more': 'XXX'} |
|
230 |
assert resp.json['fields']['blockdata'] == 'hello' |
|
231 |
assert resp.json['fields']['blockdata_raw'] == [ |
|
232 |
{'foo': 'plop', 'bar': 'foo', 'bar_raw': '1', 'bar_structured': 'XXX'} |
|
233 |
] |
|
234 |
assert resp.json['workflow']['status']['name'] == 'New' |
|
235 |
assert resp.json['workflow']['real_status']['name'] == 'New' |
|
236 |
assert resp.json['submission']['channel'] == 'web' |
|
237 |
assert resp.json['geolocations']['base']['lon'] == 10 |
|
238 |
assert resp.json['geolocations']['base']['lat'] == -12 |
|
239 | ||
240 |
assert [x.get('id') for x in resp.json['roles']['_receiver']] == [str(role.id)] |
|
241 |
assert [x.get('id') for x in resp.json['roles']['_foobar']] == [str(another_role.id)] |
|
242 |
assert set([x.get('id') for x in resp.json['roles']['concerned']]) == set( |
|
243 |
[str(role.id), str(another_role.id)] |
|
244 |
) |
|
245 |
assert [x.get('id') for x in resp.json['roles']['actions']] == [str(role.id)] |
|
246 | ||
247 |
# check the ?format=json endpoint returns 403 |
|
248 |
get_app(pub).get('/test/%s/?format=json' % formdata.id, status=403) |
|
249 |
get_app(pub).get(sign_uri('/test/%s/' % formdata.id, user=local_user), status=403) |
|
250 | ||
251 |
# check status visibility |
|
252 |
workflow.add_status('Status1', 'st1') |
|
253 |
workflow.possible_status[-1].visibility = ['unknown'] |
|
254 |
workflow.store() |
|
255 |
formdata.jump_status('st1') |
|
256 |
assert formdata.status == 'wf-st1' |
|
257 | ||
258 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
259 |
assert resp.json['workflow']['status'] == {'id': 'new', 'name': 'New'} |
|
260 |
assert resp.json['workflow']['real_status'] == {'id': 'st1', 'name': 'Status1'} |
|
261 | ||
262 | ||
263 |
def test_formdata_backoffice_fields(pub, local_user): |
|
264 |
test_formdata(pub, local_user) |
|
265 |
Workflow.wipe() |
|
266 |
workflow = Workflow(name='foo') |
|
267 |
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) |
|
268 |
workflow.backoffice_fields_formdef.fields = [ |
|
269 |
fields.StringField(id='bo1', label='1st backoffice field', type='string', varname='backoffice_blah'), |
|
270 |
] |
|
271 |
workflow.store() |
|
272 | ||
273 |
formdef = FormDef.select()[0] |
|
274 |
formdata = formdef.data_class().select()[0] |
|
275 |
formdata.data['bo1'] = 'Hello world' |
|
276 |
formdata.store() |
|
277 | ||
278 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user)) |
|
279 |
assert resp.json['workflow']['fields']['backoffice_blah'] == 'Hello world' |
|
280 | ||
281 | ||
282 |
def test_formdata_duplicated_varnames(pub, local_user): |
|
283 |
Role.wipe() |
|
284 |
role = Role(name='test') |
|
285 |
role.id = '123' |
|
286 |
role.store() |
|
287 |
another_role = Role(name='another') |
|
288 |
another_role.id = '321' |
|
289 |
another_role.store() |
|
290 |
FormDef.wipe() |
|
291 |
formdef = FormDef() |
|
292 |
formdef.geolocations = {'base': 'blah'} |
|
293 |
formdef.name = 'test' |
|
294 |
formdef.fields = [ |
|
295 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
296 |
fields.StringField(id='1', label='foobar2', varname='foobar'), |
|
297 |
] |
|
298 |
workflow = Workflow.get_default_workflow() |
|
299 |
workflow.roles['_foobar'] = 'Foobar' |
|
300 |
workflow.id = '2' |
|
301 |
workflow.store() |
|
302 |
formdef.workflow_id = workflow.id |
|
303 |
formdef.workflow_roles = {'_receiver': role.id, '_foobar': another_role.id} |
|
304 |
formdef.store() |
|
305 | ||
306 |
formdef.data_class().wipe() |
|
307 |
formdata = formdef.data_class()() |
|
308 |
formdata.data = { |
|
309 |
'0': 'foo', |
|
310 |
'1': 'bar', |
|
311 |
} |
|
312 |
formdata.user_id = local_user.id |
|
313 |
formdata.just_created() |
|
314 |
formdata.status = 'wf-new' |
|
315 |
formdata.evolution[-1].status = 'wf-new' |
|
316 |
formdata.store() |
|
317 | ||
318 |
local_user.roles = [role.id] |
|
319 |
local_user.store() |
|
320 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
321 |
assert resp.json['fields'] == {'foobar': 'foo'} |
|
322 | ||
323 |
formdata.data = { |
|
324 |
'0': 'foo', |
|
325 |
'1': '', |
|
326 |
} |
|
327 |
formdata.store() |
|
328 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
329 |
assert resp.json['fields'] == {'foobar': 'foo'} |
|
330 | ||
331 |
formdata.data = { |
|
332 |
'0': '', |
|
333 |
'1': 'foo', |
|
334 |
} |
|
335 |
formdata.store() |
|
336 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), status=200) |
|
337 |
assert resp.json['fields'] == {'foobar': 'foo'} |
|
338 | ||
339 | ||
340 |
def test_formdata_edit(pub, local_user): |
|
341 |
Role.wipe() |
|
342 |
role = Role(name='test') |
|
343 |
role.id = '123' |
|
344 |
role.store() |
|
345 |
another_role = Role(name='another') |
|
346 |
another_role.id = '321' |
|
347 |
another_role.store() |
|
348 |
local_user.roles = [role.id] |
|
349 |
local_user.store() |
|
350 |
FormDef.wipe() |
|
351 |
formdef = FormDef() |
|
352 |
formdef.name = 'test' |
|
353 |
formdef.fields = [ |
|
354 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
355 |
] |
|
356 |
Workflow.wipe() |
|
357 |
workflow = Workflow(name='foo') |
|
358 |
workflow.possible_status = Workflow.get_default_workflow().possible_status[:] |
|
359 |
workflow.roles['_foobar'] = 'Foobar' |
|
360 |
workflow.store() |
|
361 |
formdef.workflow_id = workflow.id |
|
362 |
formdef.workflow_roles = {'_receiver': role.id, '_foobar': another_role.id} |
|
363 |
formdef.store() |
|
364 |
formdef.data_class().wipe() |
|
365 |
formdata = formdef.data_class()() |
|
366 |
formdata.data = { |
|
367 |
'0': 'foo@localhost', |
|
368 |
} |
|
369 |
formdata.user_id = local_user.id |
|
370 |
formdata.just_created() |
|
371 |
formdata.status = 'wf-new' |
|
372 |
formdata.evolution[-1].status = 'wf-new' |
|
373 |
formdata.store() |
|
374 | ||
375 |
# not user |
|
376 |
get_app(pub).post_json( |
|
377 |
sign_uri('/api/forms/test/%s/' % formdata.id), {'data': {'0': 'bar@localhost'}}, status=403 |
|
378 |
) |
|
379 | ||
380 |
# no editable action |
|
381 |
get_app(pub).post_json( |
|
382 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
383 |
{'data': {'0': 'bar@localhost'}}, |
|
384 |
status=403, |
|
385 |
) |
|
386 | ||
387 |
wfedit = EditableWorkflowStatusItem() |
|
388 |
wfedit.id = '_wfedit' |
|
389 |
wfedit.by = [local_user.roles[0]] |
|
390 |
workflow.possible_status[1].items.append(wfedit) |
|
391 |
wfedit.parent = workflow.possible_status[1] |
|
392 |
workflow.store() |
|
393 | ||
394 |
get_app(pub).post_json( |
|
395 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
396 |
{'data': {'0': 'bar@localhost'}}, |
|
397 |
status=200, |
|
398 |
) |
|
399 |
assert formdef.data_class().select()[0].data['0'] == 'bar@localhost' |
|
400 | ||
401 |
# not editable by user role |
|
402 |
wfedit.by = ['XX'] |
|
403 |
workflow.store() |
|
404 |
get_app(pub).post_json( |
|
405 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
406 |
{'data': {'0': 'bar@localhost'}}, |
|
407 |
status=403, |
|
408 |
) |
|
409 | ||
410 |
# edit + jump |
|
411 |
wfedit.status = 'rejected' |
|
412 |
wfedit.by = [local_user.roles[0]] |
|
413 |
workflow.store() |
|
414 | ||
415 |
get_app(pub).post_json( |
|
416 |
sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user), |
|
417 |
{'data': {'0': 'bar2@localhost'}}, |
|
418 |
status=200, |
|
419 |
) |
|
420 |
assert formdef.data_class().select()[0].data['0'] == 'bar2@localhost' |
|
421 |
assert formdef.data_class().select()[0].status == 'wf-rejected' |
|
422 | ||
423 | ||
424 |
def test_formdata_with_workflow_data(pub, local_user): |
|
425 |
Role.wipe() |
|
426 |
role = Role(name='test') |
|
427 |
role.id = '123' |
|
428 |
role.store() |
|
429 | ||
430 |
local_user.roles = [role.id] |
|
431 |
local_user.store() |
|
432 | ||
433 |
FormDef.wipe() |
|
434 |
formdef = FormDef() |
|
435 |
formdef.name = 'test' |
|
436 |
formdef.fields = [] |
|
437 |
workflow = Workflow.get_default_workflow() |
|
438 |
workflow.id = '2' |
|
439 |
workflow.store() |
|
440 |
formdef.workflow_id = workflow.id |
|
441 |
formdef.workflow_roles = {'_receiver': role.id} |
|
442 |
formdef.store() |
|
443 | ||
444 |
formdef.data_class().wipe() |
|
445 |
formdata = formdef.data_class()() |
|
446 |
formdata.just_created() |
|
447 |
formdata.status = 'wf-new' |
|
448 |
formdata.evolution[-1].status = 'wf-new' |
|
449 | ||
450 |
from wcs.qommon.form import PicklableUpload as PicklableUpload3 |
|
451 | ||
452 |
upload = PicklableUpload3('test.txt', 'text/plain', 'ascii') |
|
453 |
upload.receive([b'test']) |
|
454 |
upload2 = PicklableUpload3('test.txt', 'text/plain', 'ascii') |
|
455 |
upload2.receive([b'test']) |
|
456 |
formdata.workflow_data = {'blah': upload, 'blah2': upload2, 'xxx': 23} |
|
457 |
formdata.store() |
|
458 | ||
459 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user)) |
|
460 |
assert resp.json['workflow']['data']['xxx'] == 23 |
|
461 |
assert resp.json['workflow']['data']['blah']['filename'] == 'test.txt' |
|
462 |
assert resp.json['workflow']['data']['blah']['content_type'] == 'text/plain' |
|
463 |
assert base64.decodebytes(force_bytes(resp.json['workflow']['data']['blah']['content'])) == b'test' |
|
464 |
assert base64.decodebytes(force_bytes(resp.json['workflow']['data']['blah2']['content'])) == b'test' |
|
465 | ||
466 | ||
467 |
def test_api_list_formdata(pub, local_user): |
|
468 |
Role.wipe() |
|
469 |
role = Role(name='test') |
|
470 |
role.store() |
|
471 | ||
472 |
FormDef.wipe() |
|
473 |
formdef = FormDef() |
|
474 |
formdef.name = 'test' |
|
475 |
formdef.workflow_roles = {'_receiver': role.id} |
|
476 |
formdef.fields = [ |
|
477 |
fields.StringField(id='0', label='foobar', varname='foobar', type='string'), |
|
478 |
fields.ItemField( |
|
479 |
id='1', label='foobar3', varname='foobar3', type='item', items=['foo', 'bar', 'baz'] |
|
480 |
), |
|
481 |
fields.FileField(id='2', label='foobar4', varname='file', type='file'), |
|
482 |
] |
|
483 |
formdef.store() |
|
484 | ||
485 |
data_class = formdef.data_class() |
|
486 |
data_class.wipe() |
|
487 | ||
488 |
for i in range(30): |
|
489 |
formdata = data_class() |
|
490 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
491 |
upload.receive([b'base64me']) |
|
492 |
formdata.data = {'0': 'FOO BAR %d' % i, '2': upload} |
|
493 |
formdata.user_id = local_user.id |
|
494 |
if i % 4 == 0: |
|
495 |
formdata.data['1'] = 'foo' |
|
496 |
formdata.data['1_display'] = 'foo' |
|
497 |
elif i % 4 == 1: |
|
498 |
formdata.data['1'] = 'bar' |
|
499 |
formdata.data['1_display'] = 'bar' |
|
500 |
else: |
|
501 |
formdata.data['1'] = 'baz' |
|
502 |
formdata.data['1_display'] = 'baz' |
|
503 | ||
504 |
formdata.just_created() |
|
505 |
if i % 3 == 0: |
|
506 |
formdata.jump_status('new') |
|
507 |
elif i % 3 == 1: |
|
508 |
formdata.jump_status('just_submitted') |
|
509 |
else: |
|
510 |
formdata.jump_status('finished') |
|
511 |
if i % 7 == 0: |
|
512 |
formdata.backoffice_submission = True |
|
513 |
formdata.submission_channel = 'mail' |
|
514 |
formdata.evolution[-1].time = ( |
|
515 |
datetime.datetime(2020, 1, 2, 3, 4) + datetime.timedelta(hours=i) |
|
516 |
).timetuple() |
|
517 |
formdata.store() |
|
518 | ||
519 |
# check access is denied if the user has not the appropriate role |
|
520 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user), status=403) |
|
521 | ||
522 |
# add proper role to user |
|
523 |
local_user.roles = [role.id] |
|
524 |
local_user.store() |
|
525 | ||
526 |
# check it now gets the data |
|
527 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user)) |
|
528 |
assert len(resp.json) == 30 |
|
529 |
assert datetime.datetime.strptime(resp.json[0]['receipt_time'], '%Y-%m-%dT%H:%M:%S') |
|
530 |
assert 'fields' not in resp.json[0] |
|
531 | ||
532 |
# check getting full formdata |
|
533 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on', user=local_user)) |
|
534 |
assert len(resp.json) == 30 |
|
535 |
assert 'receipt_time' in resp.json[0] |
|
536 |
assert 'fields' in resp.json[0] |
|
537 |
assert 'url' in resp.json[0]['fields']['file'] |
|
538 |
assert 'content' not in resp.json[0]['fields']['file'] # no file content in full lists |
|
539 |
assert 'user' in resp.json[0] |
|
540 |
assert 'evolution' in resp.json[0] |
|
541 |
assert len(resp.json[0]['evolution']) == 2 |
|
542 |
assert 'status' in resp.json[0]['evolution'][0] |
|
543 |
assert 'who' in resp.json[0]['evolution'][0] |
|
544 |
assert 'time' in resp.json[0]['evolution'][0] |
|
545 |
assert resp.json[0]['evolution'][0]['who']['id'] == local_user.id |
|
546 | ||
547 |
assert all('status' in x['workflow'] for x in resp.json) |
|
548 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 0'][0]['submission'][ |
|
549 |
'backoffice' |
|
550 |
] is True |
|
551 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 0'][0]['submission'][ |
|
552 |
'channel' |
|
553 |
] == 'mail' |
|
554 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 1'][0]['submission'][ |
|
555 |
'backoffice' |
|
556 |
] is False |
|
557 |
assert [x for x in resp.json if x['fields']['foobar'] == 'FOO BAR 1'][0]['submission']['channel'] == 'web' |
|
558 | ||
559 |
# check filtered results |
|
560 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=foo', user=local_user)) |
|
561 |
assert len(resp.json) == 8 |
|
562 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=bar', user=local_user)) |
|
563 |
assert len(resp.json) == 8 |
|
564 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=baz', user=local_user)) |
|
565 |
assert len(resp.json) == 14 |
|
566 | ||
567 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar=FOO BAR 3', user=local_user)) |
|
568 |
assert len(resp.json) == 1 |
|
569 | ||
570 |
# check filter on status |
|
571 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter=pending', user=local_user)) |
|
572 |
assert len(resp.json) == 20 |
|
573 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter=done', user=local_user)) |
|
574 |
assert len(resp.json) == 10 |
|
575 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter=all', user=local_user)) |
|
576 |
assert len(resp.json) == 30 |
|
577 | ||
578 |
# check filter on last update time |
|
579 |
resp = get_app(pub).get( |
|
580 |
sign_uri( |
|
581 |
'/api/forms/test/list?filter-start-mtime=on&filter-start-mtime-value=2020-01-03', user=local_user |
|
582 |
) |
|
583 |
) |
|
584 |
assert len(resp.json) == 16 |
|
585 |
resp = get_app(pub).get( |
|
586 |
sign_uri( |
|
587 |
'/api/forms/test/list?filter-start-mtime=on&filter-start-mtime-value=2020-01-03 10:00', |
|
588 |
user=local_user, |
|
589 |
) |
|
590 |
) |
|
591 |
assert len(resp.json) == 10 |
|
592 |
resp = get_app(pub).get( |
|
593 |
sign_uri( |
|
594 |
'/api/forms/test/list?filter-end-mtime=on&filter-end-mtime-value=2020-01-03', user=local_user |
|
595 |
) |
|
596 |
) |
|
597 |
assert len(resp.json) == 14 |
|
598 |
resp = get_app(pub).get( |
|
599 |
sign_uri( |
|
600 |
'/api/forms/test/list?filter-end-mtime=on&filter-end-mtime-value=2020-01-03 10:00', |
|
601 |
user=local_user, |
|
602 |
) |
|
603 |
) |
|
604 |
assert len(resp.json) == 20 |
|
605 | ||
606 |
# check limit and offset |
|
607 |
resp_all = get_app(pub).get(sign_uri('/api/forms/test/list?filter=all', user=local_user)) |
|
608 |
assert len(resp_all.json) == 30 |
|
609 |
partial_resps = [] |
|
610 |
for i in range(0, 48, 12): |
|
611 |
partial_resps.append( |
|
612 |
get_app(pub).get( |
|
613 |
sign_uri('/api/forms/test/list?filter=all&offset=%s&limit=12' % i, user=local_user) |
|
614 |
) |
|
615 |
) |
|
616 |
assert len(partial_resps[0].json) == 12 |
|
617 |
assert len(partial_resps[1].json) == 12 |
|
618 |
assert len(partial_resps[2].json) == 6 |
|
619 |
assert len(partial_resps[3].json) == 0 |
|
620 |
resp_all_ids = [x.get('id') for x in resp_all.json] |
|
621 |
resp_partial_ids = [] |
|
622 |
for resp in partial_resps: |
|
623 |
resp_partial_ids.extend([x.get('id') for x in resp.json]) |
|
624 |
assert resp_all_ids == resp_partial_ids |
|
625 | ||
626 |
# check error handling |
|
627 |
get_app(pub).get(sign_uri('/api/forms/test/list?filter=all&offset=plop', user=local_user), status=400) |
|
628 |
get_app(pub).get(sign_uri('/api/forms/test/list?filter=all&limit=plop', user=local_user), status=400) |
|
629 | ||
630 | ||
631 |
def test_api_anonymized_formdata(pub, local_user, admin_user): |
|
632 |
Role.wipe() |
|
633 |
role = Role(name='test') |
|
634 |
role.store() |
|
635 | ||
636 |
FormDef.wipe() |
|
637 |
formdef = FormDef() |
|
638 |
formdef.name = 'test' |
|
639 |
formdef.workflow_roles = {'_receiver': role.id} |
|
640 |
formdef.fields = [ |
|
641 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
642 |
fields.ItemField( |
|
643 |
id='1', label='foobar3', varname='foobar3', type='item', items=['foo', 'bar', 'baz'] |
|
644 |
), |
|
645 |
fields.FileField(id='2', label='foobar4', varname='file'), |
|
646 |
] |
|
647 |
formdef.store() |
|
648 | ||
649 |
data_class = formdef.data_class() |
|
650 |
data_class.wipe() |
|
651 | ||
652 |
for i in range(30): |
|
653 |
formdata = data_class() |
|
654 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
655 |
upload.receive([b'base64me']) |
|
656 |
formdata.data = {'0': 'FOO BAR %d' % i, '2': upload} |
|
657 |
formdata.user_id = local_user.id |
|
658 |
if i % 4 == 0: |
|
659 |
formdata.data['1'] = 'foo' |
|
660 |
formdata.data['1_display'] = 'foo' |
|
661 |
elif i % 4 == 1: |
|
662 |
formdata.data['1'] = 'bar' |
|
663 |
formdata.data['1_display'] = 'bar' |
|
664 |
else: |
|
665 |
formdata.data['1'] = 'baz' |
|
666 |
formdata.data['1_display'] = 'baz' |
|
667 | ||
668 |
formdata.just_created() |
|
669 |
if i % 3 == 0: |
|
670 |
formdata.jump_status('new') |
|
671 |
else: |
|
672 |
evo = Evolution() |
|
673 |
evo.who = admin_user.id |
|
674 |
evo.time = time.localtime() |
|
675 |
evo.status = 'wf-%s' % 'finished' |
|
676 |
formdata.evolution.append(evo) |
|
677 |
formdata.status = evo.status |
|
678 |
formdata.store() |
|
679 | ||
680 |
# check access is granted even if the user has not the appropriate role |
|
681 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?anonymise&full=on', user=local_user)) |
|
682 |
assert len(resp.json) == 30 |
|
683 |
assert 'receipt_time' in resp.json[0] |
|
684 |
assert 'fields' in resp.json[0] |
|
685 |
assert 'user' not in resp.json[0] |
|
686 |
assert 'file' not in resp.json[0]['fields'] # no file export in full lists |
|
687 |
assert 'foobar3' in resp.json[0]['fields'] |
|
688 |
assert 'foobar' not in resp.json[0]['fields'] |
|
689 |
assert 'evolution' in resp.json[0] |
|
690 |
assert len(resp.json[0]['evolution']) == 2 |
|
691 |
assert 'status' in resp.json[0]['evolution'][0] |
|
692 |
assert 'who' not in resp.json[0]['evolution'][0] |
|
693 |
assert 'time' in resp.json[0]['evolution'][0] |
|
694 |
# check evolution made by other than _submitter are exported |
|
695 |
assert 'who' in resp.json[1]['evolution'][1] |
|
696 |
assert 'id' in resp.json[1]['evolution'][1]['who'] |
|
697 |
assert 'email' in resp.json[1]['evolution'][1]['who'] |
|
698 |
assert 'NameID' in resp.json[1]['evolution'][1]['who'] |
|
699 |
assert 'name' in resp.json[1]['evolution'][1]['who'] |
|
700 | ||
701 |
# check access is granted event if there is no user |
|
702 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?anonymise&full=on')) |
|
703 |
assert len(resp.json) == 30 |
|
704 |
assert 'receipt_time' in resp.json[0] |
|
705 |
assert 'fields' in resp.json[0] |
|
706 |
assert 'user' not in resp.json[0] |
|
707 |
assert 'file' not in resp.json[0]['fields'] # no file export in full lists |
|
708 |
assert 'foobar3' in resp.json[0]['fields'] |
|
709 |
assert 'foobar' not in resp.json[0]['fields'] |
|
710 |
assert 'evolution' in resp.json[0] |
|
711 |
assert len(resp.json[0]['evolution']) == 2 |
|
712 |
assert 'status' in resp.json[0]['evolution'][0] |
|
713 |
assert 'who' not in resp.json[0]['evolution'][0] |
|
714 |
assert 'time' in resp.json[0]['evolution'][0] |
|
715 |
# check anonymise is enforced on detail view |
|
716 |
resp = get_app(pub).get(sign_uri('/api/forms/test/%s/?anonymise&full=on' % resp.json[1]['id'])) |
|
717 |
assert 'receipt_time' in resp.json |
|
718 |
assert 'fields' in resp.json |
|
719 |
assert 'user' not in resp.json |
|
720 |
assert 'file' not in resp.json['fields'] # no file export in detail |
|
721 |
assert 'foobar3' in resp.json['fields'] |
|
722 |
assert 'foobar' not in resp.json['fields'] |
|
723 |
assert 'evolution' in resp.json |
|
724 |
assert len(resp.json['evolution']) == 2 |
|
725 |
assert 'status' in resp.json['evolution'][0] |
|
726 |
assert 'who' not in resp.json['evolution'][0] |
|
727 |
assert 'time' in resp.json['evolution'][0] |
|
728 |
# check evolution made by other than _submitter are exported |
|
729 |
assert 'who' in resp.json['evolution'][1] |
|
730 |
assert 'id' in resp.json['evolution'][1]['who'] |
|
731 |
assert 'email' in resp.json['evolution'][1]['who'] |
|
732 |
assert 'NameID' in resp.json['evolution'][1]['who'] |
|
733 |
assert 'name' in resp.json['evolution'][1]['who'] |
|
734 | ||
735 | ||
736 |
def test_api_geojson_formdata(pub, local_user): |
|
737 |
Role.wipe() |
|
738 |
role = Role(name='test') |
|
739 |
role.store() |
|
740 | ||
741 |
FormDef.wipe() |
|
742 |
formdef = FormDef() |
|
743 |
formdef.name = 'test' |
|
744 |
formdef.workflow_roles = {'_receiver': role.id} |
|
745 |
formdef.fields = [ |
|
746 |
fields.StringField(id='0', label='foobar', varname='foobar', type='string'), |
|
747 |
fields.FileField(id='1', label='foobar1', type='file'), |
|
748 |
] |
|
749 |
formdef.store() |
|
750 | ||
751 |
data_class = formdef.data_class() |
|
752 |
data_class.wipe() |
|
753 | ||
754 |
formdef.geolocations = {'base': 'Location'} |
|
755 |
formdef.store() |
|
756 | ||
757 |
# check access is denied if the user has not the appropriate role |
|
758 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user), status=403) |
|
759 |
# even if there's an anonymse parameter |
|
760 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?anonymise', user=local_user), status=403) |
|
761 | ||
762 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
763 |
upload.receive([b'base64me']) |
|
764 | ||
765 |
foobar = '<font color="red">FOO BAR</font>' |
|
766 |
username = '<font color="red">Jean Darmette</font>' |
|
767 | ||
768 |
data = {'0': foobar, '1': upload} |
|
769 |
local_user.name = username |
|
770 |
local_user.store() |
|
771 |
for i in range(30): |
|
772 |
formdata = data_class() |
|
773 |
formdata.geolocations = {'base': {'lat': 48, 'lon': 2}} |
|
774 |
formdata.data = data |
|
775 |
formdata.user_id = local_user.id |
|
776 |
formdata.just_created() |
|
777 |
if i % 3 == 0: |
|
778 |
formdata.jump_status('new') |
|
779 |
else: |
|
780 |
formdata.jump_status('finished') |
|
781 |
formdata.store() |
|
782 | ||
783 |
# add proper role to user |
|
784 |
local_user.roles = [role.id] |
|
785 |
local_user.store() |
|
786 | ||
787 |
# check it gets the data |
|
788 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user)) |
|
789 |
assert 'features' in resp.json |
|
790 |
assert len(resp.json['features']) == 10 |
|
791 |
display_fields = resp.json['features'][0]['properties']['display_fields'] |
|
792 |
assert len(display_fields) == 5 |
|
793 |
for field in display_fields: |
|
794 |
if field['label'] == 'Number': |
|
795 |
assert field['varname'] == 'id' |
|
796 |
assert field['html_value'] == '1-28' |
|
797 |
assert field['value'] == '1-28' |
|
798 |
if field['label'] == 'User Label': |
|
799 |
assert field['varname'] == 'user_label' |
|
800 |
assert field['value'] == username |
|
801 |
assert field['html_value'] == "<font color="red">Jean Darmette</font>" |
|
802 |
if field['label'] == 'foobar': |
|
803 |
assert field['varname'] == 'foobar' |
|
804 |
assert field['value'] == foobar |
|
805 |
assert field['html_value'] == "<font color="red">FOO BAR</font>" |
|
806 |
if field['label'] == 'foobar1': |
|
807 |
assert field['varname'] is None |
|
808 |
assert field['value'] == "test.txt" |
|
809 |
assert field['html_value'] == ( |
|
810 |
'<div class="file-field"><a download="test.txt" href="http://example.net/backoffice/management/test/28/download?f=1">' |
|
811 |
'<span>test.txt</span></a></div>' |
|
812 |
) |
|
813 |
field_varnames = [f['varname'] for f in display_fields] |
|
814 |
assert 'foobar' not in field_varnames |
|
815 | ||
816 |
# check full=on |
|
817 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?full=on', user=local_user)) |
|
818 |
assert len(resp.json['features']) == 10 |
|
819 |
display_fields = resp.json['features'][0]['properties']['display_fields'] |
|
820 |
assert len(display_fields) == 8 |
|
821 |
field_varnames = [f['varname'] for f in display_fields] |
|
822 |
assert 'foobar' in field_varnames |
|
823 | ||
824 |
# check with a filter |
|
825 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?filter=done', user=local_user)) |
|
826 |
assert 'features' in resp.json |
|
827 |
assert len(resp.json['features']) == 20 |
|
828 | ||
829 |
# check with http basic auth |
|
830 |
app = get_app(pub) |
|
831 |
app.authorization = ('Basic', ('user', 'password')) |
|
832 |
resp = app.get('/api/forms/test/geojson?email=%s' % local_user.email, status=401) |
|
833 | ||
834 |
# add authentication info |
|
835 |
pub.load_site_options() |
|
836 |
pub.site_options.add_section('api-http-auth-geojson') |
|
837 |
pub.site_options.set('api-http-auth-geojson', 'user', 'password') |
|
838 |
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')) |
|
839 | ||
840 |
resp = app.get('/api/forms/test/geojson?email=%s' % local_user.email) |
|
841 |
assert 'features' in resp.json |
|
842 |
assert len(resp.json['features']) == 10 |
|
843 | ||
844 |
# check 404 if the formdef doesn't have geolocation support |
|
845 |
formdef.geolocations = {} |
|
846 |
formdef.store() |
|
847 |
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user), status=404) |
|
848 | ||
849 | ||
850 |
def test_api_ods_formdata(pub, local_user): |
|
851 |
Role.wipe() |
|
852 |
role = Role(name='test') |
|
853 |
role.store() |
|
854 | ||
855 |
FormDef.wipe() |
|
856 |
formdef = FormDef() |
|
857 |
formdef.name = 'test' |
|
858 |
formdef.workflow_roles = {'_receiver': role.id} |
|
859 |
formdef.fields = [ |
|
860 |
fields.StringField(id='0', label='foobar', varname='foobar', type='string'), |
|
861 |
] |
|
862 |
formdef.store() |
|
863 | ||
864 |
data_class = formdef.data_class() |
|
865 |
data_class.wipe() |
|
866 | ||
867 |
# check access is denied if the user has not the appropriate role |
|
868 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user), status=403) |
|
869 |
# even if there's an anonymise parameter |
|
870 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods?anonymise', user=local_user), status=403) |
|
871 | ||
872 |
data = {'0': 'foobar'} |
|
873 |
for i in range(30): |
|
874 |
formdata = data_class() |
|
875 |
formdata.data = data |
|
876 |
formdata.user_id = local_user.id |
|
877 |
formdata.just_created() |
|
878 |
if i % 3 == 0: |
|
879 |
formdata.jump_status('new') |
|
880 |
else: |
|
881 |
formdata.jump_status('finished') |
|
882 |
formdata.store() |
|
883 | ||
884 |
# add proper role to user |
|
885 |
local_user.roles = [role.id] |
|
886 |
local_user.store() |
|
887 | ||
888 |
# check it gets the data |
|
889 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user)) |
|
890 |
assert resp.content_type == 'application/vnd.oasis.opendocument.spreadsheet' |
|
891 | ||
892 |
# check it still gives a ods file when there is more data |
|
893 |
for i in range(300): |
|
894 |
formdata = data_class() |
|
895 |
formdata.data = data |
|
896 |
formdata.user_id = local_user.id |
|
897 |
formdata.just_created() |
|
898 |
formdata.jump_status('new') |
|
899 |
formdata.store() |
|
900 | ||
901 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ods', user=local_user)) |
|
902 |
assert resp.content_type == 'application/vnd.oasis.opendocument.spreadsheet' |
|
903 |
zipf = zipfile.ZipFile(BytesIO(resp.body)) |
|
904 |
ods_sheet = ET.parse(zipf.open('content.xml')) |
|
905 |
assert len(ods_sheet.findall('.//{%s}table-row' % ods.NS['table'])) == 311 |
|
906 | ||
907 | ||
908 |
def test_api_global_geojson(pub, local_user): |
|
909 |
Role.wipe() |
|
910 |
role = Role(name='test') |
|
911 |
role.store() |
|
912 | ||
913 |
FormDef.wipe() |
|
914 |
formdef = FormDef() |
|
915 |
formdef.name = 'test' |
|
916 |
formdef.workflow_roles = {'_receiver': role.id} |
|
917 |
formdef.fields = [] |
|
918 |
formdef.store() |
|
919 | ||
920 |
data_class = formdef.data_class() |
|
921 |
data_class.wipe() |
|
922 | ||
923 |
formdef.geolocations = {'base': 'Location'} |
|
924 |
formdef.store() |
|
925 | ||
926 |
for i in range(30): |
|
927 |
formdata = data_class() |
|
928 |
formdata.geolocations = {'base': {'lat': 48, 'lon': 2}} |
|
929 |
formdata.user_id = local_user.id |
|
930 |
formdata.just_created() |
|
931 |
if i % 3 == 0: |
|
932 |
formdata.jump_status('new') |
|
933 |
else: |
|
934 |
formdata.jump_status('finished') |
|
935 |
formdata.store() |
|
936 | ||
937 |
if not pub.is_using_postgresql(): |
|
938 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user), status=404) |
|
939 |
pytest.skip('this requires SQL') |
|
940 |
return |
|
941 | ||
942 |
# check empty content if user doesn't have the appropriate role |
|
943 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user)) |
|
944 |
assert 'features' in resp.json |
|
945 |
assert len(resp.json['features']) == 0 |
|
946 | ||
947 |
# add proper role to user |
|
948 |
local_user.roles = [role.id] |
|
949 |
local_user.store() |
|
950 | ||
951 |
# check it gets the data |
|
952 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user)) |
|
953 |
assert 'features' in resp.json |
|
954 |
assert len(resp.json['features']) == 10 |
|
955 | ||
956 |
# check with a filter |
|
957 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson?status=done', user=local_user)) |
|
958 |
assert 'features' in resp.json |
|
959 |
assert len(resp.json['features']) == 20 |
|
960 | ||
961 | ||
962 |
def test_api_global_listing(pub, local_user): |
|
963 |
if not pub.is_using_postgresql(): |
|
964 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user), status=404) |
|
965 |
pytest.skip('this requires SQL') |
|
966 |
return |
|
967 | ||
968 |
Role.wipe() |
|
969 |
role = Role(name='test') |
|
970 |
role.store() |
|
971 | ||
972 |
# check there's no crash if there are no formdefs |
|
973 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
974 |
assert len(resp.json['data']) == 0 |
|
975 | ||
976 |
FormDef.wipe() |
|
977 |
formdef = FormDef() |
|
978 |
formdef.name = 'test' |
|
979 |
formdef.workflow_roles = {'_receiver': role.id} |
|
980 |
formdef.fields = [ |
|
981 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
982 |
] |
|
983 |
formdef.store() |
|
984 | ||
985 |
data_class = formdef.data_class() |
|
986 |
data_class.wipe() |
|
987 | ||
988 |
formdef.store() |
|
989 | ||
990 |
for i in range(30): |
|
991 |
formdata = data_class() |
|
992 |
formdata.data = {'0': 'FOO BAR'} |
|
993 |
formdata.user_id = local_user.id |
|
994 |
formdata.just_created() |
|
995 |
if i % 3 == 0: |
|
996 |
formdata.jump_status('new') |
|
997 |
else: |
|
998 |
formdata.jump_status('finished') |
|
999 |
formdata.store() |
|
1000 | ||
1001 |
# check empty content if user doesn't have the appropriate role |
|
1002 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
1003 |
assert len(resp.json['data']) == 0 |
|
1004 | ||
1005 |
# add proper role to user |
|
1006 |
local_user.roles = [role.id] |
|
1007 |
local_user.store() |
|
1008 | ||
1009 |
# check it gets the data |
|
1010 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
1011 |
assert len(resp.json['data']) == 10 |
|
1012 | ||
1013 |
# check with a filter |
|
1014 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done', user=local_user)) |
|
1015 |
assert len(resp.json['data']) == 20 |
|
1016 | ||
1017 |
# check limit/offset |
|
1018 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done&limit=5', user=local_user)) |
|
1019 |
assert len(resp.json['data']) == 5 |
|
1020 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done&offset=5&limit=5', user=local_user)) |
|
1021 |
assert len(resp.json['data']) == 5 |
|
1022 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=done&offset=18&limit=5', user=local_user)) |
|
1023 |
assert len(resp.json['data']) == 2 |
|
1024 | ||
1025 |
# check error handling |
|
1026 |
get_app(pub).get(sign_uri('/api/forms/?status=done&limit=plop', user=local_user), status=400) |
|
1027 |
get_app(pub).get(sign_uri('/api/forms/?status=done&offset=plop', user=local_user), status=400) |
|
1028 |
get_app(pub).get(sign_uri('/api/forms/?category_id=plop', user=local_user), status=400) |
|
1029 | ||
1030 |
# check when there are missing statuses |
|
1031 |
for formdata in data_class.select(): |
|
1032 |
formdata.status = 'wf-missing' |
|
1033 |
formdata.store() |
|
1034 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all', user=local_user)) |
|
1035 |
assert resp.json['data'][0]['status'] is None |
|
1036 |
assert 'unknown' in resp.json['data'][0]['title'] |
|
1037 | ||
1038 | ||
1039 |
def test_api_global_listing_ignored_roles(pub, local_user): |
|
1040 |
test_api_global_listing(pub, local_user) |
|
1041 | ||
1042 |
role = Role(name='test2') |
|
1043 |
role.store() |
|
1044 | ||
1045 |
formdef = FormDef() |
|
1046 |
formdef.name = 'test2' |
|
1047 |
formdef.workflow_roles = {'_receiver': role.id} |
|
1048 |
formdef.fields = [ |
|
1049 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1050 |
] |
|
1051 |
formdef.store() |
|
1052 | ||
1053 |
data_class = formdef.data_class() |
|
1054 |
data_class.wipe() |
|
1055 | ||
1056 |
for i in range(10): |
|
1057 |
formdata = data_class() |
|
1058 |
formdata.data = {'0': 'FOO BAR'} |
|
1059 |
formdata.user_id = local_user.id |
|
1060 |
formdata.just_created() |
|
1061 |
formdata.jump_status('new') |
|
1062 |
formdata.store() |
|
1063 | ||
1064 |
# considering roles |
|
1065 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all&limit=100', user=local_user)) |
|
1066 |
assert len(resp.json['data']) == 30 |
|
1067 | ||
1068 |
# ignore roles |
|
1069 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all&limit=100&ignore-roles=on', user=local_user)) |
|
1070 |
assert len(resp.json['data']) == 40 |
|
1071 | ||
1072 |
# check sensitive forms are not exposed |
|
1073 |
formdef.skip_from_360_view = True |
|
1074 |
formdef.store() |
|
1075 |
resp = get_app(pub).get(sign_uri('/api/forms/?status=all&limit=100&ignore-roles=on', user=local_user)) |
|
1076 |
assert len(resp.json['data']) == 30 |
|
1077 | ||
1078 | ||
1079 |
def test_api_include_anonymised(pub, local_user): |
|
1080 |
if not pub.is_using_postgresql(): |
|
1081 |
resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user), status=404) |
|
1082 |
pytest.skip('this requires SQL') |
|
1083 |
return |
|
1084 | ||
1085 |
Role.wipe() |
|
1086 |
role = Role(name='test') |
|
1087 |
role.store() |
|
1088 | ||
1089 |
# add proper role to user |
|
1090 |
local_user.roles = [role.id] |
|
1091 |
local_user.store() |
|
1092 | ||
1093 |
FormDef.wipe() |
|
1094 |
formdef = FormDef() |
|
1095 |
formdef.name = 'test' |
|
1096 |
formdef.workflow_roles = {'_receiver': role.id} |
|
1097 |
formdef.fields = [ |
|
1098 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
1099 |
] |
|
1100 |
formdef.store() |
|
1101 | ||
1102 |
data_class = formdef.data_class() |
|
1103 |
data_class.wipe() |
|
1104 | ||
1105 |
for i in range(10): |
|
1106 |
formdata = data_class() |
|
1107 |
formdata.data = {'0': 'FOO BAR'} |
|
1108 |
formdata.user_id = local_user.id |
|
1109 |
formdata.just_created() |
|
1110 |
formdata.jump_status('new') |
|
1111 |
formdata.store() |
|
1112 | ||
1113 |
# anonymise the last one |
|
1114 |
formdata.anonymise() |
|
1115 | ||
1116 |
resp = get_app(pub).get(sign_uri('/api/forms/', user=local_user)) |
|
1117 |
assert len(resp.json['data']) == 10 |
|
1118 | ||
1119 |
resp = get_app(pub).get(sign_uri('/api/forms/?include-anonymised=on', user=local_user)) |
|
1120 |
assert len(resp.json['data']) == 10 |
|
1121 | ||
1122 |
resp = get_app(pub).get(sign_uri('/api/forms/?include-anonymised=off', user=local_user)) |
|
1123 |
assert len(resp.json['data']) == 9 |
|
1124 | ||
1125 | ||
1126 |
def test_api_ics_formdata(pub, local_user, ics_data): |
|
1127 |
role = Role.select()[0] |
|
1128 | ||
1129 |
# check access is denied if the user has not the appropriate role |
|
1130 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar', user=local_user), status=403) |
|
1131 |
# even if there's an anonymse parameter |
|
1132 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar?anonymise', user=local_user), status=403) |
|
1133 | ||
1134 |
# add proper role to user |
|
1135 |
local_user.roles = [role.id] |
|
1136 |
local_user.store() |
|
1137 | ||
1138 |
def remove_dtstamp(body): |
|
1139 |
# remove dtstamp as the precise timing may vary between two consecutive |
|
1140 |
# calls and we shouldn't care. |
|
1141 |
return re.sub('DTSTAMP:.*', 'DTSTAMP:--', body) |
|
1142 | ||
1143 |
# check 404 on incomplete ics url access |
|
1144 |
assert get_app(pub).get(sign_uri('/api/forms/test/ics/', user=local_user), status=404) |
|
1145 | ||
1146 |
# check it gets the data |
|
1147 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar', user=local_user)) |
|
1148 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/', user=local_user)) |
|
1149 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
1150 |
assert resp.headers['content-type'] == 'text/calendar; charset=utf-8' |
|
1151 |
assert resp.text.count('BEGIN:VEVENT') == 10 |
|
1152 |
# check that description contains form name, display id, workflow status, |
|
1153 |
# backoffice url and attached user |
|
1154 |
pattern = re.compile(r'DESCRIPTION:testé \| 1-\d+ \| New', re.MULTILINE) |
|
1155 |
m = pattern.findall(resp.text) |
|
1156 |
assert len(m) == 10 |
|
1157 |
assert resp.text.count('Jean Darmette') == 10 |
|
1158 |
assert resp.text.count('DTSTART') == 10 |
|
1159 | ||
1160 |
# check formdata digest summary and description contains the formdata digest |
|
1161 |
pattern = re.compile(r'SUMMARY:testé #1-\d+ - plöp \d{4}-\d{2}-\d{2} \d{2}:\d{2} plÔp', re.MULTILINE) |
|
1162 |
m = pattern.findall(resp.text) |
|
1163 |
assert len(m) == 10 |
|
1164 |
assert resp.text.count(r'plöp') == 20 |
|
1165 | ||
1166 |
# check with a filter |
|
1167 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar?filter=done', user=local_user)) |
|
1168 |
assert resp.text.count('BEGIN:VEVENT') == 20 |
|
1169 |
pattern = re.compile(r'DESCRIPTION:testé \| 1-\d+ \| Finished', re.MULTILINE) |
|
1170 |
m = pattern.findall(resp.text) |
|
1171 |
assert len(m) == 20 |
|
1172 |
assert resp.text.count('Jean Darmette') == 20 |
|
1173 | ||
1174 |
# check 404 on erroneous field var |
|
1175 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/xxx', user=local_user), status=404) |
|
1176 | ||
1177 |
# check 404 on an erroneous field var for endtime |
|
1178 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/xxx', user=local_user), status=404) |
|
1179 | ||
1180 |
# check 404 on too many path elements |
|
1181 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/foobar2/xxx', user=local_user), status=404) |
|
1182 | ||
1183 |
# check ics data with start and end varnames |
|
1184 |
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/foobar2', user=local_user)) |
|
1185 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar/foobar2/', user=local_user)) |
|
1186 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
1187 |
assert resp.text.count('DTSTART') == 10 |
|
1188 |
assert resp.text.count('DTEND') == 10 |
|
1189 | ||
1190 | ||
1191 |
def test_api_ics_formdata_http_auth(pub, local_user, admin_user, ics_data): |
|
1192 |
role = Role.select()[0] |
|
1193 | ||
1194 |
# check as admin |
|
1195 |
app = login(get_app(pub)) |
|
1196 |
resp = app.get('/api/forms/test/ics/foobar', status=200) |
|
1197 | ||
1198 |
# no access |
|
1199 |
app = get_app(pub) |
|
1200 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=401) |
|
1201 |
assert resp.headers['Www-Authenticate'] |
|
1202 | ||
1203 |
# auth but no access |
|
1204 |
app = get_app(pub) |
|
1205 |
app.authorization = ('Basic', ('user', 'password')) |
|
1206 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=401) |
|
1207 | ||
1208 |
# add authentication info |
|
1209 |
pub.load_site_options() |
|
1210 |
pub.site_options.add_section('api-http-auth-ics') |
|
1211 |
pub.site_options.set('api-http-auth-ics', 'user', 'password') |
|
1212 |
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')) |
|
1213 | ||
1214 |
# check access is denied if the user has not the appropriate role |
|
1215 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=403) |
|
1216 | ||
1217 |
# check access is denied if the user is not specified |
|
1218 |
resp = app.get('/api/forms/test/ics/foobar', status=403) |
|
1219 | ||
1220 |
# add proper role to user |
|
1221 |
local_user.roles = [role.id] |
|
1222 |
local_user.store() |
|
1223 | ||
1224 |
# check it gets the data |
|
1225 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=200) |
|
1226 |
assert resp.headers['content-type'] == 'text/calendar; charset=utf-8' |
|
1227 |
assert resp.text.count('BEGIN:VEVENT') == 10 |
|
1228 | ||
1229 |
# check it fails with a different password |
|
1230 |
app.authorization = ('Basic', ('user', 'password2')) |
|
1231 |
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=401) |
|
1232 | ||
1233 | ||
1234 |
def test_api_ics_formdata_custom_view(pub, local_user, ics_data): |
|
1235 |
role = Role.select()[0] |
|
1236 | ||
1237 |
formdef = FormDef.get_by_urlname('test') |
|
1238 | ||
1239 |
pub.custom_view_class.wipe() |
|
1240 |
custom_view = pub.custom_view_class() |
|
1241 |
custom_view.title = 'custom view' |
|
1242 |
custom_view.formdef = formdef |
|
1243 |
custom_view.columns = {'list': [{'id': '0'}]} |
|
1244 |
custom_view.filters = {} |
|
1245 |
custom_view.visibility = 'any' |
|
1246 |
custom_view.store() |
|
1247 | ||
1248 |
# check access is denied if the user has not the appropriate role |
|
1249 |
resp = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar', user=local_user), status=403) |
|
1250 |
# even if there's an anonymise parameter |
|
1251 |
resp = get_app(pub).get( |
|
1252 |
sign_uri('/api/forms/test/custom-view/ics/foobar?anonymise', user=local_user), status=403 |
|
1253 |
) |
|
1254 | ||
1255 |
# add proper role to user |
|
1256 |
local_user.roles = [role.id] |
|
1257 |
local_user.store() |
|
1258 | ||
1259 |
def remove_dtstamp(body): |
|
1260 |
# remove dtstamp as the precise timing may vary between two consecutive |
|
1261 |
# calls and we shouldn't care. |
|
1262 |
return re.sub('DTSTAMP:.*', 'DTSTAMP:--', body) |
|
1263 | ||
1264 |
# check it gets the data |
|
1265 |
resp = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar', user=local_user)) |
|
1266 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar/', user=local_user)) |
|
1267 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
1268 |
assert resp.headers['content-type'] == 'text/calendar; charset=utf-8' |
|
1269 |
assert resp.text.count('BEGIN:VEVENT') == 10 |
|
1270 | ||
1271 |
# check ics data with start and end varnames |
|
1272 |
resp = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar/foobar2', user=local_user)) |
|
1273 |
resp2 = get_app(pub).get(sign_uri('/api/forms/test/custom-view/ics/foobar/foobar2/', user=local_user)) |
|
1274 |
assert remove_dtstamp(resp.text) == remove_dtstamp(resp2.text) |
|
1275 |
assert resp.text.count('DTSTART') == 10 |
|
1276 |
assert resp.text.count('DTEND') == 10 |
|
1277 | ||
1278 | ||
1279 |
def test_api_invalid_http_basic_auth(pub, local_user, admin_user, ics_data): |
|
1280 |
app = get_app(pub) |
|
1281 |
app.get( |
|
1282 |
'/api/forms/test/ics/foobar?email=%s' % local_user.email, |
|
1283 |
headers={'Authorization': 'Basic garbage'}, |
|
1284 |
status=401, |
|
1285 |
) |
tests/api/test_formdef.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import base64 |
|
4 |
import datetime |
|
5 |
import json |
|
6 |
import os |
|
7 |
import time |
|
8 | ||
9 |
import mock |
|
10 |
import pytest |
|
11 |
from django.utils.encoding import force_text |
|
12 |
from django.utils.six import StringIO |
|
13 |
from django.utils.six.moves.urllib import parse as urllib |
|
14 |
from quixote import get_publisher |
|
15 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app |
|
16 | ||
17 |
from wcs import fields, qommon |
|
18 |
from wcs.api_utils import sign_url |
|
19 |
from wcs.data_sources import NamedDataSource |
|
20 |
from wcs.formdef import FormDef |
|
21 |
from wcs.qommon.form import PicklableUpload |
|
22 |
from wcs.qommon.http_request import HTTPRequest |
|
23 |
from wcs.roles import Role |
|
24 |
from wcs.wf.jump import JumpWorkflowStatusItem |
|
25 |
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef |
|
26 | ||
27 |
from .utils import sign_uri |
|
28 | ||
29 | ||
30 |
def pytest_generate_tests(metafunc): |
|
31 |
if 'pub' in metafunc.fixturenames: |
|
32 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
33 | ||
34 | ||
35 |
@pytest.fixture |
|
36 |
def pub(request, emails): |
|
37 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
38 | ||
39 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
40 |
pub.set_app_dir(req) |
|
41 |
pub.cfg['identification'] = {'methods': ['password']} |
|
42 |
pub.cfg['language'] = {'language': 'en'} |
|
43 |
pub.write_cfg() |
|
44 | ||
45 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
46 |
'''\ |
|
47 |
[api-secrets] |
|
48 |
coucou = 1234 |
|
49 |
''' |
|
50 |
) |
|
51 | ||
52 |
return pub |
|
53 | ||
54 | ||
55 |
def teardown_module(module): |
|
56 |
clean_temporary_pub() |
|
57 | ||
58 | ||
59 |
@pytest.fixture |
|
60 |
def local_user(): |
|
61 |
get_publisher().user_class.wipe() |
|
62 |
user = get_publisher().user_class() |
|
63 |
user.name = 'Jean Darmette' |
|
64 |
user.email = 'jean.darmette@triffouilis.fr' |
|
65 |
user.name_identifiers = ['0123456789'] |
|
66 |
user.store() |
|
67 |
return user |
|
68 | ||
69 | ||
70 |
def test_formdef_list(pub): |
|
71 |
Role.wipe() |
|
72 |
role = Role(name='Foo bar') |
|
73 |
role.id = '14' |
|
74 |
role.store() |
|
75 | ||
76 |
FormDef.wipe() |
|
77 |
formdef = FormDef() |
|
78 |
formdef.name = 'test' |
|
79 |
formdef.description = 'plop' |
|
80 |
formdef.keywords = 'mobile, test' |
|
81 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
82 |
formdef.fields = [] |
|
83 |
formdef.store() |
|
84 | ||
85 |
# anonymous access -> 403 |
|
86 |
resp1 = get_app(pub).get('/json', status=403) |
|
87 |
resp2 = get_app(pub).get('/', headers={'Accept': 'application/json'}, status=403) |
|
88 |
resp3 = get_app(pub).get('/api/formdefs/', status=403) |
|
89 | ||
90 |
# signed request |
|
91 |
resp1 = get_app(pub).get(sign_uri('/json')) |
|
92 |
resp2 = get_app(pub).get(sign_uri('/'), headers={'Accept': 'application/json'}) |
|
93 |
resp3 = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
94 |
assert resp1.json == resp2.json == resp3.json |
|
95 |
assert resp1.json['data'][0]['title'] == 'test' |
|
96 |
assert resp1.json['data'][0]['url'] == 'http://example.net/test/' |
|
97 |
assert resp1.json['data'][0]['redirection'] is False |
|
98 |
assert resp1.json['data'][0]['always_advertise'] is False |
|
99 |
assert resp1.json['data'][0]['description'] == 'plop' |
|
100 |
assert resp1.json['data'][0]['keywords'] == ['mobile', 'test'] |
|
101 |
assert list(resp1.json['data'][0]['functions'].keys()) == ['_receiver'] |
|
102 |
assert resp1.json['data'][0]['functions']['_receiver']['label'] == 'Recipient' |
|
103 |
assert resp1.json['data'][0]['functions']['_receiver']['role']['slug'] == role.slug |
|
104 |
assert resp1.json['data'][0]['functions']['_receiver']['role']['name'] == role.name |
|
105 |
assert 'count' not in resp1.json['data'][0] |
|
106 | ||
107 |
# backoffice_submission formdef : none |
|
108 |
resp1 = get_app(pub).get('/api/formdefs/?backoffice-submission=on', status=403) |
|
109 |
resp1 = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
110 |
assert resp1.json['err'] == 0 |
|
111 |
assert len(resp1.json['data']) == 0 |
|
112 | ||
113 |
formdef.data_class().wipe() |
|
114 | ||
115 |
# a draft |
|
116 |
formdata = formdef.data_class()() |
|
117 |
formdata.data = {} |
|
118 |
formdata.just_created() |
|
119 |
formdata.status = 'draft' |
|
120 |
formdata.store() |
|
121 | ||
122 |
other_formdef = FormDef() |
|
123 |
other_formdef.name = 'test 2' |
|
124 |
other_formdef.fields = [] |
|
125 |
other_formdef.store() |
|
126 |
other_formdata = other_formdef.data_class()() |
|
127 |
other_formdata.data = {} |
|
128 |
other_formdata.just_created() |
|
129 |
other_formdata.store() |
|
130 | ||
131 |
# formdata created: |
|
132 |
# - 1 day ago (=3*4) |
|
133 |
# - 7 days ago (=2*2) |
|
134 |
# - 29 days ago (=1*1) |
|
135 |
# - 31 days ago (=0) |
|
136 |
for days in [1, 1, 1, 7, 7, 29, 31]: |
|
137 |
formdata = formdef.data_class()() |
|
138 |
formdata.data = {} |
|
139 |
formdata.just_created() |
|
140 |
formdata.receipt_time = (datetime.datetime.now() - datetime.timedelta(days=days)).timetuple() |
|
141 |
formdata.store() |
|
142 | ||
143 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?include-count=on')) |
|
144 |
if not pub.is_using_postgresql(): |
|
145 |
assert resp.json['data'][0]['count'] == 8 |
|
146 |
else: |
|
147 |
# 3*4 + 2*2 + 1*1 |
|
148 |
assert resp.json['data'][0]['count'] == 17 |
|
149 | ||
150 | ||
151 |
def test_limited_formdef_list(pub, local_user): |
|
152 |
Role.wipe() |
|
153 |
role = Role(name='Foo bar') |
|
154 |
role.id = '14' |
|
155 |
role.store() |
|
156 | ||
157 |
FormDef.wipe() |
|
158 |
formdef = FormDef() |
|
159 |
formdef.name = 'test' |
|
160 |
formdef.description = 'plop' |
|
161 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
162 |
formdef.fields = [] |
|
163 |
formdef.store() |
|
164 | ||
165 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
166 |
assert resp.json['err'] == 0 |
|
167 |
assert len(resp.json['data']) == 1 |
|
168 |
assert resp.json['data'][0]['authentication_required'] is False |
|
169 |
# not present in backoffice-submission formdefs |
|
170 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
171 |
assert resp.json['err'] == 0 |
|
172 |
assert len(resp.json['data']) == 0 |
|
173 | ||
174 |
# check it's not advertised |
|
175 |
formdef.roles = [role.id] |
|
176 |
formdef.store() |
|
177 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
178 |
resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=')) |
|
179 |
resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX')) |
|
180 |
resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) |
|
181 |
assert resp.json['err'] == 0 |
|
182 |
assert len(resp.json['data']) == 1 # advertised in naked calls (as done from combo) |
|
183 |
assert len(resp2.json['data']) == 0 # not advertised otherwise |
|
184 |
assert resp2.json == resp3.json == resp4.json |
|
185 |
# still not present in backoffice-submission formdefs |
|
186 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
187 |
assert resp.json['err'] == 0 |
|
188 |
assert len(resp.json['data']) == 0 |
|
189 | ||
190 |
# unless user has correct roles |
|
191 |
local_user.roles = [role.id] |
|
192 |
local_user.store() |
|
193 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) |
|
194 |
assert resp.json['err'] == 0 |
|
195 |
assert len(resp.json['data']) == 1 |
|
196 | ||
197 |
local_user.roles = [] |
|
198 |
local_user.store() |
|
199 | ||
200 |
# check it's also included in anonymous/signed calls, but marked for |
|
201 |
# authentication |
|
202 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
203 |
assert resp.json['data'][0] |
|
204 |
assert resp.json['data'][0]['authentication_required'] is True |
|
205 | ||
206 |
# check it's advertised |
|
207 |
formdef.always_advertise = True |
|
208 |
formdef.store() |
|
209 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
210 |
resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=')) |
|
211 |
resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX')) |
|
212 |
resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) |
|
213 |
assert resp.json['err'] == 0 |
|
214 |
assert len(resp.json['data']) == 1 |
|
215 |
assert resp.json['data'][0]['authentication_required'] |
|
216 |
assert resp.json == resp2.json == resp3.json == resp4.json |
|
217 | ||
218 |
formdef.required_authentication_contexts = ['fedict'] |
|
219 |
formdef.store() |
|
220 |
resp = get_app(pub).get(sign_uri('/api/formdefs/')) |
|
221 |
assert resp.json['data'][0]['required_authentication_contexts'] == ['fedict'] |
|
222 | ||
223 | ||
224 |
def test_formdef_list_redirection(pub): |
|
225 |
FormDef.wipe() |
|
226 |
formdef = FormDef() |
|
227 |
formdef.name = 'test' |
|
228 |
formdef.disabled = True |
|
229 |
formdef.disabled_redirection = 'http://example.net' |
|
230 |
formdef.fields = [] |
|
231 |
formdef.store() |
|
232 | ||
233 |
resp1 = get_app(pub).get(sign_uri('/json')) |
|
234 |
assert resp1.json['err'] == 0 |
|
235 |
assert resp1.json['data'][0]['title'] == 'test' |
|
236 |
assert resp1.json['data'][0]['url'] == 'http://example.net/test/' |
|
237 |
assert resp1.json['data'][0]['redirection'] is True |
|
238 |
assert 'count' not in resp1.json['data'][0] |
|
239 | ||
240 | ||
241 |
def test_backoffice_submission_formdef_list(pub, local_user): |
|
242 |
Role.wipe() |
|
243 |
role = Role(name='Foo bar') |
|
244 |
role.id = '14' |
|
245 |
role.store() |
|
246 | ||
247 |
FormDef.wipe() |
|
248 |
formdef = FormDef() |
|
249 |
formdef.name = 'test' |
|
250 |
formdef.description = 'plop' |
|
251 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
252 |
formdef.fields = [] |
|
253 |
formdef.store() |
|
254 | ||
255 |
formdef2 = FormDef() |
|
256 |
formdef2.name = 'ignore me' |
|
257 |
formdef2.fields = [] |
|
258 |
formdef2.store() |
|
259 | ||
260 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
261 |
assert resp.json['err'] == 0 |
|
262 |
assert len(resp.json['data']) == 0 |
|
263 | ||
264 |
# check it's not advertised ... |
|
265 |
formdef.backoffice_submission_roles = [role.id] |
|
266 |
formdef.store() |
|
267 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
268 |
assert resp.json['err'] == 0 |
|
269 |
assert len(resp.json['data']) == 0 |
|
270 | ||
271 |
# even if it's advertised on frontoffice |
|
272 |
formdef.always_advertise = True |
|
273 |
formdef.store() |
|
274 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
275 |
assert resp.json['err'] == 0 |
|
276 |
assert len(resp.json['data']) == 0 |
|
277 | ||
278 |
# even if user is admin |
|
279 |
local_user.is_admin = True |
|
280 |
local_user.store() |
|
281 |
resp = get_app(pub).get( |
|
282 |
sign_uri('/api/formdefs/?backoffice-submission=on&NameID=%s' % local_user.name_identifiers[0]) |
|
283 |
) |
|
284 |
assert resp.json['err'] == 0 |
|
285 |
assert len(resp.json['data']) == 0 |
|
286 |
local_user.is_admin = False |
|
287 |
local_user.store() |
|
288 | ||
289 |
# ... unless user has correct roles |
|
290 |
local_user.roles = [role.id] |
|
291 |
local_user.store() |
|
292 |
resp = get_app(pub).get( |
|
293 |
sign_uri('/api/formdefs/?backoffice-submission=on&NameID=%s' % local_user.name_identifiers[0]) |
|
294 |
) |
|
295 |
assert resp.json['err'] == 0 |
|
296 |
assert len(resp.json['data']) == 1 |
|
297 |
assert 'backoffice_submission_url' in resp.json['data'][0] |
|
298 | ||
299 |
# but not advertised if it's a redirection |
|
300 |
formdef.disabled = True |
|
301 |
formdef.disabled_redirection = 'http://example.net' |
|
302 |
formdef.store() |
|
303 |
resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on')) |
|
304 |
assert resp.json['err'] == 0 |
|
305 |
assert len(resp.json['data']) == 0 |
|
306 | ||
307 | ||
308 |
def test_formdef_schema(pub): |
|
309 |
Workflow.wipe() |
|
310 |
workflow = Workflow(name='test') |
|
311 |
st1 = workflow.add_status('Status1', 'st1') |
|
312 |
jump = JumpWorkflowStatusItem() |
|
313 |
jump.status = 'st2' |
|
314 |
jump.timeout = 100 |
|
315 |
st1.items.append(jump) |
|
316 |
st2 = workflow.add_status('Status2', 'st2') |
|
317 |
jump = JumpWorkflowStatusItem() |
|
318 |
jump.status = 'st3' |
|
319 |
st2.items.append(jump) |
|
320 |
st2 = workflow.add_status('Status3', 'st3') |
|
321 |
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) |
|
322 |
workflow.backoffice_fields_formdef.fields = [ |
|
323 |
fields.StringField(id='bo1', label='1st backoffice field', type='string', varname='backoffice_blah'), |
|
324 |
] |
|
325 |
workflow.store() |
|
326 |
FormDef.wipe() |
|
327 |
formdef = FormDef() |
|
328 |
formdef.name = 'test' |
|
329 |
formdef.fields = [ |
|
330 |
fields.StringField(id='0', label='foobar'), |
|
331 |
fields.ItemField( |
|
332 |
id='1', |
|
333 |
label='foobar1', |
|
334 |
varname='foobar1', |
|
335 |
data_source={ |
|
336 |
'type': 'json', |
|
337 |
'value': 'http://datasource.com', |
|
338 |
}, |
|
339 |
), |
|
340 |
fields.ItemsField( |
|
341 |
id='2', |
|
342 |
label='foobar2', |
|
343 |
varname='foobar2', |
|
344 |
data_source={ |
|
345 |
'type': 'formula', |
|
346 |
'value': '[dict(id=i, text=\'label %s\' % i, foo=i) for i in range(10)]', |
|
347 |
}, |
|
348 |
), |
|
349 |
] |
|
350 | ||
351 |
formdef.workflow_id = workflow.id |
|
352 |
formdef.store() |
|
353 | ||
354 |
with mock.patch('wcs.qommon.misc.urlopen') as urlopen: |
|
355 |
urlopen.side_effect = lambda *args: StringIO( |
|
356 |
'''\ |
|
357 |
{"data": [{"id": 0, "text": "zéro", "foo": "bar"}, \ |
|
358 |
{"id": 1, "text": "uné", "foo": "bar1"}, \ |
|
359 |
{"id": 2, "text": "deux", "foo": "bar2"}]}''' |
|
360 |
) |
|
361 |
resp = get_app(pub).get('/api/formdefs/test/schema') |
|
362 |
resp2 = get_app(pub).get('/test/schema') |
|
363 |
resp3 = get_app(pub).get(sign_url('/api/formdefs/test/schema?orig=coucou', '1234')) |
|
364 |
resp4 = get_app(pub).get(sign_url('/api/formdefs/test/schema?orig=coucou', '1234')) |
|
365 | ||
366 |
# check schema |
|
367 |
assert resp.json == resp2.json |
|
368 |
assert set(resp.json.keys()) >= set( |
|
369 |
[ |
|
370 |
'enable_tracking_codes', |
|
371 |
'url_name', |
|
372 |
'description', |
|
373 |
'workflow', |
|
374 |
'expiration_date', |
|
375 |
'discussion', |
|
376 |
'has_captcha', |
|
377 |
'always_advertise', |
|
378 |
'name', |
|
379 |
'disabled', |
|
380 |
'only_allow_one', |
|
381 |
'fields', |
|
382 |
'keywords', |
|
383 |
'publication_date', |
|
384 |
'detailed_emails', |
|
385 |
'disabled_redirection', |
|
386 |
] |
|
387 |
) |
|
388 |
assert resp.json['name'] == 'test' |
|
389 | ||
390 |
# fields checks |
|
391 |
assert resp.json['fields'][0]['label'] == 'foobar' |
|
392 |
assert resp.json['fields'][0]['type'] == 'string' |
|
393 | ||
394 |
assert resp.json['fields'][1]['label'] == 'foobar1' |
|
395 |
assert resp.json['fields'][1]['type'] == 'item' |
|
396 | ||
397 |
# check structured items are only exported for authenticated callers |
|
398 |
assert resp.json['fields'][1]['items'] == [] |
|
399 |
assert resp.json['fields'][2]['items'] == [] |
|
400 |
assert 'structured_items' not in resp.json['fields'][1] |
|
401 |
assert 'structured_items' not in resp.json['fields'][2] |
|
402 | ||
403 |
assert len(resp3.json['fields'][1]['structured_items']) == 3 |
|
404 |
assert resp3.json['fields'][1]['structured_items'][0]['id'] == 0 |
|
405 |
assert resp3.json['fields'][1]['structured_items'][0]['text'] == u'zéro' |
|
406 |
assert resp3.json['fields'][1]['structured_items'][0]['foo'] == 'bar' |
|
407 |
assert resp3.json['fields'][1]['items'][0] == u'zéro' |
|
408 | ||
409 |
assert resp3.json['fields'][2]['label'] == 'foobar2' |
|
410 |
assert resp3.json['fields'][2]['type'] == 'items' |
|
411 |
assert len(resp3.json['fields'][2]['structured_items']) == 10 |
|
412 |
assert resp3.json['fields'][2]['structured_items'][0]['id'] == 0 |
|
413 |
assert resp3.json['fields'][2]['structured_items'][0]['text'] == 'label 0' |
|
414 |
assert resp3.json['fields'][2]['structured_items'][0]['foo'] == 0 |
|
415 |
assert resp3.json['fields'][2]['items'][0] == 'label 0' |
|
416 | ||
417 |
# if structured_items fails no values |
|
418 |
assert 'structured_items' not in resp4.json['fields'][1] |
|
419 |
assert resp4.json['fields'][1]['items'] == [] |
|
420 | ||
421 |
# workflow checks |
|
422 |
assert len(resp.json['workflow']['statuses']) == 3 |
|
423 |
assert resp.json['workflow']['statuses'][0]['id'] == 'st1' |
|
424 |
assert resp.json['workflow']['statuses'][0]['endpoint'] is False |
|
425 |
assert resp.json['workflow']['statuses'][0]['waitpoint'] is True |
|
426 |
assert resp.json['workflow']['statuses'][1]['id'] == 'st2' |
|
427 |
assert resp.json['workflow']['statuses'][1]['endpoint'] is False |
|
428 |
assert resp.json['workflow']['statuses'][1]['waitpoint'] is False |
|
429 |
assert resp.json['workflow']['statuses'][2]['id'] == 'st3' |
|
430 |
assert resp.json['workflow']['statuses'][2]['endpoint'] is True |
|
431 |
assert resp.json['workflow']['statuses'][2]['waitpoint'] is True |
|
432 |
assert len(resp.json['workflow']['fields']) == 1 |
|
433 | ||
434 |
assert resp.json['workflow']['fields'][0]['label'] == '1st backoffice field' |
|
435 | ||
436 |
get_app(pub).get('/api/formdefs/xxx/schema', status=404) |
|
437 | ||
438 | ||
439 |
def test_post_invalid_json(pub, local_user): |
|
440 |
resp = get_app(pub).post( |
|
441 |
'/api/formdefs/test/submit', params='not a json payload', content_type='application/json', status=400 |
|
442 |
) |
|
443 |
assert resp.json['err'] == 1 |
|
444 |
assert resp.json['err_class'] == 'Invalid request' |
|
445 | ||
446 | ||
447 |
def test_formdef_submit(pub, local_user): |
|
448 |
Role.wipe() |
|
449 |
role = Role(name='test') |
|
450 |
role.store() |
|
451 |
local_user.roles = [role.id] |
|
452 |
local_user.store() |
|
453 | ||
454 |
FormDef.wipe() |
|
455 |
formdef = FormDef() |
|
456 |
formdef.name = 'test' |
|
457 |
formdef.fields = [fields.StringField(id='0', label='foobar')] |
|
458 |
formdef.store() |
|
459 |
data_class = formdef.data_class() |
|
460 | ||
461 |
resp = get_app(pub).post_json('/api/formdefs/test/submit', {'data': {}}, status=403) |
|
462 |
assert resp.json['err'] == 1 |
|
463 |
assert resp.json['err_desc'] == 'unsigned API call' |
|
464 | ||
465 |
def url(): |
|
466 |
signed_url = sign_url( |
|
467 |
'http://example.net/api/formdefs/test/submit' |
|
468 |
+ '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
469 |
'1234', |
|
470 |
) |
|
471 |
return signed_url[len('http://example.net') :] |
|
472 | ||
473 |
resp = get_app(pub).post_json(url(), {'data': {}}) |
|
474 |
assert resp.json['err'] == 0 |
|
475 |
assert resp.json['data']['url'] == ('http://example.net/test/%s/' % resp.json['data']['id']) |
|
476 |
assert resp.json['data']['backoffice_url'] == ( |
|
477 |
'http://example.net/backoffice/management/test/%s/' % resp.json['data']['id'] |
|
478 |
) |
|
479 |
assert resp.json['data']['api_url'] == ('http://example.net/api/forms/test/%s/' % resp.json['data']['id']) |
|
480 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
481 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
482 |
assert data_class.get(resp.json['data']['id']).tracking_code is None |
|
483 | ||
484 |
local_user2 = get_publisher().user_class() |
|
485 |
local_user2.name = 'Test' |
|
486 |
local_user2.email = 'foo@localhost' |
|
487 |
local_user2.store() |
|
488 |
resp = get_app(pub).post_json(url(), {'data': {}, 'user': {'NameID': [], 'email': local_user2.email}}) |
|
489 |
assert data_class.get(resp.json['data']['id']).user.email == local_user2.email |
|
490 | ||
491 |
resp = get_app(pub).post( |
|
492 |
url(), json.dumps({'data': {}}), status=400 |
|
493 |
) # missing Content-Type: application/json header |
|
494 |
assert resp.json['err_desc'] == 'expected JSON but missing appropriate content-type' |
|
495 | ||
496 |
# check qualified content type are recognized |
|
497 |
resp = get_app(pub).post(url(), json.dumps({'data': {}}), content_type='application/json; charset=utf-8') |
|
498 |
assert resp.json['data']['url'] |
|
499 | ||
500 |
formdef.disabled = True |
|
501 |
formdef.store() |
|
502 |
resp = get_app(pub).post_json(url(), {'data': {}}, status=403) |
|
503 |
assert resp.json['err'] == 1 |
|
504 |
assert resp.json['err_desc'] == 'disabled form' |
|
505 | ||
506 |
formdef.disabled = False |
|
507 |
formdef.store() |
|
508 |
resp = get_app(pub).post_json(url(), {'meta': {'backoffice-submission': True}, 'data': {}}, status=403) |
|
509 |
formdef.backoffice_submission_roles = ['xx'] |
|
510 |
formdef.store() |
|
511 |
resp = get_app(pub).post_json(url(), {'meta': {'backoffice-submission': True}, 'data': {}}, status=403) |
|
512 |
formdef.backoffice_submission_roles = [role.id] |
|
513 |
formdef.store() |
|
514 |
resp = get_app(pub).post_json(url(), {'meta': {'backoffice-submission': True}, 'data': {}}) |
|
515 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
516 |
assert data_class.get(resp.json['data']['id']).backoffice_submission is True |
|
517 |
assert data_class.get(resp.json['data']['id']).user_id is None |
|
518 |
assert data_class.get(resp.json['data']['id']).submission_agent_id == str(local_user.id) |
|
519 | ||
520 |
formdef.enable_tracking_codes = True |
|
521 |
formdef.store() |
|
522 |
resp = get_app(pub).post_json(url(), {'data': {}}) |
|
523 |
assert data_class.get(resp.json['data']['id']).tracking_code |
|
524 | ||
525 |
resp = get_app(pub).post_json(url(), {'meta': {'draft': True}, 'data': {}}) |
|
526 |
assert data_class.get(resp.json['data']['id']).status == 'draft' |
|
527 | ||
528 |
resp = get_app(pub).post_json( |
|
529 |
url(), |
|
530 |
{ |
|
531 |
'meta': {'backoffice-submission': True}, |
|
532 |
'data': {}, |
|
533 |
'context': {'channel': 'mail', 'comments': 'blah'}, |
|
534 |
}, |
|
535 |
) |
|
536 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
537 |
assert data_class.get(resp.json['data']['id']).backoffice_submission is True |
|
538 |
assert data_class.get(resp.json['data']['id']).user_id is None |
|
539 |
assert data_class.get(resp.json['data']['id']).submission_context == {'comments': 'blah'} |
|
540 |
assert data_class.get(resp.json['data']['id']).submission_channel == 'mail' |
|
541 | ||
542 |
data_class.wipe() |
|
543 | ||
544 | ||
545 |
def test_formdef_submit_only_one(pub, local_user): |
|
546 |
Role.wipe() |
|
547 |
role = Role(name='test') |
|
548 |
role.store() |
|
549 |
local_user.roles = [role.id] |
|
550 |
local_user.store() |
|
551 | ||
552 |
FormDef.wipe() |
|
553 |
formdef = FormDef() |
|
554 |
formdef.name = 'test' |
|
555 |
formdef.only_allow_one = True |
|
556 |
formdef.fields = [fields.StringField(id='0', label='foobar')] |
|
557 |
formdef.store() |
|
558 |
data_class = formdef.data_class() |
|
559 | ||
560 |
def url(): |
|
561 |
signed_url = sign_url( |
|
562 |
'http://example.net/api/formdefs/test/submit' |
|
563 |
+ '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
564 |
'1234', |
|
565 |
) |
|
566 |
return signed_url[len('http://example.net') :] |
|
567 | ||
568 |
resp = get_app(pub).post_json(url(), {'data': {}}) |
|
569 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
570 | ||
571 |
assert data_class.count() == 1 |
|
572 | ||
573 |
resp = get_app(pub).post_json(url(), {'data': {}}, status=403) |
|
574 |
assert resp.json['err'] == 1 |
|
575 |
assert resp.json['err_desc'] == 'only one formdata by user is allowed' |
|
576 | ||
577 |
formdata = data_class.select()[0] |
|
578 |
formdata.user_id = '1000' # change owner |
|
579 |
formdata.store() |
|
580 | ||
581 |
resp = get_app(pub).post_json(url(), {'data': {}}, status=200) |
|
582 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
583 |
assert data_class.count() == 2 |
|
584 | ||
585 | ||
586 |
def test_formdef_submit_with_varname(pub, local_user): |
|
587 |
NamedDataSource.wipe() |
|
588 |
data_source = NamedDataSource(name='foobar') |
|
589 |
source = [{'id': '1', 'text': 'foo', 'more': 'XXX'}, {'id': '2', 'text': 'bar', 'more': 'YYY'}] |
|
590 |
data_source.data_source = {'type': 'formula', 'value': repr(source)} |
|
591 |
data_source.store() |
|
592 | ||
593 |
data_source = NamedDataSource(name='foobar_jsonp') |
|
594 |
data_source.data_source = {'type': 'formula', 'value': 'http://example.com/jsonp'} |
|
595 |
data_source.store() |
|
596 | ||
597 |
Role.wipe() |
|
598 |
role = Role(name='test') |
|
599 |
role.store() |
|
600 |
local_user.roles = [role.id] |
|
601 |
local_user.store() |
|
602 | ||
603 |
FormDef.wipe() |
|
604 |
formdef = FormDef() |
|
605 |
formdef.name = 'test' |
|
606 |
formdef.fields = [ |
|
607 |
fields.StringField(id='0', label='foobar0', varname='foobar0'), |
|
608 |
fields.ItemField(id='1', label='foobar1', varname='foobar1', data_source={'type': 'foobar'}), |
|
609 |
fields.ItemField(id='2', label='foobar2', varname='foobar2', data_source={'type': 'foobar_jsonp'}), |
|
610 |
fields.DateField(id='3', label='foobar3', varname='date'), |
|
611 |
fields.FileField(id='4', label='foobar4', varname='file'), |
|
612 |
fields.MapField(id='5', label='foobar5', varname='map'), |
|
613 |
fields.StringField(id='6', label='foobar6', varname='foobar6'), |
|
614 |
] |
|
615 |
formdef.store() |
|
616 |
data_class = formdef.data_class() |
|
617 | ||
618 |
resp = get_app(pub).post_json('/api/formdefs/test/submit', {'data': {}}, status=403) |
|
619 |
assert resp.json['err'] == 1 |
|
620 |
assert resp.json['err_desc'] == 'unsigned API call' |
|
621 | ||
622 |
signed_url = sign_url( |
|
623 |
'http://example.net/api/formdefs/test/submit' |
|
624 |
+ '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
625 |
'1234', |
|
626 |
) |
|
627 |
url = signed_url[len('http://example.net') :] |
|
628 |
payload = { |
|
629 |
'data': { |
|
630 |
'foobar0': 'xxx', |
|
631 |
'foobar1': '1', |
|
632 |
'foobar1_structured': { |
|
633 |
'id': '1', |
|
634 |
'text': 'foo', |
|
635 |
'more': 'XXX', |
|
636 |
}, |
|
637 |
'foobar2': 'bar', |
|
638 |
'foobar2_raw': '10', |
|
639 |
'date': '1970-01-01', |
|
640 |
'file': { |
|
641 |
'filename': 'test.txt', |
|
642 |
'content': force_text(base64.b64encode(b'test')), |
|
643 |
}, |
|
644 |
'map': { |
|
645 |
'lat': 1.5, |
|
646 |
'lon': 2.25, |
|
647 |
}, |
|
648 |
} |
|
649 |
} |
|
650 |
resp = get_app(pub).post_json(url, payload) |
|
651 |
assert resp.json['err'] == 0 |
|
652 |
assert data_class.get(resp.json['data']['id']).status == 'wf-new' |
|
653 |
assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) |
|
654 |
assert data_class.get(resp.json['data']['id']).tracking_code is None |
|
655 |
assert data_class.get(resp.json['data']['id']).data['0'] == 'xxx' |
|
656 |
assert data_class.get(resp.json['data']['id']).data['1'] == '1' |
|
657 |
assert data_class.get(resp.json['data']['id']).data['1_structured'] == source[0] |
|
658 |
assert data_class.get(resp.json['data']['id']).data['2'] == '10' |
|
659 |
assert data_class.get(resp.json['data']['id']).data['2_display'] == 'bar' |
|
660 |
assert data_class.get(resp.json['data']['id']).data['3'] == time.struct_time( |
|
661 |
(1970, 1, 1, 0, 0, 0, 3, 1, -1) |
|
662 |
) |
|
663 | ||
664 |
assert data_class.get(resp.json['data']['id']).data['4'].orig_filename == 'test.txt' |
|
665 |
assert data_class.get(resp.json['data']['id']).data['4'].get_content() == b'test' |
|
666 |
assert data_class.get(resp.json['data']['id']).data['5'] == '1.5;2.25' |
|
667 |
# test bijectivity |
|
668 |
assert ( |
|
669 |
formdef.fields[3].get_json_value(data_class.get(resp.json['data']['id']).data['3']) |
|
670 |
== payload['data']['date'] |
|
671 |
) |
|
672 |
for k in payload['data']['file']: |
|
673 |
data = data_class.get(resp.json['data']['id']).data['4'] |
|
674 |
assert formdef.fields[4].get_json_value(data)[k] == payload['data']['file'][k] |
|
675 |
assert ( |
|
676 |
formdef.fields[5].get_json_value(data_class.get(resp.json['data']['id']).data['5']) |
|
677 |
== payload['data']['map'] |
|
678 |
) |
|
679 | ||
680 |
data_class.wipe() |
|
681 | ||
682 | ||
683 |
def test_formdef_submit_from_wscall(pub, local_user): |
|
684 |
test_formdef_submit_with_varname(pub, local_user) |
|
685 |
formdef = FormDef.select()[0] |
|
686 |
workflow = Workflow.get_default_workflow() |
|
687 |
workflow.id = '2' |
|
688 |
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) |
|
689 |
workflow.backoffice_fields_formdef.fields = [ |
|
690 |
fields.StringField(id='bo1', label='1st backoffice field', type='string', varname='backoffice_blah'), |
|
691 |
] |
|
692 |
workflow.store() |
|
693 |
formdef.workflow = workflow |
|
694 |
formdef.store() |
|
695 | ||
696 |
formdata = formdef.data_class()() |
|
697 |
formdata.just_created() |
|
698 | ||
699 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
700 |
upload.receive([b'test']) |
|
701 | ||
702 |
formdata.data = { |
|
703 |
'0': 'xxx', |
|
704 |
'1': '1', |
|
705 |
'1_display': '1', |
|
706 |
'1_structured': { |
|
707 |
'id': '1', |
|
708 |
'text': 'foo', |
|
709 |
'more': 'XXX', |
|
710 |
}, |
|
711 |
'2': '10', |
|
712 |
'2_display': 'bar', |
|
713 |
'3': time.strptime('1970-01-01', '%Y-%m-%d'), |
|
714 |
'4': upload, |
|
715 |
'5': '1.5;2.25', |
|
716 |
'bo1': 'backoffice field', |
|
717 |
} |
|
718 |
formdata.just_created() |
|
719 |
formdata.evolution[-1].status = 'wf-new' |
|
720 |
formdata.store() |
|
721 | ||
722 |
payload = json.loads(json.dumps(formdata.get_json_export_dict(), cls=qommon.misc.JSONEncoder)) |
|
723 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
724 |
url = signed_url[len('http://example.net') :] |
|
725 | ||
726 |
resp = get_app(pub).post_json(url, payload) |
|
727 |
assert resp.json['err'] == 0 |
|
728 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
729 |
assert new_formdata.data['0'] == formdata.data['0'] |
|
730 |
assert new_formdata.data['1'] == formdata.data['1'] |
|
731 |
assert new_formdata.data['1_display'] == formdata.data['1_display'] |
|
732 |
assert new_formdata.data['1_structured'] == formdata.data['1_structured'] |
|
733 |
assert new_formdata.data['2'] == formdata.data['2'] |
|
734 |
assert new_formdata.data['2_display'] == formdata.data['2_display'] |
|
735 |
assert new_formdata.data['3'] == formdata.data['3'] |
|
736 |
assert new_formdata.data['4'].get_content() == formdata.data['4'].get_content() |
|
737 |
assert new_formdata.data['5'] == formdata.data['5'] |
|
738 |
assert new_formdata.data['bo1'] == formdata.data['bo1'] |
|
739 |
assert not new_formdata.data.get('6') |
|
740 |
assert new_formdata.user_id is None |
|
741 | ||
742 |
# add an extra attribute |
|
743 |
payload['extra'] = {'foobar6': 'YYY'} |
|
744 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
745 |
url = signed_url[len('http://example.net') :] |
|
746 |
resp = get_app(pub).post_json(url, payload) |
|
747 |
assert resp.json['err'] == 0 |
|
748 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
749 |
assert new_formdata.data['0'] == formdata.data['0'] |
|
750 |
assert new_formdata.data['6'] == 'YYY' |
|
751 | ||
752 |
# add user |
|
753 |
formdata.user_id = local_user.id |
|
754 |
formdata.store() |
|
755 | ||
756 |
payload = json.loads(json.dumps(formdata.get_json_export_dict(), cls=qommon.misc.JSONEncoder)) |
|
757 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
758 |
url = signed_url[len('http://example.net') :] |
|
759 | ||
760 |
resp = get_app(pub).post_json(url, payload) |
|
761 |
assert resp.json['err'] == 0 |
|
762 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
763 |
assert str(new_formdata.user_id) == str(local_user.id) |
|
764 | ||
765 |
# test missing map data |
|
766 |
del formdata.data['5'] |
|
767 | ||
768 |
payload = json.loads(json.dumps(formdata.get_json_export_dict(), cls=qommon.misc.JSONEncoder)) |
|
769 |
signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') |
|
770 |
url = signed_url[len('http://example.net') :] |
|
771 | ||
772 |
resp = get_app(pub).post_json(url, payload) |
|
773 |
assert resp.json['err'] == 0 |
|
774 |
new_formdata = formdef.data_class().get(resp.json['data']['id']) |
|
775 |
assert new_formdata.data.get('5') is None |
|
776 | ||
777 | ||
778 |
def test_formdef_submit_structured(pub, local_user): |
|
779 |
Role.wipe() |
|
780 |
role = Role(name='test') |
|
781 |
role.store() |
|
782 |
local_user.roles = [role.id] |
|
783 |
local_user.store() |
|
784 | ||
785 |
FormDef.wipe() |
|
786 |
formdef = FormDef() |
|
787 |
formdef.name = 'test' |
|
788 |
formdef.fields = [ |
|
789 |
fields.ItemField( |
|
790 |
id='0', |
|
791 |
label='foobar', |
|
792 |
varname='foobar', |
|
793 |
data_source={ |
|
794 |
'type': 'json', |
|
795 |
'value': 'http://datasource.com', |
|
796 |
}, |
|
797 |
), |
|
798 |
fields.ItemField( |
|
799 |
id='1', |
|
800 |
label='foobar1', |
|
801 |
varname='foobar1', |
|
802 |
data_source={ |
|
803 |
'type': 'formula', |
|
804 |
'value': '[dict(id=i, text=\'label %s\' % i, foo=i) for i in range(10)]', |
|
805 |
}, |
|
806 |
), |
|
807 |
] |
|
808 |
formdef.store() |
|
809 |
data_class = formdef.data_class() |
|
810 | ||
811 |
def url(): |
|
812 |
signed_url = sign_url( |
|
813 |
'http://example.net/api/formdefs/test/submit' |
|
814 |
'?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), |
|
815 |
'1234', |
|
816 |
) |
|
817 |
return signed_url[len('http://example.net') :] |
|
818 | ||
819 |
with mock.patch('wcs.qommon.misc.urlopen') as urlopen: |
|
820 |
urlopen.side_effect = lambda *args: StringIO( |
|
821 |
'''\ |
|
822 |
{"data": [{"id": 0, "text": "zéro", "foo": "bar"}, \ |
|
823 |
{"id": 1, "text": "uné", "foo": "bar1"}, \ |
|
824 |
{"id": 2, "text": "deux", "foo": "bar2"}]}''' |
|
825 |
) |
|
826 |
resp = get_app(pub).post_json( |
|
827 |
url(), |
|
828 |
{ |
|
829 |
'data': { |
|
830 |
'0': '0', |
|
831 |
"1": '3', |
|
832 |
} |
|
833 |
}, |
|
834 |
) |
|
835 | ||
836 |
formdata = data_class.get(resp.json['data']['id']) |
|
837 |
assert formdata.status == 'wf-new' |
|
838 |
assert formdata.data['0'] == '0' |
|
839 |
assert formdata.data['0_display'] == 'zéro' |
|
840 |
assert formdata.data['0_structured'] == { |
|
841 |
'id': 0, |
|
842 |
'text': 'zéro', |
|
843 |
'foo': 'bar', |
|
844 |
} |
|
845 |
assert formdata.data['1'] == '3' |
|
846 |
assert formdata.data['1_display'] == 'label 3' |
|
847 |
assert formdata.data['1_structured'] == { |
|
848 |
'id': 3, |
|
849 |
'text': 'label 3', |
|
850 |
'foo': 3, |
|
851 |
} |
|
852 | ||
853 |
data_class.wipe() |
tests/api/test_user.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import datetime |
|
4 |
import os |
|
5 | ||
6 |
import pytest |
|
7 |
from quixote import get_publisher |
|
8 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app |
|
9 | ||
10 |
from wcs import fields |
|
11 |
from wcs.formdef import FormDef |
|
12 |
from wcs.qommon.form import PicklableUpload |
|
13 |
from wcs.qommon.http_request import HTTPRequest |
|
14 |
from wcs.qommon.ident.password_accounts import PasswordAccount |
|
15 |
from wcs.roles import Role |
|
16 |
from wcs.workflows import Workflow, WorkflowVariablesFieldsFormDef |
|
17 | ||
18 |
from .utils import sign_uri |
|
19 | ||
20 | ||
21 |
def pytest_generate_tests(metafunc): |
|
22 |
if 'pub' in metafunc.fixturenames: |
|
23 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
24 | ||
25 | ||
26 |
@pytest.fixture |
|
27 |
def pub(request, emails): |
|
28 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
29 | ||
30 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
31 |
pub.set_app_dir(req) |
|
32 |
pub.cfg['identification'] = {'methods': ['password']} |
|
33 |
pub.cfg['language'] = {'language': 'en'} |
|
34 |
pub.write_cfg() |
|
35 | ||
36 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
37 |
'''\ |
|
38 |
[api-secrets] |
|
39 |
coucou = 1234 |
|
40 |
''' |
|
41 |
) |
|
42 | ||
43 |
return pub |
|
44 | ||
45 | ||
46 |
def teardown_module(module): |
|
47 |
clean_temporary_pub() |
|
48 | ||
49 | ||
50 |
@pytest.fixture |
|
51 |
def local_user(): |
|
52 |
get_publisher().user_class.wipe() |
|
53 |
user = get_publisher().user_class() |
|
54 |
user.name = 'Jean Darmette' |
|
55 |
user.email = 'jean.darmette@triffouilis.fr' |
|
56 |
user.name_identifiers = ['0123456789'] |
|
57 |
user.store() |
|
58 |
return user |
|
59 | ||
60 | ||
61 |
@pytest.fixture |
|
62 |
def admin_user(): |
|
63 |
get_publisher().user_class.wipe() |
|
64 |
user = get_publisher().user_class() |
|
65 |
user.name = 'John Doe Admin' |
|
66 |
user.email = 'john.doe@example.com' |
|
67 |
user.name_identifiers = ['0123456789'] |
|
68 |
user.is_admin = True |
|
69 |
user.store() |
|
70 | ||
71 |
account = PasswordAccount(id='admin') |
|
72 |
account.set_password('admin') |
|
73 |
account.user_id = user.id |
|
74 |
account.store() |
|
75 | ||
76 |
return user |
|
77 | ||
78 | ||
79 |
def test_roles(pub, local_user): |
|
80 |
Role.wipe() |
|
81 |
role = Role(name='Hello World') |
|
82 |
role.emails = ['toto@example.com', 'zozo@example.com'] |
|
83 |
role.details = 'kouign amann' |
|
84 |
role.store() |
|
85 | ||
86 |
resp = get_app(pub).get('/api/roles', status=403) |
|
87 | ||
88 |
resp = get_app(pub).get(sign_uri('/api/roles')) |
|
89 |
assert resp.json['data'][0]['text'] == 'Hello World' |
|
90 |
assert resp.json['data'][0]['slug'] == 'hello-world' |
|
91 |
assert resp.json['data'][0]['emails'] == ['toto@example.com', 'zozo@example.com'] |
|
92 |
assert resp.json['data'][0]['emails_to_members'] is False |
|
93 |
assert resp.json['data'][0]['details'] == 'kouign amann' |
|
94 | ||
95 |
# also check old endpoint, for compatibility |
|
96 |
resp = get_app(pub).get(sign_uri('/roles'), headers={'Accept': 'application/json'}) |
|
97 |
assert resp.json['data'][0]['text'] == 'Hello World' |
|
98 |
assert resp.json['data'][0]['slug'] == 'hello-world' |
|
99 |
assert resp.json['data'][0]['emails'] == ['toto@example.com', 'zozo@example.com'] |
|
100 |
assert resp.json['data'][0]['emails_to_members'] is False |
|
101 |
assert resp.json['data'][0]['details'] == 'kouign amann' |
|
102 | ||
103 | ||
104 |
def test_users(pub, local_user): |
|
105 |
resp = get_app(pub).get('/api/users/', status=403) |
|
106 | ||
107 |
resp = get_app(pub).get(sign_uri('/api/users/')) |
|
108 |
assert resp.json['data'][0]['user_display_name'] == local_user.name |
|
109 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
110 |
assert resp.json['data'][0]['user_id'] == local_user.id |
|
111 | ||
112 |
role = Role(name='Foo bar') |
|
113 |
role.store() |
|
114 |
local_user.roles = [role.id] |
|
115 |
local_user.store() |
|
116 | ||
117 |
resp = get_app(pub).get(sign_uri('/api/users/?q=jean')) |
|
118 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
119 |
assert len(resp.json['data'][0]['user_roles']) == 1 |
|
120 |
assert resp.json['data'][0]['user_roles'][0]['name'] == 'Foo bar' |
|
121 | ||
122 |
resp = get_app(pub).get(sign_uri('/api/users/?q=foobar')) |
|
123 |
assert len(resp.json['data']) == 0 |
|
124 | ||
125 |
from wcs.admin.settings import UserFieldsFormDef |
|
126 | ||
127 |
formdef = UserFieldsFormDef(pub) |
|
128 |
formdef.fields.append(fields.StringField(id='3', label='test', type='string')) |
|
129 |
formdef.store() |
|
130 | ||
131 |
local_user.form_data = {'3': 'HELLO'} |
|
132 |
local_user.set_attributes_from_formdata(local_user.form_data) |
|
133 |
local_user.store() |
|
134 | ||
135 |
resp = get_app(pub).get(sign_uri('/api/users/?q=HELLO')) |
|
136 |
assert len(resp.json['data']) == 1 |
|
137 |
resp = get_app(pub).get(sign_uri('/api/users/?q=foobar')) |
|
138 |
assert len(resp.json['data']) == 0 |
|
139 | ||
140 | ||
141 |
def test_users_unaccent(pub, local_user): |
|
142 |
local_user.name = 'Jean Sénisme' |
|
143 |
local_user.store() |
|
144 |
resp = get_app(pub).get(sign_uri('/api/users/?q=jean')) |
|
145 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
146 | ||
147 |
resp = get_app(pub).get(sign_uri('/api/users/?q=senisme')) |
|
148 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
149 | ||
150 |
resp = get_app(pub).get(sign_uri('/api/users/?q=sénisme')) |
|
151 |
assert resp.json['data'][0]['user_email'] == local_user.email |
|
152 | ||
153 |
resp = get_app(pub).get(sign_uri('/api/users/?q=blah')) |
|
154 |
assert len(resp.json['data']) == 0 |
|
155 | ||
156 | ||
157 |
def test_user_by_nameid(pub, local_user): |
|
158 |
resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user), status=404) |
|
159 |
local_user.name_identifiers = ['xyz'] |
|
160 |
local_user.store() |
|
161 |
resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user)) |
|
162 |
assert str(resp.json['id']) == str(local_user.id) |
|
163 | ||
164 | ||
165 |
def test_user_roles(pub, local_user): |
|
166 |
local_user.name_identifiers = ['xyz'] |
|
167 |
local_user.store() |
|
168 |
role = Role(name='Foo bar') |
|
169 |
role.store() |
|
170 |
local_user.roles = [role.id] |
|
171 |
local_user.store() |
|
172 |
resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user)) |
|
173 |
assert len(resp.json['user_roles']) == 1 |
|
174 |
assert resp.json['user_roles'][0]['name'] == 'Foo bar' |
|
175 | ||
176 | ||
177 |
def test_user_forms(pub, local_user): |
|
178 |
Workflow.wipe() |
|
179 |
workflow = Workflow.get_default_workflow() |
|
180 |
workflow.id = '2' |
|
181 |
workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow) |
|
182 |
workflow.variables_formdef.fields.append( |
|
183 |
fields.DateField(label='Test', type='date', varname='option_date') |
|
184 |
) |
|
185 |
workflow.store() |
|
186 | ||
187 |
FormDef.wipe() |
|
188 |
formdef = FormDef() |
|
189 |
formdef.name = 'test' |
|
190 |
formdef.fields = [ |
|
191 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
192 |
fields.StringField(id='1', label='foobar2'), |
|
193 |
fields.DateField(id='2', label='date', type='date', varname='date'), |
|
194 |
] |
|
195 |
formdef.keywords = 'hello, world' |
|
196 |
formdef.disabled = False |
|
197 |
formdef.enable_tracking_codes = True |
|
198 |
formdef.workflow = workflow |
|
199 |
formdef.workflow_options = {'option_date': datetime.date(2020, 1, 15).timetuple()} |
|
200 |
formdef.store() |
|
201 |
formdef.data_class().wipe() |
|
202 | ||
203 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
204 |
assert resp.json['err'] == 0 |
|
205 |
assert len(resp.json['data']) == 0 |
|
206 | ||
207 |
formdata = formdef.data_class()() |
|
208 |
formdata.data = { |
|
209 |
'0': 'foo@localhost', |
|
210 |
'1': 'xxx', |
|
211 |
'2': datetime.date(2020, 1, 15).timetuple(), |
|
212 |
} |
|
213 |
formdata.user_id = local_user.id |
|
214 |
formdata.just_created() |
|
215 |
formdata.jump_status('new') |
|
216 |
formdata.store() |
|
217 | ||
218 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
219 |
resp2 = get_app(pub).get(sign_uri('/myspace/forms', user=local_user)) |
|
220 |
resp3 = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id)) |
|
221 |
assert resp.json['err'] == 0 |
|
222 |
assert len(resp.json['data']) == 1 |
|
223 |
assert resp.json['data'][0]['form_name'] == 'test' |
|
224 |
assert resp.json['data'][0]['form_slug'] == 'test' |
|
225 |
assert resp.json['data'][0]['form_status'] == 'New' |
|
226 |
assert datetime.datetime.strptime(resp.json['data'][0]['form_receipt_datetime'], '%Y-%m-%dT%H:%M:%S') |
|
227 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
228 |
assert resp.json == resp2.json == resp3.json |
|
229 | ||
230 |
resp = get_app(pub).get(sign_uri('/api/user/forms?full=on', user=local_user)) |
|
231 |
assert resp.json['err'] == 0 |
|
232 |
assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost' |
|
233 |
assert resp.json['data'][0]['fields']['date'] == '2020-01-15' |
|
234 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
235 |
assert resp.json['data'][0]['form_option_option_date'] == '2020-01-15' |
|
236 |
resp2 = get_app(pub).get(sign_uri('/api/user/forms?&full=on', user=local_user)) |
|
237 |
assert resp.json == resp2.json |
|
238 | ||
239 |
formdef.disabled = True |
|
240 |
formdef.store() |
|
241 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
242 |
assert resp.json['err'] == 0 |
|
243 |
assert len(resp.json['data']) == 1 |
|
244 | ||
245 |
# check digest is part of contents |
|
246 |
formdef.digest_template = 'XYZ' |
|
247 |
formdef.data_class().get(formdata.id).store() |
|
248 |
assert formdef.data_class().get(formdata.id).digest == 'XYZ' |
|
249 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
250 |
assert resp.json['data'][0]['form_digest'] == 'XYZ' |
|
251 | ||
252 |
resp = get_app(pub).get(sign_uri('/api/user/forms?NameID=xxx')) |
|
253 |
assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []} |
|
254 |
resp2 = get_app(pub).get(sign_uri('/api/user/forms?&NameID=xxx')) |
|
255 |
assert resp.json == resp2.json |
|
256 | ||
257 |
formdata = formdef.data_class()() |
|
258 |
formdata.user_id = local_user.id |
|
259 |
formdata.status = 'draft' |
|
260 |
formdata.receipt_time = datetime.datetime(2015, 1, 1).timetuple() |
|
261 |
formdata.store() |
|
262 | ||
263 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
264 |
assert resp.json['err'] == 0 |
|
265 |
assert len(resp.json['data']) == 1 |
|
266 | ||
267 |
resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user)) |
|
268 |
assert resp.json['err'] == 0 |
|
269 |
assert len(resp.json['data']) == 1 |
|
270 | ||
271 |
formdef.disabled = False |
|
272 |
formdef.store() |
|
273 | ||
274 |
resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user)) |
|
275 |
assert resp.json['err'] == 0 |
|
276 |
assert len(resp.json['data']) == 2 |
|
277 | ||
278 |
formdata = formdef.data_class()() |
|
279 |
formdata.data = {'0': 'foo@localhost', '1': 'xyy'} |
|
280 |
formdata.user_id = local_user.id |
|
281 |
formdata.just_created() |
|
282 |
formdata.receipt_time = (datetime.datetime.now() + datetime.timedelta(days=1)).timetuple() |
|
283 |
formdata.jump_status('new') |
|
284 |
formdata.store() |
|
285 | ||
286 |
resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) |
|
287 |
assert len(resp.json['data']) == 2 |
|
288 |
resp2 = get_app(pub).get(sign_uri('/api/user/forms?sort=desc', user=local_user)) |
|
289 |
assert len(resp2.json['data']) == 2 |
|
290 |
assert resp2.json['data'][0] == resp.json['data'][1] |
|
291 |
assert resp2.json['data'][1] == resp.json['data'][0] |
|
292 | ||
293 | ||
294 |
def test_user_forms_limit_offset(pub, local_user): |
|
295 |
if not pub.is_using_postgresql(): |
|
296 |
pytest.skip('this requires SQL') |
|
297 |
return |
|
298 | ||
299 |
FormDef.wipe() |
|
300 |
formdef = FormDef() |
|
301 |
formdef.name = 'test limit offset' |
|
302 |
formdef.fields = [ |
|
303 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
304 |
fields.StringField(id='1', label='foobar2'), |
|
305 |
] |
|
306 |
formdef.keywords = 'hello, world' |
|
307 |
formdef.disabled = False |
|
308 |
formdef.enable_tracking_codes = False |
|
309 |
formdef.store() |
|
310 |
formdef.data_class().wipe() |
|
311 | ||
312 |
for i in range(50): |
|
313 |
formdata = formdef.data_class()() |
|
314 |
formdata.data = {'0': 'foo@localhost', '1': str(i)} |
|
315 |
formdata.user_id = local_user.id |
|
316 |
formdata.just_created() |
|
317 |
formdata.receipt_time = (datetime.datetime.now() + datetime.timedelta(days=i)).timetuple() |
|
318 |
formdata.jump_status('new') |
|
319 |
formdata.store() |
|
320 | ||
321 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id)) |
|
322 |
assert resp.json['err'] == 0 |
|
323 |
assert len(resp.json['data']) == 50 |
|
324 | ||
325 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms?limit=10' % local_user.id)) |
|
326 |
assert resp.json['err'] == 0 |
|
327 |
assert len(resp.json['data']) == 10 |
|
328 |
assert [x['form_number_raw'] for x in resp.json['data']] == [str(x) for x in range(1, 11)] |
|
329 | ||
330 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms?limit=10&offset=45' % local_user.id)) |
|
331 |
assert resp.json['err'] == 0 |
|
332 |
assert len(resp.json['data']) == 5 |
|
333 |
assert [x['form_number_raw'] for x in resp.json['data']] == [str(x) for x in range(46, 51)] |
|
334 | ||
335 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms?limit=10&sort=desc' % local_user.id)) |
|
336 |
assert resp.json['err'] == 0 |
|
337 |
assert len(resp.json['data']) == 10 |
|
338 |
assert [x['form_number_raw'] for x in resp.json['data']] == [str(x) for x in range(50, 40, -1)] |
|
339 | ||
340 | ||
341 |
def test_user_forms_from_agent(pub, local_user): |
|
342 |
Role.wipe() |
|
343 |
role = Role(name='Foo bar') |
|
344 |
role.store() |
|
345 | ||
346 |
agent_user = get_publisher().user_class() |
|
347 |
agent_user.name = 'Agent' |
|
348 |
agent_user.email = 'agent@example.com' |
|
349 |
agent_user.name_identifiers = ['ABCDE'] |
|
350 |
agent_user.roles = [role.id] |
|
351 |
agent_user.store() |
|
352 | ||
353 |
FormDef.wipe() |
|
354 |
formdef = FormDef() |
|
355 |
formdef.name = 'test' |
|
356 |
formdef.fields = [ |
|
357 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
358 |
fields.StringField(id='1', label='foobar2'), |
|
359 |
] |
|
360 |
formdef.store() |
|
361 |
formdef.data_class().wipe() |
|
362 | ||
363 |
formdata = formdef.data_class()() |
|
364 |
formdata.data = {'0': 'foo@localhost', '1': 'xxx'} |
|
365 |
formdata.user_id = local_user.id |
|
366 |
formdata.just_created() |
|
367 |
formdata.jump_status('new') |
|
368 |
formdata.store() |
|
369 | ||
370 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user)) |
|
371 |
assert resp.json['err'] == 0 |
|
372 |
assert len(resp.json['data']) == 1 |
|
373 |
assert resp.json['data'][0]['form_name'] == 'test' |
|
374 |
assert resp.json['data'][0]['form_slug'] == 'test' |
|
375 |
assert resp.json['data'][0]['form_status'] == 'New' |
|
376 |
assert resp.json['data'][0]['readable'] is False |
|
377 | ||
378 |
formdef.skip_from_360_view = True |
|
379 |
formdef.store() |
|
380 | ||
381 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user)) |
|
382 |
assert len(resp.json['data']) == 0 |
|
383 | ||
384 |
formdef.workflow_roles = {'_receiver': str(role.id)} |
|
385 |
formdef.store() |
|
386 |
formdef.data_class().rebuild_security() |
|
387 |
resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user)) |
|
388 |
assert len(resp.json['data']) == 1 |
|
389 | ||
390 |
agent_user.roles = [] |
|
391 |
agent_user.store() |
|
392 |
get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user), status=403) |
|
393 | ||
394 | ||
395 |
def test_user_drafts(pub, local_user): |
|
396 |
FormDef.wipe() |
|
397 |
formdef = FormDef() |
|
398 |
formdef.name = 'test' |
|
399 |
formdef.fields = [ |
|
400 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
|
401 |
fields.StringField(id='1', label='foobar2'), |
|
402 |
fields.FileField(id='2', label='foobar3', varname='file'), |
|
403 |
] |
|
404 |
formdef.keywords = 'hello, world' |
|
405 |
formdef.disabled = False |
|
406 |
formdef.enable_tracking_codes = True |
|
407 |
formdef.store() |
|
408 | ||
409 |
formdef.data_class().wipe() |
|
410 | ||
411 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
412 |
assert resp.json['err'] == 0 |
|
413 |
assert len(resp.json['data']) == 0 |
|
414 | ||
415 |
formdata = formdef.data_class()() |
|
416 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
417 |
upload.receive([b'base64me']) |
|
418 |
formdata.data = {'0': 'foo@localhost', '1': 'xxx', '2': upload} |
|
419 |
formdata.user_id = local_user.id |
|
420 |
formdata.page_no = 1 |
|
421 |
formdata.status = 'draft' |
|
422 |
formdata.receipt_time = datetime.datetime(2015, 1, 1).timetuple() |
|
423 |
formdata.store() |
|
424 | ||
425 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
426 |
resp2 = get_app(pub).get(sign_uri('/myspace/drafts', user=local_user)) |
|
427 |
assert resp.json['err'] == 0 |
|
428 |
assert len(resp.json['data']) == 1 |
|
429 |
assert resp.json == resp2.json |
|
430 |
assert 'fields' not in resp.json['data'][0] |
|
431 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
432 | ||
433 |
resp = get_app(pub).get(sign_uri('/api/user/drafts?full=on', user=local_user)) |
|
434 |
assert resp.json['err'] == 0 |
|
435 |
assert 'fields' in resp.json['data'][0] |
|
436 |
assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost' |
|
437 |
assert 'url' in resp.json['data'][0]['fields']['file'] |
|
438 |
assert 'content' not in resp.json['data'][0]['fields']['file'] # no file content in full lists |
|
439 |
assert resp.json['data'][0]['keywords'] == ['hello', 'world'] |
|
440 | ||
441 |
formdef.enable_tracking_codes = False |
|
442 |
formdef.store() |
|
443 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
444 |
assert resp.json['err'] == 0 |
|
445 |
assert len(resp.json['data']) == 1 |
|
446 | ||
447 |
formdef.enable_tracking_codes = True |
|
448 |
formdef.disabled = True |
|
449 |
formdef.store() |
|
450 |
resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) |
|
451 |
assert resp.json['err'] == 0 |
|
452 |
assert len(resp.json['data']) == 0 |
|
453 | ||
454 |
resp = get_app(pub).get(sign_uri('/api/user/drafts?NameID=xxx')) |
|
455 |
assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []} |
|
456 |
resp2 = get_app(pub).get(sign_uri('/api/user/drafts?&NameID=xxx')) |
|
457 |
assert resp.json == resp2.json |
tests/api/test_utils.py | ||
---|---|---|
1 | 1 |
import pytest |
2 | 2 |
from quixote import cleanup, get_response |
3 |
from utilities import clean_temporary_pub, create_temporary_pub |
|
3 | 4 | |
4 | 5 |
from wcs import sessions |
5 | 6 |
from wcs.api_utils import get_query_flag |
6 | 7 |
from wcs.qommon.http_request import HTTPRequest |
7 | 8 | |
8 |
from utilities import create_temporary_pub, clean_temporary_pub |
|
9 | ||
10 | 9 | |
11 | 10 |
def setup_module(module): |
12 | 11 |
cleanup() |
tests/api/test_workflow.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import os |
|
4 | ||
5 |
import pytest |
|
6 |
from quixote import get_publisher |
|
7 |
from utilities import clean_temporary_pub, create_temporary_pub, get_app |
|
8 | ||
9 |
from wcs import fields |
|
10 |
from wcs.formdef import FormDef |
|
11 |
from wcs.qommon.http_request import HTTPRequest |
|
12 |
from wcs.qommon.ident.password_accounts import PasswordAccount |
|
13 |
from wcs.roles import Role |
|
14 |
from wcs.wf.jump import JumpWorkflowStatusItem |
|
15 |
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem |
|
16 |
from wcs.workflows import Workflow |
|
17 | ||
18 |
from .utils import sign_uri |
|
19 | ||
20 | ||
21 |
def pytest_generate_tests(metafunc): |
|
22 |
if 'pub' in metafunc.fixturenames: |
|
23 |
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) |
|
24 | ||
25 | ||
26 |
@pytest.fixture |
|
27 |
def pub(request, emails): |
|
28 |
pub = create_temporary_pub(sql_mode=(request.param == 'sql')) |
|
29 | ||
30 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
31 |
pub.set_app_dir(req) |
|
32 |
pub.cfg['identification'] = {'methods': ['password']} |
|
33 |
pub.cfg['language'] = {'language': 'en'} |
|
34 |
pub.write_cfg() |
|
35 | ||
36 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write( |
|
37 |
'''\ |
|
38 |
[api-secrets] |
|
39 |
coucou = 1234 |
|
40 |
''' |
|
41 |
) |
|
42 | ||
43 |
return pub |
|
44 | ||
45 | ||
46 |
def teardown_module(module): |
|
47 |
clean_temporary_pub() |
|
48 | ||
49 | ||
50 |
@pytest.fixture |
|
51 |
def local_user(): |
|
52 |
get_publisher().user_class.wipe() |
|
53 |
user = get_publisher().user_class() |
|
54 |
user.name = 'Jean Darmette' |
|
55 |
user.email = 'jean.darmette@triffouilis.fr' |
|
56 |
user.name_identifiers = ['0123456789'] |
|
57 |
user.store() |
|
58 |
return user |
|
59 | ||
60 | ||
61 |
@pytest.fixture |
|
62 |
def admin_user(): |
|
63 |
get_publisher().user_class.wipe() |
|
64 |
user = get_publisher().user_class() |
|
65 |
user.name = 'John Doe Admin' |
|
66 |
user.email = 'john.doe@example.com' |
|
67 |
user.name_identifiers = ['0123456789'] |
|
68 |
user.is_admin = True |
|
69 |
user.store() |
|
70 | ||
71 |
account = PasswordAccount(id='admin') |
|
72 |
account.set_password('admin') |
|
73 |
account.user_id = user.id |
|
74 |
account.store() |
|
75 | ||
76 |
return user |
|
77 | ||
78 | ||
79 |
def test_workflow_trigger(pub, local_user): |
|
80 |
workflow = Workflow(name='test') |
|
81 |
st1 = workflow.add_status('Status1', 'st1') |
|
82 |
jump = JumpWorkflowStatusItem() |
|
83 |
jump.trigger = 'XXX' |
|
84 |
jump.status = 'st2' |
|
85 |
st1.items.append(jump) |
|
86 |
jump.parent = st1 |
|
87 |
workflow.add_status('Status2', 'st2') |
|
88 |
workflow.store() |
|
89 | ||
90 |
FormDef.wipe() |
|
91 |
formdef = FormDef() |
|
92 |
formdef.name = 'test' |
|
93 |
formdef.fields = [] |
|
94 |
formdef.workflow_id = workflow.id |
|
95 |
formdef.store() |
|
96 | ||
97 |
formdef.data_class().wipe() |
|
98 |
formdata = formdef.data_class()() |
|
99 |
formdata.just_created() |
|
100 |
formdata.store() |
|
101 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
102 | ||
103 |
get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200) |
|
104 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
105 | ||
106 |
# check with trailing slash |
|
107 |
formdata.store() # reset |
|
108 |
get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX/'), status=200) |
|
109 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
110 | ||
111 |
Role.wipe() |
|
112 |
role = Role(name='xxx') |
|
113 |
role.store() |
|
114 | ||
115 |
jump.by = [role.id] |
|
116 |
workflow.store() |
|
117 | ||
118 |
formdata.store() # (will get back to wf-st1) |
|
119 |
get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=403) |
|
120 | ||
121 |
get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX', user=local_user), status=403) |
|
122 | ||
123 |
local_user.roles = [role.id] |
|
124 |
local_user.store() |
|
125 |
get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX', user=local_user), status=200) |
|
126 | ||
127 | ||
128 |
def test_workflow_trigger_with_data(pub, local_user): |
|
129 |
workflow = Workflow(name='test') |
|
130 |
st1 = workflow.add_status('Status1', 'st1') |
|
131 |
jump = JumpWorkflowStatusItem() |
|
132 |
jump.trigger = 'XXX' |
|
133 |
jump.status = 'st2' |
|
134 |
st1.items.append(jump) |
|
135 |
jump.parent = st1 |
|
136 |
workflow.add_status('Status2', 'st2') |
|
137 |
workflow.store() |
|
138 | ||
139 |
FormDef.wipe() |
|
140 |
formdef = FormDef() |
|
141 |
formdef.name = 'test' |
|
142 |
formdef.fields = [] |
|
143 |
formdef.workflow_id = workflow.id |
|
144 |
formdef.store() |
|
145 | ||
146 |
formdef.data_class().wipe() |
|
147 |
formdata = formdef.data_class()() |
|
148 |
formdata.just_created() |
|
149 |
formdata.store() |
|
150 | ||
151 |
get_app(pub).post_json( |
|
152 |
sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200, params={'test': 'data'} |
|
153 |
) |
|
154 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
155 |
assert formdef.data_class().get(formdata.id).workflow_data == {'test': 'data'} |
|
156 | ||
157 |
# post with empty dictionary |
|
158 |
formdata.store() # reset |
|
159 |
get_app(pub).post_json(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200, params={}) |
|
160 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
161 |
assert not formdef.data_class().get(formdata.id).workflow_data |
|
162 | ||
163 |
# post with empty data |
|
164 |
formdata.store() # reset |
|
165 |
get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=200) |
|
166 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
167 |
assert not formdef.data_class().get(formdata.id).workflow_data |
|
168 | ||
169 |
# post with empty data, but declare json content-type |
|
170 |
formdata.store() # reset |
|
171 |
get_app(pub).post( |
|
172 |
sign_uri(formdata.get_url() + 'jump/trigger/XXX'), |
|
173 |
status=200, |
|
174 |
headers={'content-type': 'application/json'}, |
|
175 |
) |
|
176 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
177 |
assert not formdef.data_class().get(formdata.id).workflow_data |
|
178 | ||
179 |
# post with invalid JSON data |
|
180 |
formdata.store() # reset |
|
181 |
get_app(pub).post( |
|
182 |
sign_uri(formdata.get_url() + 'jump/trigger/XXX'), |
|
183 |
status=400, |
|
184 |
headers={'content-type': 'application/json'}, |
|
185 |
params='ERROR', |
|
186 |
) |
|
187 | ||
188 | ||
189 |
def test_workflow_trigger_with_condition(pub, local_user): |
|
190 |
workflow = Workflow(name='test') |
|
191 |
st1 = workflow.add_status('Status1', 'st1') |
|
192 |
jump = JumpWorkflowStatusItem() |
|
193 |
jump.trigger = 'XXX' |
|
194 |
jump.condition = {'type': 'django', 'value': 'form_var_foo == "bar"'} |
|
195 |
jump.status = 'st2' |
|
196 |
st1.items.append(jump) |
|
197 |
jump.parent = st1 |
|
198 |
workflow.add_status('Status2', 'st2') |
|
199 |
workflow.store() |
|
200 | ||
201 |
FormDef.wipe() |
|
202 |
formdef = FormDef() |
|
203 |
formdef.name = 'test' |
|
204 |
formdef.fields = [fields.StringField(id='0', label='foo', varname='foo')] |
|
205 |
formdef.workflow_id = workflow.id |
|
206 |
formdef.store() |
|
207 | ||
208 |
formdef.data_class().wipe() |
|
209 |
formdata = formdef.data_class()() |
|
210 |
formdata.data = {'0': 'foo'} |
|
211 |
formdata.just_created() |
|
212 |
formdata.store() |
|
213 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
214 | ||
215 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX'), status=403) |
|
216 |
assert resp.json == {'err_desc': 'unmet condition', 'err': 1} |
|
217 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
218 |
# check without json |
|
219 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX', format=None), status=403) |
|
220 |
assert resp.content_type == 'text/html' |
|
221 | ||
222 |
formdata.data['0'] = 'bar' |
|
223 |
formdata.store() |
|
224 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX')) |
|
225 |
assert resp.json == {'err': 0, 'url': None} |
|
226 | ||
227 | ||
228 |
def test_workflow_trigger_jump_once(pub, local_user): |
|
229 |
workflow = Workflow(name='test') |
|
230 |
st1 = workflow.add_status('Status1', 'st1') |
|
231 |
st2 = workflow.add_status('Status2', 'st2') |
|
232 |
workflow.add_status('Status3', 'st3') |
|
233 |
jump = JumpWorkflowStatusItem() |
|
234 |
jump.trigger = 'XXX' |
|
235 |
jump.status = 'st2' |
|
236 |
st1.items.append(jump) |
|
237 |
jump.parent = st1 |
|
238 |
jump = JumpWorkflowStatusItem() |
|
239 |
jump.trigger = 'XXX' |
|
240 |
jump.status = 'st3' |
|
241 |
st2.items.append(jump) |
|
242 |
jump.parent = st2 |
|
243 |
workflow.store() |
|
244 | ||
245 |
FormDef.wipe() |
|
246 |
formdef = FormDef() |
|
247 |
formdef.name = 'test' |
|
248 |
formdef.fields = [] |
|
249 |
formdef.workflow_id = workflow.id |
|
250 |
formdef.store() |
|
251 | ||
252 |
formdef.data_class().wipe() |
|
253 |
formdata = formdef.data_class()() |
|
254 |
formdata.just_created() |
|
255 |
formdata.store() |
|
256 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
257 | ||
258 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX')) |
|
259 |
assert resp.json == {'err': 0, 'url': None} |
|
260 |
assert formdef.data_class().get(formdata.id).status == 'wf-st2' |
|
261 | ||
262 |
resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX')) |
|
263 |
assert resp.json == {'err': 0, 'url': None} |
|
264 |
assert formdef.data_class().get(formdata.id).status == 'wf-st3' |
|
265 | ||
266 | ||
267 |
def test_workflow_global_webservice_trigger(pub, local_user): |
|
268 |
workflow = Workflow(name='test') |
|
269 |
workflow.add_status('Status1', 'st1') |
|
270 | ||
271 |
ac1 = workflow.add_global_action('Action', 'ac1') |
|
272 |
trigger = ac1.append_trigger('webservice') |
|
273 |
trigger.identifier = 'plop' |
|
274 | ||
275 |
add_to_journal = RegisterCommenterWorkflowStatusItem() |
|
276 |
add_to_journal.id = '_add_to_journal' |
|
277 |
add_to_journal.comment = 'HELLO WORLD' |
|
278 |
ac1.items.append(add_to_journal) |
|
279 |
add_to_journal.parent = ac1 |
|
280 | ||
281 |
workflow.store() |
|
282 | ||
283 |
FormDef.wipe() |
|
284 |
formdef = FormDef() |
|
285 |
formdef.name = 'test' |
|
286 |
formdef.fields = [] |
|
287 |
formdef.workflow_id = workflow.id |
|
288 |
formdef.store() |
|
289 | ||
290 |
formdef.data_class().wipe() |
|
291 |
formdata = formdef.data_class()() |
|
292 |
formdata.just_created() |
|
293 |
formdata.store() |
|
294 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
295 | ||
296 |
# call to undefined hook |
|
297 |
get_app(pub).post(sign_uri(formdata.get_url() + 'hooks/XXX/'), status=404) |
|
298 |
get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/XXX/'), status=404) |
|
299 | ||
300 |
# anonymous call |
|
301 |
get_app(pub).post(formdata.get_url() + 'hooks/plop/', status=200) |
|
302 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD' |
|
303 | ||
304 |
add_to_journal.comment = 'HELLO WORLD 2' |
|
305 |
workflow.store() |
|
306 |
get_app(pub).post(formdata.get_api_url() + 'hooks/plop/', status=200) |
|
307 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD 2' |
|
308 | ||
309 |
# call requiring user |
|
310 |
add_to_journal.comment = 'HELLO WORLD 3' |
|
311 |
trigger.roles = ['logged-users'] |
|
312 |
workflow.store() |
|
313 |
get_app(pub).post(formdata.get_api_url() + 'hooks/plop/', status=403) |
|
314 |
get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/'), status=200) |
|
315 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD 3' |
|
316 | ||
317 |
# call requiring roles |
|
318 |
add_to_journal.comment = 'HELLO WORLD 4' |
|
319 |
trigger.roles = ['logged-users'] |
|
320 |
workflow.store() |
|
321 |
Role.wipe() |
|
322 |
role = Role(name='xxx') |
|
323 |
role.store() |
|
324 |
trigger.roles = [role.id] |
|
325 |
workflow.store() |
|
326 |
get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/'), status=403) |
|
327 |
get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), status=403) |
|
328 | ||
329 |
local_user.roles = [role.id] |
|
330 |
local_user.store() |
|
331 |
get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), status=200) |
|
332 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD 4' |
|
333 | ||
334 |
# call adding data |
|
335 |
add_to_journal.comment = 'HELLO {{plop_test}}' |
|
336 |
workflow.store() |
|
337 |
get_app(pub).post_json( |
|
338 |
sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), {'test': 'foobar'}, status=200 |
|
339 |
) |
|
340 |
# (django templating make it turn into HTML) |
|
341 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == '<div>HELLO foobar</div>' |
|
342 | ||
343 |
# call adding data but with no actions |
|
344 |
ac1.items = [] |
|
345 |
workflow.store() |
|
346 |
get_app(pub).post_json( |
|
347 |
sign_uri(formdata.get_api_url() + 'hooks/plop/', user=local_user), {'test': 'BAR'}, status=200 |
|
348 |
) |
|
349 |
assert formdef.data_class().get(formdata.id).workflow_data == {'plop': {'test': 'BAR'}} |
|
350 | ||
351 | ||
352 |
def test_workflow_global_webservice_trigger_no_trailing_slash(pub, local_user): |
|
353 |
workflow = Workflow(name='test') |
|
354 |
workflow.add_status('Status1', 'st1') |
|
355 | ||
356 |
ac1 = workflow.add_global_action('Action', 'ac1') |
|
357 |
trigger = ac1.append_trigger('webservice') |
|
358 |
trigger.identifier = 'plop' |
|
359 | ||
360 |
add_to_journal = RegisterCommenterWorkflowStatusItem() |
|
361 |
add_to_journal.id = '_add_to_journal' |
|
362 |
add_to_journal.comment = 'HELLO WORLD' |
|
363 |
ac1.items.append(add_to_journal) |
|
364 |
add_to_journal.parent = ac1 |
|
365 | ||
366 |
workflow.store() |
|
367 | ||
368 |
FormDef.wipe() |
|
369 |
formdef = FormDef() |
|
370 |
formdef.name = 'test' |
|
371 |
formdef.fields = [] |
|
372 |
formdef.workflow_id = workflow.id |
|
373 |
formdef.store() |
|
374 | ||
375 |
formdef.data_class().wipe() |
|
376 |
formdata = formdef.data_class()() |
|
377 |
formdata.just_created() |
|
378 |
formdata.store() |
|
379 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
380 | ||
381 |
# call to undefined hook |
|
382 |
get_app(pub).post(sign_uri(formdata.get_url() + 'hooks/XXX'), status=404) |
|
383 |
get_app(pub).post(sign_uri(formdata.get_api_url() + 'hooks/XXX'), status=404) |
|
384 | ||
385 |
# anonymous call |
|
386 |
get_app(pub).post(formdata.get_url() + 'hooks/plop', status=200) |
|
387 |
assert formdef.data_class().get(formdata.id).evolution[-1].parts[-1].content == 'HELLO WORLD' |
tests/api/utils.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import base64 |
|
4 |
import datetime |
|
5 |
import hashlib |
|
6 |
import hmac |
|
7 | ||
8 |
from django.utils.encoding import force_bytes |
|
9 |
from django.utils.six.moves.urllib import parse as urllib |
|
10 |
from django.utils.six.moves.urllib import parse as urlparse |
|
11 | ||
12 | ||
13 |
def sign_uri(uri, user=None, format='json'): |
|
14 |
timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' |
|
15 |
scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri) |
|
16 |
if query: |
|
17 |
query += '&' |
|
18 |
if format: |
|
19 |
query += 'format=%s&' % format |
|
20 |
query += 'orig=coucou&algo=sha256×tamp=' + timestamp |
|
21 |
if user: |
|
22 |
query += '&email=' + urllib.quote(user.email) |
|
23 |
query += '&signature=%s' % urllib.quote( |
|
24 |
base64.b64encode(hmac.new(b'1234', force_bytes(query), hashlib.sha256).digest()) |
|
25 |
) |
|
26 |
return urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) |
|
0 |
- |