Projet

Général

Profil

0001-api-change-schema-of-formdefs-user-forms-and-user-dr.patch

Thomas Noël, 16 janvier 2018 12:07

Télécharger (25,2 ko)

Voir les différences:

Subject: [PATCH] api: change schema of formdefs, user/forms and user/drafts
 APIs (#13184)

All responses now have the form {"err": x, "data": y}.
 help/fr/api-user.page | 146 ++++++++++++++++++++++--------------------
 tests/test_api.py     | 173 ++++++++++++++++++++++++++++++--------------------
 wcs/api.py            |  13 ++--
 3 files changed, 189 insertions(+), 143 deletions(-)
help/fr/api-user.page
63 63
    </p>
64 64
<screen>
65 65
<output style="prompt">$ </output><input>curl https://www.example.net/api/user/forms</input>
66
<output>[
67
    {
68
        "category_id": "1",
69
        "category_name": "Divers",
70
        "datetime": "2014-03-28 15:36:52",
71
        "form_name": "Demande d'inscription",
72
        "form_slug": "demande-d-inscription",
73
        "form_number": "123",
74
        "form_number_raw": "123",
75
        "form_receipt_date": "28/03/2014",
76
        "form_receipt_time": "15:36",
77
        "form_status": "Nouveau",
78
        "form_status_is_endpoint": false,
79
        "form_uri": "demande-d-inscription/123/",
80
        "form_url": "http://www.example.net/demande-d-inscription/123/",
81
        "form_url_backoffice": "http://www.example.net/backoffice/demande-d-inscription/123/",
82
        "name": "Demande d'inscription",
83
        "status": "Nouveau",
84
        "title": "Demande d'inscription #123 (Nouveau)",
85
        "url": "http://www.example.net/demande-d-inscription/123/",
86
    },
87
    {
88
        "category_id": "2",
89
        "category_name": "Prise de rendez-vous",
90
        "datetime": "2014-03-17 10:39:52",
91
        "form_name": "Rendez-vous avec le service B",
92
        "form_slug": "rendez-vous-service-b",
93
        "form_number": "456",
94
        "form_number_raw": "456",
95
        "form_receipt_date": "17/03/2014",
96
        "form_receipt_time": "10:39",
97
        "form_status": "En cours",
98
        "form_status_is_endpoint": false,
99
        "form_uri": "rendez-vous-service-b/456/",
100
        "form_url": "http://www.example.net/rendez-vous-service-b/456/",
101
        "form_url_backoffice": "http://www.example.net/backoffice/rendez-vous-service-b/456/",
102
        "name": "Rendez-vous avec le service B"",
103
        "status": "Nouveau",
104
        "title": "Rendez-vous avec le service B #456 (En cours)",
105
        "url": "http://www.example.net/rendez-vous-service-b/456/",
106
    },
107
    {
108
        "category_id": "3",
109
        "category_name": "Modification de vos coordonn\u00e9es",
110
        "datetime": "2014-03-17 10:42:17",
111
        "form_name": "Changement d'adresse",
112
        "form_slug": "changement-d-adresse",
113
        "form_number": "424",
114
        "form_number_raw": "424",
115
        "form_receipt_date": "17/03/2014",
116
        "form_receipt_time": "10:42",
117
        "form_status": "Traitement de la demande termin\u00e9",
118
        "form_status_is_endpoint": true,
119
        "form_uri": "changement-d-adresse/424/",
120
        "form_url": "http://www.example.net/changement-d-adresse/424/",
121
        "form_url_backoffice": "http://www.example.net/backoffice/changement-d-adresse/424/",
122
        "name": "Changement d'adresse",
123
        "status": "Traitement de la demande termin\u00e9",
124
        "title": "Changement d'adresse #424 (Traitement de la demande termin\u00e9)",
125
        "url": "http://www.example.net/changement-d-adresse/424/",
126
    }
127
]</output></screen>
66
<output>{
67
    "err": 0,
68
    "data": [
69
        {
70
            "category_id": "1",
71
            "category_name": "Divers",
72
            "datetime": "2014-03-28 15:36:52",
73
            "form_name": "Demande d'inscription",
74
            "form_slug": "demande-d-inscription",
75
            "form_number": "123",
76
            "form_number_raw": "123",
77
            "form_receipt_date": "28/03/2014",
78
            "form_receipt_time": "15:36",
79
            "form_status": "Nouveau",
80
            "form_status_is_endpoint": false,
81
            "form_uri": "demande-d-inscription/123/",
82
            "form_url": "http://www.example.net/demande-d-inscription/123/",
83
            "form_url_backoffice": "http://www.example.net/backoffice/demande-d-inscription/123/",
84
            "name": "Demande d'inscription",
85
            "status": "Nouveau",
86
            "title": "Demande d'inscription #123 (Nouveau)",
87
            "url": "http://www.example.net/demande-d-inscription/123/",
88
        },
89
        {
90
            "category_id": "2",
91
            "category_name": "Prise de rendez-vous",
92
            "datetime": "2014-03-17 10:39:52",
93
            "form_name": "Rendez-vous avec le service B",
94
            "form_slug": "rendez-vous-service-b",
95
            "form_number": "456",
96
            "form_number_raw": "456",
97
            "form_receipt_date": "17/03/2014",
98
            "form_receipt_time": "10:39",
99
            "form_status": "En cours",
100
            "form_status_is_endpoint": false,
101
            "form_uri": "rendez-vous-service-b/456/",
102
            "form_url": "http://www.example.net/rendez-vous-service-b/456/",
103
            "form_url_backoffice": "http://www.example.net/backoffice/rendez-vous-service-b/456/",
104
            "name": "Rendez-vous avec le service B"",
105
            "status": "Nouveau",
106
            "title": "Rendez-vous avec le service B #456 (En cours)",
107
            "url": "http://www.example.net/rendez-vous-service-b/456/",
108
        },
109
        {
110
            "category_id": "3",
111
            "category_name": "Modification de vos coordonn\u00e9es",
112
            "datetime": "2014-03-17 10:42:17",
113
            "form_name": "Changement d'adresse",
114
            "form_slug": "changement-d-adresse",
115
            "form_number": "424",
116
            "form_number_raw": "424",
117
            "form_receipt_date": "17/03/2014",
118
            "form_receipt_time": "10:42",
119
            "form_status": "Traitement de la demande termin\u00e9",
120
            "form_status_is_endpoint": true,
121
            "form_uri": "changement-d-adresse/424/",
122
            "form_url": "http://www.example.net/changement-d-adresse/424/",
123
            "form_url_backoffice": "http://www.example.net/backoffice/changement-d-adresse/424/",
124
            "name": "Changement d'adresse",
125
            "status": "Traitement de la demande termin\u00e9",
126
            "title": "Changement d'adresse #424 (Traitement de la demande termin\u00e9)",
127
            "url": "http://www.example.net/changement-d-adresse/424/",
128
        }
129
    ]
130
}</output></screen>
128 131

  
129 132
<p>
130 133
Il est possible de recevoir un ensemble plus complet de données en passant un
......
143 146

  
144 147
<screen>
145 148
<output style="prompt">$ </output><input>curl https://www.example.net/api/user/drafts</input>
146
<output>[
147
    {
148
        "datetime": "2014-07-21 10:15:21",
149
        "name": "Demande de relecture",
150
        "title": "Demande de relecture, brouillon enregistré le 21/07/2014 10:15",
151
        "url": "http://www.example.net/demande-de-relecture/164"
152
    }
153
]</output></screen>
149
<output>{
150
    "err": 0,
151
    "data": [
152
        {
153
            "datetime": "2014-07-21 10:15:21",
154
            "name": "Demande de relecture",
155
            "title": "Demande de relecture, brouillon enregistré le 21/07/2014 10:15",
156
            "url": "http://www.example.net/demande-de-relecture/164"
157
        }
158
    ]
159
}</output></screen>
154 160

  
155 161
<note>
156 162
<p>Note de compatibilité : cette information est également disponible à
tests/test_api.py
174 174
    output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature))
