pipedream 2.1.20__tar.gz → 2.2.0__tar.gz

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 (243) hide show
  1. {pipedream-2.1.20 → pipedream-2.2.0}/PKG-INFO +4 -1
  2. {pipedream-2.1.20 → pipedream-2.2.0}/pyproject.toml +4 -1
  3. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/client_wrapper.py +2 -2
  4. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/oauth_tokens/client.py +38 -8
  5. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/oauth_tokens/raw_client.py +30 -4
  6. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/pipedream.py +103 -0
  7. pipedream-2.2.0/src/pipedream/private_key_jwt.py +204 -0
  8. {pipedream-2.1.20 → pipedream-2.2.0}/LICENSE +0 -0
  9. {pipedream-2.1.20 → pipedream-2.2.0}/README.md +0 -0
  10. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/__init__.py +0 -0
  11. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/_default_clients.py +0 -0
  12. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/accounts/__init__.py +0 -0
  13. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/accounts/client.py +0 -0
  14. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/accounts/raw_client.py +0 -0
  15. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/actions/__init__.py +0 -0
  16. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/actions/client.py +0 -0
  17. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/actions/raw_client.py +0 -0
  18. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/actions/types/__init__.py +0 -0
  19. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/actions/types/actions_list_request_registry.py +0 -0
  20. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/app_categories/__init__.py +0 -0
  21. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/app_categories/client.py +0 -0
  22. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/app_categories/raw_client.py +0 -0
  23. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/app_overrides/__init__.py +0 -0
  24. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/app_overrides/client.py +0 -0
  25. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/app_overrides/raw_client.py +0 -0
  26. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/apps/__init__.py +0 -0
  27. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/apps/client.py +0 -0
  28. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/apps/raw_client.py +0 -0
  29. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/apps/types/__init__.py +0 -0
  30. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/apps/types/apps_list_request_sort_direction.py +0 -0
  31. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/apps/types/apps_list_request_sort_key.py +0 -0
  32. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/client.py +0 -0
  33. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/components/__init__.py +0 -0
  34. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/components/client.py +0 -0
  35. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/components/raw_client.py +0 -0
  36. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/components/types/__init__.py +0 -0
  37. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/components/types/components_list_request_registry.py +0 -0
  38. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/__init__.py +0 -0
  39. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/api_error.py +0 -0
  40. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/datetime_utils.py +0 -0
  41. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/file.py +0 -0
  42. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/force_multipart.py +0 -0
  43. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/http_client.py +0 -0
  44. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/http_response.py +0 -0
  45. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/http_sse/__init__.py +0 -0
  46. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/http_sse/_api.py +0 -0
  47. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/http_sse/_decoders.py +0 -0
  48. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/http_sse/_exceptions.py +0 -0
  49. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/http_sse/_models.py +0 -0
  50. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/jsonable_encoder.py +0 -0
  51. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/logging.py +0 -0
  52. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/oauth_token_provider.py +0 -0
  53. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/pagination.py +0 -0
  54. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/parse_error.py +0 -0
  55. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/pydantic_utilities.py +0 -0
  56. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/query_encoder.py +0 -0
  57. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/remove_none_from_dict.py +0 -0
  58. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/request_options.py +0 -0
  59. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/core/serialization.py +0 -0
  60. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/deployed_triggers/__init__.py +0 -0
  61. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/deployed_triggers/client.py +0 -0
  62. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/deployed_triggers/raw_client.py +0 -0
  63. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/environment.py +0 -0
  64. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/errors/__init__.py +0 -0
  65. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/errors/bad_request_error.py +0 -0
  66. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/errors/not_found_error.py +0 -0
  67. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/errors/too_many_requests_error.py +0 -0
  68. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/file_stash/__init__.py +0 -0
  69. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/file_stash/client.py +0 -0
  70. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/file_stash/raw_client.py +0 -0
  71. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/oauth_apps/__init__.py +0 -0
  72. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/oauth_apps/client.py +0 -0
  73. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/oauth_apps/raw_client.py +0 -0
  74. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/oauth_tokens/__init__.py +0 -0
  75. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/project_environment/__init__.py +0 -0
  76. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/project_environment/client.py +0 -0
  77. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/project_environment/raw_client.py +0 -0
  78. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/projects/__init__.py +0 -0
  79. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/projects/client.py +0 -0
  80. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/projects/raw_client.py +0 -0
  81. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/proxy/__init__.py +0 -0
  82. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/proxy/client.py +0 -0
  83. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/proxy/raw_client.py +0 -0
  84. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/py.typed +0 -0
  85. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/tokens/__init__.py +0 -0
  86. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/tokens/client.py +0 -0
  87. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/tokens/raw_client.py +0 -0
  88. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/triggers/__init__.py +0 -0
  89. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/triggers/client.py +0 -0
  90. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/triggers/raw_client.py +0 -0
  91. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/triggers/types/__init__.py +0 -0
  92. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/triggers/types/triggers_list_request_registry.py +0 -0
  93. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/__init__.py +0 -0
  94. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/account.py +0 -0
  95. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/account_credentials.py +0 -0
  96. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/account_id.py +0 -0
  97. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/app.py +0 -0
  98. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/app_auth_type.py +0 -0
  99. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/app_category.py +0 -0
  100. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/app_override.py +0 -0
  101. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/app_scope_profiles_item.py +0 -0
  102. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/app_scope_profiles_item_name.py +0 -0
  103. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/backend_client_opts.py +0 -0
  104. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/client_opts.py +0 -0
  105. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/component.py +0 -0
  106. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/component_stash.py +0 -0
  107. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/component_type.py +0 -0
  108. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop.py +0 -0
  109. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_airtable_base_id.py +0 -0
  110. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_airtable_field_id.py +0 -0
  111. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_airtable_table_id.py +0 -0
  112. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_airtable_view_id.py +0 -0
  113. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_alert.py +0 -0
  114. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_alert_type.py +0 -0
  115. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_any.py +0 -0
  116. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_any_options_item.py +0 -0
  117. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_app.py +0 -0
  118. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_apphook.py +0 -0
  119. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_base.py +0 -0
  120. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_base_type.py +0 -0
  121. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_boolean.py +0 -0
  122. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_boolean_options_item.py +0 -0
  123. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_data_store.py +0 -0
  124. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_db.py +0 -0
  125. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_dir.py +0 -0
  126. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_dir_access_mode.py +0 -0
  127. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_discord_channel.py +0 -0
  128. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_discord_channel_array.py +0 -0
  129. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_http.py +0 -0
  130. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_http_request.py +0 -0
  131. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_integer.py +0 -0
  132. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_integer_array.py +0 -0
  133. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_integer_array_options_item.py +0 -0
  134. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_integer_options_item.py +0 -0
  135. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_object.py +0 -0
  136. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_object_options_item.py +0 -0
  137. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_sql.py +0 -0
  138. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_sql_auth.py +0 -0
  139. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_sql_options_item.py +0 -0
  140. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_string.py +0 -0
  141. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_string_array.py +0 -0
  142. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_string_array_options_item.py +0 -0
  143. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_string_format.py +0 -0
  144. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_string_options_item.py +0 -0
  145. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_timer.py +0 -0
  146. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_timer_default.py +0 -0
  147. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_timer_option.py +0 -0
  148. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configurable_prop_timer_static.py +0 -0
  149. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configure_prop_options.py +0 -0
  150. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configure_prop_options_item.py +0 -0
  151. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configure_prop_opts.py +0 -0
  152. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configure_prop_response.py +0 -0
  153. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value.py +0 -0
  154. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_any.py +0 -0
  155. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_app.py +0 -0
  156. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_boolean.py +0 -0
  157. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_integer.py +0 -0
  158. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_object.py +0 -0
  159. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_sql.py +0 -0
  160. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_string.py +0 -0
  161. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_prop_value_string_array.py +0 -0
  162. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/configured_props.py +0 -0
  163. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/connect_token.py +0 -0
  164. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/connect_usage.py +0 -0
  165. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/connect_usage_response.py +0 -0
  166. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/create_o_auth_token_response.py +0 -0
  167. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/create_token_response.py +0 -0
  168. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/delete_trigger_opts.py +0 -0
  169. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/deploy_trigger_response.py +0 -0
  170. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/deployed_component.py +0 -0
  171. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/dynamic_props.py +0 -0
  172. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/emitted_event.py +0 -0
  173. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/emitter.py +0 -0
  174. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/emitter_type.py +0 -0
  175. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/error_response.py +0 -0
  176. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/external_user.py +0 -0
  177. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_accounts_response.py +0 -0
  178. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_app_category_response.py +0 -0
  179. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_app_response.py +0 -0
  180. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_apps_response.py +0 -0
  181. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_component_response.py +0 -0
  182. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_components_response.py +0 -0
  183. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_trigger_events_response.py +0 -0
  184. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_trigger_response.py +0 -0
  185. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_trigger_webhooks_response.py +0 -0
  186. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_trigger_workflows_response.py +0 -0
  187. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_triggers_response.py +0 -0
  188. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_users_response.py +0 -0
  189. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_webhook_response.py +0 -0
  190. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/get_webhook_with_signing_key_response.py +0 -0
  191. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_interface.py +0 -0
  192. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_request_auth.py +0 -0
  193. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_request_auth_type.py +0 -0
  194. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_request_body.py +0 -0
  195. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_request_body_mode.py +0 -0
  196. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_request_body_type.py +0 -0
  197. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_request_config.py +0 -0
  198. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/http_request_field.py +0 -0
  199. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/list_accounts_response.py +0 -0
  200. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/list_app_categories_response.py +0 -0
  201. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/list_app_overrides_response.py +0 -0
  202. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/list_apps_response.py +0 -0
  203. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/list_oauth_apps_response.py +0 -0
  204. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/list_projects_response.py +0 -0
  205. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/oauth_app.py +0 -0
  206. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/observation.py +0 -0
  207. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/observation_error.py +0 -0
  208. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/page_info.py +0 -0
  209. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/project.py +0 -0
  210. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/project_environment.py +0 -0
  211. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/project_info_response.py +0 -0
  212. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/project_info_response_app.py +0 -0
  213. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/prop_option.py +0 -0
  214. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/prop_option_nested.py +0 -0
  215. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/prop_option_value.py +0 -0
  216. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/proxy_response.py +0 -0
  217. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/proxy_response_binary.py +0 -0
  218. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/reload_props_opts.py +0 -0
  219. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/reload_props_response.py +0 -0
  220. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/run_action_opts_stash_id.py +0 -0
  221. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/run_action_response.py +0 -0
  222. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/set_webhook_response.py +0 -0
  223. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/start_connect_opts.py +0 -0
  224. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/stash_id.py +0 -0
  225. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/timer_cron.py +0 -0
  226. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/timer_interface.py +0 -0
  227. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/timer_interval.py +0 -0
  228. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/too_many_requests_error_body.py +0 -0
  229. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/tool_annotations.py +0 -0
  230. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/trigger_webhook.py +0 -0
  231. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/validate_token_response.py +0 -0
  232. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/webhook.py +0 -0
  233. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/webhook_with_optional_signing_key.py +0 -0
  234. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/types/webhook_with_signing_key.py +0 -0
  235. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/usage/__init__.py +0 -0
  236. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/usage/client.py +0 -0
  237. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/usage/raw_client.py +0 -0
  238. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/users/__init__.py +0 -0
  239. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/users/client.py +0 -0
  240. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/users/raw_client.py +0 -0
  241. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/version.py +0 -0
  242. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/workflows/__init__.py +0 -0
  243. {pipedream-2.1.20 → pipedream-2.2.0}/src/pipedream/workflows/client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pipedream
