Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serializes: api defintion and default implementation #15

Closed
ppanero opened this issue Jun 11, 2020 · 0 comments · Fixed by #19
Closed

serializes: api defintion and default implementation #15

ppanero opened this issue Jun 11, 2020 · 0 comments · Fixed by #19
Assignees

Comments

@ppanero
Copy link
Member

ppanero commented Jun 11, 2020

Table of contents

  1. Serializers
  2. Serializable
  3. Reponse

Serializers

They are in charge of transforming python objects, record(s), into a certain format or representation (e.g. JSON, XML). It is in charge of serializing both single and list of objects responses. The API should look like:

class SerializerMixinInterface:
    """Serializer Interface."""

    def serialize_object(self, object, response_ctx, *args, **kwargs):
        """Serialize a single record reponse according to the response ctx."""
        raise NotImplementedError()

    def serialize_object_list(self, object_list, response_ctx, *args, **kwargs):
        """Serialize a list of records reponse according to the response ctx."""
        raise NotImplementedError()

    def serialize_error(self, error, response_ctx, *args, **kwargs):
        """Serialize an error reponse according to the response ctx."""
        raise NotImplementedError() 

Things that are required by serialize_object:

What Where is it How is it currently accessed
pid_value In the PersistentIdentifier object An enriched Record API class containing a record has been defined. An instance of said Record class is the object received as parameter
self link, which implies view name to user url_for and the pid_value link factory is still an open question the record link generation is passed as a partial function in the response_ctx argument. Then it is called with the record object for the pid_value

Things that are required by serialize_object_list:

What Where is it How is it currently accessed
pid_value In the PersistentIdentifier object This problem is delegated to each call of serialize_object. See more details above.
self link, which implies view name to user url_for and the pid_value link factory is still an open question This problem is delegated to each call of serialize_object. See more details above.
paginated links, which implies view name to user url_for pagination values are available in the resource_requestctx. however, the function is still not coded. how is it passed to the serializer? another partial?
aggregations in the ES response currently they are passed to the function as a parameter. however, it should be independent of it.
total in the ES reponse it could simply be calculated as the total of records passed in the list, should be resilient enough to rely on that.

Ideally serialize_object_list should not be much more than a simple loop on the list calling serialize_object for each one. Could it be already be coded by default with some "add_extra" function that adds the total, aggregations, etc form context?

The main problem here relies on how the ES response is treated and where, in order for this function to receive a list of python objects. In addition, how much overhead (memory/time) does this add? Somehow we are serializing back and forth (ES --> Python --> Accepted-Mimetype, instead of ES --> Accepted-Mimetype). Nonetheless, this way is much more clean and maintainable.

Open questions:

  • Formatting, should this take care of "prettyprint"?
  • Should auxiliary functions such as metadata processing be defined in the interface?. For example, process_metadata is used mainly to replace_refs. It is small/Invenio specific enough to be accommodated into serialize_object. If for cleanness/clarity sake it should be splitted into more functions it should be up to the specific serializer. A new mixin could be defined for Invenio specific serializers.

Related to

Serializable

The object received by serialize_object, and every object of the object list received by serialize_object, should implement an interface (called Serializable now, but a better name can be found). This interface would allow to access certain properties such as:

class Serailizable:
    @property
    def object():
        return self.record
    
    @property
    def id():
        return self.pid.pid_value
    
    @property
    def version():
        return self.record.revision_id
    
    @property
    def last_modified():
        return self.record.modified

Note that the above return values above are examples for the Record. Another case would be for example a User class, when being serialized it would need to build the self link. For that the id() would return for example the user_name.

Response

class ItemResponse:
    """Item response representation.

    Builds up a reponse based on the response_code and the content."""

    def __init__(self, serializer=None, *args, **kwargs):
        self.serializer = serializer
    
    def make_response(code, content):
        # This should do the link building (e.g. sign posting): self, pagination, query, etc.
        # In case of list responses, it would also take care of extras such as aggregation
        request.make_response(
            response_code=code, 
            body=self.serializer.serialize_object(content) # content is the object,
            headers=self.make_header()
        )
        
    def make_error_response(reason, message):
        request.make_response(
            get_http_code_for_error(reason), message
        )  
class ItemView(BaseView):
    """Item view representation.

    Allows reading, (partial) updating and deleting an item."""

    def __init__(self, *args, **kwargs):
        super(ItemView, self).__init__(*args, **kwargs)
        self.item_parser = self.resource.config.item_request_parser
        self.item_serializers = self.resource.config.item_serializers
    
    @content_regotiation # this decorator might be added to the BaseView in the same fashion than `with_request_resourcectx`
    @authentication # this would add to the resource_requestctx the user needs/data
    def get(self, *args, **kwargs):
        resource_requestctx.request_args = self.item_parser.parse()
        item_handler = self.item_handlers[request_resourcectx.accept_mimetype] # this could be pushed up to the content-negotiation
        
        try:
            content = self.resource.read()

            return item_handler.make_response(200, content)
        except as e:
            return item_handler.make_error_response(e.reason, e.message)        

More at https://codimd.web.cern.ch/lMgvRdXRQZS45oKqqrRZpw

@ppanero ppanero added this to Triage in InvenioRDM June Board via automation Jun 11, 2020
@ppanero ppanero moved this from Triage to To do in InvenioRDM June Board Jun 11, 2020
@ppanero ppanero moved this from To do to Backend To do in InvenioRDM June Board Jun 11, 2020
@ppanero ppanero moved this from Backend To do to In progress in InvenioRDM June Board Jun 12, 2020
@ppanero ppanero self-assigned this Jun 12, 2020
@ppanero ppanero mentioned this issue Jun 12, 2020
@ppanero ppanero moved this from In progress to In Review in InvenioRDM June Board Jun 15, 2020
InvenioRDM June Board automation moved this from In Review to Done Jun 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

1 participant