[PATCH 2 of 2 jsonschema] Use resource interfaces for context of API views

Denis Laxalde denis.laxalde at logilab.fr
Wed Jun 20 10:08:05 CEST 2018


# HG changeset patch
# User Denis Laxalde <denis.laxalde at logilab.fr>
# Date 1529423173 -7200
#      Tue Jun 19 17:46:13 2018 +0200
# Node ID fe8187f8d25285adfd850655057045ea7929f488
# Parent  52dcc1f1a1439b0c0019646b4172fe94644b9788
# Available At https://hg.logilab.org/review/cubicweb-jsonschema
#              hg pull https://hg.logilab.org/review/cubicweb-jsonschema -r fe8187f8d252
# EXP-Topic resources-interfaces
Use resource interfaces for context of API views

We define interfaces (using zope.interface) for main resource classes
and refer to these interfaces (as dotted names) in API views' context
instead of registering these views against the resource classes
directly.

The idea is to make it possible for downstream applications to register
their own resource classes that could be selected by standard API views.

Interfaces have attributes and methods declared based on what appears to
be needed on API views. We use interface inheritance to reduce
duplication (e.g. the IMapped interface is a base for all interface but
IRoot; IRelatedEntities is a base for IRelatedEntity).

Notice that this somehow conflicts with the Mappable abstract class that
was defined previously in resources module; so we drop this abstract
class and replace it by an IMapped interface.

diff --git a/cubicweb_jsonschema/api/entities.py b/cubicweb_jsonschema/api/entities.py
--- a/cubicweb_jsonschema/api/entities.py
+++ b/cubicweb_jsonschema/api/entities.py
@@ -31,10 +31,7 @@ from .. import (
     CREATION_ROLE,
 )
 from ..resources import (
-    EntityResource,
     ETypeResource,
-    RelatedEntitiesResource,
-    RelatedEntityResource,
     RootResource,
     WorkflowTransitionsResource,
 )
