port-ocean 0.19.2__tar.gz → 0.20.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. {port_ocean-0.19.2 → port_ocean-0.20.0}/PKG-INFO +1 -1
  2. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/config/settings.py +1 -0
  3. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/context/ocean.py +2 -7
  4. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/webhook/abstract_webhook_processor.py +18 -2
  5. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/webhook/processor_manager.py +107 -65
  6. port_ocean-0.20.0/port_ocean/core/handlers/webhook/webhook_event.py +140 -0
  7. port_ocean-0.20.0/port_ocean/core/integrations/mixins/live_events.py +88 -0
  8. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/ocean.py +19 -3
  9. port_ocean-0.20.0/port_ocean/tests/core/handlers/mixins/test_live_events.py +404 -0
  10. port_ocean-0.20.0/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +106 -0
  11. port_ocean-0.20.0/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +1264 -0
  12. port_ocean-0.20.0/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +106 -0
  13. {port_ocean-0.19.2 → port_ocean-0.20.0}/pyproject.toml +1 -1
  14. port_ocean-0.19.2/port_ocean/core/handlers/webhook/webhook_event.py +0 -77
  15. port_ocean-0.19.2/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +0 -115
  16. port_ocean-0.19.2/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +0 -391
  17. port_ocean-0.19.2/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +0 -65
  18. {port_ocean-0.19.2 → port_ocean-0.20.0}/LICENSE.md +0 -0
  19. {port_ocean-0.19.2 → port_ocean-0.20.0}/README.md +0 -0
  20. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/Dockerfile.Deb +0 -0
  21. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/Dockerfile.alpine +0 -0
  22. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/Dockerfile.base.builder +0 -0
  23. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/Dockerfile.base.runner +0 -0
  24. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/Dockerfile.dockerignore +0 -0
  25. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/Makefile +0 -0
  26. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/grpcio.sh +0 -0
  27. {port_ocean-0.19.2 → port_ocean-0.20.0}/integrations/_infra/init.sh +0 -0
  28. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/__init__.py +0 -0
  29. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/bootstrap.py +0 -0
  30. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/__init__.py +0 -0
  31. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cli.py +0 -0
  32. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/__init__.py +0 -0
  33. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/defaults/__init___.py +0 -0
  34. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/defaults/clean.py +0 -0
  35. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/defaults/dock.py +0 -0
  36. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/defaults/group.py +0 -0
  37. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/list_integrations.py +0 -0
  38. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/main.py +0 -0
  39. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/new.py +0 -0
  40. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/pull.py +0 -0
  41. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/sail.py +0 -0
  42. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/commands/version.py +0 -0
  43. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/__init__.py +0 -0
  44. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
  45. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/extensions.py +0 -0
  46. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
  47. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
  48. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
  49. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
  50. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
  51. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
  52. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
  53. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
  54. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
  55. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
  56. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
  57. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
  58. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
  59. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
  60. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
  61. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
  62. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
  63. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
  64. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/cli/utils.py +0 -0
  65. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/__init__.py +0 -0
  66. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/auth/__init__.py +0 -0
  67. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/auth/auth_client.py +0 -0
  68. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/auth/oauth_client.py +0 -0
  69. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/__init__.py +0 -0
  70. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/authentication.py +0 -0
  71. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/client.py +0 -0
  72. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/mixins/__init__.py +0 -0
  73. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/mixins/blueprints.py +0 -0
  74. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/mixins/entities.py +0 -0
  75. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/mixins/integrations.py +0 -0
  76. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/mixins/migrations.py +0 -0
  77. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/mixins/organization.py +0 -0
  78. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/retry_transport.py +0 -0
  79. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/types.py +0 -0
  80. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/clients/port/utils.py +0 -0
  81. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/config/__init__.py +0 -0
  82. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/config/base.py +0 -0
  83. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/config/dynamic.py +0 -0
  84. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/consumers/__init__.py +0 -0
  85. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/consumers/kafka_consumer.py +0 -0
  86. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/context/__init__.py +0 -0
  87. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/context/event.py +0 -0
  88. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/context/resource.py +0 -0
  89. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/__init__.py +0 -0
  90. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/defaults/__init__.py +0 -0
  91. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/defaults/clean.py +0 -0
  92. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/defaults/common.py +0 -0
  93. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/defaults/initialize.py +0 -0
  94. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/__init__.py +0 -0
  95. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/base.py +0 -0
  96. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/factory.py +0 -0
  97. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/http.py +0 -0
  98. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/kafka.py +0 -0
  99. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/once.py +0 -0
  100. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/polling.py +0 -0
  101. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/event_listener/webhooks_only.py +0 -0
  102. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/__init__.py +0 -0
  103. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/base.py +0 -0
  104. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
  105. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
  106. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
  107. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
  108. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
  109. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
  110. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
  111. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entity_processor/base.py +0 -0
  112. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
  113. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
  114. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/port_app_config/api.py +0 -0
  115. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/port_app_config/base.py +0 -0
  116. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/port_app_config/models.py +0 -0
  117. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/queue/__init__.py +0 -0
  118. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/queue/abstract_queue.py +0 -0
  119. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/queue/local_queue.py +0 -0
  120. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
  121. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
  122. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/handlers/webhook/__init__.py +0 -0
  123. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/__init__.py +0 -0
  124. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/base.py +0 -0
  125. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/mixins/__init__.py +0 -0
  126. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/mixins/events.py +0 -0
  127. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/mixins/handler.py +0 -0
  128. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/mixins/sync.py +0 -0
  129. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/mixins/sync_raw.py +0 -0
  130. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/integrations/mixins/utils.py +0 -0
  131. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/models.py +0 -0
  132. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/ocean_types.py +0 -0
  133. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/utils/entity_topological_sorter.py +0 -0
  134. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/core/utils/utils.py +0 -0
  135. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/debug_cli.py +0 -0
  136. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/__init__.py +0 -0
  137. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/api.py +0 -0
  138. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/base.py +0 -0
  139. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/clients.py +0 -0
  140. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/context.py +0 -0
  141. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/core.py +0 -0
  142. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/port_defaults.py +0 -0
  143. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/utils.py +0 -0
  144. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/exceptions/webhook_processor.py +0 -0
  145. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/helpers/__init__.py +0 -0
  146. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/helpers/async_client.py +0 -0
  147. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/helpers/retry.py +0 -0
  148. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/log/__init__.py +0 -0
  149. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/log/handlers.py +0 -0
  150. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/log/logger_setup.py +0 -0
  151. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/log/sensetive.py +0 -0
  152. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/middlewares.py +0 -0
  153. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/py.typed +0 -0
  154. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/run.py +0 -0
  155. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/sonar-project.properties +0 -0
  156. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/__init__.py +0 -0
  157. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/clients/__init__.py +0 -0
  158. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/clients/oauth/__init__.py +0 -0
  159. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/clients/oauth/test_oauth_client.py +0 -0
  160. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
  161. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/clients/port/mixins/test_organization_mixin.py +0 -0
  162. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/conftest.py +0 -0
  163. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/defaults/test_common.py +0 -0
  164. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +0 -0
  165. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +0 -0
  166. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py +0 -0
  167. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/handlers/port_app_config/test_api.py +0 -0
  168. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/handlers/port_app_config/test_base.py +0 -0
  169. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/handlers/queue/test_local_queue.py +0 -0
  170. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/test_utils.py +0 -0
  171. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/utils/test_entity_topological_sorter.py +0 -0
  172. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/core/utils/test_resolve_entities_diff.py +0 -0
  173. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/helpers/__init__.py +0 -0
  174. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/helpers/fake_port_api.py +0 -0
  175. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/helpers/fixtures.py +0 -0
  176. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/helpers/integration.py +0 -0
  177. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/helpers/ocean_app.py +0 -0
  178. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/helpers/port_client.py +0 -0
  179. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/helpers/smoke_test.py +0 -0
  180. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/log/test_handlers.py +0 -0
  181. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/test_ocean.py +0 -0
  182. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/test_smoke.py +0 -0
  183. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/utils/test_async_iterators.py +0 -0
  184. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/tests/utils/test_cache.py +0 -0
  185. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/__init__.py +0 -0
  186. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/async_http.py +0 -0
  187. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/async_iterators.py +0 -0
  188. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/cache.py +0 -0
  189. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/misc.py +0 -0
  190. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/queue_utils.py +0 -0
  191. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/repeat.py +0 -0
  192. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/signal.py +0 -0
  193. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/utils/time.py +0 -0
  194. {port_ocean-0.19.2 → port_ocean-0.20.0}/port_ocean/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.19.2
