connector-py 4.180.0__tar.gz → 4.181.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 (338) hide show
  1. {connector_py-4.180.0 → connector_py-4.181.0}/PKG-INFO +1 -1
  2. connector_py-4.181.0/connector/__about__.py +1 -0
  3. {connector_py-4.180.0 → connector_py-4.181.0}/connector/client.py +6 -1
  4. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/base_clients.py +101 -6
  5. {connector_py-4.180.0 → connector_py-4.181.0}/connector_py.egg-info/PKG-INFO +1 -1
  6. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_base_clients.py +366 -1
  7. connector_py-4.180.0/connector/__about__.py +0 -1
  8. {connector_py-4.180.0 → connector_py-4.181.0}/LICENSE.txt +0 -0
  9. {connector_py-4.180.0 → connector_py-4.181.0}/README.md +0 -0
  10. {connector_py-4.180.0 → connector_py-4.181.0}/connector/__init__.py +0 -0
  11. {connector_py-4.180.0 → connector_py-4.181.0}/connector/auth_helper.py +0 -0
  12. {connector_py-4.180.0 → connector_py-4.181.0}/connector/ca_certs.py +0 -0
  13. {connector_py-4.180.0 → connector_py-4.181.0}/connector/cli.py +0 -0
  14. {connector_py-4.180.0 → connector_py-4.181.0}/connector/compile.py +0 -0
  15. {connector_py-4.180.0 → connector_py-4.181.0}/connector/config.py +0 -0
  16. {connector_py-4.180.0 → connector_py-4.181.0}/connector/error.py +0 -0
  17. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/__init__.py +0 -0
  18. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/__init__.py +0 -0
  19. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/account_status.py +0 -0
  20. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/account_type.py +0 -0
  21. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activate_account.py +0 -0
  22. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activate_account200_response.py +0 -0
  23. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activate_account_request.py +0 -0
  24. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activate_account_response.py +0 -0
  25. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activated_account.py +0 -0
  26. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activity_event_type.py +0 -0
  27. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activity_record.py +0 -0
  28. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activity_record_activity_type.py +0 -0
  29. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activity_record_actor.py +0 -0
  30. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activity_record_entitlement.py +0 -0
  31. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activity_record_target.py +0 -0
  32. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/activity_type.py +0 -0
  33. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/amount.py +0 -0
  34. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/app_category.py +0 -0
  35. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/app_info.py +0 -0
  36. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/app_info200_response.py +0 -0
  37. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/app_info_request.py +0 -0
  38. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/app_info_request_payload.py +0 -0
  39. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/app_info_response.py +0 -0
  40. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/assign_entitlement.py +0 -0
  41. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/assign_entitlement200_response.py +0 -0
  42. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/assign_entitlement_request.py +0 -0
  43. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/assign_entitlement_response.py +0 -0
  44. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/assigned_entitlement.py +0 -0
  45. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/auth_credential.py +0 -0
  46. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/authorization_url.py +0 -0
  47. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/basic_authentication.py +0 -0
  48. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/basic_credential.py +0 -0
  49. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/capability_schema.py +0 -0
  50. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/creatable_account.py +0 -0
  51. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/create_account.py +0 -0
  52. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/create_account200_response.py +0 -0
  53. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/create_account_entitlement.py +0 -0
  54. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/create_account_request.py +0 -0
  55. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/create_account_response.py +0 -0
  56. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/created_account.py +0 -0
  57. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/custom_attribute_customized_type.py +0 -0
  58. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/custom_attribute_schema.py +0 -0
  59. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/custom_attribute_type.py +0 -0
  60. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/deactivate_account.py +0 -0
  61. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/deactivate_account200_response.py +0 -0
  62. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/deactivate_account_request.py +0 -0
  63. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/deactivate_account_response.py +0 -0
  64. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/deactivated_account.py +0 -0
  65. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/delete_account.py +0 -0
  66. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/delete_account200_response.py +0 -0
  67. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/delete_account_request.py +0 -0
  68. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/delete_account_response.py +0 -0
  69. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/deleted_account.py +0 -0
  70. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/downgrade_license.py +0 -0
  71. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/downgrade_license200_response.py +0 -0
  72. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/downgrade_license_request.py +0 -0
  73. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/downgrade_license_response.py +0 -0
  74. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/downgraded_license.py +0 -0
  75. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/entitlement_requirement.py +0 -0
  76. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/entitlement_type.py +0 -0
  77. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/error.py +0 -0
  78. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/error_code.py +0 -0
  79. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/error_response.py +0 -0
  80. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/expense.py +0 -0
  81. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/expense_approval_status.py +0 -0
  82. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/expense_filters.py +0 -0
  83. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/expense_payment_status.py +0 -0
  84. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/expense_type.py +0 -0
  85. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/find_entitlement_associations.py +0 -0
  86. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/find_entitlement_associations200_response.py +0 -0
  87. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/find_entitlement_associations_request.py +0 -0
  88. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/find_entitlement_associations_response.py +0 -0
  89. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/found_account_data.py +0 -0
  90. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/found_entitlement_association.py +0 -0
  91. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/found_entitlement_data.py +0 -0
  92. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/found_resource_data.py +0 -0
  93. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_account.py +0 -0
  94. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_account200_response.py +0 -0
  95. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_account_request.py +0 -0
  96. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_account_response.py +0 -0
  97. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_authorization_url.py +0 -0
  98. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_authorization_url200_response.py +0 -0
  99. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_authorization_url_request.py +0 -0
  100. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_authorization_url_response.py +0 -0
  101. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_connected_info.py +0 -0
  102. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_connected_info200_response.py +0 -0
  103. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_connected_info_request.py +0 -0
  104. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_connected_info_response.py +0 -0
  105. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_last_activity.py +0 -0
  106. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_last_activity200_response.py +0 -0
  107. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_last_activity_request.py +0 -0
  108. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/get_last_activity_response.py +0 -0
  109. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_authorization_callback.py +0 -0
  110. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_authorization_callback200_response.py +0 -0
  111. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_authorization_callback_request.py +0 -0
  112. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_authorization_callback_response.py +0 -0
  113. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_client_credentials.py +0 -0
  114. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_client_credentials_request.py +0 -0
  115. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_client_credentials_request200_response.py +0 -0
  116. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/handle_client_credentials_response.py +0 -0
  117. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/info.py +0 -0
  118. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/info200_response.py +0 -0
  119. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/info_response.py +0 -0
  120. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/jwt_claims.py +0 -0
  121. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/jwt_credential.py +0 -0
  122. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/jwt_headers.py +0 -0
  123. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/last_activity_data.py +0 -0
  124. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_accounts.py +0 -0
  125. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_accounts200_response.py +0 -0
  126. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_accounts_request.py +0 -0
  127. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_accounts_response.py +0 -0
  128. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_activity_records.py +0 -0
  129. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_activity_records200_response.py +0 -0
  130. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_activity_records_request.py +0 -0
  131. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_activity_records_response.py +0 -0
  132. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_connector_app_ids200_response.py +0 -0
  133. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_custom_attributes_schema.py +0 -0
  134. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_custom_attributes_schema200_response.py +0 -0
  135. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_custom_attributes_schema_request.py +0 -0
  136. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_custom_attributes_schema_response.py +0 -0
  137. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_entitlements.py +0 -0
  138. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_entitlements200_response.py +0 -0
  139. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_entitlements_request.py +0 -0
  140. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_entitlements_response.py +0 -0
  141. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_expenses.py +0 -0
  142. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_expenses200_response.py +0 -0
  143. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_expenses_request.py +0 -0
  144. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_expenses_response.py +0 -0
  145. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_resources.py +0 -0
  146. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_resources200_response.py +0 -0
  147. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_resources_request.py +0 -0
  148. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/list_resources_response.py +0 -0
  149. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/normalized_expense_approval_status.py +0 -0
  150. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/normalized_expense_payment_status.py +0 -0
  151. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth1_credential.py +0 -0
  152. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth_authentication.py +0 -0
  153. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth_authorization.py +0 -0
  154. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth_client_credential.py +0 -0
  155. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth_client_credential_authentication.py +0 -0
  156. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth_client_credential_authorization.py +0 -0
  157. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth_credential.py +0 -0
  158. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/o_auth_scopes.py +0 -0
  159. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/oauth_credentials.py +0 -0
  160. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/page.py +0 -0
  161. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/refresh_access_token.py +0 -0
  162. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/refresh_access_token200_response.py +0 -0
  163. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/refresh_access_token_request.py +0 -0
  164. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/refresh_access_token_response.py +0 -0
  165. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/release_resources.py +0 -0
  166. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/release_resources200_response.py +0 -0
  167. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/release_resources_request.py +0 -0
  168. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/release_resources_response.py +0 -0
  169. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/release_resources_status.py +0 -0
  170. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/resource_type.py +0 -0
  171. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/service_account_credential.py +0 -0
  172. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/service_account_type.py +0 -0
  173. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/spend_user.py +0 -0
  174. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/standard_capability_name.py +0 -0
  175. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/time_range.py +0 -0
  176. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/token_authentication.py +0 -0
  177. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/token_credential.py +0 -0
  178. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/token_type.py +0 -0
  179. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/transfer_data.py +0 -0
  180. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/transfer_data200_response.py +0 -0
  181. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/transfer_data_request.py +0 -0
  182. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/transfer_data_response.py +0 -0
  183. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/transfer_data_status.py +0 -0
  184. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/unassign_entitlement.py +0 -0
  185. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/unassign_entitlement200_response.py +0 -0
  186. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/unassign_entitlement_request.py +0 -0
  187. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/unassign_entitlement_response.py +0 -0
  188. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/unassigned_entitlement.py +0 -0
  189. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/update_account200_response.py +0 -0
  190. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/update_account_request.py +0 -0
  191. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/update_account_response.py +0 -0
  192. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/updateable_account.py +0 -0
  193. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/validate_credentials.py +0 -0
  194. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/validate_credentials200_response.py +0 -0
  195. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/validate_credentials_request.py +0 -0
  196. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/validate_credentials_response.py +0 -0
  197. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/validated_credentials.py +0 -0
  198. {connector_py-4.180.0 → connector_py-4.181.0}/connector/generated/models/vendor.py +0 -0
  199. {connector_py-4.180.0 → connector_py-4.181.0}/connector/handlers/lumos_log_handler.py +0 -0
  200. {connector_py-4.180.0 → connector_py-4.181.0}/connector/http_server.py +0 -0
  201. {connector_py-4.180.0 → connector_py-4.181.0}/connector/httpx_rewrite.py +0 -0
  202. {connector_py-4.180.0 → connector_py-4.181.0}/connector/integration.py +0 -0
  203. {connector_py-4.180.0 → connector_py-4.181.0}/connector/main.py +0 -0
  204. {connector_py-4.180.0 → connector_py-4.181.0}/connector/modules.py +0 -0
  205. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/__init__.py +0 -0
  206. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/capabilities/__init__.py +0 -0
  207. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/capabilities/errors.py +0 -0
  208. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/capabilities/executor.py +0 -0
  209. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/capabilities/factory.py +0 -0
  210. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/capability.py +0 -0
  211. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/errors.py +0 -0
  212. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/fingerprint.py +0 -0
  213. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/integration.py +0 -0
  214. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/modules/__init__.py +0 -0
  215. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/modules/base_module.py +0 -0
  216. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/modules/credentials_module.py +0 -0
  217. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/modules/info_module.py +0 -0
  218. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/modules/oauth_module.py +0 -0
  219. {connector_py-4.180.0 → connector_py-4.181.0}/connector/oai/modules/oauth_module_types.py +0 -0
  220. {connector_py-4.180.0 → connector_py-4.181.0}/connector/observability/__init__.py +0 -0
  221. {connector_py-4.180.0 → connector_py-4.181.0}/connector/observability/instrument.py +0 -0
  222. {connector_py-4.180.0 → connector_py-4.181.0}/connector/observability/logging.py +0 -0
  223. {connector_py-4.180.0 → connector_py-4.181.0}/connector/observability/observer.py +0 -0
  224. {connector_py-4.180.0 → connector_py-4.181.0}/connector/py.typed +0 -0
  225. {connector_py-4.180.0 → connector_py-4.181.0}/connector/pydantic.py +0 -0
  226. {connector_py-4.180.0 → connector_py-4.181.0}/connector/response_logging.py +0 -0
  227. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/__init__.py +0 -0
  228. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/create.py +0 -0
  229. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/.pre-commit-config.yaml +0 -0
  230. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/LICENSE.txt +0 -0
  231. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/README.md +0 -0
  232. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/__about__.py +0 -0
  233. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/__init__.py +0 -0
  234. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/capabilities_read.py +0 -0
  235. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/capabilities_write.py +0 -0
  236. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/client.py +0 -0
  237. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/constants.py +0 -0
  238. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/dto/__init__.py +0 -0
  239. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/dto/user.py +0 -0
  240. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/enums.py +0 -0
  241. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/integration.py +0 -0
  242. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/main.py +0 -0
  243. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/pagination.py +0 -0
  244. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/connector_name_here/settings.py +0 -0
  245. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/pyproject.toml +0 -0
  246. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/__init__.py +0 -0
  247. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/common_mock_data.py +0 -0
  248. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_all_capabilities.py +0 -0
  249. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_read_capabilities/__init__.py +0 -0
  250. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_read_capabilities/test_find_entitlement_associations_cases.py +0 -0
  251. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_read_capabilities/test_get_last_activity_cases.py +0 -0
  252. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_read_capabilities/test_list_accounts_cases.py +0 -0
  253. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_read_capabilities/test_list_entitlements_cases.py +0 -0
  254. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_read_capabilities/test_list_resources_cases.py +0 -0
  255. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_read_capabilities/test_validate_credentials_cases.py +0 -0
  256. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_write_capabilities/__init__.py +0 -0
  257. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_write_capabilities/test_activate_account_cases.py +0 -0
  258. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_write_capabilities/test_assign_entitlement_cases.py +0 -0
  259. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_write_capabilities/test_create_account_cases.py +0 -0
  260. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_write_capabilities/test_deactivate_account_cases.py +0 -0
  261. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_write_capabilities/test_delete_account_cases.py +0 -0
  262. {connector_py-4.180.0 → connector_py-4.181.0}/connector/scaffold/templates/tests/test_write_capabilities/test_unassign_entitlement_cases.py +0 -0
  263. {connector_py-4.180.0 → connector_py-4.181.0}/connector/serializers/__init__.py +0 -0
  264. {connector_py-4.180.0 → connector_py-4.181.0}/connector/serializers/request.py +0 -0
  265. {connector_py-4.180.0 → connector_py-4.181.0}/connector/shared_types.py +0 -0
  266. {connector_py-4.180.0 → connector_py-4.181.0}/connector/spec/openapi.yaml +0 -0
  267. {connector_py-4.180.0 → connector_py-4.181.0}/connector/tests/__init__.py +0 -0
  268. {connector_py-4.180.0 → connector_py-4.181.0}/connector/tests/coverage_check.py +0 -0
  269. {connector_py-4.180.0 → connector_py-4.181.0}/connector/tests/gather_cases.py +0 -0
  270. {connector_py-4.180.0 → connector_py-4.181.0}/connector/tests/mock_httpx.py +0 -0
  271. {connector_py-4.180.0 → connector_py-4.181.0}/connector/tests/test_mock_httpx.py +0 -0
  272. {connector_py-4.180.0 → connector_py-4.181.0}/connector/tests/type_definitions.py +0 -0
  273. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/__init__.py +0 -0
  274. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/account.py +0 -0
  275. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/case_insensitive_dict.py +0 -0
  276. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/client_utils.py +0 -0
  277. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/httpx_auth.py +0 -0
  278. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/jwt_utils.py +0 -0
  279. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/oauth1_utils.py +0 -0
  280. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/pagination.py +0 -0
  281. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/proxy_utils.py +0 -0
  282. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/rate_limiting.py +0 -0
  283. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/sync_to_async.py +0 -0
  284. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/test.py +0 -0
  285. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/test_case_insensitive_dict.py +0 -0
  286. {connector_py-4.180.0 → connector_py-4.181.0}/connector/utils/validation_utils.py +0 -0
  287. {connector_py-4.180.0 → connector_py-4.181.0}/connector_py.egg-info/SOURCES.txt +0 -0
  288. {connector_py-4.180.0 → connector_py-4.181.0}/connector_py.egg-info/dependency_links.txt +0 -0
  289. {connector_py-4.180.0 → connector_py-4.181.0}/connector_py.egg-info/entry_points.txt +0 -0
  290. {connector_py-4.180.0 → connector_py-4.181.0}/connector_py.egg-info/requires.txt +0 -0
  291. {connector_py-4.180.0 → connector_py-4.181.0}/connector_py.egg-info/top_level.txt +0 -0
  292. {connector_py-4.180.0 → connector_py-4.181.0}/pyproject.toml +0 -0
  293. {connector_py-4.180.0 → connector_py-4.181.0}/setup.cfg +0 -0
  294. {connector_py-4.180.0 → connector_py-4.181.0}/tests/__init__.py +0 -0
  295. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/__init__.py +0 -0
  296. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/capabilities/__init__.py +0 -0
  297. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/capabilities/test_executor.py +0 -0
  298. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/capabilities/test_executor_cases.py +0 -0
  299. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/capabilities/test_factory.py +0 -0
  300. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/schema_linter_checks.py +0 -0
  301. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/shared_types.py +0 -0
  302. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_appinfo_all_connectors.py +0 -0
  303. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_capability.py +0 -0
  304. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_credentials_module.py +0 -0
  305. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_dispatch_cases.py +0 -0
  306. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_dispatch_settings_cases.py +0 -0
  307. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_exception_handler.py +0 -0
  308. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_exception_handler_handled_cases.py +0 -0
  309. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_get_capability_annotations_cases.py +0 -0
  310. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_info_cases.py +0 -0
  311. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_info_module.py +0 -0
  312. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_integration.py +0 -0
  313. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_modules.py +0 -0
  314. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_modules_cases.py +0 -0
  315. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_oauth_module.py +0 -0
  316. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_register_capability_cases.py +0 -0
  317. {connector_py-4.180.0 → connector_py-4.181.0}/tests/oai/test_validate_capability_cases.py +0 -0
  318. {connector_py-4.180.0 → connector_py-4.181.0}/tests/observability/__init__.py +0 -0
  319. {connector_py-4.180.0 → connector_py-4.181.0}/tests/observability/test_instrument.py +0 -0
  320. {connector_py-4.180.0 → connector_py-4.181.0}/tests/observability/test_observer.py +0 -0
  321. {connector_py-4.180.0 → connector_py-4.181.0}/tests/test_cli.py +0 -0
  322. {connector_py-4.180.0 → connector_py-4.181.0}/tests/test_compile.py +0 -0
  323. {connector_py-4.180.0 → connector_py-4.181.0}/tests/test_config.py +0 -0
  324. {connector_py-4.180.0 → connector_py-4.181.0}/tests/test_httpx_rewrite.py +0 -0
  325. {connector_py-4.180.0 → connector_py-4.181.0}/tests/test_lumos_log_handler.py +0 -0
  326. {connector_py-4.180.0 → connector_py-4.181.0}/tests/test_scaffold.py +0 -0
  327. {connector_py-4.180.0 → connector_py-4.181.0}/tests/test_scopes.py +0 -0
  328. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/__init__.py +0 -0
  329. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/definitions.py +0 -0
  330. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_account_utils.py +0 -0
  331. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_client_utils.py +0 -0
  332. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_fixed_rate_limiting.py +0 -0
  333. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_fixed_rate_limiting_concurrent.py +0 -0
  334. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_jwt_utils.py +0 -0
  335. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_pagination.py +0 -0
  336. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_pagination_decode_cases.py +0 -0
  337. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_pagination_duality_cases.py +0 -0
  338. {connector_py-4.180.0 → connector_py-4.181.0}/tests/utils/test_pagination_encode_cases.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: connector-py