3
- Version: 2.1.20
3
+ Version: 2.2.0
4
4
  Summary:
5
5
  Requires-Python: >=3.10,<4.0
6
6
  Classifier: Intended Audience :: Developers
@@ -20,11 +20,14 @@ Classifier: Programming Language :: Python :: 3.15
20
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Typing :: Typed
22
22
  Provides-Extra: aiohttp
23
+ Provides-Extra: private-key
23
24
  Requires-Dist: aiohttp (>=3.13.4,<4) ; (python_version >= "3.9") and (extra == "aiohttp")
25
+ Requires-Dist: cryptography (>=3.4.0) ; extra == "private-key"
24
26
  Requires-Dist: httpx (>=0.21.2)
25
27
  Requires-Dist: httpx-aiohttp (==0.1.8) ; (python_version >= "3.9") and (extra == "aiohttp")
26
28
  Requires-Dist: pydantic (>=1.9.2)
27
29
  Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
30
+ Requires-Dist: pyjwt (>=2.8.0,<3) ; extra == "private-key"
28
31
  Requires-Dist: typing_extensions (>=4.0.0)
29
32
  Project-URL: Repository, https://github.com/PipedreamHQ/pipedream-sdk-python
30
33
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ dynamic = ["version"]
4
4
 
5
5
  [tool.poetry]