3
+ Version: 0.20.0
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
@@ -72,6 +72,7 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
72
72
  create_port_resources_origin: CreatePortResourcesOrigin | None = None
73
73
  send_raw_data_examples: bool = True
74
74
  oauth_access_token_file_path: str | None = None
75
+ base_url: str | None = None
75
76
  port: PortSettings
76
77
  event_listener: EventListenerSettingsType = Field(
77
78
  default=cast(EventListenerSettingsType, {"type": "POLLING"})
@@ -146,12 +146,7 @@ class PortOceanContext:
146
146
  async def sync_raw_all(self) -> None:
147
147
  await self.integration.sync_raw_all(trigger_type="manual")
148
148
 
149
- def add_webhook_processor(
150
- self,
151
- path: str,
152
- processor: type,
153
- events_filter: Callable[[Any], bool] = lambda _: True,
154
- ) -> None:
149
+ def add_webhook_processor(self, path: str, processor: type) -> None:
155
150
  """
156
151
  Registers a webhook processor for a specific path.
157
152
 
@@ -175,7 +170,7 @@ class PortOceanContext:
175
170
  Raises:
176
171
  ValueError: If the processor does not extend AbstractWebhookProcessor.
177
172
  """
178
- self.app.webhook_manager.register_processor(path, processor, events_filter)
173
+ self.app.webhook_manager.register_processor(path, processor)
179
174
 
180
175
 
181
176
  _port_ocean: PortOceanContext = PortOceanContext(None)
@@ -1,9 +1,15 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from loguru import logger
3
3
 
4
+ from port_ocean.core.handlers.port_app_config.models import ResourceConfig
4
5
  from port_ocean.exceptions.webhook_processor import RetryableError
5
6
 
6
- from .webhook_event import WebhookEvent, EventPayload, EventHeaders
7
+ from .webhook_event import (
8
+ WebhookEvent,
9
+ EventPayload,
10
+ EventHeaders,
11
+ WebhookEventRawResults,
12
+ )
7
13
 
8
14
 
9
15
  class AbstractWebhookProcessor(ABC):
@@ -96,6 +102,16 @@ class AbstractWebhookProcessor(ABC):
96
102
  pass
97
103
 
98
104
  @abstractmethod
99
- async def handle_event(self, payload: EventPayload) -> None:
105
+ async def handle_event(
106
+ self, payload: EventPayload, resource: ResourceConfig
107
+ ) -> WebhookEventRawResults:
100
108
  """Process the event."""
101
109
  pass
110
+
111
+ @abstractmethod
112
+ def should_process_event(self, event: WebhookEvent) -> bool:
113
+ pass
114
+
115
+ @abstractmethod
116
+ def get_matching_kinds(self, event: WebhookEvent) -> list[str]:
117
+ pass
@@ -1,10 +1,15 @@
1
- from typing import Dict, Type, Set, Callable
1
+ from copy import deepcopy
2
+ from typing import Dict, Type, Set
2
3
  from fastapi import APIRouter, Request
3
4
  from loguru import logger
4
5
  import asyncio
5
- from dataclasses import dataclass
6
6
 
7
- from .webhook_event import WebhookEvent, WebhookEventTimestamp
7
+ from port_ocean.context.event import EventType, event_context
8
+ from port_ocean.core.handlers.port_app_config.models import ResourceConfig
9
+ from port_ocean.core.integrations.mixins.events import EventsMixin
10
+ from port_ocean.core.integrations.mixins.live_events import LiveEventsMixin
11
+ from .webhook_event import WebhookEvent, WebhookEventRawResults, LiveEventTimestamp
12
+ from port_ocean.context.event import event
8
13
 
9
14
 
10
15
  from .abstract_webhook_processor import AbstractWebhookProcessor
@@ -12,15 +17,7 @@ from port_ocean.utils.signal import SignalHandler
12
17
  from port_ocean.core.handlers.queue import AbstractQueue, LocalQueue
13
18
 
14
19
 
15
- @dataclass
16
- class ProcessorRegistration:
17
- """Represents a registered processor with its filter"""
18
-
19
- processor: Type[AbstractWebhookProcessor]
20
- filter: Callable[[WebhookEvent], bool]
21
-
22
-
23
- class WebhookProcessorManager:
20
+ class LiveEventsProcessorManager(LiveEventsMixin, EventsMixin):
24
21
  """Manages webhook processors and their routes"""
25
22
 
26
23
  def __init__(
@@ -31,7 +28,7 @@ class WebhookProcessorManager:
31
28
  max_wait_seconds_before_shutdown: float = 5.0,
32
29
  ) -> None:
33
30
  self._router = router
34
- self._processors: Dict[str, list[ProcessorRegistration]] = {}
31
+ self._processors_classes: Dict[str, list[Type[AbstractWebhookProcessor]]] = {}
35
32
  self._event_queues: Dict[str, AbstractQueue[WebhookEvent]] = {}
36
33
  self._webhook_processor_tasks: Set[asyncio.Task[None]] = set()
37
34
  self._max_event_processing_seconds = max_event_processing_seconds
@@ -40,6 +37,7 @@ class WebhookProcessorManager:
40
37
 
41
38
  async def start_processing_event_messages(self) -> None:
42
39
  """Start processing events for all registered paths"""
40
+ await self.initialize_handlers()
43
41
  loop = asyncio.get_event_loop()
44
42
  for path in self._event_queues.keys():
45
43
  try:
@@ -50,42 +48,73 @@ class WebhookProcessorManager:
50
48
  logger.exception(f"Error starting queue processor for {path}: {str(e)}")
51
49
 
52
50
  def _extract_matching_processors(
53
- self, event: WebhookEvent, path: str
54
- ) -> list[AbstractWebhookProcessor]:
51
+ self, webhook_event: WebhookEvent, path: str
52
+ ) -> list[tuple[ResourceConfig, AbstractWebhookProcessor]]:
55
53
  """Find and extract the matching processor for an event"""
