Exemple #1
0
 def __init__(self,
              resource,
              backref=None,
              io="rw",
              attribute=None,
              **kwargs):
     self.reference = ResourceReference(resource)
     self.attribute = attribute
     self.backref = backref
     self.io = io
Exemple #2
0
 def __init__(self, resource, attribute=None):
     self.target_reference = ResourceReference(resource)
     super(ItemUri, self).__init__(lambda: {
         "type":
         "string",
         "pattern":
         "^{}\/[^/]+$".format(re.escape(self.target.route_prefix))
     },
                                   io="r",
                                   attribute=attribute)
Exemple #3
0
    def __init__(self, resource, **kwargs):
        self.target_reference = ResourceReference(resource)

        def schema():
            key_converters = self.target.meta.key_converters
            response_schema = self.formatter_key.response
            if len(key_converters) > 1:
                request_schema = {
                    # "type": [self.formatter_key.matcher_type()] + [nk.matcher_type() for nk in natural_keys],
                    "anyOf": [nk.request for nk in key_converters]
                }
            else:
                request_schema = self.formatter_key.request
            return response_schema, request_schema

        super(ToOne, self).__init__(schema, **kwargs)
Exemple #4
0
class Relation(RouteSet, ResourceBound):
    """
    Used to define a relation to another :class:`ModelResource`.
    """
    def __init__(self, resource, backref=None, io="rw", attribute=None, **kwargs):
        self.reference = ResourceReference(resource)
        self.attribute = attribute
        self.backref = backref
        self.io = io

    @cached_property
    def target(self):
        return self.reference.resolve(self.resource)

    # FIXME can only be loaded after target is added to API
    def routes(self):
        io = self.io
        rule = '/{}'.format(attribute_to_route_uri(self.attribute))

        relation_route = ItemRoute(rule='{}/<{}:target_id>'.format(rule, self.target.meta.id_converter))
        relations_route = ItemRoute(rule=rule)

        if "r" in io:
            @relations_route.GET
            def relation_instances(resource, item, page, per_page):
                return resource.manager.relation_instances(item,
                                                           self.attribute,
                                                           self.target,
                                                           page,
                                                           per_page)

            relations_route.request_schema = FieldSet({
                "page": Integer(minimum=1, default=1),
                "per_page": Integer(minimum=1,
                                    default=20,  # FIXME use API reference
                                    maximum=50)
            })

            relations_route.response_schema = RelationInstances(self.target)

        if "w" in io:
            @relations_route.POST
            def relation_add(resource, item, target_item):
                resource.manager.relation_add(item, self.attribute, self.target, target_item)
                resource.manager.commit()
                return target_item

            relation_add.request_schema = ToOne(self.target)
            relation_add.response_schema = ToOne(self.target)

            @relation_route.DELETE
            def relation_remove(resource, item, target_id):
                target_item = self.target.manager.read(target_id)
                resource.manager.relation_remove(item, self.attribute, self.target, target_item)
                resource.manager.commit()
                return None, 204
            yield relation_route

        if io:
            yield relations_route
Exemple #5
0
    def __init__(self, resource, patchable=False, **kwargs):
        self.target_reference = ResourceReference(resource)
        self.patchable = patchable

        def schema():
            def _response_schema():
                if self.resource == self.target:
                    return {"$ref": "#"}
                return {"$ref": self.target.routes["describedBy"].rule_factory(self.target)}

            if not not self.patchable:
                return _response_schema()
            else:
                return _response_schema(), self.target.schema.patchable.update

        super(Inline, self).__init__(schema, **kwargs)
Exemple #6
0
class ItemUri(Raw):
    """
    A string field that formats the url of a resource item; read-only.
    """
    def __init__(self, resource, attribute=None):
        self.target_reference = ResourceReference(resource)
        super(ItemUri, self).__init__(lambda: {
            "type":
            "string",
            "pattern":
            "^{}\/[^/]+$".format(re.escape(self.target.route_prefix))
        },
                                      io="r",
                                      attribute=attribute)

    @cached_property
    def target(self):
        return self.target_reference.resolve()

    def format(self, value):
        return '{}/{}'.format(self.target.route_prefix, value)

    def converter(self, value):
        try:
            endpoint, args = route_from(value, 'GET')
        except Exception as e:
            raise e
        return self.target.manager.id_field.convert(args['id'])