175 175
    assert output.json == {'data': []}
176 176
    output = get_app(pub).get('/json?%s&signature=%s' % (query, signature))
177
    assert output.json == []
177
    assert output.json == {'err': 0, 'data': []}
178 178

  
179 179
def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub):
180 180
    # check the categories and forms endpoints accept an unknown NameID
......
188 188
    output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature))
189 189
    assert output.json == {'data': []}
190 190
    output = get_app(pub).get('/json?%s&signature=%s' % (query, signature))
191
    assert output.json == []
191
    assert output.json == {'err': 0, 'data': []}
192 192

  
193 193
def test_get_user_from_api_query_string_error_success_sha1(pub, local_user):
194 194
    timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z'
......
316 316
    resp2 = get_app(pub).get('/', headers={'Accept': 'application/json'})
317 317
    resp3 = get_app(pub).get('/api/formdefs/')
318 318
    assert resp1.json == resp2.json == resp3.json
319
    assert resp1.json[0]['title'] == 'test'
320
    assert resp1.json[0]['url'] == 'http://example.net/test/'
321
    assert resp1.json[0]['redirection'] == False
322
    assert resp1.json[0]['description'] == 'plop'
323
    assert resp1.json[0]['keywords'] == ['mobile', 'test']
324
    assert resp1.json[0]['functions'].keys() == ['_receiver']