56
- matching_processors = [
57
- registration.processor
58
- for registration in self._processors[path]
59
- if registration.filter(event)
60
- ]
61
54
 
62
- if not matching_processors:
55
+ created_processors: list[tuple[ResourceConfig, AbstractWebhookProcessor]] = []
56
+
57
+ for processor_class in self._processors_classes[path]:
58
+ processor = processor_class(webhook_event.clone())
59
+ if processor.should_process_event(webhook_event):
60
+ kinds = processor.get_matching_kinds(webhook_event)
61
+ for kind in kinds:
62
+ for resource in event.port_app_config.resources:
63
+ if resource.kind == kind:
64
+ created_processors.append((resource, processor))
65
+
66
+ if not created_processors:
63
67
  raise ValueError("No matching processors found")
64
68
 
65
- created_processors: list[AbstractWebhookProcessor] = []
66
- for processor_class in matching_processors:
67
- processor = processor_class(event.clone())
68
- created_processors.append(processor)
69
+ logger.info(
70
+ "Found matching processors for webhook event",
71
+ processors_count=len(created_processors),
72
+ webhook_path=path,
73
+ )
69
74
  return created_processors
70
75
 
71
76
  async def process_queue(self, path: str) -> None:
72
77
  """Process events for a specific path in order"""
