From 6b977e82c21119ea179f3665032bd5bcf2ba8cfa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 8 Nov 2022 13:49:27 +0100
Subject: [PATCH] api: split full param (#69108)

new params:
- include-fields
- include-evolution
- include-roles
- include-submission
- include-workflow
---
 tests/api/test_formdata.py   | 135 ++++++++++++++++++++++++++++-------
 wcs/backoffice/management.py |  25 ++++++-
 wcs/formdata.py              | 116 ++++++++++++++++--------------
 3 files changed, 195 insertions(+), 81 deletions(-)

diff --git a/tests/api/test_formdata.py b/tests/api/test_formdata.py
index bfe808a24..79d9fd7c4 100644
--- a/tests/api/test_formdata.py
+++ b/tests/api/test_formdata.py
@@ -277,15 +277,23 @@ def test_formdata_user_fields(pub, local_user):
     formdata.just_created()
     formdata.store()
 
-    resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user))
-    assert 'user' not in resp.json[0]
-    resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on', user=local_user))
-    assert resp.json[0]['user'] == {
-        'id': local_user.id,
-        'NameID': ['0123456789'],
-        'name': 'Jean Darmette',
-        'email': 'jean.darmette@triffouilis.fr',
-    }
+    for params in [
+        '',
+        '?include-evolution=on',
+        '?include-roles=on',
+        '?include-submission=on',
+        '?include-workflow=on',
+    ]:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'user' not in resp.json[0]
+    for params in ['?full=on', '?include-fields=on']:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert resp.json[0]['user'] == {
+            'id': local_user.id,
+            'NameID': ['0123456789'],
+            'name': 'Jean Darmette',
+            'email': 'jean.darmette@triffouilis.fr',
+        }
 
 
 def test_formdata_submission_fields(pub, local_user):
@@ -322,14 +330,22 @@ def test_formdata_submission_fields(pub, local_user):
     formdata.just_created()
     formdata.store()
 
-    resp = get_app(pub).get(sign_uri('/api/forms/test/list', user=local_user))
-    assert 'submission' not in resp.json[0]
-    resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on', user=local_user))
-    assert resp.json[0]['submission'] == {
-        'backoffice': False,
-        'channel': 'web',
-        'agent': {'id': agent.id, 'name': 'agent'},
-    }
+    for params in [
+        '',
+        '?include-fields=on',
+        '?include-evolution=on',
+        '?include-roles=on',
+        '?include-workflow=on',
+    ]:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'submission' not in resp.json[0]
+    for params in ['?full=on', '?include-submission=on']:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert resp.json[0]['submission'] == {
+            'backoffice': False,
+            'channel': 'web',
+            'agent': {'id': agent.id, 'name': 'agent'},
+        }
     resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user))
     assert resp.json['submission'] == {
         'backoffice': False,
@@ -547,6 +563,18 @@ def test_formdata_with_workflow_data(pub, local_user):
     assert resp.json['workflow']['data']['blah']['content_type'] == 'text/plain'
     assert base64.decodebytes(force_bytes(resp.json['workflow']['data']['blah']['content'])) == b'test'
     assert base64.decodebytes(force_bytes(resp.json['workflow']['data']['blah2']['content'])) == b'test'
+    for params in [
+        '',
+        '?include-fields=on',
+        '?include-evolution=on',
+        '?include-roles=on',
+        '?include-submission=on',
+    ]:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'workflow' not in resp.json[0]
+    for params in ['?full=on', '?include-workflow=on']:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'workflow' in resp.json[0]
 
 
 def test_formdata_with_evolution_part_attachment(pub, local_user):
@@ -595,9 +623,22 @@ def test_formdata_with_evolution_part_attachment(pub, local_user):
     assert len(resp.json['evolution']) == 1
     assert 'parts' not in resp.json['evolution'][0]
 
-    # check this doesn't get into list of forms API
-    resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on', user=local_user))
-    assert 'hello.txt' not in resp.text
+    for params in [
+        '',
+        '?include-fields=on',
+        '?include-roles=on',
+        '?include-submission=on',
+        '?include-workflow=on',
+    ]:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'evolution' not in resp.json[0]
+        # check this doesn't get into list of forms API
+        assert 'hello.txt' not in resp.text
+    for params in ['?full=on', '?include-evolution=on']:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'evolution' in resp.json[0]
+        # check this doesn't get into list of forms API
+        assert 'hello.txt' not in resp.text
 
 
 def test_formdata_with_evolution_part_attachment_to(pub, local_user):
@@ -656,10 +697,20 @@ def test_formdata_with_evolution_part_attachment_to(pub, local_user):
     assert resp.json['evolution'][0]['parts'][0]['type'] == 'workflow-comment'
 
     # check this doesn't get into list of forms API
-    resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on', user=local_user))
-    assert len(resp.json[0]['evolution']) == 1
-    assert len(resp.json[0]['evolution'][0]['parts']) == 1
-    assert resp.json[0]['evolution'][0]['parts'][0]['type'] == 'workflow-comment'
+    for params in [
+        '',
+        '?include-fields=on',
+        '?include-roles=on',
+        '?include-submission=on',
+        '?include-workflow=on',
+    ]:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'evolution' not in resp.json[0]
+    for params in ['?full=on', '?include-evolution=on']:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert len(resp.json[0]['evolution']) == 1
+        assert len(resp.json[0]['evolution'][0]['parts']) == 1
+        assert resp.json[0]['evolution'][0]['parts'][0]['type'] == 'workflow-comment'
 
 
 def test_formdata_with_evolution_part_comment(pub, local_user):
@@ -706,6 +757,42 @@ def test_formdata_with_evolution_part_comment(pub, local_user):
     assert 'parts' not in resp.json['evolution'][0]
 
 
+def test_formdata_with_roles(pub, local_user):
+    pub.role_class.wipe()
+    role = pub.role_class(name='test')
+    role.store()
+    local_user.roles = [role.id]
+    local_user.store()
+
+    FormDef.wipe()
+    formdef = FormDef()
+    formdef.name = 'test'
+    formdef.workflow_roles = {'_receiver': role.id}
+    formdef.fields = []
+    formdef.store()
+
+    data_class = formdef.data_class()
+    data_class.wipe()
+    formdata = data_class()
+    formdata.just_created()
+    formdata.status = 'wf-new'
+    formdata.user_id = local_user.id
+    formdata.store()
+
+    for params in [
+        '',
+        '?include-fields=on',
+        '?include-evolution=on',
+        '?include-submission=on',
+        '?include-workflow=on',
+    ]:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'roles' not in resp.json[0]
+    for params in ['?full=on', '?include-roles=on']:
+        resp = get_app(pub).get(sign_uri('/api/forms/test/list%s' % params, user=local_user))
+        assert 'roles' in resp.json[0]
+
+
 def test_api_list_formdata(pub, local_user):
     pub.role_class.wipe()
     role = pub.role_class(name='test')
diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index 85d4c2d27..5778e7efc 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -2461,16 +2461,25 @@ class FormPage(FormdefDirectoryBase):
             view_digest_key = 'custom-view:%s' % self.view.get_url_slug()
             if view_digest_key in (self.formdef.digest_templates or {}):
                 digest_key = view_digest_key
-        if get_request().form.get('full') == 'on':
+        full = get_request().form.get('full') == 'on'
+        include_fields = get_request().form.get('include-fields') == 'on' or full
+        include_evolution = get_request().form.get('include-evolution') == 'on' or full
+        include_roles = get_request().form.get('include-roles') == 'on' or full
+        include_submission = get_request().form.get('include-submission') == 'on' or full
+        include_workflow = get_request().form.get('include-workflow') == 'on' or full
+        if include_fields or include_evolution or include_roles or include_submission or include_workflow:
             output = JsonFileExportAfterJob(self.formdef).create_json_export(
                 items,
                 user=user,
                 anonymise=anonymise,
                 digest_key=digest_key,
-                include_evolution=True,
+                include_evolution=include_evolution,
                 include_files=False,
-                include_roles=True,
+                include_roles=include_roles,
+                include_submission=include_submission,
+                include_fields=include_fields,
                 include_unnamed_fields=False,
+                include_workflow=include_workflow,
             )
         else:
             output = [
@@ -4315,8 +4324,12 @@ class JsonFileExportAfterJob(CsvExportAfterJob):
         include_evolution,
         include_files,
         include_roles,
+        include_submission,
+        include_fields,
         include_unnamed_fields,
+        include_workflow,
     ):
+        # noqa pylint: disable=too-many-arguments
         formdef = self.kwargs['formdef_class'].get(self.kwargs['formdef_id'])
         prefetched_users = None
         prefetched_roles = None
@@ -4355,7 +4368,10 @@ class JsonFileExportAfterJob(CsvExportAfterJob):
                 include_evolution=include_evolution,
                 include_files=include_files,
                 include_roles=include_roles,
+                include_submission=include_submission,
+                include_fields=include_fields,
                 include_unnamed_fields=include_unnamed_fields,
+                include_workflow=include_workflow,
             )
             data.pop('digests')
             if digest_key:
@@ -4375,7 +4391,10 @@ class JsonFileExportAfterJob(CsvExportAfterJob):
                     include_evolution=False,
                     include_files=True,
                     include_roles=False,
+                    include_submission=True,
+                    include_fields=True,
                     include_unnamed_fields=True,
+                    include_workflow=True,
                 )
             },
             indent=2,
diff --git a/wcs/formdata.py b/wcs/formdata.py
index 9c7092d36..66a891f15 100644
--- a/wcs/formdata.py
+++ b/wcs/formdata.py
@@ -1357,58 +1357,65 @@ class FormData(StorableObject):
         prefetched_roles=None,
         include_evolution=True,
         include_roles=True,
+        include_submission=True,
+        include_fields=True,
         include_unnamed_fields=False,
+        include_workflow=True,
     ):
+        # noqa pylint: disable=too-many-arguments
         data = {}
         data['id'] = str(self.id)
-        data['digests'] = self.digests
         data['display_id'] = self.get_display_id()
         data['display_name'] = self.get_display_name()
+        data['digests'] = self.digests
         data['text'] = self.get_display_label(digest_key=digest_key)
+        data['url'] = self.get_url()
         data['receipt_time'] = datetime.datetime(*self.receipt_time[:6])
         data['last_update_time'] = datetime.datetime(*self.last_update_time[:6])
-        data['criticality_level'] = self.criticality_level
-        data['url'] = self.get_url()
-        data['api_url'] = self.get_api_url()
-        data['backoffice_url'] = self.get_backoffice_url()
 
-        try:
-            if prefetched_users is not None:
-                user = prefetched_users.get(str(self.user_id))
-            else:
-                user = get_publisher().user_class.get(self.user_id)
-        except KeyError:
-            user = None
-        if not anonymise and user:
-            from .carddef import CardDef
+        if include_fields:
+            data['criticality_level'] = self.criticality_level
+            data['api_url'] = self.get_api_url()
+            data['backoffice_url'] = self.get_backoffice_url()
 
-            data['user'] = user.get_json_export_dict(full=isinstance(self.formdef, CardDef))
+            try:
+                if prefetched_users is not None:
+                    user = prefetched_users.get(str(self.user_id))
+                else:
+                    user = get_publisher().user_class.get(self.user_id)
+            except KeyError:
+                user = None
+            if not anonymise and user:
+                from .carddef import CardDef
 
-        data['fields'] = self.get_json_dict(
-            self.formdef.fields,
-            include_files=include_files,
-            anonymise=anonymise,
-            include_unnamed_fields=include_unnamed_fields,
-        )
+                data['user'] = user.get_json_export_dict(full=isinstance(self.formdef, CardDef))
 
