sdk_python icon indicating copy to clipboard operation
sdk_python copied to clipboard

KeyError 'Id' in _unwrap_response_single (creating callbacks)

Open LeLunZ opened this issue 3 years ago • 1 comments

Steps to reproduce:

  1. Create a Bunq Context load it and then try to register a callback.

(only showing the callback part here)

filters = [NotificationFilterUrl('REQUEST', url),
               NotificationFilterUrl('DRAFT_PAYMENT', url)]

endpoint.NotificationFilterUrlUser.create(notification_filters=filters)

What should happen:

  1. Don't know, because it's no where documented..... (also tinker example is old)

What happens:

  1. Throws an KeyError (probably in the response)

Traceback

File "/app/helper/bunq_helper.py", line 17, in add_callback_url home_1 | endpoint.NotificationFilterUrlUser.create( home_1 | File "/usr/local/lib/python3.9/site-packages/bunq/sdk/model/generated/endpoint.py", line 30113, in create home_1 | cls._process_for_id(response_raw) home_1 | File "/usr/local/lib/python3.9/site-packages/bunq/sdk/model/core/bunq_model.py", line 73, in _process_for_id home_1 | cls._unwrap_response_single(obj, cls._FIELD_ID) home_1 | File "/usr/local/lib/python3.9/site-packages/bunq/sdk/model/core/bunq_model.py", line 61, in _unwrap_response_single home_1 | return obj[cls._FIELD_RESPONSE][cls._INDEX_FIRST][wrapper] home_1 | KeyError: 'Id' homeserver_home_1 exited with code 1

SDK version and environment

  • [ ] Tested on 1.14.1
  • [ ] Sandbox
  • [X] Production
  • [X] Tested on 1.14.18

Response id

  • Response id: There it fails. There is no response Id in this object....

Extra info:

As response we get this object:

{
  "Response": [
    {
      "NotificationFilterUrl": {
        "id": XXXXXXXX,
        "created": "2021-01-05 20:44:21.473228",
        "updated": "2021-01-05 20:44:21.473228",
        "category": "REQUEST",
        "notification_target": "XXXXXXXXXXX"
      }
    },
    {
      "NotificationFilterUrl": {
        "id": XXXXXXXXX,
        "created": "2021-01-05 20:44:21.478947",
        "updated": "2021-01-05 20:44:21.478947",
        "category": "DRAFT_PAYMENT",
        "notification_target": "XXXXXXXXXX"
      }
    }
  ]
}

Now in _unwrap_response_single we do this:

  • return obj[cls._FIELD_RESPONSE][cls._INDEX_FIRST][wrapper]
  • cls._FIELD_RESPONSE is "Response"
  • cls._INDEX_FIRST is 0
  • wrapper is "Id"

So if we take the first element of "Response" there is no "Id". Only a NotificationFilterUrl... And then again there is no "Id" only a "id"...

LeLunZ avatar Jan 05 '21 20:01 LeLunZ

@classmethod BunqModel._from_json_list assumes that the endpoint name is the same as the model name.
This does not work for the "abstract" type NotificationFilterUrl which can be loaded from multiple endpoints (NotificationFilterUrlUser, NotificationFilterUrlMonetaryAccount)

item_unwrapped in BunqModel._from_json_list needs to be deserialized to NotificationFilterUrl (wrapper) when cls is NotificationFilterUrlUser or NotificationFilterUrlMonetaryAccount

# bunq/sdk/model/core/bunq_model.py:56
@classmethod
    def _unwrap_response_single(cls,
                                obj: Dict,
                                wrapper: str = None)
...

# bunq/sdk/model/core/bunq_model.py:60
         if wrapper is not None:
            return obj[cls._FIELD_RESPONSE][cls._INDEX_FIRST][wrapper]
            from bunq.sdk.model.generated.endpoint import NotificationFilterUrlUser, NotificationFilterUrlMonetaryAccount
            if cls == NotificationFilterUrlUser or cls == NotificationFilterUrlMonetaryAccount:
                return obj[cls._FIELD_RESPONSE][cls._INDEX_FIRST]['NotificationFilterUrl']
            else:
                return obj[cls._FIELD_RESPONSE][cls._INDEX_FIRST][wrapper]

# bunq/sdk/model/core/bunq_model.py:91
@classmethod
    def _from_json_list(cls,
                        response_raw: BunqResponseRaw,
                        wrapper: str = None)
...
# bunq/sdk/model/core/bunq_model.py:102
      for item in array:
         item_unwrapped = item if wrapper is None else item[wrapper]

         # item_unwrapped needs to be deserialized to NotificationFilterUrl (wrapper) 
            #   when cls is NotificationFilterUrlUser or NotificationFilterUrlMonetaryAccount
            if wrapper=='NotificationFilterUrl':
                from bunq.sdk.model.generated.endpoint import NotificationFilterUrlUser, NotificationFilterUrlMonetaryAccount
                from bunq.sdk.model.generated.object_ import NotificationFilterUrl
                if cls == NotificationFilterUrlUser or cls == NotificationFilterUrlMonetaryAccount:
                    cls_orig = cls
                    cls = NotificationFilterUrl
                    #print(f'NotificationFilterUrlUser deserialization, changing cls from: {cls_orig} to: {cls}')
                    #TODO: Test deserialize for NotificationFilterUrlUser and NotificationFilterUrlMonetaryAccount

Please see: https://github.com/bunq/sdk_python/pull/157/commits/e4fecd8454f235ad8530d2c97d132a28dc873772

rikvermeer avatar Apr 25 '21 20:04 rikvermeer