325
    assert resp1.json[0]['functions']['_receiver']['label'] == 'Recipient'
326
    assert resp1.json[0]['functions']['_receiver']['role']['slug'] == role.slug
327
    assert resp1.json[0]['functions']['_receiver']['role']['name'] == role.name
319
    assert resp1.json['data'][0]['title'] == 'test'
320
    assert resp1.json['data'][0]['url'] == 'http://example.net/test/'
321
    assert resp1.json['data'][0]['redirection'] == False
322
    assert resp1.json['data'][0]['description'] == 'plop'
323
    assert resp1.json['data'][0]['keywords'] == ['mobile', 'test']
324
    assert resp1.json['data'][0]['functions'].keys() == ['_receiver']
325
    assert resp1.json['data'][0]['functions']['_receiver']['label'] == 'Recipient'
326
    assert resp1.json['data'][0]['functions']['_receiver']['role']['slug'] == role.slug
327
    assert resp1.json['data'][0]['functions']['_receiver']['role']['name'] == role.name
328
    assert 'count' not in resp1.json['data'][0]
328 329

  
329 330
    # backoffice_submission formdef : none
330 331
    resp1 = get_app(pub).get('/api/formdefs/?backoffice-submission=on')
331
    assert len(resp1.json) == 0
332
    assert resp1.json['err'] == 0
333
    assert len(resp1.json['data']) == 0
332 334

  
333 335
def test_limited_formdef_list(pub, local_user):
334 336
    Role.wipe()
......
345 347
    formdef.store()
346 348

  
347 349
    resp = get_app(pub).get('/api/formdefs/')
348
    assert len(resp.json) == 1
350
    assert resp.json['err'] == 0
351
    assert len(resp.json['data']) == 1
349 352
    # not present in backoffice-submission formdefs
350 353
    resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on')
351
    assert len(resp.json) == 0
354
    assert resp.json['err'] == 0
355
    assert len(resp.json['data']) == 0
352 356

  
353 357
    # check it's not advertised
354 358
    formdef.roles = [role.id]
......
357 361
    resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID='))
358 362
    resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX'))
359 363
    resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0]))
360
    assert len(resp.json) == 0
364
    assert resp.json['err'] == 0
365
    assert len(resp.json['data']) == 0
361 366
    assert resp.json == resp2.json == resp3.json == resp4.json
362 367
    # still not present in backoffice-submission formdefs
363 368
    resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on')
364
    assert len(resp.json) == 0
369
    assert resp.json['err'] == 0
370
    assert len(resp.json['data']) == 0
365 371

  
366 372
    # unless user has correct roles
367 373
    local_user.roles = [role.id]
368 374
    local_user.store()
369 375
    resp = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0]))
370
    assert len(resp.json) == 1
376
    assert resp.json['err'] == 0
377
    assert len(resp.json['data']) == 1
371 378

  
372 379
    local_user.roles = []
373 380
    local_user.store()
......
379 386
    resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID='))
380 387
    resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX'))
381 388
    resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0]))
382
    assert len(resp.json) == 1
383
    assert resp.json[0]['authentication_required']
389
    assert resp.json['err'] == 0
390
    assert len(resp.json['data']) == 1
391
    assert resp.json['data'][0]['authentication_required']
384 392
    assert resp.json == resp2.json == resp3.json == resp4.json
385 393

  
386 394
    formdef.required_authentication_contexts = ['fedict']
387 395
    formdef.store()
388 396
    resp = get_app(pub).get('/api/formdefs/')
389
    assert resp.json[0]['required_authentication_contexts'] == ['fedict']
397
    assert resp.json['data'][0]['required_authentication_contexts'] == ['fedict']
390 398

  
391 399
def test_formdef_list_redirection(pub):
392 400
    FormDef.wipe()
