From cf5ed2dd85cccd4c3c1d0e9d302ce4e3382ce919 Mon Sep 17 00:00:00 2001 From: iancart Date: Mon, 21 Apr 2025 14:03:45 -0400 Subject: [PATCH 1/5] Added support for "array of objects" query parameter type. --- meraki/rest_session.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/meraki/rest_session.py b/meraki/rest_session.py index 697ff4e..da31987 100644 --- a/meraki/rest_session.py +++ b/meraki/rest_session.py @@ -6,12 +6,52 @@ 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. + + 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. + """ + if isinstance(data, (str, bytes)): + return data + elif hasattr(data, "read"): + return data + elif hasattr(data, "__iter__"): + result = [] + for k, vs in to_key_val_list(data): + if isinstance(vs, basestring) or not hasattr(vs, "__iter__"): + vs = [vs] + for v in vs: + if v is not None and not isinstance(v, dict): + result.append( + ( + k.encode("utf-8") if isinstance(k, str) else k, + v.encode("utf-8") if isinstance(v, str) else v, + ) + ) + else: + 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 urlencode(result, doseq=True) + else: + return data + +requests.models.RequestEncodingMixin._encode_params = encode_params + def user_agent_extended(be_geo_id, caller): # Generate extended portion of the User-Agent user_agent = dict() From b2532eac08366dc92a02e91de69bab2b076dc9c9 Mon Sep 17 00:00:00 2001 From: Ian Carter Date: Mon, 21 Apr 2025 15:59:06 -0400 Subject: [PATCH 2/5] Added support for "array of objects" query parameter type. Added documentation for the new encode_params function --- meraki/rest_session.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/meraki/rest_session.py b/meraki/rest_session.py index da31987..dc322a2 100644 --- a/meraki/rest_session.py +++ b/meraki/rest_session.py @@ -20,6 +20,17 @@ def encode_params(_, data): 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¶m[]=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¶m[]key_2=value_2 """ if isinstance(data, (str, bytes)): return data @@ -27,18 +38,33 @@ def encode_params(_, data): 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 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 + {"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( ( From 8932f5e7c5c1be834ed1d7b888b2dcdcc33d518e Mon Sep 17 00:00:00 2001 From: Ian Carter Date: Mon, 21 Apr 2025 16:02:48 -0400 Subject: [PATCH 3/5] Added support for "array of objects" query parameter type. Added documentation for the new encode_params function --- meraki/rest_session.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/meraki/rest_session.py b/meraki/rest_session.py index dc322a2..758cd94 100644 --- a/meraki/rest_session.py +++ b/meraki/rest_session.py @@ -50,7 +50,7 @@ def encode_params(_, data): # List params if v is not None and not isinstance(v, dict): """ - Add a query parameter pair for each value to the list of results. + 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( @@ -62,7 +62,8 @@ def encode_params(_, data): # Dict params else: """ - Append each dict key to the parameter name + 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(): From 1073d04fcb513f42d360aa636f9945c724034447 Mon Sep 17 00:00:00 2001 From: Ian Carter Date: Mon, 21 Apr 2025 16:05:55 -0400 Subject: [PATCH 4/5] Added support for "array of objects" query parameter type. Added documentation for the new encode_params function --- meraki/rest_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meraki/rest_session.py b/meraki/rest_session.py index 758cd94..872fb8c 100644 --- a/meraki/rest_session.py +++ b/meraki/rest_session.py @@ -73,10 +73,11 @@ def encode_params(_, data): (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): From 91fb93a1a64d40c0b3f06fe745485205af263dc4 Mon Sep 17 00:00:00 2001 From: "John M. Kuchta" Date: Mon, 21 Apr 2025 14:47:43 -0700 Subject: [PATCH 5/5] Minor fixes * Docstring updates * Added missing return statement --- meraki/rest_session.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/meraki/rest_session.py b/meraki/rest_session.py index 872fb8c..5f0173a 100644 --- a/meraki/rest_session.py +++ b/meraki/rest_session.py @@ -77,11 +77,14 @@ def encode_params(_, data): 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: @@ -204,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 @@ -302,6 +305,9 @@ def request(self, metadata, method, url, **kwargs): else: retries = self.handle_4xx_errors(metadata, operation, reason, response, retries, status, tag) + return response + + def handle_4xx_errors(self, metadata, operation, reason, response, retries, status, tag): try: message = response.json()