6
6
  name = "pipedream"
7
- version = "2.1.20"
7
+ version = "2.2.0"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  authors = []
@@ -38,10 +38,12 @@ Repository = 'https://github.com/PipedreamHQ/pipedream-sdk-python'
38
38
  [tool.poetry.dependencies]
39
39
  python = "^3.10"
40
40
  aiohttp = { version = ">=3.13.4,<4", optional = true, python = ">=3.9"}
41
+ cryptography = { version = ">=3.4.0", optional = true}
41
42
  httpx = ">=0.21.2"
42
43
  httpx-aiohttp = { version = "0.1.8", optional = true, python = ">=3.9"}
43
44
  pydantic = ">= 1.9.2"
44
45
  pydantic-core = ">=2.18.2,<3.0.0"
46
+ pyjwt = { version = ">=2.8.0,<3", optional = true}
45
47
  typing_extensions = ">= 4.0.0"
46
48
 
47
49
  [tool.poetry.group.dev.dependencies]
@@ -93,4 +95,5 @@ requires = ["poetry-core"]
93
95
  build-backend = "poetry.core.masonry.api"
94
96
 
95
97
  [tool.poetry.extras]
98
+ private-key=["pyjwt", "cryptography"]
96
99
  aiohttp=["aiohttp", "httpx-aiohttp"]
@@ -34,12 +34,12 @@ class BaseClientWrapper:
34
34
  import platform
35
35
 
36
36
  headers: typing.Dict[str, str] = {
37
- "User-Agent": "pipedream/2.1.20",
37
+ "User-Agent": "pipedream/2.2.0",
38
38
  "X-Fern-Language": "Python",
39
39
  "X-Fern-Runtime": f"python/{platform.python_version()}",
40
40
  "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}",
41
41
  "X-Fern-SDK-Name": "pipedream",
42
- "X-Fern-SDK-Version": "2.1.20",
42
+ "X-Fern-SDK-Version": "2.2.0",
43
43
  **(self.get_custom_headers() or {}),
44
44
  }