-        data['workflow'] = {}
-        wf_status = self.get_visible_status(user)
-        if wf_status:
-            data['workflow']['status'] = {'id': wf_status.id, 'name': wf_status.name}
-        wf_real_status = self.get_status()
-        if wf_real_status:
-            data['workflow']['real_status'] = {'id': wf_real_status.id, 'name': wf_real_status.name}
-        # Workflow data have unknown purpose, do not store them in anonymised export
-        if self.workflow_data and not anonymise:
-            data['workflow']['data'] = self.workflow_data
-        if self.formdef.workflow.get_backoffice_fields():
-            data['workflow']['fields'] = self.get_json_dict(
-                self.formdef.workflow.get_backoffice_fields(),
+            data['fields'] = self.get_json_dict(
+                self.formdef.fields,
                 include_files=include_files,
                 anonymise=anonymise,
                 include_unnamed_fields=include_unnamed_fields,
             )
 
+        if include_workflow:
+            data['workflow'] = {}
+            wf_status = self.get_visible_status(user)
+            if wf_status:
+                data['workflow']['status'] = {'id': wf_status.id, 'name': wf_status.name}
+            wf_real_status = self.get_status()
+            if wf_real_status:
+                data['workflow']['real_status'] = {'id': wf_real_status.id, 'name': wf_real_status.name}
+            # Workflow data have unknown purpose, do not store them in anonymised export
+            if self.workflow_data and not anonymise:
+                data['workflow']['data'] = self.workflow_data
+            if self.formdef.workflow.get_backoffice_fields():
+                data['workflow']['fields'] = self.get_json_dict(
+                    self.formdef.workflow.get_backoffice_fields(),
+                    include_files=include_files,
+                    anonymise=anonymise,
+                    include_unnamed_fields=include_unnamed_fields,
+                )
+
         if include_roles:
             # add a roles dictionary, with workflow functions and two special
             # entries for concerned/actions roles.
@@ -1438,26 +1445,27 @@ class FormData(StorableObject):
                 role_list = [x.get_json_export_dict() for x in role_list if x is not None]
                 data['roles'][role_key] = role_list
 
-        data['submission'] = {
-            'backoffice': self.backoffice_submission,
-            'channel': self.submission_channel or 'web',
-        }
-        try:
-            if prefetched_users is not None:
-                agent = prefetched_users.get(str(self.submission_agent_id))
-            else:
-                agent = get_publisher().user_class.get(self.submission_agent_id)
-        except KeyError:
-            agent = None
-        if agent:
-            data['submission']['agent'] = agent.get_json_export_dict()
-        parent = self.get_parent()
-        if parent:
-            data['submission']['parent'] = {
-                'url': parent.get_url(),
-                'backoffice_url': parent.get_backoffice_url(),
-                'api_url': parent.get_api_url(),
+        if include_submission:
+            data['submission'] = {
+                'backoffice': self.backoffice_submission,
+                'channel': self.submission_channel or 'web',
             }
+            try:
+                if prefetched_users is not None:
+                    agent = prefetched_users.get(str(self.submission_agent_id))
+                else:
+                    agent = get_publisher().user_class.get(self.submission_agent_id)
+            except KeyError:
+                agent = None
+            if agent:
+                data['submission']['agent'] = agent.get_json_export_dict()
+            parent = self.get_parent()
+            if parent:
+                data['submission']['parent'] = {
+                    'url': parent.get_url(),
+                    'backoffice_url': parent.get_backoffice_url(),
+                    'api_url': parent.get_api_url(),
+                }
 
         if self.evolution and include_evolution:
             evolution = data['evolution'] = []
@@ -1470,7 +1478,7 @@ class FormData(StorableObject):
                     )
                 )
 
-        if self.geolocations:
+        if include_fields and self.geolocations:
             data['geolocations'] = {}
             for k, v in self.geolocations.items():
                 data['geolocations'][k] = v.copy()
-- 
2.35.1

