Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions meraki/rest_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,85 @@
from datetime import datetime

import requests
from requests.utils import to_key_val_list
from requests.compat import basestring, urlencode

from meraki.__init__ import __version__
from meraki.common import *
from meraki.config import *


def encode_params(_, data):
"""Encode parameters in a piece of data.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to request more documentation here explaining why we're doing this. You can keep it high-level, but it should make sense to both folks familiar with the requests library, and also to folks who are not Python experts.

You might could reference issues in the GitHub tracker as well.


Will successfully encode parameters when passed as a dict or a list of
2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
if parameters are supplied as a dict.

MERAKI OVERRIDE:
By default, when parameters are supplied as a dict, only the object keys
are encoded.

Ex. {"param": [{"key_1":"value_1"}, {"key_2":"value_2"}]} => ?param[]=key_1&param[]=key_2

Now when parameters are supplied as a dict, dict keys will be appended to
parameter names. This adds support for the "array of objects" query parameter type.

Ex. {"param": [{"key_1":"value_1"}, {"key_2":"value_2"}]} => ?param[]key_1=value_1&param[]key_2=value_2
"""
if isinstance(data, (str, bytes)):
return data
elif hasattr(data, "read"):
return data
elif hasattr(data, "__iter__"):
result = []
# Get each query parameter key value pair
for k, vs in to_key_val_list(data):
"""
Turn value into list/iterable if it is not already.
Ex. {"param": "value"} => {"param": ["value"]}
"""
if isinstance(vs, basestring) or not hasattr(vs, "__iter__"):
vs = [vs]
for v in vs:
# List params
if v is not None and not isinstance(v, dict):
"""
Add a query parameter key-value pair for each value to the list of results.
Ex. {"param": ["value_1", "value_2"]} => [(param, value_1), (param, value_2)]
"""
result.append(
(
k.encode("utf-8") if isinstance(k, str) else k,
v.encode("utf-8") if isinstance(v, str) else v,
)
)
# Dict params
else:
"""
Append each dict key to the parameter name.
Add a query parameter key-value pair for each value to the list of results.
{"param": [{"key_1": "value_1"}, {"key_2": "value_2"}]} => [(param + key_1, value1), (param + key_2, value2)]
"""
for k_1, v_1 in v.items():
result.append(
(
(k + k_1).encode("utf-8") if isinstance(k, str) else k_1,
(v + v_1).encode("utf-8") if isinstance(v, str) else v_1,
)
)
# Return URL encoded string
return urlencode(result, doseq=True)
else:
return data


# Monkey patch the _encode_params from the requests library with the encode_params function above
requests.models.RequestEncodingMixin._encode_params = encode_params


def user_agent_extended(be_geo_id, caller):
# Generate extended portion of the User-Agent
# Generate the extended portion of the User-Agent
user_agent = dict()

if caller:
Expand Down Expand Up @@ -136,7 +207,7 @@ def request(self, metadata, method, url, **kwargs):
else:
abs_url = self._base_url + url

# Set maximum number of retries
# Set the maximum number of retries
retries = self._maximum_retries

# Option to simulate non-safe API calls without actually sending them
Expand Down Expand Up @@ -236,6 +307,7 @@ def request(self, metadata, method, url, **kwargs):

return response


def handle_4xx_errors(self, metadata, operation, reason, response, retries, status, tag):
try:
message = response.json()
Expand Down