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

All responses now have the form {"err": x, "data": y}.
This commit is contained in:
Benjamin Dauvergne 2017-11-04 03:01:13 +01:00 committed by Thomas NOEL
parent b6e5b36890
commit 5cc3847843
3 changed files with 195 additions and 141 deletions

View File

@ -63,68 +63,71 @@ l'adresse <code>/user</code>.
</p>
<screen>
<output style="prompt">$ </output><input>curl https://www.example.net/api/user/forms</input>
<output>[
{
"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/",
}
]</output></screen>
<output>{
"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/",
}
]
}</output></screen>
<p>
Il est possible de recevoir un ensemble plus complet de données en passant un
@ -143,14 +146,17 @@ brouillons, un paramètre <code>include-drafts=true</code> peut être passé.
<screen>
<output style="prompt">$ </output><input>curl https://www.example.net/api/user/drafts</input>
<output>[
{
"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"
}
]</output></screen>
<output>{
"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"
}
]
}</output></screen>
<note>
<p>Note de compatibilité : cette information est également disponible à

View File

@ -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,19 +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]['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]['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
assert 'count' not in resp1.json['data'][0]
# 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()
@ -345,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]
@ -357,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()
@ -379,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()
@ -398,9 +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]['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]['redirection'] == True
assert 'count' not in resp1.json['data'][0]
def test_backoffice_submission_formdef_list(pub, local_user):
Role.wipe()
@ -417,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()
@ -912,23 +927,25 @@ 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]['redirection'] == False
assert resp.json[0]['category'] == 'Category'
assert resp.json[0]['category_slug'] == 'category'
assert not 'count' in resp.json[0]
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]['redirection'] == False
assert resp.json['data'][0]['category'] == 'Category'
assert resp.json['data'][0]['category_slug'] == 'category'
assert 'count' not in resp.json['data'][0]
resp = get_app(pub).get('/api/categories/category/formdefs/?include-count=on')
assert resp.json[0]['title'] == 'test'
assert resp.json[0]['url'] == 'http://example.net/test/'
assert resp.json[0]['count'] == 0
assert resp.json['data'][0]['title'] == 'test'
assert resp.json['data'][0]['url'] == 'http://example.net/test/'
assert resp.json['data'][0]['count'] == 0
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')
@ -939,18 +956,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)
@ -1203,7 +1223,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'}
@ -1215,23 +1236,33 @@ 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'],
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 datetime.datetime.strptime(resp.json['data'][0]['form_receipt_datetime'],
'%Y-%m-%dT%H:%M:%S')
assert resp.json[0]['keywords'] == ['hello', 'world']
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']
assert resp.json['err'] == 0
assert resp.json['data'][0]['fields']['foobar'] == 'foo@localhost'
assert resp.json['data'][0]['keywords'] == ['hello', 'world']
resp2 = get_app(pub).get(sign_uri('/api/user/forms?&full=on', user=local_user))
assert resp.json == resp2.json
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': []}
resp2 = get_app(pub).get(sign_uri('/api/user/forms?&NameID=xxx'))
assert resp.json == resp2.json
formdata = formdef.data_class()()
formdata.user_id = local_user.id
@ -1240,18 +1271,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):
@ -1270,7 +1304,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')
@ -1284,27 +1319,37 @@ 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': []}
resp2 = get_app(pub).get(sign_uri('/api/user/drafts?&NameID=xxx'))
assert resp.json == resp2.json
def test_api_list_formdata(pub, local_user):
Role.wipe()

View File

@ -450,7 +450,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:
@ -556,9 +556,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):
@ -576,7 +579,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)
@ -694,7 +697,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')