Projet

Général

Profil

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

Thomas Noël, 04 novembre 2017 03:36

Télécharger (21,3 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 | 148 ++++++++++++++++++++++++++------------------------
 tests/test_api.py     | 122 ++++++++++++++++++++++++-----------------
 wcs/api.py            |  22 +++++---
 3 files changed, 162 insertions(+), 130 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
......
147 150
    </p>
148 151

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

  
160 166
<note>
161 167
<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]['count'] == 0
322
    assert resp1.json[0]['redirection'] == False
323
    assert resp1.json[0]['description'] == 'plop'
324
    assert resp1.json[0]['keywords'] == ['mobile', 'test']
325
    assert resp1.json[0]['functions'].keys() == ['_receiver']
326
    assert resp1.json[0]['functions']['_receiver']['label'] == 'Recipient'
327
    assert resp1.json[0]['functions']['_receiver']['role']['slug'] == role.slug
328
    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]['count'] == 0
322
    assert resp1.json['data'][0]['redirection'] == False
323
    assert resp1.json['data'][0]['description'] == 'plop'
324
    assert resp1.json['data'][0]['keywords'] == ['mobile', 'test']
325
    assert resp1.json['data'][0]['functions'].keys() == ['_receiver']
326
    assert resp1.json['data'][0]['functions']['_receiver']['label'] == 'Recipient'
327
    assert resp1.json['data'][0]['functions']['_receiver']['role']['slug'] == role.slug
328
    assert resp1.json['data'][0]['functions']['_receiver']['role']['name'] == role.name
329 329

  
330 330
def test_limited_formdef_list(pub, local_user):
331 331
    Role.wipe()
......
342 342
    formdef.store()
343 343

  
344 344
    resp = get_app(pub).get('/api/formdefs/')
345
    assert len(resp.json) == 1
345
    assert resp.json['err'] == 0
346
    assert len(resp.json['data']) == 1
346 347

  
347 348
    # check it's not advertised
348 349
    formdef.roles = [role.id]
......
351 352
    resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID='))
352 353
    resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX'))
353 354
    resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0]))
354
    assert len(resp.json) == 0
355
    assert resp.json['err'] == 0
356
    assert len(resp.json['data']) == 0
355 357
    assert resp.json == resp2.json == resp3.json == resp4.json
356 358

  
357 359
    # unless user has correct roles
358 360
    local_user.roles = [role.id]
359 361
    local_user.store()
360 362
    resp = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0]))
361
    assert len(resp.json) == 1
363
    assert resp.json['err'] == 0
364
    assert len(resp.json['data']) == 1
362 365

  
363 366
    local_user.roles = []
364 367
    local_user.store()
......
370 373
    resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID='))
371 374
    resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX'))
372 375
    resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0]))
373
    assert len(resp.json) == 1
374
    assert resp.json[0]['authentication_required']
376
    assert resp.json['err'] == 0
377
    assert len(resp.json['data']) == 1
378
    assert resp.json['data'][0]['authentication_required']
375 379
    assert resp.json == resp2.json == resp3.json == resp4.json
376 380

  
377 381
    formdef.required_authentication_contexts = ['fedict']
378 382
    formdef.store()
379 383
    resp = get_app(pub).get('/api/formdefs/')
380
    assert resp.json[0]['required_authentication_contexts'] == ['fedict']
384
    assert resp.json['data'][0]['required_authentication_contexts'] == ['fedict']
381 385

  
382 386
def test_formdef_list_redirection(pub):
383 387
    FormDef.wipe()
......
389 393
    formdef.store()
390 394

  
391 395
    resp1 = get_app(pub).get('/json')
392
    assert resp1.json[0]['title'] == 'test'
393
    assert resp1.json[0]['url'] == 'http://example.net/test/'
394
    assert resp1.json[0]['count'] == 0
395
    assert resp1.json[0]['redirection'] == True
396
    assert resp1.json['err'] == 0
397
    assert resp1.json['data'][0]['title'] == 'test'
398
    assert resp1.json['data'][0]['url'] == 'http://example.net/test/'
399
    assert resp1.json['data'][0]['count'] == 0
400
    assert resp1.json['data'][0]['redirection'] == True
396 401

  
397 402

  
398 403
def test_formdef_schema(pub):
......
841 846
    resp = get_app(pub).get('/api/categories/category/formdefs/')
842 847
    resp2 = get_app(pub).get('/category/json')
843 848
    assert resp.json == resp2.json
844
    assert len(resp.json) == 2
845
    assert resp.json[0]['title'] == 'test'
846
    assert resp.json[0]['url'] == 'http://example.net/test/'
847
    assert resp.json[0]['count'] == 0
848
    assert resp.json[0]['redirection'] == False
849
    assert resp.json[0]['category'] == 'Category'
850
    assert resp.json[0]['category_slug'] == 'category'
849
    assert resp.json['err'] == 0
850
    assert len(resp.json['data']) == 2
851
    assert resp.json['data'][0]['title'] == 'test'
852
    assert resp.json['data'][0]['url'] == 'http://example.net/test/'
853
    assert resp.json['data'][0]['count'] == 0
854
    assert resp.json['data'][0]['redirection'] == False
855
    assert resp.json['data'][0]['category'] == 'Category'
856
    assert resp.json['data'][0]['category_slug'] == 'category'
851 857

  
852 858
    get_app(pub).get('/api/categories/XXX/formdefs/', status=404)
853 859

  
......
1100 1106
    formdef.data_class().wipe()
1101 1107

  
1102 1108
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1103
    assert len(resp.json) == 0
1109
    assert resp.json['err'] == 0
1110
    assert len(resp.json['data']) == 0
1104 1111

  
1105 1112
    formdata = formdef.data_class()()
1106 1113
    formdata.data = {'0': 'foo@localhost', '1': 'xxx'}
......
1112 1119
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1113 1120
    resp2 = get_app(pub).get(sign_uri('/myspace/forms', user=local_user))
1114 1121
    resp3 = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id))
1115
    assert len(resp.json) == 1
1116
    assert resp.json[0]['form_name'] == 'test'
1117
    assert resp.json[0]['form_slug'] == 'test'
1118
    assert resp.json[0]['form_status'] == 'New'
1119
    assert datetime.datetime.strptime(resp.json[0]['form_receipt_datetime'],
1120
                                      '%Y-%m-%dT%H:%M:%S')
1121
    assert resp.json[0]['keywords'] == ['hello', 'world']
1122
    assert resp.json['err'] == 0
1123
    assert len(resp.json['data']) == 1
1124
    assert resp.json['data'][0]['form_name'] == 'test'
1125
    assert resp.json['data'][0]['form_slug'] == 'test'
1126
    assert resp.json['data'][0]['form_status'] == 'New'
1127
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1122 1128
    assert resp.json == resp2.json == resp3.json
1123 1129

  
1124
    resp = get_app(pub).get(sign_uri('/api/user/forms?full=on', user=local_user))
1125
    assert resp.json[0]['fields']['foobar'] == 'foo@localhost'
1126
    assert resp.json[0]['keywords'] == ['hello', 'world']
1130
    resp = get_app(pub).get(sign_uri('/api/user/forms?&full=on', user=local_user))
1131
    assert resp.json['err'] == 0
1132
    assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost'
1133
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1127 1134

  
1128 1135
    formdef.disabled = True
1129 1136
    formdef.store()
1130 1137
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1131
    assert len(resp.json) == 1
1138
    assert resp.json['err'] == 0
1139
    assert len(resp.json['data']) == 1
1140

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

  
1132 1144

  
1133 1145
def test_user_drafts(pub, local_user):
1134 1146
    FormDef.wipe()
......
1144 1156
    formdef.store()
1145 1157

  
1146 1158
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1147
    assert len(resp.json) == 0
1159
    assert resp.json['err'] == 0
1160
    assert len(resp.json['data']) == 0
1148 1161

  
1149 1162
    formdata = formdef.data_class()()
1150 1163
    upload = PicklableUpload('test.txt', 'text/plain', 'ascii')
......
1158 1171

  
1159 1172
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1160 1173
    resp2 = get_app(pub).get(sign_uri('/myspace/drafts', user=local_user))
1161
    assert len(resp.json) == 1
1174
    assert resp.json['err'] == 0