45
45
  if self._project_environment is not None:
@@ -30,7 +30,11 @@ class OauthTokensClient:
30
30
  self,
31
31
  *,
32
32
  client_id: str,
33
- client_secret: str,
33
+ client_secret: typing.Optional[str] = OMIT,
34
+ client_assertion_type: typing.Optional[
35
+ typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]
36
+ ] = OMIT,
37
+ client_assertion: typing.Optional[str] = OMIT,
34
38
  scope: typing.Optional[str] = OMIT,
35
39
  request_options: typing.Optional[RequestOptions] = None,
36
40
  ) -> CreateOAuthTokenResponse:
@@ -41,7 +45,14 @@ class OauthTokensClient:
41
45
  ----------
42
46
  client_id : str
43
47
 
44
- client_secret : str
48
+ client_secret : typing.Optional[str]
49
+ The client secret, for clients that authenticate with a client secret.
50
+
51
+ client_assertion_type : typing.Optional[typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]]
52
+ Required with `client_assertion`.
53
+
54
+ client_assertion : typing.Optional[str]
55
+ For clients that authenticate with a public key: a JWT signed with the client's private key (ES256 or RS256). Claims: `iss` and `sub` set to the client ID, `aud` set to `https://api.pipedream.com`, `exp` at most 1 hour ahead, and a unique `jti`. Sign a new assertion for every request, including retries.
45
56
 
46
57
  scope : typing.Optional[str]
47
58
  Optional space-separated scopes for the access token. Defaults to `*`.
@@ -65,11 +76,15 @@ class OauthTokensClient:
65
76
  )
66
77
  client.oauth_tokens.create(
67
78
  client_id="client_id",
68
- client_secret="client_secret",
69
79
  )
70
80
  """
71
81
  _response = self._raw_client.create(
72
- client_id=client_id, client_secret=client_secret, scope=scope, request_options=request_options
82
+ client_id=client_id,
83
+ client_secret=client_secret,
84
+ client_assertion_type=client_assertion_type,
85
+ client_assertion=client_assertion,
86
+ scope=scope,
87
+ request_options=request_options,
73
88
  )
74
89
  return _response.data
75
90
 
@@ -93,7 +108,11 @@ class AsyncOauthTokensClient:
93
108
  self,
94
109
  *,
95
110
  client_id: str,
96
- client_secret: str,
111
+ client_secret: typing.Optional[str] = OMIT,
112
+ client_assertion_type: typing.Optional[
113
+ typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]
114
+ ] = OMIT,
115
+ client_assertion: typing.Optional[str] = OMIT,
97
116
  scope: typing.Optional[str] = OMIT,
98
117
  request_options: typing.Optional[RequestOptions] = None,
99
118
  ) -> CreateOAuthTokenResponse:
@@ -104,7 +123,14 @@ class AsyncOauthTokensClient:
104
123
  ----------
105
124
  client_id : str
106
125
 
107
- client_secret : str
126
+ client_secret : typing.Optional[str]
127
+ The client secret, for clients that authenticate with a client secret.
128
+
129
+ client_assertion_type : typing.Optional[typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]]
130
+ Required with `client_assertion`.
131
+
132
+ client_assertion : typing.Optional[str]
133
+ For clients that authenticate with a public key: a JWT signed with the client's private key (ES256 or RS256). Claims: `iss` and `sub` set to the client ID, `aud` set to `https://api.pipedream.com`, `exp` at most 1 hour ahead, and a unique `jti`. Sign a new assertion for every request, including retries.
108
134
 
109
135
  scope : typing.Optional[str]
110
136
  Optional space-separated scopes for the access token. Defaults to `*`.
@@ -133,13 +159,17 @@ class AsyncOauthTokensClient:
133
159
  async def main() -> None:
134
160
  await client.oauth_tokens.create(
135
161
  client_id="client_id",
136
- client_secret="client_secret",
137
162
  )
138
163
 
139
164
 
140
165
  asyncio.run(main())
141
166
  """
142
167
  _response = await self._raw_client.create(
143
- client_id=client_id, client_secret=client_secret, scope=scope, request_options=request_options
168
+ client_id=client_id,
169
+ client_secret=client_secret,
170
+ client_assertion_type=client_assertion_type,
171
+ client_assertion=client_assertion,
172
+ scope=scope,
173
+ request_options=request_options,
144
174
  )
145
175
  return _response.data
@@ -25,7 +25,11 @@ class RawOauthTokensClient:
25
25
  self,
26
26
  *,
27
27
  client_id: str,
28
- client_secret: str,
28
+ client_secret: typing.Optional[str] = OMIT,
29
+ client_assertion_type: typing.Optional[
30
+ typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]
31
+ ] = OMIT,
32
+ client_assertion: typing.Optional[str] = OMIT,
29
33
  scope: typing.Optional[str] = OMIT,
30
34
  request_options: typing.Optional[RequestOptions] = None,
