growsurf-python 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. growsurf/_base_client.py +29 -10
  2. growsurf/_version.py +1 -1
  3. growsurf/resources/campaign/campaign.py +848 -20
  4. growsurf/resources/campaign/design.py +26 -24
  5. growsurf/resources/campaign/options.py +14 -14
  6. growsurf/resources/campaign/participant.py +309 -28
  7. growsurf/resources/campaign/rewards.py +16 -12
  8. growsurf/types/__init__.py +22 -0
  9. growsurf/types/affiliate_application.py +67 -0
  10. growsurf/types/affiliate_application_answer.py +24 -0
  11. growsurf/types/affiliate_application_list_response.py +22 -0
  12. growsurf/types/affiliate_invite.py +45 -0
  13. growsurf/types/affiliate_invite_list_response.py +22 -0
  14. growsurf/types/campaign/__init__.py +9 -0
  15. growsurf/types/campaign/campaign.py +14 -4
  16. growsurf/types/campaign/campaign_design.py +12 -5
  17. growsurf/types/campaign/campaign_options.py +5 -5
  18. growsurf/types/campaign/participant.py +27 -4
  19. growsurf/types/campaign/participant_add_params.py +8 -0
  20. growsurf/types/campaign/participant_analytics_params.py +11 -3
  21. growsurf/types/campaign/participant_analytics_response.py +19 -6
  22. growsurf/types/campaign/participant_get_payout_destination_response.py +50 -0
  23. growsurf/types/campaign/participant_list_payouts_params.py +1 -1
  24. growsurf/types/campaign/participant_request_payout_destination_confirmation_params.py +14 -0
  25. growsurf/types/campaign/participant_request_payout_destination_confirmation_response.py +24 -0
  26. growsurf/types/campaign/participant_update_params.py +9 -3
  27. growsurf/types/campaign/referral_source.py +1 -1
  28. growsurf/types/campaign/reward.py +11 -3
  29. growsurf/types/campaign/reward_create_params.py +1 -3
  30. growsurf/types/campaign/reward_tax_valuation.py +14 -5
  31. growsurf/types/campaign/reward_update_params.py +1 -3
  32. growsurf/types/campaign_create_affiliate_invite_params.py +20 -0
  33. growsurf/types/campaign_create_mobile_participant_token_params.py +8 -0
  34. growsurf/types/campaign_create_params.py +1 -1
  35. growsurf/types/campaign_list_affiliate_applications_params.py +18 -0
  36. growsurf/types/campaign_list_affiliate_invites_params.py +18 -0
  37. growsurf/types/campaign_list_payouts_params.py +1 -1
  38. growsurf/types/campaign_retrieve_analytics_params.py +9 -7
  39. growsurf/types/campaign_retrieve_analytics_response.py +29 -3
  40. growsurf/types/campaign_review_affiliate_application_params.py +53 -0
  41. growsurf/types/email_analytics.py +36 -0
  42. growsurf/types/participant_payout_list.py +3 -1
  43. {growsurf_python-1.2.1.dist-info → growsurf_python-1.3.0.dist-info}/METADATA +5 -6
  44. {growsurf_python-1.2.1.dist-info → growsurf_python-1.3.0.dist-info}/RECORD +46 -33
  45. {growsurf_python-1.2.1.dist-info → growsurf_python-1.3.0.dist-info}/WHEEL +0 -0
  46. {growsurf_python-1.2.1.dist-info → growsurf_python-1.3.0.dist-info}/licenses/LICENSE +0 -0
growsurf/_base_client.py CHANGED
@@ -789,6 +789,15 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
789
789
  timeout = sleep_seconds * jitter
790
790
  return timeout if timeout >= 0 else 0
791
791
 
792
+ def _request_is_retry_safe(self, options: FinalRequestOptions) -> bool:
793
+ """Return whether replaying the request cannot duplicate a customer-visible mutation."""
794
+ method = options.method.lower()
795
+ url = URL(str(options.url))
796
+ path = url.path.rstrip("/")
797
+ return method in {"get", "head"} or (
798
+ method == "post" and url.is_relative_url and path == "/api-key/rotate"
799
+ )
800
+
792
801
  def _should_retry(self, response: httpx.Response) -> bool:
793
802
  # Note: this is not a standard header
794
803
  should_retry_header = response.headers.get("x-should-retry")
@@ -994,9 +1003,10 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
994
1003
  # options are mutated later & we then retry, the retries are