Exemple #7
0
class Inline(Raw, ResourceBound):
    """
    Formats and converts items in a :class:`ModelResource` using the resource's ``schema``.

    :param resource: a resource reference as in :class:`ToOne`
    :param bool patchable: whether to allow partial objects
    """
    def __init__(self, resource, patchable=False, **kwargs):
        self.target_reference = ResourceReference(resource)
        self.patchable = patchable

        def schema():
            def _response_schema():
                if self.resource == self.target:
                    return {"$ref": "#"}
                return {
                    "$ref":
                    self.target.routes["describedBy"].rule_factory(self.target)
                }

            if not self.patchable:
                return _response_schema()
            else:
                return _response_schema(), self.target.schema.patchable.update

        super(Inline, self).__init__(schema, **kwargs)

    def rebind(self, resource):
        if self.target_reference.value == 'self':
            return self.__class__(
                'self',
                patchable=self.patchable,
                default=self.default,
                attribute=self.attribute,
                nullable=self.nullable,
                title=self.title,
                description=self.description,
                io=self.io,
            ).bind(resource)
        else:
            return self

    @cached_property
    def target(self):
        return self.target_reference.resolve(self.resource)

    def format(self, item):
        return self.target.schema.format(item)

    def convert(self, item, update=False, validate=True):
        if not validate:
            raise NotImplementedError()

        return self.target.schema.convert(item,
                                          update=update,
                                          patchable=self.patchable)
Exemple #8
0
class Inline(Raw, ResourceBound):
    """
    Formats and converts items in a :class:`ModelResource` using the resource's ``schema``.

    :param resource: a resource reference as in :class:`ToOne`
    :param bool patchable: whether to allow partial objects
    """

    def __init__(self, resource, patchable=False, **kwargs):
        self.target_reference = ResourceReference(resource)
        self.patchable = patchable

        def schema():
            def _response_schema():
                if self.resource == self.target:
                    return {"$ref": "#"}
                return {"$ref": self.target.routes["describedBy"].rule_factory(self.target)}

            if not self.patchable:
                return _response_schema()
            else:
                return _response_schema(), self.target.schema.patchable.update

        super(Inline, self).__init__(schema, **kwargs)

    def rebind(self, resource):
        if self.target_reference.value == 'self':
            return self.__class__(
                'self',
                patchable=self.patchable,
                default=self.default,
                attribute=self.attribute,
                nullable=self.nullable,
                title=self.title,
                description=self.description,
                io=self.io
            ).bind(resource)
        else:
            return self

    @cached_property
    def target(self):
        return self.target_reference.resolve(self.resource)

    def format(self, item):
        return self.target.schema.format(item)

    def convert(self, item, update=False, validate=True):
        if not validate:
            raise NotImplementedError()

        return self.target.schema.convert(item, update=update, patchable=self.patchable)
Exemple #9
0
    def __init__(self, resource, patchable=False, **kwargs):
        self.target_reference = ResourceReference(resource)
        self.patchable = patchable

        def schema():
            def _response_schema():
                if self.resource == self.target:
                    return {"$ref": "#"}
                return {"$ref": self.resource.routes["describedBy"].rule_factory(self.resource)}

            if not not self.patchable:
                return _response_schema()
            else:
                return _response_schema(), self.target.schema.patchable.update

        super(Inline, self).__init__(schema, **kwargs)
Exemple #10
0
    def __init__(self, resource, **kwargs):
        self.target_reference = ResourceReference(resource)

        def schema():
            target = self.target
            key_converters = self.target.meta.key_converters
            response_schema = self.formatter_key.response
            if len(key_converters) > 1:
                request_schema = {
                    # "type": [self.formatter_key.matcher_type()] + [nk.matcher_type() for nk in natural_keys],
                    "anyOf": [nk.request for nk in key_converters]
                }
            else:
                request_schema = self.formatter_key.request
            return response_schema, request_schema

        super(ToOne, self).__init__(schema, **kwargs)
