Repository: abdullahselek/HerePy

Issue:
In `RoutingAPI.route_v8`, some parameters are ignored.
When executing `route_v8`, `avoid`, `exclude` and `truck` parameters are ignored. 

1. Only first key from argument dict is taken from those arguments [[1](https://github.com/abdullahselek/HerePy/blob/a8f1b580482fbd88d9e6660cf17cc4c63407f279/herepy/routing_api.py#L460)], [[2](https://github.com/abdullahselek/HerePy/blob/a8f1b580482fbd88d9e6660cf17cc4c63407f279/herepy/routing_api.py#L466)], [[3](https://github.com/abdullahselek/HerePy/blob/a8f1b580482fbd88d9e6660cf17cc4c63407f279/herepy/routing_api.py#L480)]
2. Arguments are formatted incorrectly. This results in being silently ignored by the API. Calling `route_v8(transport_mode, origin, dest, truck={"length": ["15000"]})` creates url parameter `...&truck={'height': '15000'}` while it should be `...&truck[height]=15000` 

Discussion:
codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/abdullahselek/HerePy/pull/84?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 92.98%. Comparing base [(`a8f1b58`)](https://app.codecov.io/gh/abdullahselek/HerePy/commit/a8f1b580482fbd88d9e6660cf17cc4c63407f279?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek) to head [(`eaeca38`)](https://app.codecov.io/gh/abdullahselek/HerePy/commit/eaeca38a2af86172af0ec43ff8149b1174bf6943?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek).

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master      #84   +/-   ##
=======================================
  Coverage   92.98%   92.98%           
=======================================
  Files          26       26           
  Lines        1754     1754           
=======================================
  Hits         1631     1631           
  Misses        123      123           
```

| [Flag](https://app.codecov.io/gh/abdullahselek/HerePy/pull/84/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/abdullahselek/HerePy/pull/84/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek) | `92.98% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/abdullahselek/HerePy/pull/84?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abdullah+Selek).


Patch:
diff --git a/herepy/routing_api.py b/herepy/routing_api.py
index c2e2df2..4068c3d 100644
--- a/herepy/routing_api.py
+++ b/herepy/routing_api.py
@@ -457,17 +457,9 @@ class RoutingApi(HEREApi):
         if alternatives:
             data["alternatives"] = alternatives
         if avoid:
-            key = list(avoid.keys())[0]
-            values = list(avoid.values())[0]
-            data["avoid"] = {
-                key: ",".join(values),
-            }
+            data["avoid"] = {key: ",".join(vals) for key, vals in avoid.items()}
         if exclude:
-            key = list(exclude.keys())[0]
-            values = list(exclude.values())[0]
-            data["exclude"] = {
-                key: ",".join(values),
-            }
+            data["exclude"] = {key: ",".join(vals) for key, vals in exclude.items()}
         if units:
             data["units"] = units.__str__()
         if lang:
@@ -477,11 +469,7 @@ class RoutingApi(HEREApi):
         if span_fields:
             data["spans"] = ",".join([field.__str__() for field in span_fields])
         if truck:
-            key = list(truck.keys())[0]
-            values = list(truck.values())[0]
-            data["truck"] = {
-                key: ",".join(values),
-            }
+            data["truck"] = {key: ",".join(vals) for key, vals in truck.items()}
         if scooter:
             data["scooter"] = scooter
 
diff --git a/herepy/utils.py b/herepy/utils.py
index ac63dfc..5b384b2 100644
--- a/herepy/utils.py
+++ b/herepy/utils.py
@@ -18,16 +18,23 @@ class Utils(object):
           parameters (dict):
             dictionary of query parameters to be converted.
         Returns:
-          A URL-encoded string in "key=value&key=value" form
+          A URL-encoded string in "key=value&key=value" form. If the parameter value is
+            a dict, the encoded string is converted to "main_key[sub_key]=sub_value".
         """
         if parameters is None:
             return None
         if not isinstance(parameters, dict):
             raise HEREError("`parameters` must be a dict.")
         else:
-            return urlencode(
-                dict((k, v) for k, v in parameters.items() if v is not None)
-            )
+            parameters = dict((k, v) for k, v in parameters.items() if v is not None)
+            result = dict()
+            for key, value in parameters.items():
+                if isinstance(value, dict):
+                    for sub_key, sub_value in value.items():
+                        result[f"{key}[{sub_key}]"] = sub_value
+                else:
+                    result[key] = value
+            return urlencode(result)
 
     @staticmethod
     def build_url(url, extra_params=None):


Test patch:
diff --git a/tests/test_routing_api.py b/tests/test_routing_api.py
index 89b2169..76b1e5f 100644
--- a/tests/test_routing_api.py
+++ b/tests/test_routing_api.py
@@ -1215,3 +1215,22 @@ class RoutingApiTest(unittest.TestCase):
         )
         self.assertTrue(response)
         self.assertIsInstance(response, herepy.RoutingResponseV8)
+
+    @responses.activate
+    def test_route_v8_url_parameters_multiple(self):
+        responses.add(
+            responses.GET,
+            "https://router.hereapi.com/v8/routes",
+            "{}",
+            status=200,
+            match=[
+                responses.matchers.query_param_matcher(
+                    {"truck[height]": "15000", "truck[width]": "3000"}, strict_match=False
+                )
+            ],
+        )
+        self._api.route_v8(transport_mode=herepy.RoutingTransportMode.truck,
+            origin=[41.9798, -87.8801],
+            destination=[41.9043, -87.9216],
+            truck={"height": ["15000"], "width": ["3000"]}
+        )
diff --git a/tests/test_utils.py b/tests/test_utils.py
index c9684d5..8afc183 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -29,3 +29,11 @@ class UtilsTest(unittest.TestCase):
             "https://geocoder.cit.api.here.com/6.2/geocode.json", data
         )
         self.assertTrue(url)
+
+    def test_build_url_sub_data(self):
+        data = {
+            "key1": "val",
+            "key2": {"sub_key": "sub_val", "sub_key2": "sub_val2"},
+        }
+        url = Utils.build_url("https://router.hereapi.com/v8/routes", data)
+        self.assertEqual(url, "https://router.hereapi.com/v8/routes?key1=val&key2%5Bsub_key%5D=sub_val&key2%5Bsub_key2%5D=sub_val2")