995
1004
  # given the original options
996
1005
  input_options = model_copy(options)
997
- if input_options.idempotency_key is None and input_options.method.lower() != "get":
1006
+ if input_options.idempotency_key is None and self._request_is_retry_safe(input_options):
998
1007
  # ensure the idempotency key is reused between requests
999
- input_options.idempotency_key = self._idempotency_key()
1008
+ if input_options.method.lower() == "post":
1009
+ input_options.idempotency_key = self._idempotency_key()
1000
1010
 
1001
1011
  response: httpx.Response | None = None
1002
1012
  max_retries = input_options.get_max_retries(self.max_retries)
@@ -1030,7 +1040,7 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
1030
1040
  except httpx.TimeoutException as err:
1031
1041
  log.debug("Encountered httpx.TimeoutException", exc_info=True)
1032
1042
 
1033
- if remaining_retries > 0:
1043
+ if remaining_retries > 0 and self._request_is_retry_safe(input_options):
1034
1044
  self._sleep_for_retry(
1035
1045
  retries_taken=retries_taken,
1036
1046
  max_retries=max_retries,
@@ -1044,7 +1054,7 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
1044
1054
  except Exception as err:
1045
1055
  log.debug("Encountered Exception", exc_info=True)
1046
1056
 
1047
- if remaining_retries > 0:
1057
+ if remaining_retries > 0 and self._request_is_retry_safe(input_options):
1048
1058
  self._sleep_for_retry(
1049
1059
  retries_taken=retries_taken,
1050
1060
  max_retries=max_retries,
@@ -1070,7 +1080,11 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
1070
1080
  except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1071
1081
  log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1072
1082
 
1073
- if remaining_retries > 0 and self._should_retry(err.response):
1083
+ if (
1084
+ remaining_retries > 0
1085
+ and self._request_is_retry_safe(input_options)
1086
+ and self._should_retry(err.response)
1087
+ ):
1074
1088
  err.response.close()
1075
1089
  self._sleep_for_retry(
1076
1090
  retries_taken=retries_taken,
@@ -1579,9 +1593,10 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1579
1593
  # options are mutated later & we then retry, the retries are
1580
1594
  # given the original options
1581
1595
  input_options = model_copy(options)
1582
- if input_options.idempotency_key is None and input_options.method.lower() != "get":
1596
+ if input_options.idempotency_key is None and self._request_is_retry_safe(input_options):
1583
1597
  # ensure the idempotency key is reused between requests
1584
- input_options.idempotency_key = self._idempotency_key()
1598
+ if input_options.method.lower() == "post":
1599
+ input_options.idempotency_key = self._idempotency_key()
1585
1600
 
1586
1601
  response: httpx.Response | None = None
1587
1602
  max_retries = input_options.get_max_retries(self.max_retries)
@@ -1614,7 +1629,7 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1614
1629
  except httpx.TimeoutException as err:
1615
1630
  log.debug("Encountered httpx.TimeoutException", exc_info=True)
1616
1631
 
1617
- if remaining_retries > 0:
1632
+ if remaining_retries > 0 and self._request_is_retry_safe(input_options):
1618
1633
  await self._sleep_for_retry(
1619
1634
  retries_taken=retries_taken,
1620
1635
  max_retries=max_retries,
@@ -1628,7 +1643,7 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1628
1643
  except Exception as err:
1629
1644
  log.debug("Encountered Exception", exc_info=True)
1630
1645
 
1631
- if remaining_retries > 0:
1646
+ if remaining_retries > 0 and self._request_is_retry_safe(input_options):
1632
1647
  await self._sleep_for_retry(
1633
1648
  retries_taken=retries_taken,
1634
1649
  max_retries=max_retries,
@@ -1654,7 +1669,11 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1654
1669
  except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1655
1670
  log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1656
1671
 
1657
- if remaining_retries > 0 and self._should_retry(err.response):
1672
+ if (
1673
+ remaining_retries > 0
1674
+ and self._request_is_retry_safe(input_options)
1675
+ and self._should_retry(err.response)
1676
+ ):
1658
1677
  await err.response.aclose()
1659
1678
  await self._sleep_for_retry(
1660
1679
  retries_taken=retries_taken,
growsurf/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "growsurf"
4
- __version__ = "1.2.1" # x-release-please-version
4
+ __version__ = "1.3.0" # x-release-please-version