......
398 406
    formdef.store()
399 407

  
400 408
    resp1 = get_app(pub).get('/json')
401
    assert resp1.json[0]['title'] == 'test'
402
    assert resp1.json[0]['url'] == 'http://example.net/test/'
403
    assert resp1.json[0]['redirection'] == True
409
    assert resp1.json['err'] == 0
410
    assert resp1.json['data'][0]['title'] == 'test'
411
    assert resp1.json['data'][0]['url'] == 'http://example.net/test/'
412
    assert resp1.json['data'][0]['redirection'] == True
413
    assert 'count' not in resp1.json['data'][0]
404 414

  
405 415
def test_backoffice_submission_formdef_list(pub, local_user):
406 416
    Role.wipe()
......
417 427
    formdef.store()
418 428

  
419 429
    resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on')
420
    assert len(resp.json) == 0
430
    assert resp.json['err'] == 0
431
    assert len(resp.json['data']) == 0
421 432

  
422 433
    # check it's not advertised ...
423 434
    formdef.backoffice_submission_roles = [role.id]
424 435
    formdef.store()
425 436
    resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on')
426
    assert len(resp.json) == 0
437
    assert resp.json['err'] == 0
438
    assert len(resp.json['data']) == 0
427 439

  
428 440
    # even if it's advertised on frontoffice
429 441
    formdef.always_advertise = True
430 442
    formdef.store()
431 443
    resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on')
432
    assert len(resp.json) == 0
444
    assert resp.json['err'] == 0
445
    assert len(resp.json['data']) == 0
433 446

  
434 447
    # ... unless user has correct roles
435 448
    local_user.roles = [role.id]
436 449
    local_user.store()
437 450
    resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on&NameID=%s' %
438 451
                                     local_user.name_identifiers[0]))
439
    assert len(resp.json) == 1
452
    assert resp.json['err'] == 0
453
    assert len(resp.json['data']) == 1
440 454

  
441 455
    # but not advertised if it's a redirection
442 456
    formdef.disabled = True
443 457
    formdef.disabled_redirection = 'http://example.net'
444 458
    formdef.store()
445 459
    resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on')
446
    assert len(resp.json) == 0
460
    assert resp.json['err'] == 0
461
    assert len(resp.json['data']) == 0
447 462

  
448 463
def test_formdef_schema(pub):
449 464
    Workflow.wipe()
......
912 927
    resp = get_app(pub).get('/api/categories/category/formdefs/')
913 928
    resp2 = get_app(pub).get('/category/json')
914 929
    assert resp.json == resp2.json
915
    assert len(resp.json) == 2
916
    assert resp.json[0]['title'] == 'test'
917
    assert resp.json[0]['url'] == 'http://example.net/test/'
918
    assert resp.json[0]['redirection'] == False
919
    assert resp.json[0]['category'] == 'Category'
920
    assert resp.json[0]['category_slug'] == 'category'
921
    assert not 'count' in resp.json[0]
930
    assert resp.json['err'] == 0
931
    assert len(resp.json['data']) == 2
932
    assert resp.json['data'][0]['title'] == 'test'
933
    assert resp.json['data'][0]['url'] == 'http://example.net/test/'
934
    assert resp.json['data'][0]['redirection'] == False
935
    assert resp.json['data'][0]['category'] == 'Category'
936
    assert resp.json['data'][0]['category_slug'] == 'category'
937
    assert 'count' not in resp.json['data'][0]
922 938

  
923 939
    resp = get_app(pub).get('/api/categories/category/formdefs/?include-count=on')
924
    assert resp.json[0]['title'] == 'test'
925
    assert resp.json[0]['url'] == 'http://example.net/test/'
926
    assert resp.json[0]['count'] == 0
940
    assert resp.json['data'][0]['title'] == 'test'
941
    assert resp.json['data'][0]['url'] == 'http://example.net/test/'
942
    assert resp.json['data'][0]['count'] == 0
927 943

  
928 944
    get_app(pub).get('/api/categories/XXX/formdefs/', status=404)
929 945

  
930 946
    resp = get_app(pub).get('/api/categories/category/formdefs/?backoffice-submission=on')
931
    assert len(resp.json) == 0
947
    assert resp.json['err'] == 0
948
    assert len(resp.json['data']) == 0
932 949

  
933 950
    Role.wipe()
934 951
    role = Role(name='test')