31
35
  ) -> HttpResponse[CreateOAuthTokenResponse]:
@@ -36,7 +40,14 @@ class RawOauthTokensClient:
36
40
  ----------
37
41
  client_id : str
38
42
 
39
- client_secret : str
43
+ client_secret : typing.Optional[str]
44
+ The client secret, for clients that authenticate with a client secret.
45
+
46
+ client_assertion_type : typing.Optional[typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]]
47
+ Required with `client_assertion`.
48
+
49
+ client_assertion : typing.Optional[str]
50
+ For clients that authenticate with a public key: a JWT signed with the client's private key (ES256 or RS256). Claims: `iss` and `sub` set to the client ID, `aud` set to `https://api.pipedream.com`, `exp` at most 1 hour ahead, and a unique `jti`. Sign a new assertion for every request, including retries.
40
51
 
41
52
  scope : typing.Optional[str]
42
53
  Optional space-separated scopes for the access token. Defaults to `*`.
@@ -55,6 +66,8 @@ class RawOauthTokensClient:
55
66
  json={
56
67
  "client_id": client_id,
57
68
  "client_secret": client_secret,
69
+ "client_assertion_type": client_assertion_type,
70
+ "client_assertion": client_assertion,
58
71
  "scope": scope,
59
72
  "grant_type": "client_credentials",
60
73
  },
@@ -103,7 +116,11 @@ class AsyncRawOauthTokensClient:
103
116
  self,
104
117
  *,
105
118
  client_id: str,
106
- client_secret: str,
119
+ client_secret: typing.Optional[str] = OMIT,
120
+ client_assertion_type: typing.Optional[
121
+ typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]
122
+ ] = OMIT,
123
+ client_assertion: typing.Optional[str] = OMIT,
107
124
  scope: typing.Optional[str] = OMIT,
108
125
  request_options: typing.Optional[RequestOptions] = None,
109
126
  ) -> AsyncHttpResponse[CreateOAuthTokenResponse]:
@@ -114,7 +131,14 @@ class AsyncRawOauthTokensClient:
114
131
  ----------
115
132
  client_id : str
116
133
 
117
- client_secret : str
134
+ client_secret : typing.Optional[str]
135
+ The client secret, for clients that authenticate with a client secret.
136
+
137
+ client_assertion_type : typing.Optional[typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]]
138
+ Required with `client_assertion`.
139
+
140
+ client_assertion : typing.Optional[str]
141
+ For clients that authenticate with a public key: a JWT signed with the client's private key (ES256 or RS256). Claims: `iss` and `sub` set to the client ID, `aud` set to `https://api.pipedream.com`, `exp` at most 1 hour ahead, and a unique `jti`. Sign a new assertion for every request, including retries.
118
142
 
119
143
  scope : typing.Optional[str]
120
144
  Optional space-separated scopes for the access token. Defaults to `*`.
@@ -133,6 +157,8 @@ class AsyncRawOauthTokensClient:
133
157
  json={
134
158
  "client_id": client_id,
135
159
  "client_secret": client_secret,
160
+ "client_assertion_type": client_assertion_type,
161
+ "client_assertion": client_assertion,
136
162
  "scope": scope,
137
163
  "grant_type": "client_credentials",
138
164
  },
@@ -5,6 +5,12 @@ import httpx
5
5
  from .client import AsyncClient, Client
6
6
  from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
7
7
  from .core.logging import LogConfig, Logger
8
+ from .private_key_jwt import (
9
+ AsyncPrivateKeyJwtTokenProvider,
10
+ ClientAssertionSigner,
11
+ PrivateKeyJwtTokenProvider,
12
+ issuer_from_base_url,
13
+ )
8
14
  from .types.project_environment import ProjectEnvironment
9
15
  from .workflows.client import AsyncWorkflowsClient, WorkflowsClient
10
16
 
@@ -31,6 +37,52 @@ def install_request_hook(
31
37
  ).append(hook)
32
38
 
33
39
 