3
- Version: 4.180.0
3
+ Version: 4.181.0
4
4
  Summary: An Abstract Tool to Perform Actions on Integrations.
5
5
  Author-email: teamlumos <security@lumos.com>
6
6
  License-Expression: Apache-2.0
@@ -0,0 +1 @@
1
+ __version__ = "4.181.0"
@@ -1,5 +1,9 @@
1
1
  from .httpx_rewrite import AsyncClient, GqlHTTPXAsyncTransport, HTTPXAsyncTransport
2
- from .oai.base_clients import BaseGraphQLSession, BaseIntegrationClient
2
+ from .oai.base_clients import (
3
+ BaseGraphQLSession,
4
+ BaseIntegrationClient,
5
+ RateLimitedHTTPXAsyncTransport,
6
+ )
3
7
  from .oai.capability import (
4
8
  get_basic_auth,
5
9
  get_jwt_auth,
@@ -20,6 +24,7 @@ from .utils.sync_to_async import sync_to_async
20
24
  __all__ = [
21
25
  "GqlHTTPXAsyncTransport",
22
26
  "HTTPXAsyncTransport",
27
+ "RateLimitedHTTPXAsyncTransport",
23
28
  "BaseGraphQLSession",
24
29
  "BaseIntegrationClient",
25
30
  "get_basic_auth",
@@ -10,11 +10,11 @@ from connector_sdk_types.generated import ErrorCode
10
10
  from gql import Client
11
11
  from gql.client import AsyncClientSession
12
12
  from gql.dsl import DSLSchema
13
- from graphql import GraphQLSchema, build_client_schema, build_schema
13
+ from graphql import DocumentNode, GraphQLSchema, build_client_schema, build_schema
14
14
  from httpx import Response
15
15
  from typing_extensions import Self
16
16
 
17
- from connector.httpx_rewrite import AsyncClient
17
+ from connector.httpx_rewrite import AsyncClient, HTTPXAsyncTransport
18
18
  from connector.oai.capability import Request
19
19
  from connector.oai.errors import ConnectorError
20
20
  from connector.utils.rate_limiting import RateLimitConfig, RateLimiter
@@ -178,6 +178,75 @@ class RateLimitedClient(AsyncClient):
178
178
  await self.base_client.__aexit__(exc_type, exc_val, exc_tb)
179
179
 
180
180
 
181
+ class RateLimitedHTTPXAsyncTransport(HTTPXAsyncTransport):
182
+ """A wrapper around HTTPXAsyncTransport that applies rate limiting to GraphQL requests."""
183
+
184
+ def __init__(self, base_transport: HTTPXAsyncTransport, rate_limit_config: RateLimitConfig):
185
+ # Copy all attributes from base transport, but exclude 'execute' to avoid shadowing our method
186
+ base_dict = {k: v for k, v in base_transport.__dict__.items() if k != "execute"}
187
+ self.__dict__.update(base_dict)
188
+ self.base_transport = base_transport
189
+ self.rate_limiter = RateLimiter[Callable[[], Any], Any](rate_limit_config)
190
+
191
+ async def connect(self):
192
+ """Connect the underlying transport and replace its client with a rate-limited one."""
193
+ await self.base_transport.connect()
194
+
195
+ # Replace the base transport's client with a rate-limited version
196
+ if hasattr(self.base_transport, "client") and self.base_transport.client:
197
+ # The transport's client should be our AsyncClient type, but we need to handle the type
198
+ if isinstance(self.base_transport.client, AsyncClient):
199
+ self.base_transport.client = RateLimitedClient(
200
+ self.base_transport.client, self.rate_limiter.config
201
+ )
202
+
203
+ # Copy the client reference
204
+ self.client = self.base_transport.client
205
+
206
+ async def execute(
207
+ self,
208
+ document: DocumentNode,
209
+ variable_values: dict[str, Any] | None = None,
210
+ operation_name: str | None = None,
211
+ extra_args: dict[str, Any] | None = None,
212
+ upload_files: bool = False,
213
+ ):
214
+ """Execute a GraphQL request with rate limiting."""
215
+
216
+ async def request_func():
217
+ result = await self.base_transport.execute(
218
+ document=document,
219
+ variable_values=variable_values,
220
+ operation_name=operation_name,
221
+ extra_args=extra_args,
222
+ upload_files=upload_files,
223
+ )
224
+ return result
225
+
226
+ # Use the rate limiter to execute the request
227
+ responses = await self.rate_limiter.execute_requests([request_func], lambda x: x())
228
+ if responses:
229
+ return responses[0]
230
+
231
+ raise ConnectorError(
232
+ message="No response from GraphQL API",
233
+ error_code=ErrorCode.API_ERROR,
234
+ )
235
+
236
+ def get_state(self) -> tuple[RateLimitConfig, float]:
237
+ """Get the current rate limit state."""
238
+ return self.rate_limiter.config, self.rate_limiter.current_delay
239
+
240
+ async def close(self):
241
+ """Close the underlying transport."""
242
+ if hasattr(self.base_transport, "close"):
243
+ await self.base_transport.close()
244
+
245
+ def __getattr__(self, name):
246
+ """Delegate attribute access to the underlying base_transport."""
247
+ return getattr(self.base_transport, name)
248
+
249
+
181
250
  class BaseIntegrationClient:
182
251
  _http_client: AsyncClient | RateLimitedClient
183
252
  _rate_limit_config: RateLimitConfig | None = None
@@ -248,8 +317,11 @@ class BaseIntegrationClient:
248
317
 
249
318
 
250
319
  class BaseGraphQLSession(AsyncClientSession):
251
- def __init__(self, args: Request):
252
- super().__init__(client=self.build_client(args))
320
+ _rate_limit_config: RateLimitConfig | None = None
321
+
322
+ def __init__(self, args: Request, rate_limit_config: RateLimitConfig | None = None):
323
+ client = self.build_client(args, rate_limit_config)
324
+ super().__init__(client=client)
253
325
 
254
326
  async def __aenter__(self) -> Self:
255
327
  await self.client.__aenter__()
@@ -267,8 +339,31 @@ class BaseGraphQLSession(AsyncClientSession):
267
339
  pass
268
340
 
269
341
  @classmethod
270
- def build_client(cls, args: Request) -> Client:
271
- return Client(**cls.prepare_client_args(args))
342
+ def build_client(
343
+ cls, args: Request, rate_limit_config: RateLimitConfig | None = None
344
+ ) -> Client:
345
+ client_args = cls.prepare_client_args(args)
346
+
347
+ # Apply rate limiting if configured
348
+ rate_limiting = rate_limit_config or cls._rate_limit_config
349
+ if rate_limiting is not None and "transport" in client_args:
350
+ transport = client_args["transport"]
351
+ if isinstance(transport, HTTPXAsyncTransport):
352
+ client_args["transport"] = RateLimitedHTTPXAsyncTransport(transport, rate_limiting)
353
+
354
+ return Client(**client_args)
355
+
356
+ def get_current_rate_limits(self) -> tuple[RateLimitConfig | None, float]:
357
+ """
358
+ Get the current rate limit state.
359
+
360
+ Returns a tuple of the rate limit config and the current delay. (or None if the client is not rate limited)
361
+ """
362
+ if hasattr(self.client, "transport") and isinstance(
363
+ self.client.transport, RateLimitedHTTPXAsyncTransport
364
+ ):
365
+ return self.client.transport.get_state()
366
+ return None, 0
272
367
 
273
368
  @classmethod
274
369
  def load_schema(cls, schema_file_path: str | Path) -> GraphQLSchema:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: connector-py
3
- Version: 4.180.0
3
+ Version: 4.181.0
4
4
  Summary: An Abstract Tool to Perform Actions on Integrations.
5
5
  Author-email: teamlumos <security@lumos.com>
6
6
  License-Expression: Apache-2.0
@@ -11,15 +11,18 @@ from connector.generated import (
11
11
  TokenCredential,
12
12
  )
13
13
  from connector.oai.base_clients import (
14
+ BaseGraphQLSession,
14
15
  BaseIntegrationClient,
15
16
  BatchRequest,
16
17
  BatchRequests,
17
18
  RateLimitedClient,
19
+ RateLimitedHTTPXAsyncTransport,
18
20
  )
19
21
  from connector.oai.capability import Request, get_token_auth
20
22
  from connector.oai.errors import ConnectorError
21
23
  from connector.utils.httpx_auth import BearerAuth
22
- from connector.utils.rate_limiting import RateLimitConfig, RateLimitStrategy
24
+ from connector.utils.rate_limiting import RateLimitConfig, RateLimiter, RateLimitStrategy
25
+ from connector_sdk_types.generated import ErrorCode
23
26
 
24
27
 
25
28
  @pytest.fixture(autouse=True)
@@ -579,3 +582,365 @@ class TestIntegrationScenarios:
579
582
  assert len(responses) == 2
580
583
  assert responses[0].status_code == 200
581
584
  assert responses[1].status_code == 404
585
+
586
+
587
+ class TestRateLimitedHTTPXAsyncTransport:
588
+ """Test cases for RateLimitedHTTPXAsyncTransport."""
589
+
590
+ @pytest.fixture
591
+ def mock_base_transport(self):
592
+ """Create a mock HTTPXAsyncTransport for testing."""
593
+ from types import SimpleNamespace
594
+
595
+ transport = SimpleNamespace()
596
+ transport.some_attr = "value"
597
+ return transport
598
+
599
+ async def test_rate_limited_graphql_execution(self, mock_base_transport, rate_limit_config):
600
+ """Test that GraphQL requests are executed through rate limiter."""
601
+ mock_result = {"data": {"test": "result"}}
602
+ mock_base_transport.execute = AsyncMock(return_value=mock_result)
603
+
604
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
605
+
606
+ async def mock_execute_requests(self, requests, handler):
607
+ results = []
608
+ for req in requests:
609
+ result = await handler(req)
610
+ results.append(result)
611
+ return results
612
+
613
+ with patch.object(RateLimiter, "execute_requests", mock_execute_requests):
614
+ result = await transport.execute("query { test }", variable_values={})
615
+
616
+ assert result == mock_result
617
+ mock_base_transport.execute.assert_called_once()
618
+ call_args = mock_base_transport.execute.call_args
619
+ assert call_args.kwargs.get("document") == "query { test }"
620
+ assert call_args.kwargs.get("variable_values") == {}
621
+
622
+ async def test_connect_wraps_async_client(self, rate_limit_config):
623
+ """Test that connect wraps AsyncClient with RateLimitedClient."""
624
+ from connector.httpx_rewrite import AsyncClient
625
+
626
+ class MockTransport:
627
+ def __init__(self):
628
+ self.client = None
629
+ self.connect_called = False
630
+
631
+ async def connect(self):
632
+ self.connect_called = True
633
+
634
+ real_client = AsyncClient(base_url="https://example.com")
635
+ base_transport = MockTransport()
636
+ base_transport.client = real_client
637
+
638
+ transport = RateLimitedHTTPXAsyncTransport(base_transport, rate_limit_config)
639
+ await transport.connect()
640
+
641
+ assert base_transport.connect_called
642
+ assert isinstance(base_transport.client, RateLimitedClient)
643
+ assert base_transport.client.base_client is real_client
644
+ assert transport.client is base_transport.client
645
+
646
+ async def test_connect_with_non_async_client(self, mock_base_transport, rate_limit_config):
647
+ """Test that connect does not wrap non-AsyncClient instances."""
648
+ mock_client = MagicMock()
649
+ mock_base_transport.client = mock_client
650
+ mock_base_transport.connect = AsyncMock()
651
+
652
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
653
+ await transport.connect()
654
+
655
+ mock_base_transport.connect.assert_called_once()
656
+ assert mock_base_transport.client is mock_client
657
+ assert not isinstance(mock_base_transport.client, RateLimitedClient)
658
+ assert transport.client is mock_client
659
+
660
+ async def test_connect_without_client(self, mock_base_transport, rate_limit_config):
661
+ """Test that connect handles case when base transport has no client."""
662
+ mock_base_transport.connect = AsyncMock()
663
+ mock_base_transport.client = None
664
+
665
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
666
+ await transport.connect()
667
+
668
+ mock_base_transport.connect.assert_called_once()
669
+ assert transport.client is None
670
+
671
+ async def test_connect_without_client_attribute(self, rate_limit_config):
672
+ """Test that connect handles case when base transport doesn't have client attribute.
673
+
674
+ This tests the branch on line 195 where hasattr(self.base_transport, "client") returns False.
675
+ We use a mock that raises AttributeError when accessing the client attribute to test this branch.
676
+ """
677
+
678
+ class MockTransportWithoutClient:
679
+ def __init__(self):
680
+ self.connect_called = False
681
+
682
+ async def connect(self):
683
+ self.connect_called = True
684
+
685
+ def __getattribute__(self, name):
686
+ if name == "client":
687
+ raise AttributeError(
688
+ f"'{type(self).__name__}' object has no attribute 'client'"
689
+ )
690
+ return super().__getattribute__(name)
691
+
692
+ mock_base_transport = MockTransportWithoutClient()
693
+
694
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
695
+
696
+ assert not hasattr(transport.base_transport, "client")
697
+
698
+ with pytest.raises(AttributeError):
699
+ await transport.connect()
700
+
701
+ async def test_connect_isinstance_async_client_true(self, rate_limit_config):
702
+ """Test that connect wraps AsyncClient when isinstance check is True, covering line 197 positive branch."""
703
+ from connector.httpx_rewrite import AsyncClient
704
+
705
+ class MockTransport:
706
+ def __init__(self):
707
+ self.client = None
708
+ self.connect_called = False
709
+
710
+ async def connect(self):
711
+ self.connect_called = True
712
+
713
+ real_client = AsyncClient(base_url="https://example.com")
714
+ base_transport = MockTransport()
715
+ base_transport.client = real_client
716
+
717
+ transport = RateLimitedHTTPXAsyncTransport(base_transport, rate_limit_config)
718
+ await transport.connect()
719
+
720
+ assert base_transport.connect_called
721
+ assert isinstance(base_transport.client, RateLimitedClient)
722
+ assert base_transport.client.base_client is real_client
723
+ assert transport.client is base_transport.client
724
+
725
+ async def test_connect_isinstance_async_client_false(self, rate_limit_config):
726
+ """Test that connect does not wrap when isinstance check is False, covering line 197 negative branch."""
727
+ from connector.httpx_rewrite import AsyncClient
728
+
729
+ class MockTransport:
730
+ def __init__(self):
731
+ self.client = None
732
+ self.connect_called = False
733
+
734
+ async def connect(self):
735
+ self.connect_called = True
736
+
737
+ mock_client = MagicMock()
738
+ mock_client.some_attr = "value"
739
+ base_transport = MockTransport()
740
+ base_transport.client = mock_client
741
+
742
+ transport = RateLimitedHTTPXAsyncTransport(base_transport, rate_limit_config)
743
+ await transport.connect()
744
+
745
+ assert base_transport.connect_called
746
+ assert base_transport.client is mock_client
747
+ assert not isinstance(base_transport.client, RateLimitedClient)
748
+ assert not isinstance(base_transport.client, AsyncClient)
749
+ assert transport.client is mock_client
750
+
751
+ async def test_execute_with_all_parameters(self, mock_base_transport, rate_limit_config):
752
+ """Test execute method with all GraphQL parameters."""
753
+ mock_result = {"data": {"test": "result"}}
754
+ mock_base_transport.execute = AsyncMock(return_value=mock_result)
755
+
756
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
757
+
758
+ async def mock_execute_requests(self, requests, handler):
759
+ results = []
760
+ for req in requests:
761
+ result = await handler(req)
762
+ results.append(result)
763
+ return results
764
+
765
+ with patch.object(RateLimiter, "execute_requests", mock_execute_requests):
766
+ result = await transport.execute(
767
+ "query { test }", variable_values={"var": "value"}, operation_name="TestQuery"
768
+ )
769
+
770
+ assert result == mock_result
771
+ mock_base_transport.execute.assert_called_once()
772
+ call_args = mock_base_transport.execute.call_args
773
+ assert call_args.kwargs.get("document") == "query { test }"
774
+ assert call_args.kwargs.get("variable_values") == {"var": "value"}
775
+ assert call_args.kwargs.get("operation_name") == "TestQuery"
776
+
777
+ async def test_execute_calls_request_func_through_handler(
778
+ self, mock_base_transport, rate_limit_config
779
+ ):
780
+ """Test that execute calls request_func() through the handler lambda (lines 209-214).
781
+
782
+ This verifies that:
783
+ 1. A request_func closure is created (line 208-211) that calls base_transport.execute
784
+ 2. The request_func is passed to rate_limiter.execute_requests (line 214)
785
+ 3. The handler lambda (lambda x: x()) is used to call request_func (line 214)
786
+ 4. request_func() executes and calls base_transport.execute
787
+ """
788
+ mock_result = {"data": {"test": "result"}}
789
+ mock_base_transport.execute = AsyncMock(return_value=mock_result)
790
+
791
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
792
+
793
+ async def mock_execute_requests(self, requests, handler):
794
+ results = []
795
+ for req in requests:
796
+ result = await handler(req)
797
+ results.append(result)
798
+ return results
799
+
800
+ with patch.object(RateLimiter, "execute_requests", mock_execute_requests):
801
+ result = await transport.execute("query { test }", variable_values={"var": "value"})
802
+
803
+ assert result == mock_result
804
+ mock_base_transport.execute.assert_called_once()
805
+
806
+ call_args = mock_base_transport.execute.call_args
807
+ assert call_args.kwargs.get("document") == "query { test }"
808
+ assert call_args.kwargs.get("variable_values") == {"var": "value"}
809
+
810
+ async def test_execute_raises_on_no_response(self, mock_base_transport, rate_limit_config):
811
+ """Test that execute raises ConnectorError when rate limiter returns no response.
812
+
813
+ This tests the negative branch of line 215 where responses is falsy (empty list).
814
+ """
815
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
816
+
817
+ async def empty_execute_requests(self, requests, handler):
818
+ return []
819
+
820
+ with patch.object(RateLimiter, "execute_requests", empty_execute_requests):
821
+ with pytest.raises(ConnectorError) as exc_info:
822
+ await transport.execute("query { test }")
823
+
824
+ assert exc_info.value.message == "No response from GraphQL API"
825
+ assert exc_info.value.error_code == ErrorCode.API_ERROR
826
+
827
+ async def test_execute_with_successful_response(self, mock_base_transport, rate_limit_config):
828
+ """Test that execute returns response when rate limiter returns a response.
829
+
830
+ This tests the positive branch of line 215 where responses is truthy (non-empty list).
831
+ We patch at the class level to ensure the mock is actually used.
832
+ """
833
+ mock_result = {"data": {"test": "result"}}
834
+ mock_base_transport.execute = AsyncMock(return_value=mock_result)
835
+
836
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
837
+
838
+ async def mock_execute_requests(self, requests, handler):
839
+ results = []
840
+ for req in requests:
841
+ result = await handler(req)
842
+ results.append(result)
843
+ return results
844
+
845
+ with patch.object(RateLimiter, "execute_requests", mock_execute_requests):
846
+ result = await transport.execute("query { test }")
847
+
848
+ assert result == mock_result
849
+ mock_base_transport.execute.assert_called_once()
850
+
851
+ def test_delegation_and_state(self, mock_base_transport, rate_limit_config):
852
+ """Test attribute delegation and state retrieval."""
853
+ mock_base_transport.delegated_method = MagicMock(return_value="delegated_value")
854
+ transport = RateLimitedHTTPXAsyncTransport(mock_base_transport, rate_limit_config)
855
+
856
+ assert transport.some_attr == "value"
857
+ assert transport.delegated_method() == "delegated_value"
858
+
859
+ config, delay = transport.get_state()
860
+ assert config is rate_limit_config
861
+ assert isinstance(delay, float)
862
+
863
+
864
+ class TestBaseGraphQLSession:
865
+ """Test cases for BaseGraphQLSession."""
866
+
867
+ class ConcreteTestGraphQLSession(BaseGraphQLSession):
868
+ """Concrete implementation for testing."""
869
+
870
+ @classmethod
871
+ def prepare_client_args(cls, args: Request) -> dict[str, Any]:
872
+ from connector.httpx_rewrite import HTTPXAsyncTransport
873
+
874
+ return {
875
+ "transport": HTTPXAsyncTransport(
876
+ url="https://example.com/graphql",
877
+ auth=BearerAuth(
878
+ token=get_token_auth(args).token,
879
+ token_prefix="",
880
+ auth_header="Authorization",
881
+ ),
882
+ ),
883
+ }
884
+
885
+ def test_build_client_applies_rate_limiting(self, sample_request, rate_limit_config):
886
+ """Test that build_client wraps transport with rate limiter when config provided."""
887
+ client = self.ConcreteTestGraphQLSession.build_client(sample_request, rate_limit_config)
888
+
889
+ assert client is not None
890
+ assert isinstance(client.transport, RateLimitedHTTPXAsyncTransport)
891
+ assert client.transport.rate_limiter.config is rate_limit_config
892
+
893
+ client_no_rate_limit = self.ConcreteTestGraphQLSession.build_client(sample_request)
894
+ assert client_no_rate_limit is not None
895
+ assert not isinstance(client_no_rate_limit.transport, RateLimitedHTTPXAsyncTransport)
896
+
897
+ class TestSessionWithClassConfig(self.ConcreteTestGraphQLSession):
898
+ _rate_limit_config = RateLimitConfig(
899
+ app_id="class-config",
900
+ requests_per_window=5,
901
+ window_seconds=30,
902
+ )
903
+
904
+ client_with_class_config = TestSessionWithClassConfig.build_client(sample_request)
905
+ assert isinstance(client_with_class_config.transport, RateLimitedHTTPXAsyncTransport)
906
+
907
+ def test_get_current_rate_limits(self, sample_request, rate_limit_config):
908
+ """Test rate limit state retrieval."""
909
+ client_with_rate_limit = self.ConcreteTestGraphQLSession.build_client(
910
+ sample_request, rate_limit_config
911
+ )
912
+ session_with_rate_limit = self.ConcreteTestGraphQLSession(sample_request, rate_limit_config)
913
+ session_with_rate_limit.client = client_with_rate_limit
914
+
915
+ config, delay = session_with_rate_limit.get_current_rate_limits()
916
+ assert config is rate_limit_config
917
+ assert isinstance(delay, float)
918
+
919
+ client_no_rate_limit = self.ConcreteTestGraphQLSession.build_client(sample_request)
920
+ session_no_rate_limit = self.ConcreteTestGraphQLSession(sample_request)
921
+ session_no_rate_limit.client = client_no_rate_limit
922
+
923
+ config, delay = session_no_rate_limit.get_current_rate_limits()
924
+ assert config is None
925
+ assert delay == 0
926
+
927
+ async def test_context_manager(self, sample_request):
928
+ """Test async context manager functionality."""
929
+ with patch("connector.oai.base_clients.Client") as mock_client_class:
930
+ mock_client = MagicMock()
931
+ mock_client.__aenter__ = AsyncMock(return_value=mock_client)
932
+ mock_client.__aexit__ = AsyncMock()
933
+ mock_client_class.return_value = mock_client
934
+
935
+ session = self.ConcreteTestGraphQLSession(sample_request)
936
+
937
+ async with session as ctx:
938
+ assert ctx is session
939
+
940
+ mock_client.__aenter__.assert_called_once()
941
+ mock_client.__aexit__.assert_called_once()
942
+
943
+ test_exception = ValueError("Test error")
944
+ with pytest.raises(ValueError, match="Test error"):
945
+ async with session:
946
+ raise test_exception
@@ -1 +0,0 @@
1
- __version__ = "4.180.0"
File without changes