@@ -59,7 +56,7 @@ def _created(request, resource):
 
 @view_config(
     route_name='cubicweb-jsonschema.entities',
-    context=RootResource,
+    context='cubicweb_jsonschema.resources.IRoot',
     request_method='GET',
     decorator=[describedby],
     permission='authenticated',
@@ -74,7 +71,7 @@ def get_root(context, request):
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=ETypeResource,
+    context='cubicweb_jsonschema.resources.IEntities',
     request_method='GET',
     decorator=[
         describedby,
@@ -105,7 +102,7 @@ def allow_for_entity(entity):
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=EntityResource,
+    context='cubicweb_jsonschema.resources.IEntity',
     request_method='GET',
     decorator=[
         describedby,
@@ -123,7 +120,7 @@ def get_entity(context, request):
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=ETypeResource,
+    context='cubicweb_jsonschema.resources.IEntities',
     request_method='POST',
 )
 def create_entity(context, request):
@@ -142,7 +139,7 @@ def create_entity(context, request):
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=EntityResource,
+    context='cubicweb_jsonschema.resources.IEntity',
     request_method='PUT',
     decorator=[entity_from_context],
 )
@@ -163,7 +160,7 @@ def update_entity(context, request):
 
 @view_config(
     route_name='delete_entity',
-    context=EntityResource,
+    context='cubicweb_jsonschema.resources.IEntity',
     request_method='DELETE',
     decorator=[entity_from_context],
 )
@@ -195,7 +192,7 @@ def allow_for_entity_relation(entity, rt
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=RelatedEntitiesResource,
+    context='cubicweb_jsonschema.resources.IRelatedEntities',
     request_method='GET',
     decorator=[
         describedby,
@@ -214,7 +211,7 @@ def get_related_entities(context, reques
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=RelatedEntitiesResource,
+    context='cubicweb_jsonschema.resources.IRelatedEntities',
     request_method='POST',
 )
 def post_related_entities(context, request):
@@ -256,7 +253,7 @@ def post_related_entities(context, reque
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=RelatedEntityResource,
+    context='cubicweb_jsonschema.resources.IRelatedEntity',
     request_method='GET',
     decorator=[
         describedby,
@@ -272,7 +269,7 @@ def get_related_entity(context, request)
 
 @json_config(
     route_name='cubicweb-jsonschema.entities',
-    context=RelatedEntityResource,
+    context='cubicweb_jsonschema.resources.IRelatedEntity',
     request_method='PUT',
 )
 def update_related_entity(context, request):
@@ -292,7 +289,7 @@ def update_related_entity(context, reque
 
 @view_config(
     route_name='delete_entity',
-    context=RelatedEntityResource,
+    context='cubicweb_jsonschema.resources.IRelatedEntity',
     request_method='DELETE',
 )
 def delete_relation(context, request):
diff --git a/cubicweb_jsonschema/api/schema.py b/cubicweb_jsonschema/api/schema.py
--- a/cubicweb_jsonschema/api/schema.py
+++ b/cubicweb_jsonschema/api/schema.py
@@ -73,7 +73,7 @@ def links_sortkey(link):
 
 
 @jsonschema_config(
-    context=resources.RootResource,
+    context='cubicweb_jsonschema.resources.IRoot',
     decorator=[describes],
 )
 def application_schema(context, request):
@@ -102,7 +102,7 @@ def application_schema(context, request)
 
 
 @jsonschema_config(
-    context=resources.ETypeResource,
+    context='cubicweb_jsonschema.resources.IEntities',
     decorator=[
         describes,
         schema_role(VIEW_ROLE, CREATION_ROLE),
@@ -120,7 +120,7 @@ def etype_schema(context, request):
 
 
 @jsonschema_config(
-    context=resources.EntityResource,
+    context='cubicweb_jsonschema.resources.IEntity',
     decorator=[
         describes,
         schema_role(VIEW_ROLE, EDITION_ROLE),
@@ -138,7 +138,7 @@ def entity_schema(context, request):
 
 
 @jsonschema_config(
-    context=resources.RelatedEntitiesResource,
+    context='cubicweb_jsonschema.resources.IRelatedEntities',
     decorator=[
         describes,
         schema_role(VIEW_ROLE, CREATION_ROLE),
@@ -153,7 +153,7 @@ def related_entities_schema(context, req
 
 
 @jsonschema_config(
-    context=resources.RelatedEntityResource,
+    context='cubicweb_jsonschema.resources.IRelatedEntity',
     decorator=[
         describes,
         schema_role(VIEW_ROLE, EDITION_ROLE),
diff --git a/cubicweb_jsonschema/resources.py b/cubicweb_jsonschema/resources.py
--- a/cubicweb_jsonschema/resources.py
+++ b/cubicweb_jsonschema/resources.py
@@ -33,8 +33,12 @@ from pyramid.security import (
     Authenticated,
     DENY_ALL,
 )
+from zope.interface import (
+    Attribute,
+    Interface,
+    implementer,
+)
 
-from ._compat import ABCMeta
 from .api import json_problem
 
 from rql import nodes, TypeResolverException
@@ -155,15 +159,27 @@ class TraversalResource(object):
         self.__name__ = self.segment
 
 
- at add_metaclass(ABCMeta)
-class Mappable(object):
-    """Abstract class for resources associated with a mapper."""
+class IResource(Interface):
+    """Base interface for resource classes."""
+    __parent__ = Attribute("Parent resource object")
+    __name__ = Attribute("Resource name")
 
-    @abc.abstractmethod
-    def mapper(self, schema_role=None):
+    def __getitem__(value):
+        raise KeyError(value)
+
+
+class IMapped(IResource):
+    """Interface for resources associated with a mapper."""
+
+    def mapper(schema_role=None):
         """Return the mapper associated with this resource."""
 
 
+class IRoot(IResource):
+    """Root resource interface"""
+
+
+ at implementer(IRoot)
 class RootResource(PluggableResource):
     """Root resources for entities."""
     __parent__ = None
@@ -261,7 +277,15 @@ class Paginable(object):
         return rset
 
 
- at Mappable.register
+class IEntities(IMapped):
+    """Resource interface for a collection of entities."""
+
+    def has_perm(action):
+        """Return True if permission for `action` is granted."""
+        return False
+
+
+ at implementer(IEntities)
 class ETypeResource(Paginable, PluggableResource):
     """A resource class for an entity type."""
 
@@ -318,7 +342,12 @@ class ETypeResource(Paginable, Pluggable
         return vreg.schema[self.etype].has_perm(self.request.cw_cnx, action)
 
 
- at Mappable.register
+class IEntity(IMapped):
+    """Entity resource interface."""
+    entity = Attribute("Entity bound to this resource")
+
+
+ at implementer(IEntity)
 class EntityResource(PluggableResource):
     """A resource class for an entity. It provide method to retrieve an entity
     by eid.
@@ -383,7 +412,7 @@ class EntityResource(PluggableResource):
             entity=entity, resource=self)
 
 
- at Mappable.register
+ at implementer(IMapped)
 @EntityResource.child_resource
 class WorkflowTransitionsResource(TraversalResource):
     """Resource for workflow transitions of an entity."""
@@ -405,7 +434,13 @@ class WorkflowTransitionsResource(Traver
             etype='TrInfo', for_entity=self.for_entity, resource=self)
 
 
- at Mappable.register
+class IRelatedEntities(IMapped):
+    """Related entities resource interface."""
+    rtype = Attribute("Relation type name")
+    role = Attribute("Role of parent resource in the relation")
+
+
+ at implementer(IRelatedEntities)
 class RelatedEntitiesResource(Paginable, PluggableResource):
     """A resource wrapping entities related to the entity bound to
     `entity_resource` through `rtype`/`role`.
@@ -488,7 +523,12 @@ class RelatedEntitiesResource(Paginable,
             resource=self)
 
 
- at Mappable.register
+class IRelatedEntity(IRelatedEntities):
+    """Related entity resource."""
+    entity = Attribute("Entity target of the relation")
+
+
+ at implementer(IRelatedEntity)
 class RelatedEntityResource(PluggableResource):
     """A resource an entity related to another one."""
 


More information about the cubicweb-devel mailing list