......
939 956
    formdef.backoffice_submission_roles = [role.id]
940 957
    formdef.store()
941 958
    resp = get_app(pub).get('/api/categories/category/formdefs/?backoffice-submission=on')
942
    assert len(resp.json) == 0
959
    assert resp.json['err'] == 0
960
    assert len(resp.json['data']) == 0
943 961
    resp = get_app(pub).get(sign_uri(
944 962
        '/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' %
945 963
        local_user.name_identifiers[0]))
946
    assert len(resp.json) == 0
964
    assert resp.json['err'] == 0
965
    assert len(resp.json['data']) == 0
947 966
    # ... unless user has correct roles
948 967
    local_user.roles = [role.id]
949 968
    local_user.store()
950 969
    resp = get_app(pub).get(sign_uri(
951 970
        '/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' %
952 971
        local_user.name_identifiers[0]))
953
    assert len(resp.json) == 1
972
    assert resp.json['err'] == 0
973
    assert len(resp.json['data']) == 1
954 974

  
955 975
def test_categories_full(pub):
956 976
    test_categories(pub)
......
1203 1223
    formdef.data_class().wipe()
1204 1224

  
1205 1225
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1206
    assert len(resp.json) == 0
1226
    assert resp.json['err'] == 0
1227
    assert len(resp.json['data']) == 0
1207 1228

  
1208 1229
    formdata = formdef.data_class()()
1209 1230
    formdata.data = {'0': 'foo@localhost', '1': 'xxx'}
......
1215 1236
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1216 1237
    resp2 = get_app(pub).get(sign_uri('/myspace/forms', user=local_user))
1217 1238
    resp3 = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id))
1218
    assert len(resp.json) == 1
1219
    assert resp.json[0]['form_name'] == 'test'
1220
    assert resp.json[0]['form_slug'] == 'test'
1221
    assert resp.json[0]['form_status'] == 'New'
1222
    assert datetime.datetime.strptime(resp.json[0]['form_receipt_datetime'],
1223
                                      '%Y-%m-%dT%H:%M:%S')
1224
    assert resp.json[0]['keywords'] == ['hello', 'world']
1239
    assert resp.json['err'] == 0
1240
    assert len(resp.json['data']) == 1
1241
    assert resp.json['data'][0]['form_name'] == 'test'
1242
    assert resp.json['data'][0]['form_slug'] == 'test'
1243
    assert resp.json['data'][0]['form_status'] == 'New'
1244
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1225 1245
    assert resp.json == resp2.json == resp3.json
1226 1246

  
1227
    resp = get_app(pub).get(sign_uri('/api/user/forms?full=on', user=local_user))
1228
    assert resp.json[0]['fields']['foobar'] == 'foo@localhost'
1229
    assert resp.json[0]['keywords'] == ['hello', 'world']
1247
    resp = get_app(pub).get(sign_uri('/api/user/forms?&full=on', user=local_user))
1248
    assert resp.json['err'] == 0
1249
    assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost'
1250
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1230 1251

  
1231 1252
    formdef.disabled = True
1232 1253
    formdef.store()
1233 1254
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1234
    assert len(resp.json) == 1
1255
    assert resp.json['err'] == 0
1256
    assert len(resp.json['data']) == 1
1257

  
1258
    resp = get_app(pub).get(sign_uri('/api/user/forms?&NameID=xxx'))
1259
    assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []}
1260

  
1235 1261

  
1236 1262
    formdata = formdef.data_class()()
1237 1263
    formdata.user_id = local_user.id
......
1240 1266
    formdata.store()
1241 1267

  
1242 1268
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1243
    assert len(resp.json) == 1
1269
    assert resp.json['err'] == 0
1270
    assert len(resp.json['data']) == 1
1244 1271

  
1245 1272
    resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user))
1246
    assert len(resp.json) == 1
1273
    assert resp.json['err'] == 0
1274
    assert len(resp.json['data']) == 1
1247 1275

  
1248 1276
    formdef.disabled = False
1249 1277
    formdef.store()
1250 1278

  
1251 1279
    resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user))
1252
    assert len(resp.json) == 2
1280
    assert resp.json['err'] == 0
1281
    assert len(resp.json['data']) == 2
1253 1282

  
1254
    draft_formdata = [x for x in resp.json if x['status'] == 'Draft'][0]
1283
    draft_formdata = [x for x in resp.json['data'] if x['status'] == 'Draft'][0]
