port-ocean 0.18.9__tar.gz → 0.19.1__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.

Potentially problematic release.


This version of port-ocean might be problematic. Click here for more details.

Files changed (188) hide show
  1. {port_ocean-0.18.9 → port_ocean-0.19.1}/PKG-INFO +1 -1
  2. port_ocean-0.19.1/port_ocean/clients/auth/auth_client.py +10 -0
  3. port_ocean-0.19.1/port_ocean/clients/auth/oauth_client.py +22 -0
  4. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/mixins/integrations.py +43 -4
  5. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/config/settings.py +4 -3
  6. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/defaults/initialize.py +16 -4
  7. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/helpers/retry.py +15 -1
  8. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/ocean.py +19 -7
  9. port_ocean-0.19.1/port_ocean/tests/clients/__init__.py +0 -0
  10. port_ocean-0.19.1/port_ocean/tests/clients/oauth/__init__.py +0 -0
  11. port_ocean-0.19.1/port_ocean/tests/clients/oauth/test_oauth_client.py +96 -0
  12. port_ocean-0.19.1/port_ocean/tests/helpers/__init__.py +0 -0
  13. port_ocean-0.19.1/port_ocean/tests/test_ocean.py +49 -0
  14. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/async_http.py +1 -1
  15. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/repeat.py +0 -2
  16. {port_ocean-0.18.9 → port_ocean-0.19.1}/pyproject.toml +1 -1
  17. {port_ocean-0.18.9 → port_ocean-0.19.1}/LICENSE.md +0 -0
  18. {port_ocean-0.18.9 → port_ocean-0.19.1}/README.md +0 -0
  19. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/Dockerfile.Deb +0 -0
  20. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/Dockerfile.alpine +0 -0
  21. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/Dockerfile.base.builder +0 -0
  22. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/Dockerfile.base.runner +0 -0
  23. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/Dockerfile.dockerignore +0 -0
  24. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/Makefile +0 -0
  25. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/grpcio.sh +0 -0
  26. {port_ocean-0.18.9 → port_ocean-0.19.1}/integrations/_infra/init.sh +0 -0
  27. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/__init__.py +0 -0
  28. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/bootstrap.py +0 -0
  29. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/__init__.py +0 -0
  30. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cli.py +0 -0
  31. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/__init__.py +0 -0
  32. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/defaults/__init___.py +0 -0
  33. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/defaults/clean.py +0 -0
  34. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/defaults/dock.py +0 -0
  35. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/defaults/group.py +0 -0
  36. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/list_integrations.py +0 -0
  37. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/main.py +0 -0
  38. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/new.py +0 -0
  39. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/pull.py +0 -0
  40. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/sail.py +0 -0
  41. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/commands/version.py +0 -0
  42. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/__init__.py +0 -0
  43. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
  44. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/extensions.py +0 -0
  45. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
  46. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
  47. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
  48. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
  49. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
  50. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
  51. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
  52. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
  53. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
  54. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
  55. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
  56. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
  57. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
  58. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
  59. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
  60. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
  61. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
  62. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
  63. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/cli/utils.py +0 -0
  64. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/__init__.py +0 -0
  65. {port_ocean-0.18.9/port_ocean/clients/port → port_ocean-0.19.1/port_ocean/clients/auth}/__init__.py +0 -0
  66. {port_ocean-0.18.9/port_ocean/clients/port/mixins → port_ocean-0.19.1/port_ocean/clients/port}/__init__.py +0 -0
  67. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/authentication.py +0 -0
  68. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/client.py +0 -0
  69. {port_ocean-0.18.9/port_ocean/config → port_ocean-0.19.1/port_ocean/clients/port/mixins}/__init__.py +0 -0
  70. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/mixins/blueprints.py +0 -0
  71. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/mixins/entities.py +0 -0
  72. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/mixins/migrations.py +0 -0
  73. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/mixins/organization.py +0 -0
  74. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/retry_transport.py +0 -0
  75. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/types.py +0 -0
  76. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/clients/port/utils.py +0 -0
  77. {port_ocean-0.18.9/port_ocean/consumers → port_ocean-0.19.1/port_ocean/config}/__init__.py +0 -0
  78. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/config/base.py +0 -0
  79. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/config/dynamic.py +0 -0
  80. {port_ocean-0.18.9/port_ocean/context → port_ocean-0.19.1/port_ocean/consumers}/__init__.py +0 -0
  81. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/consumers/kafka_consumer.py +0 -0
  82. {port_ocean-0.18.9/port_ocean/core → port_ocean-0.19.1/port_ocean/context}/__init__.py +0 -0
  83. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/context/event.py +0 -0
  84. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/context/ocean.py +0 -0
  85. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/context/resource.py +0 -0
  86. {port_ocean-0.18.9/port_ocean/core/handlers/entities_state_applier/port → port_ocean-0.19.1/port_ocean/core}/__init__.py +0 -0
  87. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/defaults/__init__.py +0 -0
  88. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/defaults/clean.py +0 -0
  89. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/defaults/common.py +0 -0
  90. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/__init__.py +0 -0
  91. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/base.py +0 -0
  92. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/factory.py +0 -0
  93. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/http.py +0 -0
  94. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/kafka.py +0 -0
  95. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/once.py +0 -0
  96. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/polling.py +0 -0
  97. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/event_listener/webhooks_only.py +0 -0
  98. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/__init__.py +0 -0
  99. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/base.py +0 -0
  100. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
  101. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
  102. {port_ocean-0.18.9/port_ocean/core/handlers/webhook → port_ocean-0.19.1/port_ocean/core/handlers/entities_state_applier/port}/__init__.py +0 -0
  103. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
  104. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
  105. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
  106. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
  107. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entity_processor/base.py +0 -0
  108. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
  109. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
  110. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/port_app_config/api.py +0 -0
  111. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/port_app_config/base.py +0 -0
  112. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/port_app_config/models.py +0 -0
  113. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/queue/__init__.py +0 -0
  114. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/queue/abstract_queue.py +0 -0
  115. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/queue/local_queue.py +0 -0
  116. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
  117. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
  118. {port_ocean-0.18.9/port_ocean/core/integrations → port_ocean-0.19.1/port_ocean/core/handlers/webhook}/__init__.py +0 -0
  119. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/webhook/abstract_webhook_processor.py +0 -0
  120. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/webhook/processor_manager.py +0 -0
  121. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/handlers/webhook/webhook_event.py +0 -0
  122. {port_ocean-0.18.9/port_ocean/exceptions → port_ocean-0.19.1/port_ocean/core/integrations}/__init__.py +0 -0
  123. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/integrations/base.py +0 -0
  124. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/integrations/mixins/__init__.py +0 -0
  125. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/integrations/mixins/events.py +0 -0
  126. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/integrations/mixins/handler.py +0 -0
  127. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/integrations/mixins/sync.py +0 -0
  128. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/integrations/mixins/sync_raw.py +0 -0
  129. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/integrations/mixins/utils.py +0 -0
  130. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/models.py +0 -0
  131. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/ocean_types.py +0 -0
  132. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/utils/entity_topological_sorter.py +0 -0
  133. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/core/utils/utils.py +0 -0
  134. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/debug_cli.py +0 -0
  135. {port_ocean-0.18.9/port_ocean/helpers → port_ocean-0.19.1/port_ocean/exceptions}/__init__.py +0 -0
  136. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/api.py +0 -0
  137. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/base.py +0 -0
  138. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/clients.py +0 -0
  139. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/context.py +0 -0
  140. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/core.py +0 -0
  141. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/port_defaults.py +0 -0
  142. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/utils.py +0 -0
  143. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/exceptions/webhook_processor.py +0 -0
  144. {port_ocean-0.18.9/port_ocean/log → port_ocean-0.19.1/port_ocean/helpers}/__init__.py +0 -0
  145. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/helpers/async_client.py +0 -0
  146. {port_ocean-0.18.9/port_ocean/tests → port_ocean-0.19.1/port_ocean/log}/__init__.py +0 -0
  147. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/log/handlers.py +0 -0
  148. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/log/logger_setup.py +0 -0
  149. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/log/sensetive.py +0 -0
  150. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/middlewares.py +0 -0
  151. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/py.typed +0 -0
  152. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/run.py +0 -0
  153. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/sonar-project.properties +0 -0
  154. {port_ocean-0.18.9/port_ocean/tests/helpers → port_ocean-0.19.1/port_ocean/tests}/__init__.py +0 -0
  155. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
  156. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/clients/port/mixins/test_organization_mixin.py +0 -0
  157. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/conftest.py +0 -0
  158. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/defaults/test_common.py +0 -0
  159. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +0 -0
  160. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +0 -0
  161. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py +0 -0
  162. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/port_app_config/test_api.py +0 -0
  163. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/port_app_config/test_base.py +0 -0
  164. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/queue/test_local_queue.py +0 -0
  165. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +0 -0
  166. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +0 -0
  167. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +0 -0
  168. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/test_utils.py +0 -0
  169. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/utils/test_entity_topological_sorter.py +0 -0
  170. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/core/utils/test_resolve_entities_diff.py +0 -0
  171. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/helpers/fake_port_api.py +0 -0
  172. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/helpers/fixtures.py +0 -0
  173. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/helpers/integration.py +0 -0
  174. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/helpers/ocean_app.py +0 -0
  175. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/helpers/port_client.py +0 -0
  176. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/helpers/smoke_test.py +0 -0
  177. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/log/test_handlers.py +0 -0
  178. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/test_smoke.py +0 -0
  179. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/utils/test_async_iterators.py +0 -0
  180. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/tests/utils/test_cache.py +0 -0
  181. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/__init__.py +0 -0
  182. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/async_iterators.py +0 -0
  183. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/cache.py +0 -0
  184. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/misc.py +0 -0
  185. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/queue_utils.py +0 -0
  186. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/signal.py +0 -0
  187. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/utils/time.py +0 -0
  188. {port_ocean-0.18.9 → port_ocean-0.19.1}/port_ocean/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.18.9
3
+ Version: 0.19.1
4
4
  Summary: Port Ocean is a CLI tool for managing your Port projects.
5
5
  Home-page: https://app.getport.io
6
6
  Keywords: ocean,port-ocean,port
@@ -0,0 +1,10 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ import httpx
4
+
5
+
6
+ class AuthClient(ABC):
7
+
8
+ @abstractmethod
9
+ def refresh_request_auth_creds(self, request: httpx.Request) -> httpx.Request:
10
+ pass
@@ -0,0 +1,22 @@
1
+ from port_ocean.clients.auth.auth_client import AuthClient
2
+ from port_ocean.context.ocean import ocean
3
+ from port_ocean.helpers.retry import register_on_retry_callback
4
+
5
+
6
+ class OAuthClient(AuthClient):
7
+ def __init__(self) -> None:
8
+ """
9
+ A client that can refresh a request using an access token.
10
+ """
11
+ if self.is_oauth_enabled():
12
+ register_on_retry_callback(self.refresh_request_auth_creds)
13
+
14
+ def is_oauth_enabled(self) -> bool:
15
+ return ocean.app.load_external_oauth_access_token() is not None
16
+
17
+ @property
18
+ def external_access_token(self) -> str:
19
+ access_token = ocean.app.load_external_oauth_access_token()
20
+ if access_token is None:
21
+ raise ValueError("No external access token found")
22
+ return access_token
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- from typing import Any, Dict, TYPE_CHECKING, Optional, TypedDict
2
+ from typing import Any, Dict, List, TYPE_CHECKING, Optional, TypedDict
3
3
  from urllib.parse import quote_plus