73
78
  while True:
74
- matching_processors: list[AbstractWebhookProcessor] = []
75
- event: WebhookEvent | None = None
79
+ matching_processors_with_resource: list[
80
+ tuple[ResourceConfig, AbstractWebhookProcessor]
81
+ ] = []
82
+ webhook_event: WebhookEvent | None = None
76
83
  try:
77
- event = await self._event_queues[path].get()
78
- with logger.contextualize(webhook_path=path, trace_id=event.trace_id):
79
- matching_processors = self._extract_matching_processors(event, path)
80
- await asyncio.gather(
81
- *(
82
- self._process_single_event(processor, path)
83
- for processor in matching_processors
84
+ queue = self._event_queues[path]
85
+ webhook_event = await queue.get()
86
+ with logger.contextualize(
87
+ webhook_path=path, trace_id=webhook_event.trace_id
88
+ ):
89
+ async with event_context(
90
+ EventType.HTTP_REQUEST,
91
+ trigger_type="machine",
92
+ parent_override=webhook_event.event_context,
93
+ ):
94
+ matching_processors_with_resource = (
95
+ self._extract_matching_processors(webhook_event, path)
84
96
  )
85
- )
97
+ webhook_event_raw_results_for_all_resources = await asyncio.gather(
98
+ *(
99
+ self._process_single_event(processor, path, resource)
100
+ for resource, processor in matching_processors_with_resource
101
+ )
102
+ )
103
+ if webhook_event_raw_results_for_all_resources and all(
104
+ webhook_event_raw_results_for_all_resources
105
+ ):
106
+ logger.info(
107
+ "Exporting raw event results to entities",
108
+ webhook_event_raw_results_for_all_resources_length=len(
109
+ webhook_event_raw_results_for_all_resources
110
+ ),
111
+ )
112
+ await self.sync_raw_results(
113
+ webhook_event_raw_results_for_all_resources
114
+ )
86
115
  except asyncio.CancelledError:
87
116
  logger.info(f"Queue processor for {path} is shutting down")
88
- for processor in matching_processors:
117
+ for _, processor in matching_processors_with_resource:
89
118
  await processor.cancel()
90
119
  self._timestamp_event_error(processor.event)
91
120
  break
@@ -93,49 +122,55 @@ class WebhookProcessorManager:
93
122
  logger.exception(
94
123
  f"Unexpected error in queue processor for {path}: {str(e)}"
95
124
  )
96
- for processor in matching_processors:
125
+ for _, processor in matching_processors_with_resource:
97
126
  self._timestamp_event_error(processor.event)
98
127
  finally:
99
- if event:
128
+ if webhook_event:
100
129
  await self._event_queues[path].commit()
101
130
  # Prevents committing empty events for cases where we shutdown while processing
102
- event = None
131
+ webhook_event = None
103
132
 
104
133
  def _timestamp_event_error(self, event: WebhookEvent) -> None:
105
134
  """Timestamp an event as having an error"""
106
- event.set_timestamp(WebhookEventTimestamp.FinishedProcessingWithError)
135
+ event.set_timestamp(LiveEventTimestamp.FinishedProcessingWithError)
107
136
 
108
137
  async def _process_single_event(
109
- self, processor: AbstractWebhookProcessor, path: str
110
- ) -> None:
138
+ self, processor: AbstractWebhookProcessor, path: str, resource: ResourceConfig
139
+ ) -> WebhookEventRawResults:
111
140
  """Process a single event with a specific processor"""