1255 1284
    assert draft_formdata.get('url')[-1] != '/'
1256 1285

  
1257 1286
def test_user_drafts(pub, local_user):
......
1270 1299
    formdef.data_class().wipe()
1271 1300

  
1272 1301
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1273
    assert len(resp.json) == 0
1302
    assert resp.json['err'] == 0
1303
    assert len(resp.json['data']) == 0
1274 1304

  
1275 1305
    formdata = formdef.data_class()()
1276 1306
    upload = PicklableUpload('test.txt', 'text/plain', 'ascii')
......
1284 1314

  
1285 1315
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1286 1316
    resp2 = get_app(pub).get(sign_uri('/myspace/drafts', user=local_user))
1287
    assert len(resp.json) == 1
1317
    assert resp.json['err'] == 0
1318
    assert len(resp.json['data']) == 1
1288 1319
    assert resp.json == resp2.json
1289
    assert not 'fields' in resp.json[0]
1290
    assert resp.json[0]['keywords'] == ['hello', 'world']
1320
    assert not 'fields' in resp.json['data'][0]
1321
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1291 1322

  
1292 1323
    resp = get_app(pub).get(sign_uri('/api/user/drafts?full=on', user=local_user))
1293
    assert 'fields' in resp.json[0]
1294
    assert resp.json[0]['fields']['foobar'] == 'foo@localhost'
1295
    assert 'file' not in resp.json[0]['fields'] # no file export in full lists
1296
    assert resp.json[0]['keywords'] == ['hello', 'world']
1324
    assert resp.json['err'] == 0
1325
    assert 'fields' in resp.json['data'][0]
1326
    assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost'
1327
    assert 'file' not in resp.json['data'][0]['fields']  # no file export in full lists
1328
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1297 1329

  
1298 1330
    formdef.enable_tracking_codes = False
1299 1331
    formdef.store()
1300 1332
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1301
    assert len(resp.json) == 0
1333
    assert resp.json['err'] == 0
1334
    assert len(resp.json['data']) == 0
1302 1335

  
1303 1336
    formdef.enable_tracking_codes = True
1304 1337
    formdef.disabled = True
1305 1338
    formdef.store()
1306 1339
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1307
    assert len(resp.json) == 0
1340
    assert resp.json['err'] == 0
1341
    assert len(resp.json['data']) == 0
1342

  
1343
    resp = get_app(pub).get(sign_uri('/api/user/drafts?&NameID=xxx'))
1344
    assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []}
1308 1345

  
1309 1346
def test_api_list_formdata(pub, local_user):
1310 1347
    Role.wipe()
wcs/api.py
450 450
            del formdict['category_position']
451 451

  
452 452
        get_response().set_content_type('application/json')
453
        return json.dumps(list_forms)
453
        return json.dumps({'err': 0, 'data': list_forms})
454 454

  
455 455
    def _q_lookup(self, component):
456 456
        try:
......
556 556

  
557 557
    def forms(self, include_drafts=False, include_non_drafts=True):
558 558
        get_response().set_content_type('application/json')
559
        user = self.user or get_user_from_api_query_string() or get_request().user
559
        try:
560
            user = self.user or get_user_from_api_query_string() or get_request().user
561
        except UnknownNameIdAccessForbiddenError:
562
            return json.dumps({'err': 1, 'err_desc': 'unknown NameID', 'data': []})
560 563
        if not user:
561
            raise AccessForbiddenError('no user specified')
564
            return json.dumps({'err': 1, 'err_desc': 'no user specified', 'data': []})
562 565
        forms = []
563 566
        include_drafts = include_drafts or get_request().form.get('include-drafts') == 'true'
564 567
        for form in self.get_user_forms(user):
......
576 579
                continue
577 580
            forms.append(formdata_dict)
578 581

  
579
        return json.dumps(forms,
582
        return json.dumps({'err': 0, 'data': forms},
580 583
                cls=misc.JSONEncoder,
581 584
                encoding=get_publisher().site_charset)
582 585

  
......
694 697
        for role in Role.select():
695 698
            list_roles.append(role.get_json_export_dict())
696 699
        get_response().set_content_type('application/json')
697
        return json.dumps({'data': list_roles})
700
        return json.dumps({'err': 0, 'data': list_roles})
698 701

  
699 702
    def validate_expression(self):
700 703
        get_response().set_content_type('application/json')
701
-