40
+ def _resolve_credentials(
41
+ client_secret: typing.Optional[str],
42
+ private_key: typing.Optional[str],
43
+ ) -> typing.Tuple[typing.Optional[str], typing.Optional[str]]:
44
+ """Returns (client_secret, private_key) with at most one of them set.
45
+
46
+ An explicitly passed credential wins over the other one in the environment.
47
+ Passing both, or setting both PIPEDREAM_CLIENT_SECRET and
48
+ PIPEDREAM_PRIVATE_KEY with neither passed, raises ValueError.
49
+ """
50
+ env_secret = os.getenv("PIPEDREAM_CLIENT_SECRET")
51
+ # client_secret defaults to the environment variable, so a value equal to
52
+ # it is treated as coming from the environment.
53
+ secret_passed = bool(client_secret) and client_secret != env_secret
54
+ if private_key and secret_passed:
55
+ raise ValueError("Pass either client_secret or private_key, not both")
56
+ if private_key:
57
+ return None, private_key
58
+ if secret_passed:
59
+ return client_secret, None
60
+ env_key = os.getenv("PIPEDREAM_PRIVATE_KEY")
61
+ if client_secret and env_key:
62
+ raise ValueError(
63
+ "Both PIPEDREAM_CLIENT_SECRET and PIPEDREAM_PRIVATE_KEY are set; set only the one your client uses"
64
+ )
65
+ return client_secret, env_key
66
+
67
+
68
+ def _assertion_signer(
69
+ client_id: typing.Optional[str],
70
+ private_key: str,
71
+ key_id: typing.Optional[str],
72
+ base_url: str,
73
+ ) -> ClientAssertionSigner:
74
+ if not client_id:
75
+ raise ValueError("client_id is required when authenticating with private_key")
76
+ return ClientAssertionSigner(
77
+ client_id=client_id,
78
+ private_key=private_key,
79
+ key_id=key_id or os.getenv("PIPEDREAM_KEY_ID"),
80
+ # The assertion audience is the API's issuer identifier: the base URL's
81
+ # origin (https://api.pipedream.com by default).
82
+ audience=issuer_from_base_url(base_url),
83
+ )
84
+
85
+
34
86
  class Pipedream(Client):
35
87
 
