port-ocean 0.28.19__tar.gz → 0.29.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.
Files changed (224) hide show
  1. {port_ocean-0.28.19 → port_ocean-0.29.1}/PKG-INFO +3 -2
  2. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/authentication.py +19 -0
  3. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/client.py +3 -0
  4. port_ocean-0.29.1/port_ocean/clients/port/mixins/actions.py +93 -0
  5. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/mixins/blueprints.py +0 -12
  6. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/mixins/integrations.py +5 -2
  7. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/config/settings.py +35 -3
  8. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/context/ocean.py +7 -5
  9. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/defaults/initialize.py +12 -5
  10. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/__init__.py +7 -0
  11. port_ocean-0.29.1/port_ocean/core/event_listener/actions_only.py +42 -0
  12. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/base.py +4 -1
  13. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/factory.py +18 -9
  14. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/http.py +4 -3
  15. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/kafka.py +3 -2
  16. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/once.py +5 -2
  17. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/polling.py +4 -3
  18. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/event_listener/webhooks_only.py +3 -2
  19. port_ocean-0.29.1/port_ocean/core/handlers/actions/__init__.py +7 -0
  20. port_ocean-0.29.1/port_ocean/core/handlers/actions/abstract_executor.py +150 -0
  21. port_ocean-0.29.1/port_ocean/core/handlers/actions/execution_manager.py +434 -0
  22. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/webhook/abstract_webhook_processor.py +16 -0
  23. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/webhook/processor_manager.py +30 -12
  24. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/mixins/sync_raw.py +2 -2
  25. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/models.py +35 -2
  26. port_ocean-0.29.1/port_ocean/exceptions/execution_manager.py +22 -0
  27. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/ocean.py +30 -4
  28. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/event_listener/test_kafka.py +14 -7
  29. port_ocean-0.29.1/port_ocean/tests/core/handlers/actions/test_execution_manager.py +837 -0
  30. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +3 -1
  31. {port_ocean-0.28.19 → port_ocean-0.29.1}/pyproject.toml +3 -2
  32. {port_ocean-0.28.19 → port_ocean-0.29.1}/LICENSE.md +0 -0
  33. {port_ocean-0.28.19 → port_ocean-0.29.1}/README.md +0 -0
  34. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/Dockerfile.Deb +0 -0
  35. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/Dockerfile.alpine +0 -0
  36. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/Dockerfile.base.builder +0 -0
  37. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/Dockerfile.base.runner +0 -0
  38. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/Dockerfile.dockerignore +0 -0
  39. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/Dockerfile.local +0 -0
  40. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/Makefile +0 -0
  41. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/README.md +0 -0
  42. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/entry_local.sh +0 -0
  43. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/grpcio.sh +0 -0
  44. {port_ocean-0.28.19 → port_ocean-0.29.1}/integrations/_infra/init.sh +0 -0
  45. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/__init__.py +0 -0
  46. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/bootstrap.py +0 -0
  47. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cache/__init__.py +0 -0
  48. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cache/base.py +0 -0
  49. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cache/disk.py +0 -0
  50. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cache/errors.py +0 -0
  51. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cache/memory.py +0 -0
  52. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/__init__.py +0 -0
  53. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cli.py +0 -0
  54. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/__init__.py +0 -0
  55. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/defaults/__init___.py +0 -0
  56. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/defaults/clean.py +0 -0
  57. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/defaults/dock.py +0 -0
  58. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/defaults/group.py +0 -0
  59. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/list_integrations.py +0 -0
  60. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/main.py +0 -0
  61. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/new.py +0 -0
  62. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/pull.py +0 -0
  63. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/sail.py +0 -0
  64. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/commands/version.py +0 -0
  65. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/__init__.py +0 -0
  66. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
  67. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/extensions.py +0 -0
  68. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
  69. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
  70. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
  71. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
  72. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
  73. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
  74. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
  75. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
  76. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
  77. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
  78. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
  79. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
  80. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
  81. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
  82. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
  83. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
  84. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
  85. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
  86. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/cli/utils.py +0 -0
  87. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/__init__.py +0 -0
  88. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/auth/__init__.py +0 -0
  89. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/auth/auth_client.py +0 -0
  90. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/auth/oauth_client.py +0 -0
  91. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/__init__.py +0 -0
  92. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/mixins/__init__.py +0 -0
  93. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/mixins/entities.py +0 -0
  94. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/mixins/migrations.py +0 -0
  95. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/mixins/organization.py +0 -0
  96. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/retry_transport.py +0 -0
  97. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/types.py +0 -0
  98. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/clients/port/utils.py +0 -0
  99. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/config/__init__.py +0 -0
  100. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/config/base.py +0 -0
  101. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/config/dynamic.py +0 -0
  102. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/consumers/__init__.py +0 -0
  103. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/consumers/kafka_consumer.py +0 -0
  104. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/context/__init__.py +0 -0
  105. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/context/event.py +0 -0
  106. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/context/metric_resource.py +0 -0
  107. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/context/resource.py +0 -0
  108. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/__init__.py +0 -0
  109. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/defaults/__init__.py +0 -0
  110. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/defaults/clean.py +0 -0
  111. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/defaults/common.py +0 -0
  112. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/__init__.py +0 -0
  113. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/base.py +0 -0
  114. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
  115. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
  116. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
  117. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
  118. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
  119. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
  120. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
  121. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entity_processor/base.py +0 -0
  122. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
  123. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/entity_processor/jq_input_evaluator.py +0 -0
  124. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
  125. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/port_app_config/api.py +0 -0
  126. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/port_app_config/base.py +0 -0
  127. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/port_app_config/models.py +0 -0
  128. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/queue/__init__.py +0 -0
  129. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/queue/abstract_queue.py +0 -0
  130. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/queue/group_queue.py +0 -0
  131. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/queue/local_queue.py +0 -0
  132. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
  133. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
  134. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/webhook/__init__.py +0 -0
  135. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/handlers/webhook/webhook_event.py +0 -0
  136. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/__init__.py +0 -0
  137. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/base.py +0 -0
  138. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/mixins/__init__.py +0 -0
  139. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/mixins/events.py +0 -0
  140. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/mixins/handler.py +0 -0
  141. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/mixins/live_events.py +0 -0
  142. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/mixins/sync.py +0 -0
  143. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/integrations/mixins/utils.py +0 -0
  144. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/ocean_types.py +0 -0
  145. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/utils/entity_topological_sorter.py +0 -0
  146. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/core/utils/utils.py +0 -0
  147. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/debug_cli.py +0 -0
  148. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/__init__.py +0 -0
  149. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/api.py +0 -0
  150. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/base.py +0 -0
  151. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/clients.py +0 -0
  152. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/context.py +0 -0
  153. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/core.py +0 -0
  154. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/port_defaults.py +0 -0
  155. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/utils.py +0 -0
  156. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/exceptions/webhook_processor.py +0 -0
  157. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/helpers/__init__.py +0 -0
  158. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/helpers/async_client.py +0 -0
  159. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/helpers/metric/metric.py +0 -0
  160. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/helpers/metric/utils.py +0 -0
  161. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/helpers/retry.py +0 -0
  162. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/helpers/stream.py +0 -0
  163. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/log/__init__.py +0 -0
  164. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/log/handlers.py +0 -0
  165. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/log/logger_setup.py +0 -0
  166. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/log/sensetive.py +0 -0
  167. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/middlewares.py +0 -0
  168. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/py.typed +0 -0
  169. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/run.py +0 -0
  170. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/sonar-project.properties +0 -0
  171. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/__init__.py +0 -0
  172. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/cache/__init__.py +0 -0
  173. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/cache/test_disk_cache.py +0 -0
  174. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/cache/test_memory_cache.py +0 -0
  175. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/clients/__init__.py +0 -0
  176. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/clients/oauth/__init__.py +0 -0
  177. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/clients/oauth/test_oauth_client.py +0 -0
  178. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
  179. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/clients/port/mixins/test_integrations.py +0 -0
  180. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/clients/port/mixins/test_organization_mixin.py +0 -0
  181. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/config/test_config.py +0 -0
  182. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/conftest.py +0 -0
  183. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/conftest.py +0 -0
  184. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/defaults/test_common.py +0 -0
  185. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +0 -0
  186. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +0 -0
  187. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/entity_processor/test_jq_input_evaluator.py +0 -0
  188. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/mixins/test_live_events.py +0 -0
  189. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py +0 -0
  190. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/port_app_config/test_api.py +0 -0
  191. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/port_app_config/test_base.py +0 -0
  192. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/queue/test_group_queue.py +0 -0
  193. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/queue/test_local_queue.py +0 -0
  194. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +0 -0
  195. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +0 -0
  196. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/test_utils.py +0 -0
  197. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/utils/test_entity_topological_sorter.py +0 -0
  198. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/utils/test_get_port_diff.py +0 -0
  199. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/core/utils/test_resolve_entities_diff.py +0 -0
  200. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/__init__.py +0 -0
  201. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/fake_port_api.py +0 -0
  202. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/fixtures.py +0 -0
  203. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/integration.py +0 -0
  204. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/ocean_app.py +0 -0
  205. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/port_client.py +0 -0
  206. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/smoke_test.py +0 -0
  207. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/helpers/test_retry.py +0 -0
  208. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/log/test_handlers.py +0 -0
  209. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/test_metric.py +0 -0
  210. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/test_ocean.py +0 -0
  211. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/test_smoke.py +0 -0
  212. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/utils/test_async_iterators.py +0 -0
  213. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/tests/utils/test_cache.py +0 -0
  214. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/__init__.py +0 -0
  215. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/async_http.py +0 -0
  216. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/async_iterators.py +0 -0
  217. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/cache.py +0 -0
  218. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/ipc.py +0 -0
  219. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/misc.py +0 -0
  220. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/queue_utils.py +0 -0
  221. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/repeat.py +0 -0
  222. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/signal.py +0 -0
  223. {port_ocean-0.28.19 → port_ocean-0.29.1}/port_ocean/utils/time.py +0 -0
  224. {port_ocean-0.28.19 → port_ocean-0.29.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.28.19
3
+ Version: 0.29.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
@@ -28,7 +28,7 @@ Requires-Dist: click (>=8.1.3,<9.0.0) ; extra == "cli"
28
28
  Requires-Dist: confluent-kafka (>=2.10.1,<3.0.0)
29
29
  Requires-Dist: cookiecutter (>=2.1.1,<3.0.0) ; extra == "cli"
30
30
  Requires-Dist: cryptography (>=44.0.1,<45.0.0)
31
- Requires-Dist: fastapi (>=0.116.0,<0.117.0)
31
+ Requires-Dist: fastapi (>=0.121.0,<0.122.0)
32
32
  Requires-Dist: httpx (>=0.28.1,<0.29.0)
33
33
  Requires-Dist: ijson (>=3.4.0,<4.0.0)
34
34
  Requires-Dist: jinja2 (>=3.1.6)
@@ -39,6 +39,7 @@ Requires-Dist: prometheus-client (>=0.21.1,<0.22.0)
39
39
  Requires-Dist: pydantic[dotenv] (>=1.10.8,<2.0.0)
40
40
  Requires-Dist: pydispatcher (>=2.0.7,<3.0.0)
41
41
  Requires-Dist: pyhumps (>=3.8.0,<4.0.0)
42
+ Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
42
43
  Requires-Dist: pytest-cov (>=6.0.0,<7.0.0)
43
44
  Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
44
45
  Requires-Dist: pyyaml (>=6.0,<7.0)
@@ -1,4 +1,5 @@
1
1
  import re
2
+ import jwt
2
3
  from typing import Any
3
4
 
4
5
  import httpx
@@ -90,6 +91,24 @@ class PortAuthentication:
90
91
  )
