Projet

Général

Profil

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

Benjamin Dauvergne, 28 mars 2017 15:32

Télécharger (21,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 | 148 ++++++++++++++++++++++++++------------------------
 tests/test_api.py     | 120 +++++++++++++++++++++++-----------------
 wcs/api.py            |  29 +++++-----
 3 files changed, 163 insertions(+), 134 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
169 169
    output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature))
170 170
    assert output.json == {'data': []}
171 171
    output = get_app(pub).get('/json?%s&signature=%s' % (query, signature))
172
    assert output.json == []
172
    assert output.json == {'err': 0, 'data': []}
173 173

  
174 174
def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub):
175 175
    # check the categories and forms endpoints accept an unknown NameID
......
183 183
    output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature))
184 184
    assert output.json == {'data': []}
185 185
    output = get_app(pub).get('/json?%s&signature=%s' % (query, signature))
186
    assert output.json == []
186
    assert output.json == {'err': 0, 'data': []}
187 187

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

  
325 325
def test_limited_formdef_list(pub, local_user):
326 326
    Role.wipe()
......
337 337
    formdef.store()
338 338

  
339 339
    resp = get_app(pub).get('/api/formdefs/')
340
    assert len(resp.json) == 1
340
    assert resp.json['err'] == 0
341
    assert len(resp.json['data']) == 1
341 342

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

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

  
358 361
    local_user.roles = []
359 362
    local_user.store()
......
365 368
    resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID='))
366 369
    resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX'))
367 370
    resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0]))
368
    assert len(resp.json) == 1
369
    assert resp.json[0]['authentication_required']
371
    assert resp.json['err'] == 0
372
    assert len(resp.json['data']) == 1
373
    assert resp.json['data'][0]['authentication_required']
370 374
    assert resp.json == resp2.json == resp3.json == resp4.json
371 375

  
372 376
    formdef.required_authentication_contexts = ['fedict']
......
384 388
    formdef.store()
385 389

  
386 390
    resp1 = get_app(pub).get('/json')
387
    assert resp1.json[0]['title'] == 'test'
388
    assert resp1.json[0]['url'] == 'http://example.net/test/'
389
    assert resp1.json[0]['count'] == 0
390
    assert resp1.json[0]['redirection'] == True
391
    assert resp1.json['err'] == 0
392
    assert resp1.json['data'][0]['title'] == 'test'
393
    assert resp1.json['data'][0]['url'] == 'http://example.net/test/'
394
    assert resp1.json['data'][0]['count'] == 0
395
    assert resp1.json['data'][0]['redirection'] == True
391 396

  
392 397

  
393 398
def test_formdef_schema(pub):
......
829 834
    resp = get_app(pub).get('/api/categories/category/formdefs/')
830 835
    resp2 = get_app(pub).get('/category/json')
831 836
    assert resp.json == resp2.json
832
    assert len(resp.json) == 2
833
    assert resp.json[0]['title'] == 'test'
834
    assert resp.json[0]['url'] == 'http://example.net/test/'
835
    assert resp.json[0]['count'] == 0
836
    assert resp.json[0]['redirection'] == False
837
    assert resp.json[0]['category'] == 'Category'
838
    assert resp.json[0]['category_slug'] == 'category'
837
    assert resp.json['err'] == 0
838
    assert len(resp.json['data']) == 2
839
    assert resp.json['data'][0]['title'] == 'test'
840
    assert resp.json['data'][0]['url'] == 'http://example.net/test/'
841
    assert resp.json['data'][0]['count'] == 0
842
    assert resp.json['data'][0]['redirection'] == False
843
    assert resp.json['data'][0]['category'] == 'Category'
844
    assert resp.json['data'][0]['category_slug'] == 'category'
839 845

  
840 846
    get_app(pub).get('/api/categories/XXX/formdefs/', status=404)
841 847

  
......
1088 1094
    formdef.data_class().wipe()