4
4
 
5
5
  import httpx
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
14
14
 
15
15
 
16
16
  INTEGRATION_POLLING_INTERVAL_INITIAL_SECONDS = 3
17
- INTEGRATION_POLLING_INTERVAL_BACKOFF_FACTOR = 1.15
17
+ INTEGRATION_POLLING_INTERVAL_BACKOFF_FACTOR = 1.55
18
18
  INTEGRATION_POLLING_RETRY_LIMIT = 30
19
19
  CREATE_RESOURCES_PARAM_NAME = "integration_modes"
20
20
  CREATE_RESOURCES_PARAM_VALUE = ["create_resources"]
@@ -38,6 +38,27 @@ class IntegrationClientMixin:
38
38
  self.client = client
39
39
  self._log_attributes: LogAttributes | None = None
40
40
 
41
+ async def is_integration_provision_enabled(
42
+ self, integration_type: str, should_raise: bool = True, should_log: bool = True
43
+ ) -> bool:
44
+ enabled_integrations = await self.get_provision_enabled_integrations(
45
+ should_raise, should_log
46
+ )
47
+ return integration_type in enabled_integrations
48
+
49
+ async def get_provision_enabled_integrations(
50
+ self, should_raise: bool = True, should_log: bool = True
51
+ ) -> List[str]:
52
+ logger.info("Fetching provision enabled integrations")
53
+ response = await self.client.get(
54
+ f"{self.auth.api_url}/integration/provision-enabled",
55
+ headers=await self.auth.headers(),
56
+ )
57
+
58
+ handle_status_code(response, should_raise, should_log)
59
+
60
+ return response.json().get("integrations", [])
61
+
41
62
  async def _get_current_integration(self) -> httpx.Response:
42
63
  logger.info(f"Fetching integration with id: {self.integration_identifier}")
43
64
  response = await self.client.get(
@@ -51,7 +72,25 @@ class IntegrationClientMixin:
51
72
  ) -> dict[str, Any]:
52
73
  response = await self._get_current_integration()
53
74
  handle_status_code(response, should_raise, should_log)
54
- return response.json().get("integration", {})
75
+ integration = response.json().get("integration", {})
76
+ if integration.get("config", None) or not integration:
77
+ return integration
78
+ is_provision_enabled_for_integration = integration.get(
79
+ "installationAppType", None
80
+ ) and (
81
+ await self.is_integration_provision_enabled(
82
+ integration.get("installationAppType", ""),
83
+ should_raise,
84
+ should_log,
85
+ )
86
+ )
87
+
88
+ if is_provision_enabled_for_integration:
89
+ logger.info(
90
+ "integration type is enabled, polling until provisioning is complete"
91
+ )
92
+ return self._poll_integration_until_default_provisioning_is_complete()
93
+ return integration
55
94
 
56
95
  async def get_log_attributes(self) -> LogAttributes:
57
96
  if self._log_attributes is None:
@@ -72,7 +111,7 @@ class IntegrationClientMixin:
72
111
  )
73
112
  response = await self._get_current_integration()
74
113
  integration_json = response.json()
75
- if integration_json.get("integration", {}).get("config", {}):
114
+ if integration_json.get("integration", {}).get("config", {}) != {}:
76
115
  return integration_json
77
116
 