112
141
  try:
113
142
  logger.debug("Start processing queued webhook")
114
- processor.event.set_timestamp(WebhookEventTimestamp.StartedProcessing)
143
+ processor.event.set_timestamp(LiveEventTimestamp.StartedProcessing)
115
144
 
116
- await self._execute_processor(processor)
145
+ webhook_event_raw_results = await self._execute_processor(
146
+ processor, resource
147
+ )
117
148
  processor.event.set_timestamp(
118
- WebhookEventTimestamp.FinishedProcessingSuccessfully
149
+ LiveEventTimestamp.FinishedProcessingSuccessfully
119
150
  )
151
+ return webhook_event_raw_results
120
152
  except Exception as e:
121
153
  logger.exception(f"Error processing queued webhook for {path}: {str(e)}")
122
154
  self._timestamp_event_error(processor.event)
155
+ raise
123
156
 
124
- async def _execute_processor(self, processor: AbstractWebhookProcessor) -> None:
157
+ async def _execute_processor(
158
+ self, processor: AbstractWebhookProcessor, resource: ResourceConfig
159
+ ) -> WebhookEventRawResults:
125
160
  """Execute a single processor within a max processing time"""
126
161
  try:
127
- await asyncio.wait_for(
128
- self._process_webhook_request(processor),
162
+ return await asyncio.wait_for(
163
+ self._process_webhook_request(processor, resource),
129
164
  timeout=self._max_event_processing_seconds,
130
165
  )
131
166
  except asyncio.TimeoutError:
132
- raise TimeoutError(
167
+ raise asyncio.TimeoutError(
133
168
  f"Processor processing timed out after {self._max_event_processing_seconds} seconds"
134
169
  )
135
170
 
136
171
  async def _process_webhook_request(
137
- self, processor: AbstractWebhookProcessor
138
- ) -> None:
172
+ self, processor: AbstractWebhookProcessor, resource: ResourceConfig
173
+ ) -> WebhookEventRawResults:
139
174
  """Process a webhook request with retry logic
140
175
 
141
176
  Args:
@@ -152,9 +187,13 @@ class WebhookProcessorManager:
152
187
  if not await processor.validate_payload(payload):
153
188
  raise ValueError("Invalid payload")
154
189
 
190
+ webhook_event_raw_results = None
155
191
  while True:
156
192
  try:
157
- await processor.handle_event(payload)
193
+ webhook_event_raw_results = await processor.handle_event(
194
+ payload, resource
195
+ )
196
+ webhook_event_raw_results.resource = resource
158
197
  break
159
198
 
160
199
  except Exception as e:
@@ -172,26 +211,28 @@ class WebhookProcessorManager:
172
211
  raise
173
212
 
174
213
  await processor.after_processing()
214
+ return webhook_event_raw_results
175
215
 
176
216
  def register_processor(
177
- self,
178
- path: str,
179
- processor: Type[AbstractWebhookProcessor],
180
- event_filter: Callable[[WebhookEvent], bool] = lambda _: True,
217
+ self, path: str, processor: Type[AbstractWebhookProcessor]
181
218
  ) -> None:
182
- """Register a webhook processor for a specific path with optional filter"""
219
+ """Register a webhook processor for a specific path with optional filter
220
+
221
+ Args:
222
+ path: The webhook path to register
223
+ processor: The processor class to register
224
+ kind: The resource kind to associate with this processor, or None to match any kind
225
+ """
183
226
 
184
227
  if not issubclass(processor, AbstractWebhookProcessor):
185
228
  raise ValueError("Processor must extend AbstractWebhookProcessor")
186
229
 
187
- if path not in self._processors:
188
- self._processors[path] = []
230
+ if path not in self._processors_classes:
231
+ self._processors_classes[path] = []
189
232
  self._event_queues[path] = LocalQueue()
190
233
  self._register_route(path)
191
234
 
192
- self._processors[path].append(
193
- ProcessorRegistration(processor=processor, filter=event_filter)
194
- )
235
+ self._processors_classes[path].append(processor)
195
236
 
196
237
  def _register_route(self, path: str) -> None:
197
238
  """Register a route for a specific path"""
@@ -199,9 +240,10 @@ class WebhookProcessorManager:
199
240
  async def handle_webhook(request: Request) -> Dict[str, str]:
200
241
  """Handle incoming webhook requests for a specific path."""
201
242
  try:
202
- event = await WebhookEvent.from_request(request)
203
- event.set_timestamp(WebhookEventTimestamp.AddedToQueue)
204
- await self._event_queues[path].put(event)
243
+ webhook_event = await WebhookEvent.from_request(request)
244
+ webhook_event.set_timestamp(LiveEventTimestamp.AddedToQueue)
245
+ webhook_event.set_event_context(deepcopy(event))
246
+ await self._event_queues[path].put(webhook_event)
205
247
  return {"status": "ok"}
206
248
  except Exception as e:
207
249
  logger.exception(f"Error processing webhook: {str(e)}")
@@ -0,0 +1,140 @@
1
+ from abc import ABC
2
+ from enum import StrEnum
3
+ from typing import Any, Dict, Type, TypeAlias, Optional
4
+ from uuid import uuid4
5
+ from fastapi import Request
6
+ from loguru import logger
7
+
8
+ from port_ocean.context.event import EventContext
9
+ from port_ocean.core.handlers.port_app_config.models import ResourceConfig
10
+ from port_ocean.core.ocean_types import RAW_ITEM
11
+
12
+
13
+ EventPayload: TypeAlias = Dict[str, Any]
14
+ EventHeaders: TypeAlias = Dict[str, str]
15
+
16
+
17
+ class LiveEventTimestamp(StrEnum):
18
+ """Enum for timestamp keys"""
19
+
20
+ AddedToQueue = "Added To Queue"
21
+ StartedProcessing = "Started Processing"
22
+ FinishedProcessingSuccessfully = "Finished Processing Successfully"
23
+ FinishedProcessingWithError = "Finished Processing With Error"
24
+
25
+
26
+ class LiveEvent(ABC):
27
+ """Represents a live event marker class"""
28
+
29
+ def set_timestamp(
30
+ self, timestamp: LiveEventTimestamp, params: Optional[Dict[str, Any]] = None
31
+ ) -> None:
32
+ """Set a timestamp for a specific event
33
+
34
+ Args:
35
+ timestamp: The timestamp type to set
36
+ params: Additional parameters to log with the event
37
+ """
38
+ log_params = params or {}
39
+ logger.info(
40
+ f"Event {timestamp.value}",
41
+ extra=log_params | {"timestamp_type": timestamp.value},
42
+ )
43
+ self._timestamp = timestamp
44
+
45
+
46
+ class WebhookEvent(LiveEvent):
47
+ """Represents a webhook event"""
48
+
49
+ def __init__(
50
+ self,
51
+ trace_id: str,
52
+ payload: EventPayload,
53
+ headers: EventHeaders,
54
+ original_request: Request | None = None,
55
+ ) -> None:
56
+ self.trace_id = trace_id
57
+ self.payload = payload
58
+ self.headers = headers
59
+ self._original_request = original_request
60
+ self.event_context: EventContext | None = None
61
+
62
+ @classmethod
63
+ async def from_request(
64
+ cls: Type["WebhookEvent"], request: Request
65
+ ) -> "WebhookEvent":
66
+ trace_id = str(uuid4())
67
+ payload = await request.json()
68
+
69
+ return cls(
70
+ trace_id=trace_id,
71
+ payload=payload,
72
+ headers=dict(request.headers),
73
+ original_request=request,
74
+ )
75
+
76
+ @classmethod
77
+ def from_dict(cls: Type["WebhookEvent"], data: Dict[str, Any]) -> "WebhookEvent":
78
+ return cls(
79
+ trace_id=data["trace_id"],
80
+ payload=data["payload"],
81
+ headers=data["headers"],
82
+ original_request=None,
83
+ )
84
+
85
+ def clone(self) -> "WebhookEvent":
86
+ return WebhookEvent(
87
+ trace_id=self.trace_id,
88
+ payload=self.payload,
89
+ headers=self.headers,
90
+ original_request=self._original_request,
91
+ )
92
+
93
+ def set_timestamp(
94
+ self, timestamp: LiveEventTimestamp, params: Optional[Dict[str, Any]] = None
95
+ ) -> None:
96
+ """Set a timestamp for a specific event"""
97
+ super().set_timestamp(
98
+ timestamp,
99
+ params={
100
+ "trace_id": self.trace_id,
101
+ "payload": self.payload,
102
+ "headers": self.headers,
103
+ },
104
+ )
105
+
106
+ def set_event_context(self, event_context: EventContext) -> None:
107
+ self.event_context = event_context
108
+
109
+
110
+ class WebhookEventRawResults:
111
+ """
112
+ Class for webhook event to store the updated data for the event
113
+ """
114
+
115
+ def __init__(
116
+ self,
117
+ updated_raw_results: list[RAW_ITEM],
118
+ deleted_raw_results: list[RAW_ITEM],
119
+ ) -> None:
120
+ self._resource: ResourceConfig | None = None
121
+ self._updated_raw_results = updated_raw_results
122
+ self._deleted_raw_results = deleted_raw_results
123
+
124
+ @property
125
+ def resource(self) -> ResourceConfig:
126
+ if self._resource is None:
127
+ raise ValueError("Resource has not been set")
128
+ return self._resource
129
+
130
+ @resource.setter
131
+ def resource(self, value: ResourceConfig) -> None:
132
+ self._resource = value
133
+
134
+ @property
135
+ def updated_raw_results(self) -> list[RAW_ITEM]:
136
+ return self._updated_raw_results
137
+
138
+ @property
139
+ def deleted_raw_results(self) -> list[RAW_ITEM]:
140
+ return self._deleted_raw_results
@@ -0,0 +1,88 @@
1
+ from loguru import logger
2
+ from port_ocean.clients.port.types import UserAgentType
3
+ from port_ocean.core.handlers.webhook.webhook_event import WebhookEventRawResults
4
+ from port_ocean.core.integrations.mixins.handler import HandlerMixin
5
+ from port_ocean.core.models import Entity
6
+ from port_ocean.context.ocean import ocean
7
+
8
+
9
+
10
+ class LiveEventsMixin(HandlerMixin):
11
+
12
+ async def sync_raw_results(self, webhook_events_raw_result: list[WebhookEventRawResults]) -> None:
13
+ """Process the webhook event raw results collected from multiple processors and export it.
14
+
15
+ Args:
16
+ webhook_events_raw_result: List of WebhookEventRawResults objects to process
17
+ """
18
+ entities_to_create, entities_to_delete = await self._parse_raw_event_results_to_entities(webhook_events_raw_result)
19
+ await self.entities_state_applier.upsert(entities_to_create, UserAgentType.exporter)
20
+ await self._delete_entities(entities_to_delete)
21
+
22
+
23
+ async def _parse_raw_event_results_to_entities(self, webhook_events_raw_result: list[WebhookEventRawResults]) -> tuple[list[Entity], list[Entity]]:
24
+ """Parse the webhook event raw results and return a list of entities.
25
+
26
+ Args:
27
+ webhook_events_raw_result: List of WebhookEventRawResults objects to process
28
+ """
29
+ entities: list[Entity] = []
30
+ entities_not_passed: list[Entity] = []
31
+ entities_to_delete: list[Entity] = []
32
+ for webhook_event_raw_result in webhook_events_raw_result:
33
+ for raw_item in webhook_event_raw_result.updated_raw_results:
34
+ calaculation_results = await self.entity_processor.parse_items(
35
+ webhook_event_raw_result.resource, [raw_item], parse_all=True, send_raw_data_examples_amount=0
36
+ )
37
+ entities.extend(calaculation_results.entity_selector_diff.passed)
38
+ entities_not_passed.extend(calaculation_results.entity_selector_diff.failed)
39
+
40
+ for raw_item in webhook_event_raw_result.deleted_raw_results:
41
+ deletion_results = await self.entity_processor.parse_items(
42
+ webhook_event_raw_result.resource, [raw_item], parse_all=True, send_raw_data_examples_amount=0
43
+ )
44
+ entities_to_delete.extend(deletion_results.entity_selector_diff.passed)
45
+
46
+ entities_to_remove = []
47
+ for entity in entities_to_delete + entities_not_passed:
48
+ if (entity.blueprint, entity.identifier) not in [(entity.blueprint, entity.identifier) for entity in entities]:
49
+ entities_to_remove.append(entity)
50
+
51
+ logger.info(f"Found {len(entities_to_remove)} entities to remove {', '.join(f'{entity.blueprint}/{entity.identifier}' for entity in entities_to_remove)}")
52
+ logger.info(f"Found {len(entities)} entities to upsert {', '.join(f'{entity.blueprint}/{entity.identifier}' for entity in entities)}")
53
+ return entities, entities_to_remove
54
+
55
+ async def _does_entity_exists(self, entity: Entity) -> bool:
56
+ """Check if this integration is the owner of the given entity.
57
+
58
+ Args:
59
+ entity: The entity to check ownership for
60
+
61
+ Returns:
62
+ bool: True if this integration is the owner of the entity, False otherwise
63
+ """
64
+ query = {
65
+ "combinator": "and",
66
+ "rules": [
67
+ {
68
+ "property": "$identifier",
69
+ "operator": "=",
70
+ "value": entity.identifier
71
+ },
72
+ {
73
+ "property": "$blueprint",
74
+ "operator": "=",
75
+ "value": entity.blueprint
76
+ }
77
+ ]
78
+ }
79
+ entities_at_port = await ocean.port_client.search_entities(
80
+ UserAgentType.exporter,
81
+ query
82
+ )
83
+ return len(entities_at_port) > 0
84
+
85
+ async def _delete_entities(self, entities: list[Entity]) -> None:
86
+ for entity in entities:
87
+ if await self._does_entity_exists(entity):
88
+ await self.entities_state_applier.delete([entity], UserAgentType.exporter)
@@ -26,7 +26,9 @@ from port_ocean.utils.misc import IntegrationStateStatus
26
26
  from port_ocean.utils.repeat import repeat_every
27
27
  from port_ocean.utils.signal import signal_handler
28
28
  from port_ocean.version import __integration_version__
29
- from port_ocean.core.handlers.webhook.processor_manager import WebhookProcessorManager
29
+ from port_ocean.core.handlers.webhook.processor_manager import (
30
+ LiveEventsProcessorManager,
31
+ )
30
32
 
31
33
 
32
34
  class Ocean:
@@ -54,7 +56,7 @@ class Ocean:
54
56
  )
55
57
  self.integration_router = integration_router or APIRouter()
56
58
 
57
- self.webhook_manager = WebhookProcessorManager(
59
+ self.webhook_manager = LiveEventsProcessorManager(
58
60
  self.integration_router, signal_handler
59
61
  )
60
62
 
@@ -118,6 +120,17 @@ class Ocean:
118
120
  )
119
121
  await repeated_function()
120
122
 
123
+ @property
124
+ def base_url(self) -> str:
125
+ integration_config = self.config.integration.config
126
+ if isinstance(integration_config, BaseModel):
127
+ integration_config = integration_config.dict()
128
+ if integration_config.get("app_host"):
129
+ logger.warning(
130
+ "The OCEAN__INTEGRATION__CONFIG__APP_HOST field is deprecated. Please use the OCEAN__BASE_URL field instead."
131
+ )
132
+ return self.config.base_url or integration_config.get("app_host")
133
+
121
134
  def load_external_oauth_access_token(self) -> str | None:
122
135
  if self.config.oauth_access_token_file_path is not None:
123
136
  try:
@@ -137,7 +150,10 @@ class Ocean:
137
150
  async def lifecycle(_: FastAPI) -> AsyncIterator[None]:
138
151
  try:
139
152
  await self.integration.start()
140
- await self.webhook_manager.start_processing_event_messages()
153
+ if self.base_url:
154
+ await self.webhook_manager.start_processing_event_messages()
155
+ else:
156
+ logger.warning("No base URL provided, skipping webhook processing")
141
157
  await self._setup_scheduled_resync()
142
158
  yield None
143
159
  except Exception: