[PATCH jsonschema] Add a CollectionMapper for unspecified entity types

Denis Laxalde denis.laxalde at logilab.fr
Wed Jun 20 09:41:38 CEST 2018


# HG changeset patch
# User Denis Laxalde <denis.laxalde at logilab.fr>
# Date 1529413121 -7200
#      Tue Jun 19 14:58:41 2018 +0200
# Node ID 62523c338920edf7f5b6ccd206cff69f20113ec4
# Parent  94157194f0e01bcd3acbd50003e473db39bdc0d1
# Available At https://hg.logilab.org/review/cubicweb-jsonschema
#              hg pull https://hg.logilab.org/review/cubicweb-jsonschema -r 62523c338920
# EXP-Topic heterogenous-collection-mapper
Add a CollectionMapper for unspecified entity types

This is useful for representing collection of heterogeneous entities.

This mapper would be selected by its regid only, with no extra argument
in contrast with other 'jsonschema.collection' mappers that either
accepts an "etype" parameter or a relation context. This mapper does not
handle submission (i.e. entity creation) since the target entity type is
unspecified; so we return a "false" JSON Schema for "creation" role.

In tests, we rename prior test case CollectionMapperTC as
EntityCollectionMapperTC and add back a CollectionMapperTC explicitly
testing the new mapper class.

diff --git a/cubicweb_jsonschema/mappers/collection.py b/cubicweb_jsonschema/mappers/collection.py
--- a/cubicweb_jsonschema/mappers/collection.py
+++ b/cubicweb_jsonschema/mappers/collection.py
@@ -48,6 +48,7 @@ from . import _utils
 
 
 __all__ = [
+    'CollectionMapper',
     'EntityCollectionMapper',
     'RelatedCollectionMapper',
     'CollectionItemMapper',
@@ -55,24 +56,13 @@ from . import _utils
 ]
 
 
- at JSONSchemaDeserializer.register
 @JSONSchemaSerializer.register
-class EntityCollectionMapper(JSONSchemaMapper):
+class CollectionMapper(JSONSchemaMapper):
     """Mapper for a collection of entities."""
     __regid__ = 'jsonschema.collection'
-    __select__ = match_kwargs('etype')
 
     def __repr__(self):
-        return '<{0.__class__.__name__} etype={0.etype}>'.format(self)
-
-    @property
-    def etype(self):
-        return self.cw_extra_kwargs['etype']
-
-    @property
-    def title(self):
-        """Title of the collection, plural form of entity type."""
-        return self._cw._('{}_plural').format(self.etype)
+        return '<{0.__class__.__name__}>'.format(self)
 
     @add_links
     def schema_and_definitions(self, schema_role=None):
@@ -81,12 +71,9 @@ class EntityCollectionMapper(JSONSchemaM
         return self._array_schema(schema_role=schema_role)
 
     def _submission_schema_and_definitions(self):
-        """Delegate generation of schema and definitions to the "entity"
-        mapper corresponding to the entity type in this collection.
-        """
-        mapper = self.select_mapper(
-            'jsonschema.entity', **self.cw_extra_kwargs)
-        return mapper.schema_and_definitions(schema_role=CREATION_ROLE)
+        # Disallow submission as we have no knowledge of target entity type in
+        # this mapper.
+        return False, {}
 
     def _array_schema(self, schema_role=None):
         item_mapper = self.select_mapper(
@@ -105,10 +92,49 @@ class EntityCollectionMapper(JSONSchemaM
         """
         if schema_role is not None:
             return
-        for link in super(EntityCollectionMapper, self).links(
+        for link in super(CollectionMapper, self).links(
                 schema_role=schema_role):
             yield link
 
+    def serialize(self, entities):
+        """Return a list of collection item representing each entity in
+        `entities`.
+        """
+        def mapper(entity):
+            """Select jsonschema.item mapper using entity's type."""
+            return self.select_mapper(
+                'jsonschema.item',
+                etype=entity.cw_etype, **self.cw_extra_kwargs)
+
+        return [mapper(entity).serialize(entity) for entity in entities]
+
+
+ at JSONSchemaDeserializer.register
+class EntityCollectionMapper(CollectionMapper):
+    """Mapper for a collection of entities of a given type."""
+    __regid__ = 'jsonschema.collection'
+    __select__ = CollectionMapper.__select__ & match_kwargs('etype')
+
+    def __repr__(self):
+        return '<{0.__class__.__name__} etype={0.etype}>'.format(self)
+
+    @property
+    def etype(self):
+        return self.cw_extra_kwargs['etype']
+
+    @property
+    def title(self):
+        """Title of the collection, plural form of entity type."""
+        return self._cw._('{}_plural').format(self.etype)
+
+    def _submission_schema_and_definitions(self):
+        """Delegate generation of schema and definitions to the "entity"
+        mapper corresponding to the entity type in this collection.
+        """
+        mapper = self.select_mapper(
+            'jsonschema.entity', **self.cw_extra_kwargs)
+        return mapper.schema_and_definitions(schema_role=CREATION_ROLE)
+
     def values(self, instance):
         mapper = self.select_mapper(
             'jsonschema.entity', **self.cw_extra_kwargs)
diff --git a/test/test_mappers.py b/test/test_mappers.py
--- a/test/test_mappers.py
+++ b/test/test_mappers.py
@@ -170,6 +170,53 @@ class CollectionMapperTC(CubicWebTC):
     def test_schema_view(self):
         with self.admin_access.cnx() as cnx:
             mapper = cnx.vreg['mappers'].select(
+                'jsonschema.collection', cnx)
+            j_schema = mapper.json_schema(VIEW_ROLE)
+        expected = {
+            'type': 'array',
+            'items': {
+                'properties': {
+                    'type': {'type': 'string'},
+                    'id': {'type': 'string'},
+                    'title': {'type': 'string'},
+                },
+                'type': 'object',
+            },
+        }
+        self.assertEqual(j_schema, expected)
+
+    def test_schema_creation(self):
+        with self.admin_access.cnx() as cnx:
+            mapper = cnx.vreg['mappers'].select(
+                'jsonschema.collection', cnx)
+            schema = mapper.json_schema(CREATION_ROLE)
+        self.assertEqual(schema, False)
+
+    def test_serialize(self):
+        with self.admin_access.cnx() as cnx:
+            author = cnx.create_entity('Author', name=u'bob')
+            book = cnx.create_entity('Book', title=u'b',
+                                     author=author)
+            mapper = cnx.vreg['mappers'].select(
+                'jsonschema.collection', cnx)
+            data = mapper.serialize([author, book])
+        expected = [{
+            'id': str(author.eid),
+            'title': 'bob',
+            'type': 'author',
+        }, {
+            'id': str(book.eid),
+            'title': 'b',
+            'type': 'book',
+        }]
+        self.assertEqual(data, expected)
+
+
+class EntityCollectionMapperTC(CubicWebTC):
+
+    def test_schema_view(self):
+        with self.admin_access.cnx() as cnx:
+            mapper = cnx.vreg['mappers'].select(
                 'jsonschema.collection', cnx, etype='Book')
             j_schema = mapper.json_schema(VIEW_ROLE)
         expected = {


More information about the cubicweb-devel mailing list