|
20 | 20 | Unpack,
|
21 | 21 | )
|
22 | 22 | import uuid
|
23 |
| -from urllib.parse import urlsplit, urlunsplit |
| 23 | +from urllib.parse import urlsplit, urlunsplit, parse_qs |
24 | 24 |
|
25 | 25 | # breaking circular dependency
|
26 | 26 | import stripe # noqa: IMP101
|
@@ -556,6 +556,35 @@ def _args_for_request_with_retries(
|
556 | 556 | url,
|
557 | 557 | )
|
558 | 558 |
|
| 559 | + params = params or {} |
| 560 | + if params and (method == "get" or method == "delete"): |
| 561 | + # if we're sending params in the querystring, then we have to make sure we're not |
| 562 | + # duplicating anything we got back from the server already (like in a list iterator) |
| 563 | + # so, we parse the querystring the server sends back so we can merge with what we (or the user) are trying to send |
| 564 | + existing_params = {} |
| 565 | + for k, v in parse_qs(urlsplit(url).query).items(): |
| 566 | + # note: server sends back "expand[]" but users supply "expand", so we strip the brackets from the key name |
| 567 | + if k.endswith("[]"): |
| 568 | + existing_params[k[:-2]] = v |
| 569 | + else: |
| 570 | + # all querystrings are pulled out as lists. |
| 571 | + # We want to keep the querystrings that actually are lists, but flatten the ones that are single values |
| 572 | + existing_params[k] = v[0] if len(v) == 1 else v |
| 573 | + |
| 574 | + # if a user is expanding something that wasn't expanded before, add (and deduplicate) it |
| 575 | + # this could theoretically work for other lists that we want to merge too, but that doesn't seem to be a use case |
| 576 | + # it never would have worked before, so I think we can start with `expand` and go from there |
| 577 | + if "expand" in existing_params and "expand" in params: |
| 578 | + params["expand"] = list( # type:ignore - this is a dict |
| 579 | + set([*existing_params["expand"], *params["expand"]]) |
| 580 | + ) |
| 581 | + |
| 582 | + params = { |
| 583 | + **existing_params, |
| 584 | + # user_supplied params take precedence over server params |
| 585 | + **params, |
| 586 | + } |
| 587 | + |
559 | 588 | encoded_params = urlencode(list(_api_encode(params or {}, api_mode)))
|
560 | 589 |
|
561 | 590 | # Don't use strict form encoding by changing the square bracket control
|
@@ -586,13 +615,13 @@ def _args_for_request_with_retries(
|
586 | 615 |
|
587 | 616 | if method == "get" or method == "delete":
|
588 | 617 | if params:
|
589 |
| - query = encoded_params |
590 |
| - scheme, netloc, path, base_query, fragment = urlsplit(abs_url) |
| 618 | + # if we're sending query params, we've already merged the incoming ones with the server's "url" |
| 619 | + # so we can overwrite the whole thing |
| 620 | + scheme, netloc, path, _, fragment = urlsplit(abs_url) |
591 | 621 |
|
592 |
| - if base_query: |
593 |
| - query = "%s&%s" % (base_query, query) |
594 |
| - |
595 |
| - abs_url = urlunsplit((scheme, netloc, path, query, fragment)) |
| 622 | + abs_url = urlunsplit( |
| 623 | + (scheme, netloc, path, encoded_params, fragment) |
| 624 | + ) |
596 | 625 | post_data = None
|
597 | 626 | elif method == "post":
|
598 | 627 | if (
|
|
0 commit comments