Exemple #11
0
class ItemUri(Raw):
    """
    A string field that formats the url of a resource item; read-only.
    """
    def __init__(self, resource, attribute=None):
        self.target_reference = ResourceReference(resource)
        super(ItemUri, self).__init__(lambda: {
            "type": "string",
            "pattern": "^{}\/[^/]+$".format(re.escape(self.target.route_prefix))
        }, io="r", attribute=attribute)

    @cached_property
    def target(self):
        return self.target_reference.resolve()

    def format(self, value):
        return '{}/{}'.format(self.target.route_prefix, value)
Exemple #12
0
class ItemUri(Raw):
    """
    A string field that formats the url of a resource item; read-only.
    """
    def __init__(self, resource, attribute=None):
        self.target_reference = ResourceReference(resource)
        super(ItemUri, self).__init__(lambda: {
            "type":
            "string",
            "pattern":
            "^{}\/[^/]+$".format(re.escape(self.target.route_prefix))
        },
                                      io="r",
                                      attribute=attribute)

    @cached_property
    def target(self):
        return self.target_reference.resolve()

    def format(self, value):
        return '{}/{}'.format(self.target.route_prefix, value)
Exemple #13
0
class ItemUri(Raw):
    """
    A string field that formats the url of a resource item; read-only.
    """
    def __init__(self, resource, attribute=None):
        self.target_reference = ResourceReference(resource)
        super(ItemUri, self).__init__(lambda: {
            "type": "string",
            "pattern": "^{}\/[^/]+$".format(re.escape(self.target.route_prefix))
        }, io="r", attribute=attribute)

    @cached_property
    def target(self):
        return self.target_reference.resolve()

    def format(self, value):
        return '{}/{}'.format(self.target.route_prefix, value)

    def converter(self, value):
        try:
            endpoint, args = route_from(value, 'GET')
        except Exception as e:
            raise e
        return self.target.manager.id_field.convert(args['id'])
Exemple #14
0
 def __init__(self, resource, backref=None, io="rw", attribute=None, **kwargs):
     self.reference = ResourceReference(resource)
     self.attribute = attribute
     self.backref = backref
     self.io = io
Exemple #15
0
class Relation(RouteSet, ResourceBound):
    """
    Used to define a relation to another :class:`ModelResource`.
    """

    def __init__(self, resource, backref=None, io="rw", attribute=None, **kwargs):
        self.reference = ResourceReference(resource)
        self.attribute = attribute
        self.backref = backref
        self.io = io

    @cached_property
    def target(self):
        return self.reference.resolve(self.resource)

    # FIXME can only be loaded after target is added to API
    def routes(self):
        io = self.io
        rule = '/{}'.format(attribute_to_route_uri(self.attribute))

        relation_route = ItemRoute(rule=rule)
        relations_route = ItemRoute(rule=rule)

        if "r" in io:
            def relation_instances(resource, item, page, per_page):
                return resource.manager.relation_instances(item,
                                                           self.attribute,
                                                           self.target,
                                                           page,
                                                           per_page)

            yield relations_route.for_method('GET',
                                             relation_instances,
                                             rel=self.attribute,
                                             response_schema=RelationInstances(self.target),
                                             schema=FieldSet({
                                                 "page": Integer(minimum=1, default=1),
                                                 "per_page": Integer(minimum=1,
                                                                     default=20,  # FIXME use API reference
                                                                     maximum=50)
                                             }))

        if "w" in io or "u" in io:
            def relation_add(resource, item, target_item):
                resource.manager.relation_add(item, self.attribute, self.target, target_item)
                resource.manager.commit()
                return target_item

            yield relations_route.for_method('POST',
                                             relation_add,
                                             rel=to_camel_case('add_{}'.format(self.attribute)),
                                             response_schema=ToOne(self.target),
                                             schema=ToOne(self.target))

            def relation_remove(resource, item, target_item):
                resource.manager.relation_remove(item, self.attribute, self.target, target_item)
                resource.manager.commit()
                return None, 204

            yield relation_route.for_method('DELETE',
                                            relation_remove,
                                            rel=to_camel_case('remove_{}'.format(self.attribute)),
                                            response_schema=ToOne(self.target),
                                            schema=ToOne(self.target))
