meraki / dashboard-api-python

Official Dashboard API library (SDK) for Python
MIT License
289 stars 151 forks source link

Claim and remove network device methods raise ContentTypeError #177

Closed ollipa closed 2 years ago

ollipa commented 2 years ago

When claiming or removing a device from a network, a ContentTypeError is raised. The operation itself is successful.

Library version: 1.18.1 Python version: 3.9

Code to reproduce:

from meraki.aio import AsyncDashboardAPI

token = "some_token"
network_id = "some_network_id"
serial = "some_serial"

client = AsyncDashboardAPI(token, output_log=False)
await client.networks.claimNetworkDevices(network_id, [serial])
# ContentTypeError: 0, message='Attempt to decode JSON with unexpected mimetype: text/html; charset=utf-8', url=URL('https://api.meraki.com/api/v1/networks/***/devices/claim')
await client.networks.removeNetworkDevices(network_id, serial)
# ContentTypeError: 0, message='Attempt to decode JSON with unexpected mimetype: ', url=URL('https://api.meraki.com/api/v1/networks/***/devices/remove')

Stack trace:

----> 1 await client.networks.claimNetworkDevices(network_id, [serial])

File /python3.9/site-packages/meraki/aio/rest_session.py:470, in AsyncRestSession.post(self, metadata, url, json)
    468 metadata["json"] = json
    469 async with await self.request(metadata, "POST", url, json=json) as response:
--> 470     return await response.json()

File /python3.9/site-packages/aiohttp/client_reqrep.py:1103, in ClientResponse.json(self, encoding, loads, content_type)
   1101     ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()
   1102     if not _is_expected_content_type(ctype, content_type):
-> 1103         raise ContentTypeError(
   1104             self.request_info,
   1105             self.history,
   1106             message=(
   1107                 "Attempt to decode JSON with " "unexpected mimetype: %s" % ctype
   1108             ),
   1109             headers=self.headers,
   1110         )
   1112 stripped = self._body.strip()  # type: ignore[union-attr]
   1113 if not stripped:
TKIPisalegacycipher commented 2 years ago

Is this happening on 1.18.0?

ollipa commented 2 years ago

I just tested this with 1.18.1 and the bug is still present.

TKIPisalegacycipher commented 2 years ago

So there seem to be two issues:

  1. Returning the wrong mime type. Unsure why this is happening but will investigate internally.
  2. Library requiring application/json mime type to work. That seems unnecessary if the text returned is still json formatted. Thoughts @coreGreenberet ?
coreGreenberet commented 2 years ago

1) that's not the only endpoint: See https://community.meraki.com/t5/Developers-APIs/Bug-in-AIO-SDK-for-generateDeviceCameraSnapshot/m-p/139036 there is now a case open for a very long time and nothing is happening =( 2) I could implement a workaround in the library for that, but I think that should be done on the server side. Since this would probably a small change on the server, I thought that it would have been fixed long time ago =(

TKIPisalegacycipher commented 2 years ago

I'll keep an eye on this. The non-aio version of the library doesn't have this problem, from what I can see.

coreGreenberet commented 2 years ago

The non-aio version doesn't check for the content-type, thats why it doesn't have that problem. I've removed the content-type check for now in PR #181. This should do the trick until the cloud is fixed

TKIPisalegacycipher commented 2 years ago

Hi team, I'm pretty sure the fix is deployed. I tested on my end and couldn't reproduce. Anyone else care to try? @ollipa ?

ollipa commented 2 years ago

@TKIPisalegacycipher, claim endpoint seems to be fixed now but I can still reproduce the issue with removeNetworkDevices call.

> await client.networks.removeNetworkDevices(network_id, serial)
ContentTypeError: 0, message='Attempt to decode JSON with unexpected mimetype: ', url=URL('https://api.meraki.com/api/v1/networks/***/devices/remove')

# No ContentTypeError if the device has already been removed or does not exist:
> await client.networks.removeNetworkDevices(network_id, serial)
AsyncAPIError: networks, removeNetworkDevices - 404 Not Found, None
coreGreenberet commented 2 years ago

@TKIPisalegacycipher .../camera/generateSnapshot is also not fixed

grafik

TKIPisalegacycipher commented 2 years ago

Thanks for the follow-up. removeNetworkDevices returns a 204 No Content -- why is a content-type expected?

A fix for the snapshot endpoint on the other hand is in progress.

TKIPisalegacycipher commented 2 years ago

Quick update: the snapshot issue is one with the endpoint, not with the library. Meraki plan to fix the snapshot endpoint, since it returns content.

However, 204 No Content responses are not expected to return a content-type, so no application should require one of the endpoint. If there are any other concerns, please reply here.

coreGreenberet commented 2 years ago

I will limit the response.json() to a 200 ok like in the non async version From the ClientResponse.json() Documentation

Returns: BODY as JSON data parsed by loads parameter or None if BODY is empty or contains white-spaces only.

The issue is still the wrong mime-type here.

Just out of curiosity: removeNetworkDevices is currently using POST. All other endpoints which are removing something are using DELETE. Delete methods will never return something.

TKIPisalegacycipher commented 2 years ago

Just like claim endpoints respond with a list of what was claimed, perhaps removeNetworkDevices should return a body of what was removed. These devices are not really being 'deleted' so I'm not sure a DELETE action is that intuitive. On top of that, DELETE requests don't typically have bodies--we want to support removing many serials at once, and if you want to do that, a DELETE action would require sending one delete request per serial. That seems needlessly inefficient.

TKIPisalegacycipher commented 2 years ago

I think this is solved. Please raise a new issue referencing this one if you are still having similar issues.