1175
    assert len(resp.json['data']) == 1
1162 1176
    assert resp.json == resp2.json
1163
    assert not 'fields' in resp.json[0]
1164
    assert resp.json[0]['keywords'] == ['hello', 'world']
1177
    assert not 'fields' in resp.json['data'][0]
1178
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1165 1179

  
1166 1180
    resp = get_app(pub).get(sign_uri('/api/user/drafts?full=on', user=local_user))
1167
    assert 'fields' in resp.json[0]
1168
    assert resp.json[0]['fields']['foobar'] == 'foo@localhost'
1169
    assert 'file' not in resp.json[0]['fields'] # no file export in full lists
1170
    assert resp.json[0]['keywords'] == ['hello', 'world']
1181
    assert resp.json['err'] == 0
1182
    assert 'fields' in resp.json['data'][0]
1183
    assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost'
1184
    assert 'file' not in resp.json['data'][0]['fields']  # no file export in full lists
1185
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1171 1186

  
1172 1187
    formdef.enable_tracking_codes = False
1173 1188
    formdef.store()
1174 1189
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1175
    assert len(resp.json) == 0
1190
    assert resp.json['err'] == 0
1191
    assert len(resp.json['data']) == 0
1176 1192

  
1177 1193
    formdef.enable_tracking_codes = True
1178 1194
    formdef.disabled = True
1179 1195
    formdef.store()
1180 1196
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1181
    assert len(resp.json) == 0
1197
    assert resp.json['err'] == 0
1198
    assert len(resp.json['data']) == 0
1199

  
1200
    resp = get_app(pub).get(sign_uri('/api/user/drafts?&NameID=xxx'))
1201
    assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []}
1182 1202

  
1183 1203
def test_api_list_formdata(pub, local_user):
1184 1204
    Role.wipe()
wcs/api.py
421 421
            del formdict['category_position']
422 422

  
423 423
        get_response().set_content_type('application/json')
424
        return json.dumps(list_forms)
424
        return json.dumps({'err': 0, 'data': list_forms})
425 425

  
426 426
    def _q_lookup(self, component):
427 427
        try:
......
522 522

  
523 523
    def drafts(self):
524 524
        get_response().set_content_type('application/json')
525
        user = self.user or get_user_from_api_query_string() or get_request().user
525
        try:
526
            user = self.user or get_user_from_api_query_string() or get_request().user
527
        except UnknownNameIdAccessForbiddenError:
528
            return json.dumps({'err': 1, 'err_desc': 'unknown NameID', 'data': []})
526 529
        if not user:
527
            raise AccessForbiddenError('no user specified')
530
            return json.dumps({'err': 1, 'err_desc': 'no user specified', 'data': []})
528 531
        drafts = []
529 532
        for form in self.get_user_forms(user):
530 533
            if not form.is_draft():
......
547 550
                d.update(form.get_json_export_dict(include_files=False))
548 551
            drafts.append(d)
549 552

  
550
        return json.dumps(drafts,
553
        return json.dumps({'err': 0, 'data': drafts},
551 554
                cls=misc.JSONEncoder,
552 555
                encoding=get_publisher().site_charset)
553 556

  
554 557
    def forms(self):
555 558
        get_response().set_content_type('application/json')
556
        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': []})
557 563
        if not user:
558
            raise AccessForbiddenError('no user specified')
564
            return json.dumps({'err': 1, 'err_desc': 'no user specified', 'data': []})
559 565
        forms = []
560 566
        for form in self.get_user_forms(user):
561 567
            if form.is_draft():
......
566 572
                continue
567 573
            forms.append(formdata_dict)
568 574

  
569
        return json.dumps(forms,
575
        return json.dumps({'err': 0, 'data': forms},
570 576
                cls=misc.JSONEncoder,
571 577
                encoding=get_publisher().site_charset)
572 578

  
......
684 690
        for role in Role.select():
685 691
            list_roles.append(role.get_json_export_dict())
686 692
        get_response().set_content_type('application/json')
687
        return json.dumps({'data': list_roles})
693
        return json.dumps({'err': 0, 'data': list_roles})
688 694

  
689 695
    def validate_expression(self):
690 696
        get_response().set_content_type('application/json')
691
-