From e54ad5ceec99abf9c6b30d684236de5ab8e703fb Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Sat, 10 Sep 2022 13:29:07 +0200
Subject: [PATCH] templatetags: render JSON schema oneOf required properties
 (#69049)

---
 passerelle/base/templatetags/passerelle.py |  8 +++++++-
 tests/test_templatetags.py                 | 15 +++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/passerelle/base/templatetags/passerelle.py b/passerelle/base/templatetags/passerelle.py
index 6552fc65..1d59dd3a 100644
--- a/passerelle/base/templatetags/passerelle.py
+++ b/passerelle/base/templatetags/passerelle.py
@@ -133,17 +133,17 @@ def render_json_schema(schema):
         return mark_safe(s)
 
     def html_type(s):
         return '<span class="type">%s</span>' % s
 
     if 'anyOf' in schema:
         return many_of('anyOf', schema['anyOf'])
 
-    if 'oneOf' in schema:
+    if 'oneOf' in schema and not schema.get('type') == 'object':
         return many_of('oneOf', schema['oneOf'])
 
     if 'allOf' in schema:
         return many_of('allOf', schema['allOf'])
 
     original_schema = schema
     schema = schema.copy()
     schema.pop('$schema', None)
@@ -188,16 +188,17 @@ def render_json_schema(schema):
     if typ == 'object':
         s = html_type('object')
         unflatten = schema.pop('unflatten', False)
         merge_extra = schema.pop('merge_extra', False)
         properties = schema.pop('properties', {})
         pattern_properties = schema.pop('patternProperties', {})
         required_keys = schema.pop('required', [])
         additional_properties = schema.pop('additionalProperties', True)
+        one_of = schema.pop('oneOf', [])
         if unflatten:
             s += format_html(', <em class="unflatten">{}</em>', _('unflatten'))
         if merge_extra:
             s += format_html(', <em class="merge-extra">{}</em>', _('merge extra'))
         if not additional_properties:
             s += format_html(
                 ', <em class="additional-properties-false">{}</em>', _('no additional properties')
             )
@@ -235,16 +236,18 @@ def render_json_schema(schema):
 
         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)
+            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:
@@ -257,16 +260,19 @@ def render_json_schema(schema):
 
         return mark_safe(s)
     if typ == 'boolean':
         if not schema:
             return mark_safe(html_type('boolean'))
     enum = schema.pop('enum', [])
     if enum and not schema:
         return mark_safe(' | '.join([format_html('<tt>{}</tt>', json.dumps(el)) for el in enum]))
+    required_keys = schema.pop('required', [])
+    if required_keys and not schema:
+        return format_html('<em>{0} {1!r}</em>', _('required'), ', '.join(required_keys))
     return format_html('<em>{0} {1!r}</em>', _('unknown validation'), original_schema)
 
 
 @register.simple_tag(takes_context=False)
 def render_body_schemas(body_schemas):
     if not body_schemas:
         return ''
 
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py
index a2a0a8c8..6c6fda1e 100644
--- a/tests/test_templatetags.py
+++ b/tests/test_templatetags.py
@@ -71,8 +71,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_oneof_property_required():
+    schema = {
+        'type': 'object',
+        'properties': {
+            'a': {'type': 'string'},
+            'b': {'type': 'string'},
+        },
+        'oneOf': [
+            {'required': ['a']},
+            {'required': ['b']},
+        ],
+    }
+    assert "<b>oneOf</b> [ <em>required 'a'</em> | <em>required 'b'</em>" in render_json_schema(schema)
-- 
2.30.2

