Skip to content

Nested fields not working as expected #31

Open
@gasparuben

Description

@gasparuben

Dear All,
I am trying to use response marshaling but I get an error. I managed to make the fields/marshal work as indicate in the example - Nested fields

Code

import hashlib, hmac, time
try:
    from urllib.parse import urlencode
except ImportError:
    from urllib import urlencode
import requests
from flask_restx import Namespace, Resource, fields
from dataenhancer.secret.config import ENDPOINT_ROOMS, INDICO_SERVER, API_KEY, SECRET_KEY

namespace = Namespace('indico', description="Contrast information with Indico endpoint")

timing_fields = {}
timing_fields['date'] = fields.String(readOnly=True, description='aa')
timing_fields['tz'] = fields.String(readOnly=True, description='aa')
timing_fields['time'] = fields.String(readOnly=True, description='aa')

booking_fields = {}
booking_fields['url'] = fields.String(readOnly=True, description='aa') 
booking_fields['id'] = fields.String(readOnly=True, description='aa') 
booking_fields['creator'] = fields.String(readOnly=True, description='aa') 
booking_fields['start_dt'] = fields.Nested(timing_fields)
booking_fields['end_dt'] = fields.Nested(timing_fields)

resource_fields = {'vc_room': fields.String(readOnly=True, description='aa')}
resource_fields['event'] = fields.String(readOnly=True, description='aa')
resource_fields['booking'] = fields.Nested(booking_fields)

indico_model = namespace.model('indicoevents', resource_fields)

def _build_indico_request(path, params, api_key=None, secret_key=None, only_public=False, persistent=False):
    items = list(params.items()) if hasattr(params, 'items') else list(params)
    if api_key:
        items.append(('apikey', api_key))
    if only_public:
        items.append(('onlypublic', 'yes'))
    if secret_key:
        if not persistent:
            items.append(('timestamp', str(int(time.time()))))
        items = sorted(items, key=lambda x: x[0].lower())
        url = '%s?%s' % (path, urlencode(items))
        signature = hmac.new(secret_key.encode('utf-8'), url.encode('utf-8'),
                             hashlib.sha1).hexdigest()
        items.append(('signature', signature))
    if not items:
        return path
    return '%s?%s' % (path, urlencode(items))

@namespace.doc('class')
@namespace.route('/<string:office>')
class IndicoEventsEndpoint(Resource):
    #decorators = [require_token]

    @namespace.doc('Get_all_possible_events')
    @namespace.marshal_with(indico_model)
    def get(self, office):
        params = {
            'from': 'today',
            'to': 'today'
        }
        query = _build_indico_request('{}{}.json'.format(ENDPOINT_ROOMS, office), params, api_key=API_KEY, secret_key=SECRET_key,
                                persistent=True)   
        res = requests.get('{}/{}'.format(INDICO_SERVER, query))
        res.raise_for_status()
        return res.json()

I get always the following exception:

Unable to render schema
Traceback (most recent call last):
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/api.py", line 500, in __schema__
    self._schema = Swagger(self).as_dict()
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 202, in as_dict
    **kwargs
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 391, in serialize_resource
    path[method] = self.serialize_operation(doc, method)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 397, in serialize_operation
    'responses': self.responses_for(doc, method) or None,
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 510, in responses_for
    schema = self.serialize_schema(model)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 563, in serialize_schema
    self.register_model(model)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 592, in register_model
    self.register_field(field)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 600, in register_field
    self.register_model(field.nested)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 583, in register_model
    if name not in self.api.models:
TypeError: unhashable type: 'dict'
127.0.0.1 - - [04/Feb/2020 23:07:01] "GET /api/v1/swagger.json HTTP/1.1" 500 -
127.0.0.1 - - [04/Feb/2020 23:07:01] "GET /swaggerui/favicon-32x32.png HTTP/1.1" 200 -

By doing:

$ python
Python 3.6.5 (default, Apr 28 2019, 21:42:51) 
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from flask_restx import fields, marshal
>>> import json
>>> timing_fields = {}
timing_fields['date'] = fields.String(readOnly=True, description='aa')
timing_fields['tz'] = fields.String(readOnly=True, description='aa')
timing_fields['time'] = fields.String(readOnly=True, description='aa')

booking_fields = {}
booking_fields['url'] = fields.String(readOnly=True, description='aa') 
booking_fields['id'] = fields.String(readOnly=True, description='aa') 
booking_fields['creator'] = fields.String(readOnly=True, description='aa') 
booking_fields['start_dt'] = fields.Nested(timing_fields)
booking_fields['end_dt'] = fields.Nested(timing_fields)

resource_fields = {'vc_room': fields.String(readOnly=True, description='aa')}
resource_fields['event'] = fields.String(readOnly=True, description='aa')
resource_fields['booking'] = fields.Nested(booking_fields)>>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> 
>>> 
>>> data = {
            "vc_room": "dafdafds",
            "event": "afdasfdsfda",
            "booking": {
                "url": "https://indico-staging.xx.xx/rooms/booking/402627",
                "start_dt": {
                    "date": "2020-02-03",
                    "tz": "None",
                    "time": "13:00:00"
                },
                "end_dt": {
                    "date": "2020-02-03",
                    "tz": "None",
                    "time": "17:00:00"
                },
                "id": 402627,
                "creator": "R"
            }
}... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 
>>> 
>>> 
>>> json.dumps(marshal(data, resource_fields))
'{"vc_room": "dafdafds", "event": "afdasfdsfda", "booking": {"url": "https://indico-staging.cern.ch/rooms/booking/402627", "id": "402627", "creator": "R", "start_dt": {"date": "2020-02-03", "tz": "None", "time": "13:00:00"}, "end_dt": {"date": "2020-02-03", "tz": "None", "time": "17:00:00"}}}'
>>> ^C

Is this a bug? I can provide further evidences or reduce the code to reach this issue, but it looks to me quite a general one.

Running python 3.6.5:
Flask==1.1.1
Flask-Cors==3.0.8
flask-restplus==0.13.0
flask-restx==0.1.0

Apologies if I am missing something trivial.
Thank you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions