From a3d4e0d1e00376a5e6d29fd114778c5e16ef9c2c Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Sat, 4 Nov 2017 03:01:13 +0100 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 | 169 ++++++++++++++++++++++++++++++-------------------- wcs/api.py | 13 ++-- 3 files changed, 186 insertions(+), 142 deletions(-) diff --git a/help/fr/api-user.page b/help/fr/api-user.page index c4c0f156..cb886c6f 100644 --- a/help/fr/api-user.page +++ b/help/fr/api-user.page @@ -63,68 +63,71 @@ l'adresse /user.

$ curl https://www.example.net/api/user/forms -[ - { - "category_id": "1", - "category_name": "Divers", - "datetime": "2014-03-28 15:36:52", - "form_name": "Demande d'inscription", - "form_slug": "demande-d-inscription", - "form_number": "123", - "form_number_raw": "123", - "form_receipt_date": "28/03/2014", - "form_receipt_time": "15:36", - "form_status": "Nouveau", - "form_status_is_endpoint": false, - "form_uri": "demande-d-inscription/123/", - "form_url": "http://www.example.net/demande-d-inscription/123/", - "form_url_backoffice": "http://www.example.net/backoffice/demande-d-inscription/123/", - "name": "Demande d'inscription", - "status": "Nouveau", - "title": "Demande d'inscription #123 (Nouveau)", - "url": "http://www.example.net/demande-d-inscription/123/", - }, - { - "category_id": "2", - "category_name": "Prise de rendez-vous", - "datetime": "2014-03-17 10:39:52", - "form_name": "Rendez-vous avec le service B", - "form_slug": "rendez-vous-service-b", - "form_number": "456", - "form_number_raw": "456", - "form_receipt_date": "17/03/2014", - "form_receipt_time": "10:39", - "form_status": "En cours", - "form_status_is_endpoint": false, - "form_uri": "rendez-vous-service-b/456/", - "form_url": "http://www.example.net/rendez-vous-service-b/456/", - "form_url_backoffice": "http://www.example.net/backoffice/rendez-vous-service-b/456/", - "name": "Rendez-vous avec le service B"", - "status": "Nouveau", - "title": "Rendez-vous avec le service B #456 (En cours)", - "url": "http://www.example.net/rendez-vous-service-b/456/", - }, - { - "category_id": "3", - "category_name": "Modification de vos coordonn\u00e9es", - "datetime": "2014-03-17 10:42:17", - "form_name": "Changement d'adresse", - "form_slug": "changement-d-adresse", - "form_number": "424", - "form_number_raw": "424", - "form_receipt_date": "17/03/2014", - "form_receipt_time": "10:42", - "form_status": "Traitement de la demande termin\u00e9", - "form_status_is_endpoint": true, - "form_uri": "changement-d-adresse/424/", - "form_url": "http://www.example.net/changement-d-adresse/424/", - "form_url_backoffice": "http://www.example.net/backoffice/changement-d-adresse/424/", - "name": "Changement d'adresse", - "status": "Traitement de la demande termin\u00e9", - "title": "Changement d'adresse #424 (Traitement de la demande termin\u00e9)", - "url": "http://www.example.net/changement-d-adresse/424/", - } -] +{ + "err": 0, + "data": [ + { + "category_id": "1", + "category_name": "Divers", + "datetime": "2014-03-28 15:36:52", + "form_name": "Demande d'inscription", + "form_slug": "demande-d-inscription", + "form_number": "123", + "form_number_raw": "123", + "form_receipt_date": "28/03/2014", + "form_receipt_time": "15:36", + "form_status": "Nouveau", + "form_status_is_endpoint": false, + "form_uri": "demande-d-inscription/123/", + "form_url": "http://www.example.net/demande-d-inscription/123/", + "form_url_backoffice": "http://www.example.net/backoffice/demande-d-inscription/123/", + "name": "Demande d'inscription", + "status": "Nouveau", + "title": "Demande d'inscription #123 (Nouveau)", + "url": "http://www.example.net/demande-d-inscription/123/", + }, + { + "category_id": "2", + "category_name": "Prise de rendez-vous", + "datetime": "2014-03-17 10:39:52", + "form_name": "Rendez-vous avec le service B", + "form_slug": "rendez-vous-service-b", + "form_number": "456", + "form_number_raw": "456", + "form_receipt_date": "17/03/2014", + "form_receipt_time": "10:39", + "form_status": "En cours", + "form_status_is_endpoint": false, + "form_uri": "rendez-vous-service-b/456/", + "form_url": "http://www.example.net/rendez-vous-service-b/456/", + "form_url_backoffice": "http://www.example.net/backoffice/rendez-vous-service-b/456/", + "name": "Rendez-vous avec le service B"", + "status": "Nouveau", + "title": "Rendez-vous avec le service B #456 (En cours)", + "url": "http://www.example.net/rendez-vous-service-b/456/", + }, + { + "category_id": "3", + "category_name": "Modification de vos coordonn\u00e9es", + "datetime": "2014-03-17 10:42:17", + "form_name": "Changement d'adresse", + "form_slug": "changement-d-adresse", + "form_number": "424", + "form_number_raw": "424", + "form_receipt_date": "17/03/2014", + "form_receipt_time": "10:42", + "form_status": "Traitement de la demande termin\u00e9", + "form_status_is_endpoint": true, + "form_uri": "changement-d-adresse/424/", + "form_url": "http://www.example.net/changement-d-adresse/424/", + "form_url_backoffice": "http://www.example.net/backoffice/changement-d-adresse/424/", + "name": "Changement d'adresse", + "status": "Traitement de la demande termin\u00e9", + "title": "Changement d'adresse #424 (Traitement de la demande termin\u00e9)", + "url": "http://www.example.net/changement-d-adresse/424/", + } + ] +}