91
92
  return self.last_token_object.full_token
92
93
 
94
+ async def is_machine_user(self) -> bool:
95
+ # Ensure self.last_token_object is populated
96
+ await self.token
97
+ if not self.last_token_object:
98
+ raise ValueError("No token found")
99
+
100
+ payload: dict[str, Any] = jwt.decode(
101
+ self.last_token_object.access_token, options={"verify_signature": False}
102
+ )
103
+ is_machine_user = payload.get("isMachine")
104
+ if is_machine_user is None:
105
+ logger.warning(
106
+ "Can not determine if the user is a machine user directly, checking for personal token usage instead"
107
+ )
108
+ return not payload.get("personalToken", True)
109
+
110
+ return is_machine_user
111
+
93
112
  @staticmethod
94
113
  def _is_personal_token(client_id: str) -> bool:
95
114
  email_regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
@@ -3,6 +3,7 @@ from typing import Any
3
3
  from loguru import logger
4
4
 
5
5
  from port_ocean.clients.port.authentication import PortAuthentication
6
+ from port_ocean.clients.port.mixins.actions import ActionsClientMixin
6
7
  from port_ocean.clients.port.mixins.blueprints import BlueprintClientMixin
7
8
  from port_ocean.clients.port.mixins.entities import EntityClientMixin