1089 1095

  
1090 1096
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1091
    assert len(resp.json) == 0
1097
    assert resp.json['err'] == 0
1098
    assert len(resp.json['data']) == 0
1092 1099

  
1093 1100
    formdata = formdef.data_class()()
1094 1101
    formdata.data = {'0': 'foo@localhost', '1': 'xxx'}
......
1100 1107
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1101 1108
    resp2 = get_app(pub).get(sign_uri('/myspace/forms', user=local_user))
1102 1109
    resp3 = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id))
1103
    assert len(resp.json) == 1
1104
    assert resp.json[0]['form_name'] == 'test'
1105
    assert resp.json[0]['form_slug'] == 'test'
1106
    assert resp.json[0]['form_status'] == 'New'
1107
    assert datetime.datetime.strptime(resp.json[0]['form_receipt_datetime'],
1108
                                      '%Y-%m-%dT%H:%M:%S')
1109
    assert resp.json[0]['keywords'] == ['hello', 'world']
1110
    assert resp.json['err'] == 0
1111
    assert len(resp.json['data']) == 1
1112
    assert resp.json['data'][0]['form_name'] == 'test'
1113
    assert resp.json['data'][0]['form_slug'] == 'test'
1114
    assert resp.json['data'][0]['form_status'] == 'New'
1115
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1110 1116
    assert resp.json == resp2.json == resp3.json
1111 1117

  
1112
    resp = get_app(pub).get(sign_uri('/api/user/forms?full=on', user=local_user))
1113
    assert resp.json[0]['fields']['foobar'] == 'foo@localhost'
1114
    assert resp.json[0]['keywords'] == ['hello', 'world']
1118
    resp = get_app(pub).get(sign_uri('/api/user/forms?&full=on', user=local_user))
1119
    assert resp.json['err'] == 0
1120
    assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost'
1121
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1115 1122

  
1116 1123
    formdef.disabled = True
1117 1124
    formdef.store()
1118 1125
    resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user))
1119
    assert len(resp.json) == 1
1126
    assert resp.json['err'] == 0
1127
    assert len(resp.json['data']) == 1
1128

  
1129
    resp = get_app(pub).get(sign_uri('/api/user/forms?&NameID=xxx'))
1130
    assert resp.json == {'err': 1, 'err_desc': 'no user specified', 'data': []}
1131

  
1120 1132

  
1121 1133
def test_user_drafts(pub, local_user):
1122 1134
    FormDef.wipe()
......
1132 1144
    formdef.store()
1133 1145

  
1134 1146
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1135
    assert len(resp.json) == 0
1147
    assert resp.json['err'] == 0
1148
    assert len(resp.json['data']) == 0
1136 1149

  
1137 1150
    formdata = formdef.data_class()()
1138 1151
    upload = PicklableUpload('test.txt', 'text/plain', 'ascii')
......
1146 1159

  
1147 1160
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1148 1161
    resp2 = get_app(pub).get(sign_uri('/myspace/drafts', user=local_user))
1149
    assert len(resp.json) == 1
1162
    assert resp.json['err'] == 0
1163
    assert len(resp.json['data']) == 1
1150 1164
    assert resp.json == resp2.json
1151
    assert not 'fields' in resp.json[0]
1152
    assert resp.json[0]['keywords'] == ['hello', 'world']
1165
    assert not 'fields' in resp.json['data'][0]
1166
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1153 1167

  
1154 1168
    resp = get_app(pub).get(sign_uri('/api/user/drafts?full=on', user=local_user))
1155
    assert 'fields' in resp.json[0]
1156
    assert resp.json[0]['fields']['foobar'] == 'foo@localhost'
1157
    assert 'file' not in resp.json[0]['fields'] # no file export in full lists
1158
    assert resp.json[0]['keywords'] == ['hello', 'world']
1169
    assert resp.json['err'] == 0
1170
    assert 'fields' in resp.json['data'][0]
1171
    assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost'
