Skip to content
Snippets Groups Projects

Add support for content negotiation in swh's API

2 unresolved threads
Compare and
2 files
+ 56
8
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 55
8
@@ -10,12 +10,43 @@ import json
import logging
import pickle
import requests
import datetime
from flask import Flask, Request, Response
from .serializers import (decode_response,
encode_data_client as encode_data,
msgpack_dumps, msgpack_loads, SWHJSONDecoder)
from negotiate.flask import Formatter
logger = logging.getLogger(__name__)
class SWHJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.datetime, datetime.date)):
return obj.isoformat()
if isinstance(obj, datetime.timedelta):
return str(obj)
# Let the base class default method raise the TypeError
return super().default(obj)
    • That won't be enough for all of our internal apis (but i have the feeling you know that and want to go incrementally ;) I think of the storage for example but there might be others as well (indexer?).

Please register or sign in to reply
class JSONFormatter(Formatter):
format = 'json'
mimetypes = ['application/json']
def render(self, obj):
return json.dumps(obj, cls=SWHJSONEncoder)
class MsgpackFormatter(Formatter):
format = 'msgpack'
mimetypes = ['application/x-msgpack']
def render(self, obj):
return msgpack_dumps(obj)
class RemoteException(Exception):
pass
@@ -124,21 +155,28 @@ class SWHRemoteAPI(metaclass=MetaSWHRemoteAPI):
data = encode_data(data)
response = self.raw_post(
endpoint, data, params=params,
headers={'content-type': 'application/x-msgpack'})
headers={'content-type': 'application/x-msgpack',
'accept': 'application/x-msgpack'})
    • why did you add that part? That confuses me, i'm checking ;)

      • Author Maintainer

        In the context of an API client (remote service), we want to make sure we ask the API server for msgpack encoded response. The content-type only declare we send msgpack encoded payload, not what we ask for in return.

      • Please register or sign in to reply
Please register or sign in to reply
return self._decode_response(response)
def get(self, endpoint, params=None):
response = self.raw_get(endpoint, params=params)
response = self.raw_get(
endpoint, params=params,
headers={'accept': 'application/x-msgpack'})
return self._decode_response(response)
def post_stream(self, endpoint, data, params=None):
if not isinstance(data, collections.Iterable):
raise ValueError("`data` must be Iterable")
response = self.raw_post(endpoint, data, params=params)
response = self.raw_post(
endpoint, data, params=params,
headers={'accept': 'application/x-msgpack'})
return self._decode_response(response)
def get_stream(self, endpoint, params=None, chunk_size=4096):
response = self.raw_get(endpoint, params=params, stream=True)
response = self.raw_get(endpoint, params=params, stream=True,
headers={'accept': 'application/x-msgpack'})
return response.iter_content(chunk_size)
def _decode_response(self, response):
@@ -171,16 +209,25 @@ class BytesRequest(Request):
encoding_errors = 'surrogateescape'
def encode_data_server(data):
ENCODERS = {
'application/x-msgpack': msgpack_dumps,
'application/json': json.dumps,
}
def encode_data_server(data, content_type='application/x-msgpack'):
encoded_data = ENCODERS[content_type](data)
return Response(
msgpack_dumps(data),
mimetype='application/x-msgpack',
)
encoded_data,
mimetype=content_type,
)
def decode_request(request):
content_type = request.mimetype
data = request.get_data()
if not data:
return {}
if content_type == 'application/x-msgpack':
r = msgpack_loads(data)
Loading