From 4186138fa78f8c3851f02ee4c83a71863e091ebf Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Wed, 12 Oct 2022 14:17:12 +0200
Subject: [PATCH] templatetags: display required fields on json schema (#70174)

---
 passerelle/base/templatetags/passerelle.py | 8 ++++----
 tests/test_templatetags.py                 | 8 ++++++++
 tox.ini                                    | 1 +
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/passerelle/base/templatetags/passerelle.py b/passerelle/base/templatetags/passerelle.py
index 71467189..89836ba4 100644
--- a/passerelle/base/templatetags/passerelle.py
+++ b/passerelle/base/templatetags/passerelle.py
@@ -203,23 +203,23 @@ def render_json_schema(schema):
         if title:
             s += format_html(', <em class="title">{}</em>', title)
         if schema:
             s += format_html('<tt class="raw">{!r}</tt>', schema)
         if description:
             s += format_html('\n<p class="description">{}</p>', description)
         s += ' '
 
-        def render_property_schema(key, sub):
+        def render_property_schema(key, html, sub):
             nonlocal s
 
             required = key in required_keys
             sub_description = sub.pop('description', '')
             sub_title = sub.pop('title', '')
-            s += format_html('<li>{0}', key)
+            s += format_html('<li>{0}', html)
             if required:
                 s += format_html('<span title="{}" class="required">*</span>', _('required'))
             if description or sub:
                 s += ' :'
             if sub_title:
                 s += format_html(' <em>{0}</em>', sub_title)
             elif sub_description and '\n' not in sub_description:
                 s += format_html(' <em>{0}</em>', sub_description)
@@ -233,29 +233,29 @@ def render_json_schema(schema):
             s += '\n<ul>'
 
         if properties:
             keys = properties
             if not isinstance(properties, collections.OrderedDict):
                 keys = sorted(properties, key=lambda key: key.lower())
             for key in keys:
                 sub = properties.get(key, {}).copy()
-                render_property_schema(format_html('<tt>{0}</tt>', key), sub)
+                render_property_schema(key, format_html('<tt>{0}</tt>', key), sub)
             if one_of and one_of[0].get('required'):
                 s += many_of('oneOf', one_of)
 
         if pattern_properties:
             s += format_html('<li><span>{0}</span>', _('Pattern properties'))
             s += '\n<ul>'
             for key, sub in pattern_properties.items():
                 if key:
                     pattern_key = format_html('/<tt>{0}</tt>/', key)
                 else:
                     pattern_key = format_html('<em>any</em>')
-                render_property_schema(pattern_key, sub)
+                render_property_schema(key, pattern_key, sub)
             s += '</ul>'
 
         if properties or pattern_properties:
             s += '</ul>'
 
         return mark_safe(s)
     if typ == 'boolean':
         if not schema:
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py
index d90c7fd8..62181fba 100644
--- a/tests/test_templatetags.py
+++ b/tests/test_templatetags.py
@@ -13,16 +13,17 @@
 #
 # You should have received a.deepcopy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import inspect
 
 from django.apps import apps
 from django.utils import translation
+from pyquery import PyQuery as pq
 
 from passerelle.base.templatetags.passerelle import render_body_schemas, render_json_schema
 
 
 def test_render_body_schemas(db):
     # FIXME: db should be required but the way ProxyLogger is initialized force an access to the DB
     def collect_schemas():
         for app in apps.get_app_configs():
@@ -73,16 +74,23 @@ def test_render_enum_schema():
 def test_render_pattern_description():
     schema = {'type': 'object', 'properties': {'filename': {'type': 'string', 'pattern': 'abc'}}}
     assert 'abc' in render_json_schema(schema)
 
     schema['properties']['filename']['pattern_description'] = 'efg'
     assert 'abc' not in render_json_schema(schema) and 'efg' in render_json_schema(schema)
 
 
+def test_render_required_properties():
+    schema = {'type': 'object', 'required': ['filename'], 'properties': {'filename': {'type': 'string'}}}
+    html = render_json_schema(schema)
+    assert pq(html)('li span.required')
+    assert pq(html)('li').text() == 'filename* : string'
+
+
 def test_render_oneof_property_required():
     schema = {
         'type': 'object',
         'properties': {
             'a': {'type': 'string'},
             'b': {'type': 'string'},
         },
         'oneOf': [
diff --git a/tox.ini b/tox.ini
index dc77d43c..1236646b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -75,13 +75,14 @@ deps =
   pytest!=6.0.0
   WebTest
   mock<4
   httmock
   pylint
   pylint-django
   django-webtest<1.9.3
   pytest-freezegun
+  pyquery
   responses
   mohawk
   ldaptools
 commands =
   ./pylint.sh passerelle/ tests/
-- 
2.30.2