78
117
  logger.info(
@@ -1,12 +1,12 @@
1
1
  from typing import Any, Literal, Type, cast
2
2
 
3
- from pydantic import Extra, AnyHttpUrl, parse_obj_as, parse_raw_as
3
+ from pydantic import AnyHttpUrl, Extra, parse_obj_as, parse_raw_as
4
4
  from pydantic.class_validators import root_validator, validator
5
- from pydantic.env_settings import InitSettingsSource, EnvSettingsSource, BaseSettings
5
+ from pydantic.env_settings import BaseSettings, EnvSettingsSource, InitSettingsSource
6
6
  from pydantic.fields import Field
7
7
  from pydantic.main import BaseModel
8
8
 
9
- from port_ocean.config.base import BaseOceanSettings, BaseOceanModel
9
+ from port_ocean.config.base import BaseOceanModel, BaseOceanSettings
10
10
  from port_ocean.core.event_listener import EventListenerSettingsType
11
11
  from port_ocean.core.models import CreatePortResourcesOrigin, Runtime
12
12
  from port_ocean.utils.misc import get_integration_name, get_spec_file
@@ -71,6 +71,7 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
71
71
  # Determines if Port should generate resources such as blueprints and pages instead of ocean
72
72
  create_port_resources_origin: CreatePortResourcesOrigin | None = None
73
73
  send_raw_data_examples: bool = True
74
+ oauth_access_token_file_path: str | None = None
74
75
  port: PortSettings
75
76
  event_listener: EventListenerSettingsType = Field(
76
77
  default=cast(EventListenerSettingsType, {"type": "POLLING"})
@@ -75,7 +75,7 @@ async def _initialize_required_integration_settings(
75
75
  create_port_resources_origin_in_port=integration_config.create_port_resources_origin
76
76
  == CreatePortResourcesOrigin.Port,
77
77
  )
78
- elif not integration.get("config"):
78
+ elif not integration.get("config", None):
79
79
  logger.info(
80
80
  "Encountered that the integration's mapping is empty, Initializing to default mapping"
81
81
  )
@@ -213,11 +213,20 @@ async def _initialize_defaults(
213
213
  config_class, integration_config.resources_path
214
214
  )
215
215
 
216
+ is_integration_provision_enabled = (
217
+ await port_client.is_integration_provision_enabled(
218
+ integration_config.integration.type
219
+ )
220
+ )
221
+
216
222
  if (
217
223
  not integration_config.create_port_resources_origin
218
- and integration_config.runtime.is_saas_runtime
224
+ and is_integration_provision_enabled
219
225
  ):
220
- logger.info("Setting resources origin to be Port")
226
+ # Need to set default since spec is missing
227
+ logger.info(
228
+ f"Setting resources origin to be Port (integration {integration_config.integration.type} is supported)"
229
+ )
221
230
  integration_config.create_port_resources_origin = CreatePortResourcesOrigin.Port
222
231
 
223
232
  if (
@@ -228,7 +237,10 @@ async def _initialize_defaults(
228
237
  "Resources origin is set to be Port, verifying integration is supported"
229
238
  )
230
239
  org_feature_flags = await port_client.get_organization_feature_flags()
231
- if ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG not in org_feature_flags:
240
+ if (
241
+ not is_integration_provision_enabled
242
+ or ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG not in org_feature_flags
243
+ ):
232
244
  logger.info(
233
245
  "Port origin for Integration is not supported, changing resources origin to use Ocean"
234
246
  )
@@ -9,6 +9,15 @@ from typing import Any, Callable, Coroutine, Iterable, Mapping, Union
9
9
  import httpx
10
10
  from dateutil.parser import isoparse
11
11
 
12
+ _ON_RETRY_CALLBACK: Callable[[httpx.Request], httpx.Request] | None = None
13
+
14
+
15
+ def register_on_retry_callback(
16
+ _on_retry_callback: Callable[[httpx.Request], httpx.Request]
17
+ ) -> None:
18
+ global _ON_RETRY_CALLBACK
19
+ _ON_RETRY_CALLBACK = _on_retry_callback
20
+
12
21
 
13
22
  # Adapted from https://github.com/encode/httpx/issues/108#issuecomment-1434439481
14
23
  class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
@@ -43,7 +52,6 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
43
52
  _retry_status_codes (frozenset): The HTTP status codes that can be retried.
44
53
  _jitter_ratio (float): The amount of jitter to add to the backoff time.
45
54
  _max_backoff_wait (float): The maximum time to wait between retries in seconds.
46
-
47
55
  """
48
56
 
49
57
  RETRYABLE_METHODS = frozenset(["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"])
@@ -53,6 +61,8 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
53
61
  HTTPStatus.BAD_GATEWAY,
54
62
  HTTPStatus.SERVICE_UNAVAILABLE,
55
63
  HTTPStatus.GATEWAY_TIMEOUT,
64
+ HTTPStatus.UNAUTHORIZED,
65
+ HTTPStatus.BAD_REQUEST,
56
66
  ]
57
67
  )
58
68
  MAX_BACKOFF_WAIT_IN_SECONDS = 60
@@ -316,6 +326,8 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
316
326
  if remaining_attempts < 1:
317
327
  self._log_error(request, error)
318
328
  raise
329
+ if _ON_RETRY_CALLBACK:
330
+ request = _ON_RETRY_CALLBACK(request)
319
331
  attempts_made += 1
320
332
  remaining_attempts -= 1
321
333
 
@@ -357,5 +369,7 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
357
369
  if remaining_attempts < 1:
358
370
  self._log_error(request, error)
359
371
  raise
372
+ if _ON_RETRY_CALLBACK:
373
+ request = _ON_RETRY_CALLBACK(request)
360
374
  attempts_made += 1
361
375
  remaining_attempts -= 1
@@ -1,31 +1,31 @@
1
1
  import asyncio
2
2
  import sys
3
- import threading
4
3
  from contextlib import asynccontextmanager
5
- from typing import Callable, Any, Dict, AsyncIterator, Type
4
+ import threading
5
+ from typing import Any, AsyncIterator, Callable, Dict, Type
6
6
 
7
- from fastapi import FastAPI, APIRouter
7
+ from fastapi import APIRouter, FastAPI
8
8
  from loguru import logger
9
9
  from pydantic import BaseModel
10
- from starlette.types import Scope, Receive, Send
10
+ from starlette.types import Receive, Scope, Send
11
11
 
12
- from port_ocean.core.handlers.resync_state_updater import ResyncStateUpdater
13
12
  from port_ocean.clients.port.client import PortClient
14
13
  from port_ocean.config.settings import (
15
14
  IntegrationConfiguration,
16
15
  )
17
16
  from port_ocean.context.ocean import (
18
17
  PortOceanContext,
19
- ocean,
20
18
  initialize_port_ocean_context,
19
+ ocean,
21
20
  )
21
+ from port_ocean.core.handlers.resync_state_updater import ResyncStateUpdater
22
22
  from port_ocean.core.integrations.base import BaseIntegration
23
23
  from port_ocean.log.sensetive import sensitive_log_filter
24
24
  from port_ocean.middlewares import request_handler
25
+ from port_ocean.utils.misc import IntegrationStateStatus
25
26
  from port_ocean.utils.repeat import repeat_every
26
27
  from port_ocean.utils.signal import signal_handler
27
28
  from port_ocean.version import __integration_version__
28
- from port_ocean.utils.misc import IntegrationStateStatus
29
29
  from port_ocean.core.handlers.webhook.processor_manager import WebhookProcessorManager
30
30
 
31
31
 
@@ -118,6 +118,18 @@ class Ocean:
118
118
  )
119
119
  await repeated_function()
120
120
 
121
+ def load_external_oauth_access_token(self) -> str | None:
122
+ if self.config.oauth_access_token_file_path is not None:
123
+ try:
124
+ with open(self.config.oauth_access_token_file_path, "r") as f:
125
+ return f.read()
126
+ except Exception:
127
+ logger.exception(
128
+ "Failed to load external oauth access token from file",
129
+ file_path=self.config.oauth_access_token_file_path,
130
+ )
131
+ return None
132
+
121
133
  def initialize_app(self) -> None:
122
134
  self.fast_api_app.include_router(self.integration_router, prefix="/integration")
123
135
 
File without changes
@@ -0,0 +1,96 @@
1
+ import pytest
2
+ import httpx
3
+ from unittest.mock import MagicMock, patch
4
+ from port_ocean.ocean import Ocean
5
+
6
+ from port_ocean.clients.auth.oauth_client import OAuthClient
7
+ from port_ocean.config.settings import IntegrationConfiguration
8
+
9
+
10
+ @pytest.fixture
11
+ def mock_ocean() -> Ocean:
12
+ with patch("port_ocean.ocean.Ocean.__init__", return_value=None):
13
+ ocean_mock = Ocean()
14
+ ocean_mock.config = MagicMock(spec=IntegrationConfiguration)
15
+ return ocean_mock
16
+
17
+
18
+ class MockOAuthClient(OAuthClient):
19
+ def __init__(self, is_oauth_enabled_value: bool = True):
20
+ self._is_oauth_enabled = is_oauth_enabled_value
21
+ self._access_token = "mock_access_token"
22
+ super().__init__()
23
+
24
+ def is_oauth_enabled(self) -> bool:
25
+ return self._is_oauth_enabled
26
+
27
+ def refresh_request_auth_creds(self, request: httpx.Request) -> httpx.Request:
28
+ headers = dict(request.headers)
29
+ headers["Authorization"] = f"Bearer {self.access_token}"
30
+ return httpx.Request(
31
+ method=request.method,
32
+ url=request.url,
33
+ headers=headers,
34
+ content=request.content,
35
+ )
36
+
37
+ @property
38
+ def access_token(self) -> str:
39
+ return self._access_token
40
+
41
+
42
+ @pytest.fixture
43
+ def mock_oauth_client() -> MockOAuthClient:
44
+ return MockOAuthClient()
45
+
46
+
47
+ @pytest.fixture
48
+ def disabled_oauth_client() -> MockOAuthClient:
49
+ return MockOAuthClient(is_oauth_enabled_value=False)
50
+
51
+
52
+ def test_oauth_client_initialization(mock_oauth_client: MockOAuthClient) -> None:
53
+ assert isinstance(mock_oauth_client, OAuthClient)
54
+ assert mock_oauth_client.is_oauth_enabled() is True
55
+
56
+
57
+ def test_oauth_client_disabled_initialization(
58
+ disabled_oauth_client: MockOAuthClient,
59
+ ) -> None:
60
+ assert isinstance(disabled_oauth_client, OAuthClient)
61
+ assert disabled_oauth_client.is_oauth_enabled() is False
62
+
63
+
64
+ def test_refresh_request_auth_creds(mock_oauth_client: MockOAuthClient) -> None:
65
+ # Create request with some content and existing headers
66
+ original_headers = {"Accept": "application/json", "X-Custom": "value"}
67
+ original_content = b'{"key": "value"}'
68
+ original_request = httpx.Request(
69
+ "GET",
70
+ "https://api.example.com",
71
+ headers=original_headers,
72
+ content=original_content,
73
+ )
74
+
75
+ refreshed_request = mock_oauth_client.refresh_request_auth_creds(original_request)
76
+
77
+ # Verify all attributes are identical except headers
78
+ assert refreshed_request.method == original_request.method
79
+ assert refreshed_request.url == original_request.url
80
+ assert refreshed_request.content == original_request.content
81
+
82
+ # Verify headers: should contain all original headers plus the new Authorization
83
+ for key, value in original_headers.items():
84
+ assert refreshed_request.headers[key] == value
85
+ assert refreshed_request.headers["Authorization"] == "Bearer mock_access_token"
86
+ # New headers should be:
87
+ # {'host': 'api.example.com',
88
+ # 'accept': 'application/json',
89
+ # 'x-custom': 'value',
90
+ # 'content-length': '16',
91
+ # 'authorization': '[secure]'}
92
+ assert len(refreshed_request.headers) == 5
93
+
94
+
95
+ def test_access_token_property(mock_oauth_client: MockOAuthClient) -> None:
96
+ assert mock_oauth_client.access_token == "mock_access_token"
File without changes
@@ -0,0 +1,49 @@
1
+ import pytest
2
+ from unittest.mock import MagicMock, mock_open, patch
3
+ from port_ocean.ocean import Ocean
4
+ from port_ocean.config.settings import IntegrationConfiguration
5
+
6
+
7
+ @pytest.fixture
8
+ def mock_ocean() -> Ocean:
9
+ with patch("port_ocean.ocean.Ocean.__init__", return_value=None):
10
+ ocean_mock = Ocean()
11
+ ocean_mock.config = MagicMock(spec=IntegrationConfiguration)
12
+ return ocean_mock
13
+
14
+
15
+ def test_load_external_oauth_access_token_no_file(mock_ocean: Ocean) -> None:
16
+ # Setup
17
+ mock_ocean.config.oauth_access_token_file_path = None
18
+
19
+ # Execute
20
+ result = mock_ocean.load_external_oauth_access_token()
21
+
22
+ # Assert
23
+ assert result is None
24
+
25
+
26
+ def test_load_external_oauth_access_token_with_file(mock_ocean: Ocean) -> None:
27
+ # Setup
28
+ mock_ocean.config.oauth_access_token_file_path = "/path/to/token.txt"
29
+ mock_file_content = "test_access_token"
30
+
31
+ with patch("builtins.open", mock_open(read_data=mock_file_content)):
32
+ # Execute
33
+ result = mock_ocean.load_external_oauth_access_token()
34
+
35
+ # Assert
36
+ assert result == "test_access_token"
37
+
38
+
39
+ def test_load_external_oauth_access_token_with_empty_file(mock_ocean: Ocean) -> None:
40
+ # Setup
41
+ mock_ocean.config.oauth_access_token_file_path = "/path/to/token.txt"
42
+ mock_file_content = ""
43
+
44
+ with patch("builtins.open", mock_open(read_data=mock_file_content)):
45
+ # Execute
46
+ result = mock_ocean.load_external_oauth_access_token()
47
+
48
+ # Assert
49
+ assert result == ""
@@ -18,7 +18,7 @@ def _get_http_client_context() -> httpx.AsyncClient:
18
18
 
19
19
 
20
20
  """
21
- Utilize this client for all outbound integration requests to the third-party application. It functions as a wrapper
21
+ Utilize this client for all outbound integration requests to the third-party application. It functions as a wrapper
22
22
  around the httpx.AsyncClient, incorporating retry logic at the transport layer for handling retries on 5xx errors and
23
23
  connection errors.
24
24
 
@@ -22,10 +22,8 @@ def repeat_every(
22
22
  ) -> NoArgsNoReturnDecorator:
23
23
  """
24
24
  This function returns a decorator that modifies a function so it is periodically re-executed after its first call.
25
-
26
25
  The function it decorates should accept no arguments and return nothing. If necessary, this can be accomplished
27
26
  by using `functools.partial` or otherwise wrapping the target function prior to decoration.
28
-
29
27
  Parameters
30
28
  ----------
31
29
  seconds: float
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "port-ocean"
3
- version = "0.18.9"
3
+ version = "0.19.1"
4
4
  description = "Port Ocean is a CLI tool for managing your Port projects."
5
5
  readme = "README.md"
6
6
  homepage = "https://app.getport.io"
File without changes
File without changes