Il est possible de recevoir un ensemble plus complet de données en passant un @@ -143,14 +146,17 @@ brouillons, un paramètre include-drafts=true peut être passé. $ curl https://www.example.net/api/user/drafts -[ - { - "datetime": "2014-07-21 10:15:21", - "name": "Demande de relecture", - "title": "Demande de relecture, brouillon enregistré le 21/07/2014 10:15", - "url": "http://www.example.net/demande-de-relecture/164" - } -] +{ + "err": 0, + "data": [ + { + "datetime": "2014-07-21 10:15:21", + "name": "Demande de relecture", + "title": "Demande de relecture, brouillon enregistré le 21/07/2014 10:15", + "url": "http://www.example.net/demande-de-relecture/164" + } + ] +}

Note de compatibilité : cette information est également disponible à diff --git a/tests/test_api.py b/tests/test_api.py index 679297ab..47651c6c 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -174,7 +174,7 @@ def test_get_user_from_api_query_string_error_missing_email_valid_endpoint(pub): output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature)) assert output.json == {'data': []} output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) - assert output.json == [] + assert output.json == {'err': 0, 'data': []} def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub): # check the categories and forms endpoints accept an unknown NameID @@ -188,7 +188,7 @@ def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub) output = get_app(pub).get('/categories?%s&signature=%s' % (query, signature)) assert output.json == {'data': []} output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) - assert output.json == [] + assert output.json == {'err': 0, 'data': []} def test_get_user_from_api_query_string_error_success_sha1(pub, local_user): timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' @@ -316,20 +316,21 @@ def test_formdef_list(pub): resp2 = get_app(pub).get('/', headers={'Accept': 'application/json'}) resp3 = get_app(pub).get('/api/formdefs/') assert resp1.json == resp2.json == resp3.json - assert resp1.json[0]['title'] == 'test' - assert resp1.json[0]['url'] == 'http://example.net/test/' - assert resp1.json[0]['count'] == 0 - assert resp1.json[0]['redirection'] == False - assert resp1.json[0]['description'] == 'plop' - assert resp1.json[0]['keywords'] == ['mobile', 'test'] - assert resp1.json[0]['functions'].keys() == ['_receiver'] - assert resp1.json[0]['functions']['_receiver']['label'] == 'Recipient' - assert resp1.json[0]['functions']['_receiver']['role']['slug'] == role.slug - assert resp1.json[0]['functions']['_receiver']['role']['name'] == role.name + assert resp1.json['data'][0]['title'] == 'test' + assert resp1.json['data'][0]['url'] == 'http://example.net/test/' + assert resp1.json['data'][0]['count'] == 0 + assert resp1.json['data'][0]['redirection'] == False + assert resp1.json['data'][0]['description'] == 'plop' + assert resp1.json['data'][0]['keywords'] == ['mobile', 'test'] + assert resp1.json['data'][0]['functions'].keys() == ['_receiver'] + assert resp1.json['data'][0]['functions']['_receiver']['label'] == 'Recipient' + assert resp1.json['data'][0]['functions']['_receiver']['role']['slug'] == role.slug + assert resp1.json['data'][0]['functions']['_receiver']['role']['name'] == role.name # backoffice_submission formdef : none resp1 = get_app(pub).get('/api/formdefs/?backoffice-submission=on') - assert len(resp1.json) == 0 + assert resp1.json['err'] == 0 + assert len(resp1.json['data']) == 0 def test_limited_formdef_list(pub, local_user): Role.wipe() @@ -346,10 +347,12 @@ def test_limited_formdef_list(pub, local_user): formdef.store() resp = get_app(pub).get('/api/formdefs/') - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 # not present in backoffice-submission formdefs resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 # check it's not advertised formdef.roles = [role.id] @@ -358,17 +361,20 @@ def test_limited_formdef_list(pub, local_user): resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=')) resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX')) resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 assert resp.json == resp2.json == resp3.json == resp4.json # still not present in backoffice-submission formdefs resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 # unless user has correct roles local_user.roles = [role.id] local_user.store() resp = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 local_user.roles = [] local_user.store() @@ -380,14 +386,15 @@ def test_limited_formdef_list(pub, local_user): resp2 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=')) resp3 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=XXX')) resp4 = get_app(pub).get(sign_uri('/api/formdefs/?NameID=%s' % local_user.name_identifiers[0])) - assert len(resp.json) == 1 - assert resp.json[0]['authentication_required'] + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 + assert resp.json['data'][0]['authentication_required'] assert resp.json == resp2.json == resp3.json == resp4.json formdef.required_authentication_contexts = ['fedict'] formdef.store() resp = get_app(pub).get('/api/formdefs/') - assert resp.json[0]['required_authentication_contexts'] == ['fedict'] + assert resp.json['data'][0]['required_authentication_contexts'] == ['fedict'] def test_formdef_list_redirection(pub): FormDef.wipe() @@ -399,10 +406,11 @@ def test_formdef_list_redirection(pub): formdef.store() resp1 = get_app(pub).get('/json') - assert resp1.json[0]['title'] == 'test' - assert resp1.json[0]['url'] == 'http://example.net/test/' - assert resp1.json[0]['count'] == 0 - assert resp1.json[0]['redirection'] == True + assert resp1.json['err'] == 0 + assert resp1.json['data'][0]['title'] == 'test' + assert resp1.json['data'][0]['url'] == 'http://example.net/test/' + assert resp1.json['data'][0]['count'] == 0 + assert resp1.json['data'][0]['redirection'] == True def test_backoffice_submission_formdef_list(pub, local_user): Role.wipe() @@ -419,33 +427,38 @@ def test_backoffice_submission_formdef_list(pub, local_user): formdef.store() resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 # check it's not advertised ... formdef.backoffice_submission_roles = [role.id] formdef.store() resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 # even if it's advertised on frontoffice formdef.always_advertise = True formdef.store() resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 # ... unless user has correct roles local_user.roles = [role.id] local_user.store() resp = get_app(pub).get(sign_uri('/api/formdefs/?backoffice-submission=on&NameID=%s' % local_user.name_identifiers[0])) - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 # but not advertised if it's a redirection formdef.disabled = True formdef.disabled_redirection = 'http://example.net' formdef.store() resp = get_app(pub).get('/api/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 def test_formdef_schema(pub): Workflow.wipe() @@ -914,18 +927,20 @@ def test_categories_formdefs(pub, local_user): resp = get_app(pub).get('/api/categories/category/formdefs/') resp2 = get_app(pub).get('/category/json') assert resp.json == resp2.json - assert len(resp.json) == 2 - assert resp.json[0]['title'] == 'test' - assert resp.json[0]['url'] == 'http://example.net/test/' - assert resp.json[0]['count'] == 0 - assert resp.json[0]['redirection'] == False - assert resp.json[0]['category'] == 'Category' - assert resp.json[0]['category_slug'] == 'category' + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 2 + assert resp.json['data'][0]['title'] == 'test' + assert resp.json['data'][0]['url'] == 'http://example.net/test/' + assert resp.json['data'][0]['count'] == 0 + assert resp.json['data'][0]['redirection'] == False + assert resp.json['data'][0]['category'] == 'Category' + assert resp.json['data'][0]['category_slug'] == 'category' get_app(pub).get('/api/categories/XXX/formdefs/', status=404) resp = get_app(pub).get('/api/categories/category/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 Role.wipe() role = Role(name='test') @@ -936,18 +951,21 @@ def test_categories_formdefs(pub, local_user): formdef.backoffice_submission_roles = [role.id] formdef.store() resp = get_app(pub).get('/api/categories/category/formdefs/?backoffice-submission=on') - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 resp = get_app(pub).get(sign_uri( '/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' % local_user.name_identifiers[0])) - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 # ... unless user has correct roles local_user.roles = [role.id] local_user.store() resp = get_app(pub).get(sign_uri( '/api/categories/category/formdefs/?backoffice-submission=on&NameID=%s' % local_user.name_identifiers[0])) - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 def test_categories_full(pub): test_categories(pub) @@ -1200,7 +1218,8 @@ def test_user_forms(pub, local_user): formdef.data_class().wipe() resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 formdata = formdef.data_class()() formdata.data = {'0': 'foo@localhost', '1': 'xxx'} @@ -1212,23 +1231,28 @@ def test_user_forms(pub, local_user): resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) resp2 = get_app(pub).get(sign_uri('/myspace/forms', user=local_user)) resp3 = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id)) - assert len(resp.json) == 1 - assert resp.json[0]['form_name'] == 'test' - assert resp.json[0]['form_slug'] == 'test' - assert resp.json[0]['form_status'] == 'New' - assert datetime.datetime.strptime(resp.json[0]['form_receipt_datetime'], - '%Y-%m-%dT%H:%M:%S') - assert resp.json[0]['keywords'] == ['hello', 'world'] + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 + assert resp.json['data'][0]['form_name'] == 'test' + assert resp.json['data'][0]['form_slug'] == 'test' + assert resp.json['data'][0]['form_status'] == 'New' + assert resp.json['data'][0]['keywords'] == ['hello', 'world'] assert resp.json == resp2.json == resp3.json - resp = get_app(pub).get(sign_uri('/api/user/forms?full=on', user=local_user)) - assert resp.json[0]['fields']['foobar'] == 'foo@localhost' - assert resp.json[0]['keywords'] == ['hello', 'world'] + resp = get_app(pub).get(sign_uri('/api/user/forms?&full=on', user=local_user)) + assert resp.json['err'] == 0 + assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost' + assert resp.json['data'][0]['keywords'] == ['hello', 'world'] formdef.disabled = True formdef.store() resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 + + resp = get_app(pub).get(sign_uri('/api/user/forms?&NameID=xxx')) + assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []} + formdata = formdef.data_class()() formdata.user_id = local_user.id @@ -1237,18 +1261,21 @@ def test_user_forms(pub, local_user): formdata.store() resp = get_app(pub).get(sign_uri('/api/user/forms', user=local_user)) - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user)) - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 formdef.disabled = False formdef.store() resp = get_app(pub).get(sign_uri('/api/user/forms?include-drafts=true', user=local_user)) - assert len(resp.json) == 2 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 2 - draft_formdata = [x for x in resp.json if x['status'] == 'Draft'][0] + draft_formdata = [x for x in resp.json['data'] if x['status'] == 'Draft'][0] assert draft_formdata.get('url')[-1] != '/' def test_user_drafts(pub, local_user): @@ -1267,7 +1294,8 @@ def test_user_drafts(pub, local_user): formdef.data_class().wipe() resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 formdata = formdef.data_class()() upload = PicklableUpload('test.txt', 'text/plain', 'ascii') @@ -1281,27 +1309,34 @@ def test_user_drafts(pub, local_user): resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) resp2 = get_app(pub).get(sign_uri('/myspace/drafts', user=local_user)) - assert len(resp.json) == 1 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 assert resp.json == resp2.json - assert not 'fields' in resp.json[0] - assert resp.json[0]['keywords'] == ['hello', 'world'] + assert not 'fields' in resp.json['data'][0] + assert resp.json['data'][0]['keywords'] == ['hello', 'world'] resp = get_app(pub).get(sign_uri('/api/user/drafts?full=on', user=local_user)) - assert 'fields' in resp.json[0] - assert resp.json[0]['fields']['foobar'] == 'foo@localhost' - assert 'file' not in resp.json[0]['fields'] # no file export in full lists - assert resp.json[0]['keywords'] == ['hello', 'world'] + assert resp.json['err'] == 0 + assert 'fields' in resp.json['data'][0] + assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost' + assert 'file' not in resp.json['data'][0]['fields'] # no file export in full lists + assert resp.json['data'][0]['keywords'] == ['hello', 'world'] formdef.enable_tracking_codes = False formdef.store() resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 formdef.enable_tracking_codes = True formdef.disabled = True formdef.store() resp = get_app(pub).get(sign_uri('/api/user/drafts', user=local_user)) - assert len(resp.json) == 0 + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 0 + + resp = get_app(pub).get(sign_uri('/api/user/drafts?&NameID=xxx')) + assert resp.json == {'err': 1, 'err_desc': 'unknown NameID', 'data': []} def test_api_list_formdata(pub, local_user): Role.wipe() diff --git a/wcs/api.py b/wcs/api.py index 5356d945..e335ec39 100644 --- a/wcs/api.py +++ b/wcs/api.py @@ -447,7 +447,7 @@ class ApiFormdefsDirectory(Directory): del formdict['category_position'] get_response().set_content_type('application/json') - return json.dumps(list_forms) + return json.dumps({'err': 0, 'data': list_forms}) def _q_lookup(self, component): try: @@ -553,9 +553,12 @@ class ApiUserDirectory(Directory): def forms(self, include_drafts=False, include_non_drafts=True): get_response().set_content_type('application/json') - user = self.user or get_user_from_api_query_string() or get_request().user + try: + user = self.user or get_user_from_api_query_string() or get_request().user + except UnknownNameIdAccessForbiddenError: + return json.dumps({'err': 1, 'err_desc': 'unknown NameID', 'data': []}) if not user: - raise AccessForbiddenError('no user specified') + return json.dumps({'err': 1, 'err_desc': 'no user specified', 'data': []}) forms = [] include_drafts = include_drafts or get_request().form.get('include-drafts') == 'true' for form in self.get_user_forms(user): @@ -573,7 +576,7 @@ class ApiUserDirectory(Directory): continue forms.append(formdata_dict) - return json.dumps(forms, + return json.dumps({'err': 0, 'data': forms}, cls=misc.JSONEncoder, encoding=get_publisher().site_charset) @@ -691,7 +694,7 @@ class ApiDirectory(Directory): for role in Role.select(): list_roles.append(role.get_json_export_dict()) get_response().set_content_type('application/json') - return json.dumps({'data': list_roles}) + return json.dumps({'err': 0, 'data': list_roles}) def validate_expression(self): get_response().set_content_type('application/json') -- 2.15.1