36
88
  def __init__(
@@ -39,6 +91,8 @@ class Pipedream(Client):
39
91
  access_token: typing.Optional[str] = os.getenv("PIPEDREAM_ACCESS_TOKEN"),
40
92
  client_id: typing.Optional[str] = os.getenv("PIPEDREAM_CLIENT_ID"),
41
93
  client_secret: typing.Optional[str] = os.getenv("PIPEDREAM_CLIENT_SECRET"),
94
+ private_key: typing.Optional[str] = None,
95
+ key_id: typing.Optional[str] = None,
42
96
  project_id: typing.Optional[str] = os.getenv("PIPEDREAM_PROJECT_ID"),
43
97
  project_environment: typing.Optional[ProjectEnvironment] = os.getenv(
44
98
  "PIPEDREAM_PROJECT_ENVIRONMENT",
@@ -73,8 +127,30 @@ class Pipedream(Client):
73
127
  logging=logging,
74
128
  )
75
129
 
130
+ client_secret, private_key = _resolve_credentials(client_secret, private_key)
131
+
76
132
  if access_token:
77
133
  super().__init__(token=(lambda: access_token), **common_kwargs)
134
+ elif private_key:
135
+ signer = _assertion_signer(client_id, private_key, key_id, resolved_base_url)
136
+ super().__init__(token=lambda: self._token_provider.get_token(), **common_kwargs)
137
+ # Token requests use their own unauthenticated wrapper over the same
138
+ # HTTP client, like the generated client-secret flow.
139
+ wrapper = self._client_wrapper
140
+ self._token_provider = PrivateKeyJwtTokenProvider(
141
+ client_id=typing.cast(str, client_id),
142
+ signer=signer,
143
+ client_wrapper=SyncClientWrapper(
144
+ project_id=project_id,
145
+ project_environment=project_environment,
146
+ headers=headers,
147
+ base_url=wrapper.get_base_url(),
148
+ timeout=wrapper.get_timeout(),
149
+ max_retries=wrapper.get_max_retries(),
150
+ logging=logging,
151
+ httpx_client=wrapper.httpx_client.httpx_client,
152
+ ),
153
+ )
78
154
  else:
79
155
  super().__init__(client_id=client_id, client_secret=client_secret, **common_kwargs)
80
156
 
@@ -101,6 +177,8 @@ class AsyncPipedream(AsyncClient):
101
177
  access_token: typing.Optional[str] = os.getenv("PIPEDREAM_ACCESS_TOKEN"),
102
178
  client_id: typing.Optional[str] = os.getenv("PIPEDREAM_CLIENT_ID"),
103
179
  client_secret: typing.Optional[str] = os.getenv("PIPEDREAM_CLIENT_SECRET"),
180
+ private_key: typing.Optional[str] = None,
181
+ key_id: typing.Optional[str] = None,
104
182
  project_id: typing.Optional[str] = os.getenv("PIPEDREAM_PROJECT_ID"),
105
183
  project_environment: typing.Optional[ProjectEnvironment] = os.getenv(
106
184
  "PIPEDREAM_PROJECT_ENVIRONMENT",
@@ -135,8 +213,33 @@ class AsyncPipedream(AsyncClient):
135
213
  logging=logging,
136
214
  )
137
215
 
216
+ client_secret, private_key = _resolve_credentials(client_secret, private_key)
217
+
138
218
  if access_token:
139
219
  super().__init__(token=(lambda: access_token), **common_kwargs)
220
+ elif private_key:
221
+ signer = _assertion_signer(client_id, private_key, key_id, resolved_base_url)
222
+ super().__init__(token=(lambda: ""), **common_kwargs)
223
+ # Wire the async token provider the way the generated client-secret
224
+ # flow does: no sync token, an async one, and token requests on
225
+ # their own unauthenticated wrapper over the same HTTP client.
226
+ wrapper = self._client_wrapper
227
+ token_provider = AsyncPrivateKeyJwtTokenProvider(
228
+ client_id=typing.cast(str, client_id),
229
+ signer=signer,
230
+ client_wrapper=AsyncClientWrapper(
231
+ project_id=project_id,
232
+ project_environment=project_environment,
233
+ headers=headers,
234
+ base_url=wrapper.get_base_url(),
235
+ timeout=wrapper.get_timeout(),
236
+ max_retries=wrapper.get_max_retries(),
237
+ logging=logging,
238
+ httpx_client=wrapper.httpx_client.httpx_client,
239
+ ),
240
+ )
241
+ wrapper._token = None
242
+ wrapper._async_token = token_provider.get_token
140
243
  else:
141
244
  super().__init__(client_id=client_id, client_secret=client_secret, **common_kwargs)
142
245
 
@@ -0,0 +1,204 @@
1
+ """Client assertions for private_key_jwt client authentication (RFC 7523).
2
+
3
+ An OAuth client that registered a public key authenticates at the token
4
+ endpoint with a short-lived JWT signed by the matching private key, instead of
5
+ a client secret. Requires the optional ``private-key`` extra:
6
+ ``pip install "pipedream[private-key]"`` (PyJWT with cryptography).
7
+ """
8
+
9
+ import asyncio
10
+ import datetime as dt
11
+ import json
12
+ import threading
13
+ import time
14
+ import typing
15
+ import uuid
16
+ from urllib.parse import urlsplit
17
+
18
+ import httpx
19
+ from .core.api_error import ApiError
20
+ from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
21
+ from .oauth_tokens.client import AsyncOauthTokensClient, OauthTokensClient
22
+ from .types.create_o_auth_token_response import CreateOAuthTokenResponse
23
+
24
+ CLIENT_ASSERTION_TYPE: typing.Literal["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"] = (
25
+ "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
26
+ )
27
+
28
+ # Assertions are single-use and only need to outlive one token request.
29
+ _ASSERTION_LIFETIME_SECONDS = 60
30
+ # Token requests authenticated with an assertion are retried here rather than
31
+ # by the HTTP client, so each attempt carries a newly signed assertion: the
32
+ # server rejects a reused one.
33
+ _MAX_RETRIES = 2
34
+ _TOKEN_BUFFER = dt.timedelta(minutes=2)
35
+
36
+
37
+ def issuer_from_base_url(base_url: str) -> str:
38
+ """The API's issuer identifier: the base URL's origin, without a trailing slash."""
39
+ parts = urlsplit(base_url)
40
+ return f"{parts.scheme}://{parts.netloc}"
41
+
42
+
43
+ class ClientAssertionSigner:
44
+ """Signs a new client assertion on every call to :meth:`sign`.
45
+
46
+ The assertion is a JWT with ``iss`` and ``sub`` set to the client ID, the
47
+ given ``aud``, a 60-second lifetime, and a random single-use ``jti``. The
48
+ private key may be a PEM string (PKCS#8, PKCS#1, or SEC1) or a JWK as a JSON
49
+ string; EC P-256 keys sign with ES256 and RSA keys with RS256. ``key_id``
50
+ is sent as the ``kid`` header and defaults to the JWK's ``kid``, if any.
51
+ The key is parsed and validated on construction.
52
+ """
53
+
54
+ def __init__(self, *, client_id: str, private_key: str, audience: str, key_id: typing.Optional[str] = None):
55
+ try:
56
+ import jwt # noqa: F401 (PyJWT)
57
+ from cryptography.hazmat.primitives.asymmetric import ec, rsa
58
+ except ImportError as e:
59
+ raise ImportError(
60
+ 'Authenticating with a private key requires the "private-key" extra: '
61
+ 'pip install "pipedream[private-key]"'
62
+ ) from e
63
+
64
+ self._client_id = client_id
65
+ self._audience = audience
66
+ self._key, jwk_kid = _load_private_key(private_key.strip())
67
+ self._key_id = key_id or jwk_kid
68
+
69
+ if isinstance(self._key, ec.EllipticCurvePrivateKey) and isinstance(self._key.curve, ec.SECP256R1):
70
+ self._alg = "ES256"
71
+ elif isinstance(self._key, rsa.RSAPrivateKey):
72
+ self._alg = "RS256"
73
+ else:
74
+ raise ValueError("private_key must be an EC P-256 key (ES256) or an RSA key (RS256)")
75
+
76
+ def sign(self) -> str:
77
+ import jwt
78
+
79
+ now = int(time.time())
80
+ headers: typing.Dict[str, str] = {"typ": "client-authentication+jwt"}
81
+ if self._key_id:
82
+ headers["kid"] = self._key_id
83
+ claims = {
84
+ "iss": self._client_id,
85
+ "sub": self._client_id,
86
+ "aud": self._audience,
87
+ "iat": now,
88
+ "exp": now + _ASSERTION_LIFETIME_SECONDS,
89
+ "jti": str(uuid.uuid4()),
90
+ }
91
+ return jwt.encode(claims, typing.cast(typing.Any, self._key), algorithm=self._alg, headers=headers)
92
+
93
+
94
+ def _load_private_key(text: str) -> typing.Tuple[typing.Any, typing.Optional[str]]:
95
+ import jwt
96
+ from cryptography.hazmat.primitives.serialization import load_pem_private_key
97
+
98
+ try:
99
+ if text.startswith("{"):
100
+ jwk = json.loads(text)
101
+ key = jwt.PyJWK(jwk).key
102
+ kid = jwk.get("kid") if isinstance(jwk.get("kid"), str) else None
103
+ if not hasattr(key, "sign"):
104
+ raise ValueError("not a private key")
105
+ return key, kid
106
+ return load_pem_private_key(text.encode(), password=None), None
107
+ except Exception as e:
108
+ raise ValueError(
109
+ "private_key must be a private key in PEM format or a JWK (JSON). "
110
+ "If you passed a public key, pass the private key instead."
111
+ ) from e
112
+
113
+
114
+ def _is_retryable(error: Exception) -> bool:
115
+ # Rate limits, timeouts, connection failures, and server errors are
116
+ # transient; 4xx errors such as invalid_client are not.
117
+ if isinstance(error, (httpx.TimeoutException, httpx.TransportError)):
118
+ return True
119
+ if isinstance(error, ApiError):
120
+ status = error.status_code
121
+ return status is None or status in (408, 429) or status >= 500
122
+ return False
123
+
124
+
125
+ def _expires_at(token: CreateOAuthTokenResponse) -> dt.datetime:
126
+ return dt.datetime.now() + dt.timedelta(seconds=token.expires_in) - _TOKEN_BUFFER
127
+
128
+
129
+ class PrivateKeyJwtTokenProvider:
130
+ """Retrieves and caches an OAuth access token, authenticating with client assertions."""
131
+
132
+ def __init__(self, *, client_id: str, signer: ClientAssertionSigner, client_wrapper: SyncClientWrapper):
133
+ self._client_id = client_id
134
+ self._signer = signer
135
+ self._auth_client = OauthTokensClient(client_wrapper=client_wrapper)
136
+ self._access_token: typing.Optional[str] = None
137
+ self._expires_at = dt.datetime.now()
138
+ self._lock = threading.Lock()
139
+
140
+ def get_token(self) -> str:
141
+ if self._access_token and self._expires_at > dt.datetime.now():
142
+ return self._access_token
143
+ with self._lock:
144
+ if self._access_token and self._expires_at > dt.datetime.now():
145
+ return self._access_token
146
+ token = self._request_token()
147
+ self._access_token = token.access_token
148
+ self._expires_at = _expires_at(token)
149
+ return self._access_token
150
+
151
+ def _request_token(self) -> CreateOAuthTokenResponse:
152
+ attempt = 0
153
+ while True:
154
+ try:
155
+ return self._auth_client.create(
156
+ client_id=self._client_id,
157
+ client_assertion_type=CLIENT_ASSERTION_TYPE,
158
+ client_assertion=self._signer.sign(),
159
+ request_options={"max_retries": 0},
160
+ )
161
+ except Exception as e:
162
+ if attempt >= _MAX_RETRIES or not _is_retryable(e):
163
+ raise
164
+ time.sleep(0.5 * 2**attempt)
165
+ attempt += 1
166
+
167
+
168
+ class AsyncPrivateKeyJwtTokenProvider:
169
+ """Async variant of :class:`PrivateKeyJwtTokenProvider`."""
170
+
171
+ def __init__(self, *, client_id: str, signer: ClientAssertionSigner, client_wrapper: AsyncClientWrapper):
172
+ self._client_id = client_id
173
+ self._signer = signer
174
+ self._auth_client = AsyncOauthTokensClient(client_wrapper=client_wrapper)
175
+ self._access_token: typing.Optional[str] = None
176
+ self._expires_at = dt.datetime.now()
177
+ self._lock = asyncio.Lock()
178
+
179
+ async def get_token(self) -> str:
180
+ if self._access_token and self._expires_at > dt.datetime.now():
181
+ return self._access_token
182
+ async with self._lock:
183
+ if self._access_token and self._expires_at > dt.datetime.now():
184
+ return self._access_token
185
+ token = await self._request_token()
186
+ self._access_token = token.access_token
187
+ self._expires_at = _expires_at(token)
188
+ return self._access_token
189
+
190
+ async def _request_token(self) -> CreateOAuthTokenResponse:
191
+ attempt = 0
192
+ while True:
193
+ try:
194
+ return await self._auth_client.create(
195
+ client_id=self._client_id,
196
+ client_assertion_type=CLIENT_ASSERTION_TYPE,
197
+ client_assertion=self._signer.sign(),
198
+ request_options={"max_retries": 0},
199
+ )
200
+ except Exception as e:
201
+ if attempt >= _MAX_RETRIES or not _is_retryable(e):
202
+ raise
203
+ await asyncio.sleep(0.5 * 2**attempt)
204
+ attempt += 1
File without changes
File without changes