Interfaces

There are two major entities in this framework: resources and links between them.

Resource API defines interfaces to be implemented in order to expose the entities.

NOTE: all methods must be implemnted. In case if some of the methods are not supposed to do anything, raise NotImplemntedError within their implementations and return False for respective authorization methods if needed.

Resource

Resource concept is similar to the one mentioned in Roy Fielding’s desertation.

class resource_api.interfaces.Resource(context)

Represents entity that is supposed to be exposed via public interface

Methods have the following arguments:

pk
PK of exisiting resource
data (dict)
information to be stored within the resource
params (dict)
extra parameters to be used for collection filtering
user (object)
entity that corresponds to the user that performs certain operation on the resource
UriPolicy

alias of PkUriPolicy

__init__(context)
context (object)
entity that is supposed to hold DAL (data access layer) related functionality like database connections, network sockets, etc.
can_create(user, data)

Returns True if user is allowed to create resource with certain data

can_delete(user, pk)

Returns True if user is allowed to delete the resource

can_discover(user, pk)

Returns False if user is not allowed to know about resoure’s existence

can_get_data(user, pk, data)

Returns only the fields that user is allowed to fetch

can_get_uris(user)

Returns True if user is allowed to list the items in the collection or get their count

can_update(user, pk, data)

Returns True if user is allowed to update the resource

create(user, pk, data)

Creates a new instance

delete(user, pk)

Removes the resource

exists(user, pk)

Returns True if the resource exists

get_count(user, params=None)

Returns total amount of items that fit filtering criterias

get_data(user, pk)

Returns fields of the resource

get_uris(user, params=None)

Returns an iterable over primary keys

update(user, pk, data)

Updates specified fields of a given instance

URI is represented by a PK (Primary Key) in Resource API.

Resource interface defines two types of methods.

First, DAL related CRUD methods: get_data, get_pks, set, delete, exists

Second, authorization related methods starting with can_

Each resource must define a UriPolicy:

class resource_api.interfaces.AbstractUriPolicy(resource_instance)

Defines a way to generate URI based on data that was passed when creating the resource.

__init__(resource_instance)
resource_instance (Resource instance)
entity that can be used to access previously created items
deserialize(pk)

Transforms data sent over the wire into sth. usable inside DAL

pk
PK value as it comes over the wire - e.g. string in case of HTTP
@return
PK transformed to the data type expected to by DAL in order to fetch data
generate_pk(data, link_data=None)

Generates a PK based on input data

data (dict):
the same data that is passed to Resource’s create method
link_data (dict):
the same link_data that is passed to Resource’s create method
@return
generated PK
get_schema()

Returns meta information (dict) to be included into resource’s schema

serialize(pk)

Transforms value into sth. ready to transfer over the wire

pk
PK value used within DAL to identify stored entries
@return
PK transformed into something that can be sent over the wire - e.g. string in case of HTTP
type

A string that would give a hint to the client which PK policy is in use

The default pk policy is this one:

class resource_api.interfaces.PkUriPolicy(resource_instance)

Uses value of a field marked as “pk=True” as resource’s URI

Note, there are certain cases when the URI is supposed to be generated within peristence (data acess) layer. E.g. via autoincrementing primary key in SQL database. In such case the URI is supposed to be returned by create method.

class Example(Resource):

    class UriPolicy(AbstractUriPolicy):

        def deserialize(self, pk):
            try:
                return int(pk)
            except ValueError:
                raise ValidationError("URI is not int")

        def serialize(self, pk):
            return pk

        @property
        def type(self):
            return "autoincrement_pk_policy"

        def generate_pk(self, data, link_data=None):
            return None

    def create(self, pk, data):
        # assert pk is None
        row_id = self._sql_database.create_row(data)
        return row_id

    ...

Schema and QuerySchema

Resources and Links may define schema that shall be used via Resource API for input validation.

The schema is defined the following way:

class CustomResource(Resource):

    class Schema:
        name = schema.StringField(pk=True)
        count = schema.IntegerField()

    class Links:

        class target(Link):
            class Schema:
                timestamp = schema.DateTimeField()

Schema fields are defined within a nested class with a reserved name Schema. A comprehensive reference for built-in fields can be found here.

Additionally both Resources and Links may define query schema to validate all parameters that client uses for filtering the collections.

Query schema is defined the following way:

class CustomResource(Resource):

    class QuerySchema:
        name = schema.StringField(pk=True)
        count = schema.IntegerField()

    class Links:

        class target(Link):
            class QuerySchema:
                timestamp = schema.DateTimeField()

Query parameters are defined in a similar manner as Schema ones but inside QuerySchema nested subclass. The key functional difference between two schemas is the fact that Schema may have required fields and QuerySchema may not.

NOTE: it is not necessary for Schema and QuerySchema inner classes to inherit from Schema class. Resource API adds this inheritance automatically.