1172
    assert 'file' not in resp.json['data'][0]['fields']  # no file export in full lists
1173
    assert resp.json['data'][0]['keywords'] == ['hello', 'world']
1159 1174

  
1160 1175
    formdef.enable_tracking_codes = False
1161 1176
    formdef.store()
1162 1177
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1163
    assert len(resp.json) == 0
1178
    assert resp.json['err'] == 0
1179
    assert len(resp.json['data']) == 0
1164 1180

  
1165 1181
    formdef.enable_tracking_codes = True
1166 1182
    formdef.disabled = True
1167 1183
    formdef.store()
1168 1184
    resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user))
1169
    assert len(resp.json) == 0
1185
    assert resp.json['err'] == 0
1186
    assert len(resp.json['data']) == 0
1187

  
1188
    resp = get_app(pub).get(sign_uri('/api/user/drafts?&NameID=xxx'))
1189
    assert resp.json == {'err': 1, 'err_desc': 'no user specified', 'data': []}
1170 1190

  
1171 1191
def test_api_list_formdata(pub, local_user):
1172 1192
    Role.wipe()
wcs/api.py
349 349
            del formdict['category_position']
350 350

  
351 351
        get_response().set_content_type('application/json')
352
        return json.dumps(list_forms)
352
        return json.dumps({'err': 0, 'data': list_forms})
353 353

  
354 354
    def _q_lookup(self, component):
355 355
        try:
......
445 445

  
446 446
    def drafts(self):
447 447
        get_response().set_content_type('application/json')
448
        user = self.user or get_user_from_api_query_string() or get_request().user
448
        try:
449
            user = self.user or get_user_from_api_query_string() or get_request().user
450
        except UnknownNameIdAccessForbiddenError:
451
            user = None
449 452
        if not user:
450
            raise AccessForbiddenError('no user specified')
453
            return json.dumps({'err': 1, 'err_desc': 'no user specified', 'data': []})
451 454
        drafts = []
452 455
        for form in self.get_user_forms(user):
453 456
            if not form.is_draft():
......
470 473
                d.update(form.get_json_export_dict(include_files=False))
471 474
            drafts.append(d)
472 475

  
473
        return json.dumps(drafts,
474
                cls=misc.JSONEncoder,
475
                encoding=get_publisher().site_charset)
476
        return json.dumps({'err': 0, 'data': drafts}, cls=misc.JSONEncoder,
477
                          encoding=get_publisher().site_charset)
476 478

  
477 479
    def forms(self):
478 480
        get_response().set_content_type('application/json')
479
        user = self.user or get_user_from_api_query_string() or get_request().user
481
        try:
482
            user = self.user or get_user_from_api_query_string() or get_request().user
483
        except UnknownNameIdAccessForbiddenError:
484
            user = None
480 485
        if not user:
481
            raise AccessForbiddenError('no user specified')
486
            return json.dumps({'err': 1, 'err_desc': 'no user specified', 'data': []})
482 487
        forms = []
483 488
        for form in self.get_user_forms(user):
484 489
            if form.is_draft():
......
508 513
            if get_request().form.get('full') == 'on':
509 514
                d.update(form.get_json_export_dict(include_files=False))
510 515
            forms.append(d)
511

  
512
        return json.dumps(forms,
513
                cls=misc.JSONEncoder,
514
                encoding=get_publisher().site_charset)
516
        return json.dumps({'err': 0, 'data': forms}, cls=misc.JSONEncoder,
517
                          encoding=get_publisher().site_charset)
515 518

  
516 519

  
517 520
class ApiUsersDirectory(Directory):
......
621 624
        for role in Role.select():
622 625
            list_roles.append(role.get_json_export_dict())
623 626
        get_response().set_content_type('application/json')
624
        return json.dumps({'data': list_roles})
627
        return json.dumps({'err': 0, 'data': list_roles})
625 628

  
626 629
    def validate_expression(self):
627 630
        get_response().set_content_type('application/json')
628
-