8
9
  from port_ocean.clients.port.mixins.integrations import IntegrationClientMixin
@@ -24,6 +25,7 @@ class PortClient(
24
25
  BlueprintClientMixin,
25
26
  MigrationClientMixin,
26
27
  OrganizationClientMixin,
28
+ ActionsClientMixin,
27
29
  ):
28
30
  def __init__(
29
31
  self,
@@ -54,6 +56,7 @@ class PortClient(
54
56
  BlueprintClientMixin.__init__(self, self.auth, self.client)
55
57
  MigrationClientMixin.__init__(self, self.auth, self.client)
56
58
  OrganizationClientMixin.__init__(self, self.auth, self.client)
59
+ ActionsClientMixin.__init__(self, self.auth, self.client)
57
60
 
58
61
  async def get_kafka_creds(self) -> KafkaCreds:
59
62
  logger.info("Fetching organization kafka credentials")
@@ -0,0 +1,93 @@
1
+ from typing import Any
2
+ import httpx
3
+ from loguru import logger
4
+ from port_ocean.clients.port.authentication import PortAuthentication
5
+ from port_ocean.clients.port.utils import handle_port_status_code
6
+ from port_ocean.core.models import (
7
+ ActionRun,
8
+ )
9
+ from port_ocean.exceptions.execution_manager import RunAlreadyAcknowledgedError
10
+
11
+ INTERNAL_ACTIONS_CLIENT_HEADER = {"x-port-automation-client": "true"}
12
+
13
+
14
+ class ActionsClientMixin:
15
+ def __init__(self, auth: PortAuthentication, client: httpx.AsyncClient):
16
+ self.auth = auth
17
+ self.client = client
18
+
19
+ async def create_action(
20
+ self, action: dict[str, Any], should_log: bool = True
21
+ ) -> None:
22
+ logger.info(f"Creating action: {action}")
23
+ response = await self.client.post(
24
+ f"{self.auth.api_url}/actions",
25
+ json=action,
26
+ headers=await self.auth.headers(),
27
+ )
28
+
29
+ handle_port_status_code(response, should_log=should_log)
30
+
31
+ async def claim_pending_runs(
32
+ self, limit: int, visibility_timeout_ms: int
33
+ ) -> list[ActionRun]:
34
+ response = await self.client.post(
35
+ f"{self.auth.api_url}/actions/runs/claim-pending",
36
+ headers={**(await self.auth.headers()), **INTERNAL_ACTIONS_CLIENT_HEADER},
37
+ json={
38
+ "installationId": self.auth.integration_identifier,
39
+ "limit": limit,
40
+ "visibilityTimeoutMs": visibility_timeout_ms,
41
+ },
42
+ )
43
+ if response.is_error:
44
+ logger.error("Error claiming pending runs", error=response.text)
45
+ return []
46
+
47
+ return [ActionRun.parse_obj(run) for run in response.json().get("runs", [])]
48
+
49
+ async def get_run_by_external_id(self, external_id: str) -> ActionRun | None:
50
+ response = await self.client.get(
51
+ f"{self.auth.api_url}/actions/runs?version=v2&external_run_id={external_id}",
52
+ headers=await self.auth.headers(),
53
+ )
54
+ handle_port_status_code(response)
55
+ runs = response.json().get("runs", [])
56
+ return None if not len(runs) else ActionRun.parse_obj(runs[0])
57
+
58
+ async def patch_run(
59
+ self,
60
+ run_id: str,
61
+ run: ActionRun | dict[str, Any],
62
+ should_raise: bool = True,
63
+ ) -> None:
64
+ response = await self.client.patch(
65
+ f"{self.auth.api_url}/actions/runs/{run_id}",
66
+ headers=await self.auth.headers(),
67
+ json=run.dict() if isinstance(run, ActionRun) else run,
68
+ )
69
+ handle_port_status_code(response, should_raise=should_raise)
70
+
71
+ async def acknowledge_run(self, run_id: str) -> None:
72
+ try:
73
+ response = await self.client.patch(
74
+ f"{self.auth.api_url}/actions/runs/ack",
75
+ headers={
76
+ **(await self.auth.headers()),
77
+ **INTERNAL_ACTIONS_CLIENT_HEADER,
78
+ },
79
+ json={"runId": run_id},
80
+ )
81
+ handle_port_status_code(response)
82
+ except httpx.HTTPStatusError as e:
83
+ if e.response.status_code == 409:
84
+ raise RunAlreadyAcknowledgedError()
85
+ raise
86
+
87
+ async def post_run_log(self, run_id: str, message: str) -> None:
88
+ response = await self.client.post(
89
+ f"{self.auth.api_url}/actions/runs/{run_id}/logs",
90
+ headers=await self.auth.headers(),
91
+ json={"message": message},
92
+ )
93
+ handle_port_status_code(response, should_raise=False)
@@ -81,18 +81,6 @@ class BlueprintClientMixin:
81
81
  handle_port_status_code(response, should_raise)
82
82
  return response.json().get("migrationId", "")
83
83
 
84
- async def create_action(
85
- self, action: dict[str, Any], should_log: bool = True
86
- ) -> None:
87
- logger.info(f"Creating action: {action}")
88
- response = await self.client.post(
89
- f"{self.auth.api_url}/actions",
90
- json=action,
91
- headers=await self.auth.headers(),
92
- )
93
-
94
- handle_port_status_code(response, should_log=should_log)
95
-
96
84
  async def create_scorecard(
97
85
  self,
98
86
  blueprint_identifier: str,
@@ -14,8 +14,6 @@ from port_ocean.log.sensetive import sensitive_log_filter
14
14
  if TYPE_CHECKING:
15
15
  from port_ocean.core.handlers.port_app_config.models import PortAppConfig
16
16
 
17
-
18
- ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG = "USE_PROVISIONED_DEFAULTS"
19
17
  INTEGRATION_POLLING_INTERVAL_INITIAL_SECONDS = 3
20
18
  INTEGRATION_POLLING_INTERVAL_BACKOFF_FACTOR = 1.55
21
19
  INTEGRATION_POLLING_RETRY_LIMIT = 30
@@ -153,6 +151,7 @@ class IntegrationClientMixin:
153
151
  changelog_destination: dict[str, Any],
154
152
  port_app_config: Optional["PortAppConfig"] = None,
155
153
  create_port_resources_origin_in_port: Optional[bool] = False,
154
+ actions_processing_enabled: Optional[bool] = False,
156
155
  ) -> Dict[str, Any]:
157
156
  logger.info(f"Creating integration with id: {self.integration_identifier}")
158
157
  headers = await self.auth.headers()
@@ -162,6 +161,7 @@ class IntegrationClientMixin:
162
161
  "version": self.integration_version,
163
162
  "changelogDestination": changelog_destination,
164
163
  "config": {},
164
+ "actionsProcessingEnabled": actions_processing_enabled,
165
165
  }
166
166
 
167
167
  query_params = {}
@@ -190,6 +190,7 @@ class IntegrationClientMixin:
190
190
  _type: str | None = None,
191
191
  changelog_destination: dict[str, Any] | None = None,
192
192
  port_app_config: Optional["PortAppConfig"] = None,
193
+ actions_processing_enabled: Optional[bool] = False,
193
194
  ) -> dict:
194
195
  logger.info(f"Updating integration with id: {self.integration_identifier}")
195
196
  headers = await self.auth.headers()
@@ -200,6 +201,8 @@ class IntegrationClientMixin:
200
201
  json["changelogDestination"] = changelog_destination
201
202
  if port_app_config:
202
203
  json["config"] = port_app_config.to_request()
204
+
205
+ json["actionsProcessingEnabled"] = actions_processing_enabled
203
206
  json["version"] = self.integration_version
204
207
 
205
208
  response = await self.client.patch(
@@ -1,5 +1,5 @@
1
1
  import platform
2
- from typing import Any, Literal, Optional, Type, cast
2
+ from typing import Any, Literal, Optional, Type
3
3
 
4
4
  from pydantic import AnyHttpUrl, Extra, parse_obj_as, parse_raw_as
5
5
  from pydantic.class_validators import root_validator, validator
@@ -8,10 +8,14 @@ from pydantic.fields import Field
8
8
  from pydantic.main import BaseModel
9
9
 
10
10
  from port_ocean.config.base import BaseOceanModel, BaseOceanSettings
11
- from port_ocean.core.event_listener import EventListenerSettingsType
11
+ from port_ocean.core.event_listener import (
12
+ EventListenerSettingsType,
13
+ PollingEventListenerSettings,
14
+ )
12
15
  from port_ocean.core.models import (
13
16
  CachingStorageMode,
14
17
  CreatePortResourcesOrigin,
18
+ EventListenerType,
15
19
  ProcessExecutionMode,
16
20
  Runtime,
17
21
  )
@@ -80,6 +84,14 @@ class StreamingSettings(BaseOceanModel, extra=Extra.allow):
80
84
  location: str = Field(default="/tmp/ocean/streaming")
81
85
 
82
86
 
87
+ class ActionsProcessorSettings(BaseOceanModel, extra=Extra.allow):
88
+ enabled: bool = Field(default=False)
89
+ runs_buffer_high_watermark: int = Field(default=100)
90
+ visibility_timeout_ms: int = Field(default=30000)
91
+ poll_check_interval_seconds: int = Field(default=10)
92
+ workers_count: int = Field(default=1)
93
+
94
+
83
95
  class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
84
96
  _integration_config_model: BaseModel | None = None
85
97
 
@@ -94,7 +106,9 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
94
106
  base_url: str | None = None
95
107
  port: PortSettings
96
108
  event_listener: EventListenerSettingsType = Field(
97
- default=cast(EventListenerSettingsType, {"type": "POLLING"})
109
+ default_factory=lambda: PollingEventListenerSettings(
110
+ type=EventListenerType.POLLING
111
+ )
98
112
  )
99
113
  event_workers_count: int = 1
100
114
  # If an identifier or type is not provided, it will be generated based on the integration name
@@ -122,6 +136,9 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
122
136
  yield_items_to_parse_batch_size: int = 10
123
137
 
124
138
  streaming: StreamingSettings = Field(default_factory=lambda: StreamingSettings())
139
+ actions_processor: ActionsProcessorSettings = Field(
140
+ default_factory=lambda: ActionsProcessorSettings()
141
+ )
125
142
 
126
143
  @validator("process_execution_mode")
127
144
  def validate_process_execution_mode(
@@ -197,3 +214,18 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
197
214
  raise ValueError("This integration can't be ran as Saas")
198
215
 
199
216
  return runtime
217
+
218
+ @validator("actions_processor")
219
+ def validate_actions_processor(
220
+ cls, actions_processor: ActionsProcessorSettings
221
+ ) -> ActionsProcessorSettings:
222
+ if not actions_processor.enabled:
223
+ return actions_processor
224
+
225
+ spec = get_spec_file()
226
+ if not (spec and spec.get("actionsProcessingEnabled", False)):
227
+ raise ValueError(
228
+ "Serving as an actions processor is not currently supported for this integration."
229
+ )
230
+
231
+ return actions_processor
@@ -1,4 +1,4 @@
1
- from typing import Callable, TYPE_CHECKING, Any, Literal, Union
1
+ from typing import Callable, TYPE_CHECKING, Any, Union
2
2
 
3
3
  from fastapi import APIRouter
4
4
  from port_ocean.helpers.metric.metric import Metrics
@@ -7,7 +7,7 @@ from werkzeug.local import LocalProxy
7
7
 
8
8
  from port_ocean.clients.port.types import UserAgentType
9
9
 
10
- from port_ocean.core.models import Entity
10
+ from port_ocean.core.models import Entity, EventListenerType
11
11
  from port_ocean.core.ocean_types import (
12
12
  RESYNC_EVENT_LISTENER,
13
13
  START_EVENT_LISTENER,
@@ -26,6 +26,7 @@ if TYPE_CHECKING:
26
26
  from port_ocean.core.integrations.base import BaseIntegration
27
27
  from port_ocean.ocean import Ocean
28
28
  from port_ocean.clients.port.client import PortClient
29
+ from port_ocean.core.handlers.actions.abstract_executor import AbstractExecutor
29
30
 
30
31
  from loguru import logger
31
32
 
@@ -73,9 +74,7 @@ class PortOceanContext:
73
74
  return self.app.port_client
74
75
 
75
76
  @property
76
- def event_listener_type(
77
- self,
78
- ) -> Literal["WEBHOOK", "KAFKA", "POLLING", "ONCE", "WEBHOOKS_ONLY"]:
77
+ def event_listener_type(self) -> EventListenerType:
79
78
  return self.app.config.event_listener.type
80
79
 
81
80
  def on_resync(
@@ -213,6 +212,9 @@ class PortOceanContext:
213
212
  """
214
213
  self.app.webhook_manager.register_processor(path, processor)
215
214
 
215
+ def register_action_executor(self, executor: "AbstractExecutor") -> None:
216
+ self.app.execution_manager.register_executor(executor)
217
+
216
218
 
217
219
  _port_ocean: PortOceanContext = PortOceanContext(None)
218
220
 
@@ -13,14 +13,16 @@ from port_ocean.core.defaults.common import (
13
13
  get_port_integration_defaults,
14
14
  )
15
15
  from port_ocean.core.handlers.port_app_config.models import PortAppConfig
16
- from port_ocean.core.models import Blueprint, CreatePortResourcesOrigin
16
+ from port_ocean.core.models import (
17
+ Blueprint,
18
+ CreatePortResourcesOrigin,
19
+ IntegrationFeatureFlag,
20
+ )
17
21
  from port_ocean.core.utils.utils import gather_and_split_errors_from_results
18
22
  from port_ocean.exceptions.port_defaults import (
19
23
  AbortDefaultCreationError,
20
24
  )
21
25
 
22
- ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG = "USE_PROVISIONED_DEFAULTS"
23
-
24
26
 
25
27
  def deconstruct_blueprints_to_creation_steps(
26
28
  raw_blueprints: list[dict[str, Any]],
@@ -75,6 +77,7 @@ async def _initialize_required_integration_settings(
75
77
  integration_config.integration.type,
76
78
  integration_config.event_listener.get_changelog_destination_details(),
77
79
  port_app_config=default_mapping,
80
+ actions_processing_enabled=integration_config.actions_processor.enabled,
78
81
  create_port_resources_origin_in_port=integration_config.create_port_resources_origin
79
82
  == CreatePortResourcesOrigin.Port,
80
83
  )
@@ -101,9 +104,13 @@ async def _initialize_required_integration_settings(
101
104
  integration.get("changelogDestination") != changelog_destination
102
105
  or integration.get("installationAppType") != integration_config.integration.type
103
106
  or integration.get("version") != port_client.integration_version
107
+ or integration.get("actionsProcessingEnabled")
108
+ != integration_config.actions_processor.enabled
104
109
  ):
105
110
  await port_client.patch_integration(
106
- integration_config.integration.type, changelog_destination
111
+ _type=integration_config.integration.type,
112
+ changelog_destination=changelog_destination,
113
+ actions_processing_enabled=integration_config.actions_processor.enabled,
107
114
  )
108
115
 
109
116
 
@@ -219,7 +226,7 @@ async def _initialize_defaults(
219
226
  )
220
227
  )
221
228
 
222
- has_provision_feature_flag = ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG in (
229
+ has_provision_feature_flag = IntegrationFeatureFlag.USE_PROVISIONED_DEFAULTS in (
223
230
  await port_client.get_organization_feature_flags()
224
231
  )
225
232
 
@@ -20,6 +20,10 @@ from port_ocean.core.event_listener.webhooks_only import (
20
20
  WebhooksOnlyEventListener,
21
21
  WebhooksOnlyEventListenerSettings,
22
22
  )
23
+ from port_ocean.core.event_listener.actions_only import (
24
+ ActionsOnlyEventListener,
25
+ ActionsOnlyEventListenerSettings,
26
+ )
23
27
 
24
28
 
25
29
  EventListenerSettingsType = (
@@ -28,6 +32,7 @@ EventListenerSettingsType = (
28
32
  | PollingEventListenerSettings
29
33
  | OnceEventListenerSettings
30
34
  | WebhooksOnlyEventListenerSettings
35
+ | ActionsOnlyEventListenerSettings
31
36
  )
32
37
 
33
38
  __all__ = [
@@ -42,4 +47,6 @@ __all__ = [
42
47
  "OnceEventListenerSettings",
43
48
  "WebhooksOnlyEventListener",
44
49
  "WebhooksOnlyEventListenerSettings",
50
+ "ActionsOnlyEventListener",
51
+ "ActionsOnlyEventListenerSettings",
45
52
  ]
@@ -0,0 +1,42 @@
1
+ from typing import Literal
2
+ from loguru import logger
3
+
4
+ from port_ocean.core.event_listener.base import (
5
+ BaseEventListener,
6
+ EventListenerEvents,
7
+ EventListenerSettings,
8
+ )
9
+ from port_ocean.core.models import EventListenerType
10
+
11
+
12
+ class ActionsOnlyEventListenerSettings(EventListenerSettings):
13
+ """
14
+ This class inherits from `EventListenerSettings`, which provides a foundation for creating event listener settings.
15
+ """
16
+
17
+ type: Literal[EventListenerType.ACTIONS_ONLY]
18
+ should_resync: bool = False
19
+ should_process_webhooks: bool = False
20
+
21
+
22
+ class ActionsOnlyEventListener(BaseEventListener):
23
+ """
24
+ No resync event listener.
25
+
26
+ It is used to handle events exclusively through actions without supporting resync events.
27
+
28
+ Parameters:
29
+ events (EventListenerEvents): A dictionary containing event types and their corresponding event handlers.
30
+ event_listener_config (ActionsOnlyEventListenerSettings): The event listener configuration settings.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ events: EventListenerEvents,
36
+ event_listener_config: ActionsOnlyEventListenerSettings,
37
+ ):
38
+ super().__init__(events)
39
+ self.event_listener_config = event_listener_config
40
+
41
+ async def _start(self) -> None:
42
+ logger.info("Starting Actions-only event listener")
@@ -4,6 +4,7 @@ from typing import TypedDict, Callable, Any, Awaitable
4
4
  from pydantic import Extra
5
5
 
6
6
  from port_ocean.config.base import BaseOceanModel
7
+ from port_ocean.core.models import EventListenerType
7
8
  from port_ocean.utils.signal import signal_handler
8
9
  from port_ocean.context.ocean import ocean
9
10
  from port_ocean.utils.misc import IntegrationStateStatus
@@ -78,8 +79,10 @@ class BaseEventListener:
78
79
 
79
80
 
80
81
  class EventListenerSettings(BaseOceanModel, extra=Extra.allow):
81
- type: str
82
+ type: EventListenerType
82
83
  should_resync: bool = True
84
+ should_process_webhooks: bool = True
85
+ should_run_actions: bool = True
83
86
 
84
87
  def get_changelog_destination_details(self) -> dict[str, Any]:
85
88
  """
@@ -13,6 +13,10 @@ from port_ocean.core.event_listener import (
13
13
  KafkaEventListenerSettings,
14
14
  PollingEventListenerSettings,
15
15
  )
16
+ from port_ocean.core.event_listener.actions_only import (
17
+ ActionsOnlyEventListener,
18
+ ActionsOnlyEventListenerSettings,
19
+ )
16
20
  from port_ocean.core.event_listener.base import (
17
21
  BaseEventListener,
18
22
  EventListenerEvents,
@@ -21,6 +25,7 @@ from port_ocean.core.event_listener.webhooks_only import (
21
25
  WebhooksOnlyEventListener,
22
26
  WebhooksOnlyEventListenerSettings,
23
27
  )
28
+ from port_ocean.core.models import EventListenerType
24
29
  from port_ocean.exceptions.core import UnsupportedEventListenerTypeException
25
30
 
26
31
 
@@ -57,12 +62,11 @@ class EventListenerFactory:
57
62
  wrapped_events: EventListenerEvents = {"on_resync": self.events["on_resync"]}
58
63
  event_listener: BaseEventListener
59
64
  config = self.context.config.event_listener
60
- _type = config.type.lower()
61
65
  assert_message = "Invalid event listener config, expected KafkaEventListenerSettings and got {0}"
62
- logger.info(f"Found event listener type: {_type}")
66
+ logger.info(f"Found event listener type: {config.type}")
63
67
 
64
- match _type:
65
- case "kafka":
68
+ match config.type:
69
+ case EventListenerType.KAFKA:
66
70
  assert isinstance(
67
71
  config, KafkaEventListenerSettings
68
72
  ), assert_message.format(type(config))
@@ -75,31 +79,36 @@ class EventListenerFactory:
75
79
  self.context.config.integration.type,
76
80
  )
77
81
 
78
- case "webhook":
82
+ case EventListenerType.WEBHOOK:
79
83
  assert isinstance(
80
84
  config, HttpEventListenerSettings
81
85
  ), assert_message.format(type(config))
82
86
  event_listener = HttpEventListener(wrapped_events, config)
83
87
 
84
- case "polling":
88
+ case EventListenerType.POLLING:
85
89
  assert isinstance(
86
90
  config, PollingEventListenerSettings
87
91
  ), assert_message.format(type(config))
88
92
  event_listener = PollingEventListener(wrapped_events, config)
89
93
 
90
- case "once":
94
+ case EventListenerType.ONCE:
91
95
  assert isinstance(
92
96
  config, OnceEventListenerSettings
93
97
  ), assert_message.format(type(config))
94
98
  event_listener = OnceEventListener(wrapped_events, config)
95
- case "webhooks_only":
99
+ case EventListenerType.WEBHOOKS_ONLY:
96
100
  assert isinstance(
97
101
  config, WebhooksOnlyEventListenerSettings
98
102
  ), assert_message.format(type(config))
99
103
  event_listener = WebhooksOnlyEventListener(wrapped_events, config)
104
+ case EventListenerType.ACTIONS_ONLY:
105
+ assert isinstance(
106
+ config, ActionsOnlyEventListenerSettings
107
+ ), assert_message.format(type(config))
108
+ event_listener = ActionsOnlyEventListener(wrapped_events, config)
100
109
  case _:
101
110
  raise UnsupportedEventListenerTypeException(
102
- f"Event listener {_type} not supported"
111
+ f"Event listener {config.type} not supported"
103
112
  )
104
113
 
105
114
  return event_listener
@@ -1,4 +1,4 @@
1
- from typing import Literal, Any
1
+ from typing import Any, Literal
2
2
 
3
3
  from fastapi import APIRouter
4
4
  from loguru import logger
@@ -11,6 +11,7 @@ from port_ocean.core.event_listener.base import (
11
11
  EventListenerEvents,
12
12
  EventListenerSettings,
13
13
  )
14
+ from port_ocean.core.models import EventListenerType
14
15
 
15
16
 
16
17
  class HttpEventListenerSettings(EventListenerSettings):
@@ -19,12 +20,12 @@ class HttpEventListenerSettings(EventListenerSettings):
19
20
  The `HttpEventListenerSettings` specifically includes settings related to the HTTP event listener (Webhook).
20
21
 
21
22
  Attributes:
22
- type (Literal["WEBHOOK"]): A literal indicating the type of the event listener, which is set to "WEBHOOK" for this class.
23
+ type (EventListenerType): A literal indicating the type of the event listener, which is set to "WEBHOOK" for this class.
23
24
  app_host (AnyHttpUrl): The base URL of the application hosting the webhook.
24
25
  The "AnyHttpUrl" type indicates that the value must be a valid HTTP/HTTPS URL.
25
26
  """
26
27
 
27
- type: Literal["WEBHOOK"]
28
+ type: Literal[EventListenerType.WEBHOOK]
28
29
  app_host: AnyHttpUrl = Field(..., sensitive=True)
29
30
 
30
31
  def get_changelog_destination_details(self) -> dict[str, Any]:
@@ -17,6 +17,7 @@ from port_ocean.core.event_listener.base import (
17
17
  EventListenerSettings,
18
18
  )
19
19
  from pydantic import validator
20
+ from port_ocean.core.models import EventListenerType
20
21
 
21
22
 
22
23
  class KafkaEventListenerSettings(EventListenerSettings):
@@ -25,7 +26,7 @@ class KafkaEventListenerSettings(EventListenerSettings):
25
26
  The `KafkaEventListenerSettings` specifically includes settings related to the Kafka event listener.
26
27
 
27
28
  Attributes:
28
- type (Literal["KAFKA"]): A literal indicating the type of the event listener, which is set to "KAFKA" for this class.
29
+ type (EventListenerType): A literal indicating the type of the event listener, which is set to "KAFKA" for this class.
29
30
  brokers (str): The comma-separated list of Kafka broker URLs to connect to.
30
31
  security_protocol (str): The security protocol used for communication with Kafka brokers.
31
32
  The default value is "SASL_SSL".
@@ -38,7 +39,7 @@ class KafkaEventListenerSettings(EventListenerSettings):
38
39
  The default value is 1 second.
39
40
  """
40
41
 
41
- type: Literal["KAFKA"]
42
+ type: Literal[EventListenerType.KAFKA]
42
43
  brokers: str = (
43
44
  "b-1-public.publicclusterprod.t9rw6w.c1.kafka.eu-west-1.amazonaws.com:9196,b-2-public.publicclusterprod.t9rw6w.c1.kafka.eu-west-1.amazonaws.com:9196,b-3-public.publicclusterprod.t9rw6w.c1.kafka.eu-west-1.amazonaws.com:9196"
44
45
  )
@@ -1,6 +1,6 @@
1
1
  import datetime
2
2
  import signal
3
- from typing import Literal, Any
3
+ from typing import Any, Literal
4
4
 
5
5
  from loguru import logger
6
6
 
@@ -9,6 +9,7 @@ from port_ocean.core.event_listener.base import (
9
9
  EventListenerEvents,
10
10
  EventListenerSettings,
11
11
  )
12
+ from port_ocean.core.models import EventListenerType
12
13
  from port_ocean.utils.repeat import repeat_every
13
14
  from port_ocean.context.ocean import ocean
14
15
  from port_ocean.utils.time import convert_str_to_utc_datetime, convert_to_minutes
@@ -21,7 +22,9 @@ class OnceEventListenerSettings(EventListenerSettings):
21
22
  This class inherits from `EventListenerSettings`, which provides a foundation for creating event listener settings.
22
23
  """
23
24
 
24
- type: Literal["ONCE"]
25
+ type: Literal[EventListenerType.ONCE]
26
+ should_process_webhooks: bool = False
27
+ should_run_actions: bool = False
25
28
 
26
29
 
27
30
  class OnceEventListener(BaseEventListener):