Exemple #16
0
 def __init__(self, resource, attribute=None):
     self.target_reference = ResourceReference(resource)
     super(ItemUri, self).__init__(lambda: {
         "type": "string",
         "pattern": "^{}\/[^/]+$".format(re.escape(self.target.route_prefix))
     }, io="r", attribute=attribute)
Exemple #17
0
class ToOne(Raw, ResourceBound):
    """
    Represents references between resources as `json-ref` objects.

    Resource references can be one of the following:

    - :class:`Resource` class
    - a string with a resource name
    - a string with a module name and class name of a resource
    - ``"self"`` --- which resolves to the resource this field is bound to

    :param resource: a resource reference
    """
    def __init__(self, resource, **kwargs):
        self.target_reference = ResourceReference(resource)

        def schema():
            target = self.target
            key_converters = self.target.meta.key_converters
            response_schema = self.formatter_key.response
            if len(key_converters) > 1:
                request_schema = {
                    # "type": [self.formatter_key.matcher_type()] + [nk.matcher_type() for nk in natural_keys],
                    "anyOf": [nk.request for nk in key_converters]
                }
            else:
                request_schema = self.formatter_key.request
            return response_schema, request_schema

        super(ToOne, self).__init__(schema, **kwargs)

    def rebind(self, resource):
        if self.target_reference.value == 'self':
            return self.__class__(
                'self',
                default=self.default,
                attribute=self.attribute,
                nullable=self.nullable,
                title=self.title,
                description=self.description,
                io=self.io
            ).bind(resource)
        else:
            return self

    @cached_property
    def target(self):
        return self.target_reference.resolve(self.resource)

    @cached_property
    def formatter_key(self):
        return self.target.meta.key_converters[0]

    def formatter(self, item):
        return self.formatter_key.format(item)

    def converter(self, value):
        for python_type, json_type in (
                (dict, 'object'),
                (int, 'integer'),
                ((list, tuple), 'array'),
                (six.string_types, 'string')):
            if isinstance(value, python_type):
                return self.target.meta.key_converters_by_type[json_type].convert(value)
Exemple #18
0
class ToOne(Raw, ResourceBound):
    """
    Represents references between resources as `json-ref` objects.

    Resource references can be one of the following:

    - :class:`Resource` class
    - a string with a resource name
    - a string with a module name and class name of a resource
    - ``"self"`` --- which resolves to the resource this field is bound to

    :param resource: a resource reference
    """
    def __init__(self, resource, **kwargs):
        self.target_reference = ResourceReference(resource)

        def schema():
            target = self.target
            key_converters = self.target.meta.key_converters
            response_schema = self.formatter_key.response
            if len(key_converters) > 1:
                request_schema = {
                    # "type": [self.formatter_key.matcher_type()] + [nk.matcher_type() for nk in natural_keys],
                    "anyOf": [nk.request for nk in key_converters]
                }
            else:
                request_schema = self.formatter_key.request
            return response_schema, request_schema

        super(ToOne, self).__init__(schema, **kwargs)

    def rebind(self, resource):
        if self.target_reference.value == 'self':
            return self.__class__('self',
                                  default=self.default,
                                  attribute=self.attribute,
                                  nullable=self.nullable,
                                  title=self.title,
                                  description=self.description,
                                  io=self.io).bind(resource)
        else:
            return self

    @cached_property
    def target(self):
        return self.target_reference.resolve(self.resource)

    @cached_property
    def formatter_key(self):
        return self.target.meta.key_converters[0]

    def formatter(self, item):
        return self.formatter_key.format(item)

    def converter(self, value):
        for python_type, json_type in ((dict, 'object'), (int, 'integer'),
                                       ((list, tuple), 'array'),
                                       (six.string_types, 'string')):
            if isinstance(value, python_type):
                return self.target.meta.key_converters_by_type[
                    json_type].convert(value)