port-ocean 0.30.3__tar.gz → 0.30.5__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 (228) hide show
  1. {port_ocean-0.30.3 → port_ocean-0.30.5}/PKG-INFO +1 -1
  2. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/mixins/entities.py +35 -26
  3. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/mixins/utils.py +114 -12
  4. port_ocean-0.30.5/port_ocean/tests/core/integrations/mixins/test_integration_utils.py +494 -0
  5. {port_ocean-0.30.3 → port_ocean-0.30.5}/pyproject.toml +1 -1
  6. {port_ocean-0.30.3 → port_ocean-0.30.5}/LICENSE.md +0 -0
  7. {port_ocean-0.30.3 → port_ocean-0.30.5}/README.md +0 -0
  8. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/Dockerfile.Deb +0 -0
  9. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/Dockerfile.alpine +0 -0
  10. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/Dockerfile.base.builder +0 -0
  11. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/Dockerfile.base.runner +0 -0
  12. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/Dockerfile.dockerignore +0 -0
  13. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/Dockerfile.local +0 -0
  14. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/Makefile +0 -0
  15. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/README.md +0 -0
  16. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/entry_local.sh +0 -0
  17. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/grpcio.sh +0 -0
  18. {port_ocean-0.30.3 → port_ocean-0.30.5}/integrations/_infra/init.sh +0 -0
  19. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/__init__.py +0 -0
  20. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/bootstrap.py +0 -0
  21. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cache/__init__.py +0 -0
  22. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cache/base.py +0 -0
  23. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cache/disk.py +0 -0
  24. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cache/errors.py +0 -0
  25. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cache/memory.py +0 -0
  26. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/__init__.py +0 -0
  27. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cli.py +0 -0
  28. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/__init__.py +0 -0
  29. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/defaults/__init___.py +0 -0
  30. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/defaults/clean.py +0 -0
  31. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/defaults/dock.py +0 -0
  32. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/defaults/group.py +0 -0
  33. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/list_integrations.py +0 -0
  34. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/main.py +0 -0
  35. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/new.py +0 -0
  36. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/pull.py +0 -0
  37. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/sail.py +0 -0
  38. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/commands/version.py +0 -0
  39. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/__init__.py +0 -0
  40. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
  41. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/extensions.py +0 -0
  42. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
  43. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
  44. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
  45. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
  46. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
  47. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
  48. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
  49. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
  50. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
  51. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
  52. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
  53. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
  54. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
  55. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
  56. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
  57. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
  58. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
  59. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
  60. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/cli/utils.py +0 -0
  61. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/__init__.py +0 -0
  62. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/auth/__init__.py +0 -0
  63. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/auth/auth_client.py +0 -0
  64. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/auth/oauth_client.py +0 -0
  65. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/__init__.py +0 -0
  66. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/authentication.py +0 -0
  67. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/client.py +0 -0
  68. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/mixins/__init__.py +0 -0
  69. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/mixins/actions.py +0 -0
  70. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/mixins/blueprints.py +0 -0
  71. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/mixins/integrations.py +0 -0
  72. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/mixins/migrations.py +0 -0
  73. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/mixins/organization.py +0 -0
  74. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/retry_transport.py +0 -0
  75. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/types.py +0 -0
  76. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/clients/port/utils.py +0 -0
  77. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/config/__init__.py +0 -0
  78. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/config/base.py +0 -0
  79. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/config/dynamic.py +0 -0
  80. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/config/settings.py +0 -0
  81. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/consumers/__init__.py +0 -0
  82. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/consumers/kafka_consumer.py +0 -0
  83. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/context/__init__.py +0 -0
  84. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/context/event.py +0 -0
  85. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/context/metric_resource.py +0 -0
  86. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/context/ocean.py +0 -0
  87. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/context/resource.py +0 -0
  88. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/__init__.py +0 -0
  89. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/defaults/__init__.py +0 -0
  90. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/defaults/clean.py +0 -0
  91. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/defaults/common.py +0 -0
  92. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/defaults/initialize.py +0 -0
  93. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/__init__.py +0 -0
  94. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/actions_only.py +0 -0
  95. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/base.py +0 -0
  96. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/factory.py +0 -0
  97. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/http.py +0 -0
  98. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/kafka.py +0 -0
  99. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/once.py +0 -0
  100. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/polling.py +0 -0
  101. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/event_listener/webhooks_only.py +0 -0
  102. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/__init__.py +0 -0
  103. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/actions/__init__.py +0 -0
  104. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/actions/abstract_executor.py +0 -0
  105. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/actions/execution_manager.py +0 -0
  106. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/base.py +0 -0
  107. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
  108. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
  109. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
  110. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
  111. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
  112. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
  113. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
  114. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entity_processor/base.py +0 -0
  115. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
  116. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/entity_processor/jq_input_evaluator.py +0 -0
  117. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
  118. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/port_app_config/api.py +0 -0
  119. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/port_app_config/base.py +0 -0
  120. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/port_app_config/models.py +0 -0
  121. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/queue/__init__.py +0 -0
  122. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/queue/abstract_queue.py +0 -0
  123. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/queue/group_queue.py +0 -0
  124. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/queue/local_queue.py +0 -0
  125. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
  126. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
  127. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/webhook/__init__.py +0 -0
  128. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/webhook/abstract_webhook_processor.py +0 -0
  129. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/webhook/processor_manager.py +0 -0
  130. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/handlers/webhook/webhook_event.py +0 -0
  131. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/__init__.py +0 -0
  132. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/base.py +0 -0
  133. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/mixins/__init__.py +0 -0
  134. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/mixins/events.py +0 -0
  135. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/mixins/handler.py +0 -0
  136. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/mixins/live_events.py +0 -0
  137. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/mixins/sync.py +0 -0
  138. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/integrations/mixins/sync_raw.py +0 -0
  139. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/models.py +0 -0
  140. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/ocean_types.py +0 -0
  141. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/utils/entity_topological_sorter.py +0 -0
  142. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/core/utils/utils.py +0 -0
  143. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/debug_cli.py +0 -0
  144. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/__init__.py +0 -0
  145. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/api.py +0 -0
  146. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/base.py +0 -0
  147. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/clients.py +0 -0
  148. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/context.py +0 -0
  149. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/core.py +0 -0
  150. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/execution_manager.py +0 -0
  151. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/port_defaults.py +0 -0
  152. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/utils.py +0 -0
  153. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/exceptions/webhook_processor.py +0 -0
  154. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/helpers/__init__.py +0 -0
  155. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/helpers/async_client.py +0 -0
  156. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/helpers/metric/metric.py +0 -0
  157. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/helpers/metric/utils.py +0 -0
  158. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/helpers/retry.py +0 -0
  159. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/helpers/stream.py +0 -0
  160. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/log/__init__.py +0 -0
  161. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/log/handlers.py +0 -0
  162. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/log/logger_setup.py +0 -0
  163. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/log/sensetive.py +0 -0
  164. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/middlewares.py +0 -0
  165. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/ocean.py +0 -0
  166. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/py.typed +0 -0
  167. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/run.py +0 -0
  168. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/sonar-project.properties +0 -0
  169. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/__init__.py +0 -0
  170. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/cache/__init__.py +0 -0
  171. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/cache/test_disk_cache.py +0 -0
  172. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/cache/test_memory_cache.py +0 -0
  173. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/clients/__init__.py +0 -0
  174. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/clients/oauth/__init__.py +0 -0
  175. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/clients/oauth/test_oauth_client.py +0 -0
  176. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
  177. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/clients/port/mixins/test_integrations.py +0 -0
  178. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/clients/port/mixins/test_organization_mixin.py +0 -0
  179. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/clients/test_streaming_wrapper.py +0 -0
  180. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/config/test_config.py +0 -0
  181. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/conftest.py +0 -0
  182. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/conftest.py +0 -0
  183. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/defaults/test_common.py +0 -0
  184. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/defaults/test_initialize.py +0 -0
  185. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/event_listener/test_kafka.py +0 -0
  186. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/actions/test_execution_manager.py +0 -0
  187. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +0 -0
  188. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +0 -0
  189. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/entity_processor/test_jq_input_evaluator.py +0 -0
  190. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/mixins/test_live_events.py +0 -0
  191. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py +0 -0
  192. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/port_app_config/test_api.py +0 -0
  193. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/port_app_config/test_base.py +0 -0
  194. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/queue/test_group_queue.py +0 -0
  195. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/queue/test_local_queue.py +0 -0
  196. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +0 -0
  197. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +0 -0
  198. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +0 -0
  199. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/test_utils.py +0 -0
  200. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/utils/test_entity_topological_sorter.py +0 -0
  201. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/utils/test_get_port_diff.py +0 -0
  202. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/core/utils/test_resolve_entities_diff.py +0 -0
  203. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/__init__.py +0 -0
  204. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/fake_port_api.py +0 -0
  205. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/fixtures.py +0 -0
  206. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/integration.py +0 -0
  207. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/ocean_app.py +0 -0
  208. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/port_client.py +0 -0
  209. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/smoke_test.py +0 -0
  210. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/helpers/test_retry.py +0 -0
  211. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/log/test_handlers.py +0 -0
  212. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/test_metric.py +0 -0
  213. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/test_metrics_endpoints.py +0 -0
  214. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/test_ocean.py +0 -0
  215. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/test_smoke.py +0 -0
  216. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/utils/test_async_iterators.py +0 -0
  217. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/tests/utils/test_cache.py +0 -0
  218. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/__init__.py +0 -0
  219. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/async_http.py +0 -0
  220. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/async_iterators.py +0 -0
  221. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/cache.py +0 -0
  222. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/ipc.py +0 -0
  223. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/misc.py +0 -0
  224. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/queue_utils.py +0 -0
  225. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/repeat.py +0 -0
  226. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/signal.py +0 -0
  227. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/utils/time.py +0 -0
  228. {port_ocean-0.30.3 → port_ocean-0.30.5}/port_ocean/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.30.3
3
+ Version: 0.30.5
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
@@ -262,34 +262,36 @@ class EntityClientMixin:
262
262
  }
263
263
  error_entities = {error["index"]: error for error in result.get("errors", [])}
264
264
 
265
+ ocean.metrics.inc_metric(
266
+ name=MetricType.OBJECT_COUNT_NAME,
267
+ labels=[
268
+ ocean.metrics.current_resource_kind(),
269
+ MetricPhase.LOAD,
270
+ MetricPhase.LoadResult.LOADED,
271
+ ],
272
+ value=len(successful_entities),
273
+ )
274
+
275
+ ocean.metrics.inc_metric(
276
+ name=MetricType.OBJECT_COUNT_NAME,
277
+ labels=[
278
+ ocean.metrics.current_resource_kind(),
279
+ MetricPhase.LOAD,
280
+ MetricPhase.LoadResult.FAILED,
281
+ ],
282
+ value=len(error_entities),
283
+ )
284
+
265
285
  batch_results: list[tuple[bool | None, Entity]] = []
266
286
  for entity_index, original_entity in index_to_entity.items():
267
287
  reduced_entity = self._reduce_entity(original_entity)
268
288
  if entity_index in successful_entities:
269
- ocean.metrics.inc_metric(
270
- name=MetricType.OBJECT_COUNT_NAME,
271
- labels=[
272
- ocean.metrics.current_resource_kind(),
273
- MetricPhase.LOAD,
274
- MetricPhase.LoadResult.LOADED,
275
- ],
276
- value=1,
277
- )
278
289
  success_entity = successful_entities[entity_index]
279
290
  # Create a copy of the original entity with the new identifier
280
291
  updated_entity = reduced_entity.copy()
281
292
  updated_entity.identifier = success_entity["identifier"]
282
293
  batch_results.append((True, updated_entity))
283
294
  elif entity_index in error_entities:
284
- ocean.metrics.inc_metric(
285
- name=MetricType.OBJECT_COUNT_NAME,
286
- labels=[
287
- ocean.metrics.current_resource_kind(),
288
- MetricPhase.LOAD,
289
- MetricPhase.LoadResult.FAILED,
290
- ],
291
- value=1,
292
- )
293
295
  error = error_entities[entity_index]
294
296
  if (
295
297
  error.get("identifier") == "unknown"
@@ -357,16 +359,23 @@ class EntityClientMixin:
357
359
  blueprint = entities[0].blueprint
358
360
 
359
361
  identifier_counts = Counter((e.blueprint, e.identifier) for e in entities)
360
- duplicate_count = sum(
361
- count - 1 for count in identifier_counts.values() if count > 1
362
- )
362
+ duplicates: dict[tuple[str, str], int] = {
363
+ key: cnt for key, cnt in identifier_counts.items() if cnt > 1
364
+ }
365
+
366
+ if duplicates:
367
+ # Total number of *extra* occurrences beyond the first appearance of each entity
368
+ duplicate_count = sum(cnt - 1 for cnt in duplicates.values())
369
+
370
+ # Show up to 5 example (blueprint, identifier) pairs to avoid noisy logs
371
+ duplicate_examples = list(duplicates)[:5]
363
372
 
364
- if duplicate_count:
365
- duplicate_examples = [
366
- key for key, cnt in identifier_counts.items() if cnt > 1
367
- ][:5]
368
373
  logger.warning(
369
- f"Detected {duplicate_count} duplicate entities (by blueprint and identifier) that may not be ingested because an identical identifier existed. Examples: {duplicate_examples}"
374
+ "Detected duplicate entities (by blueprint and identifier) that may not be ingested because an identical identifier existed",
375
+ extra={
376
+ "duplicate_examples": duplicate_examples,
377
+ "duplicate_count": duplicate_count,
378
+ },
370
379
  )
371
380
 
372
381
  bulk_size = self.calculate_entities_batch_size(entities)
@@ -9,6 +9,7 @@ import subprocess
9
9
  import tempfile
10
10
  from contextlib import contextmanager
11
11
  from typing import Any, AsyncGenerator, Awaitable, Callable, Generator, cast
12
+ import copy
12
13
 
13
14
  import ijson
14
15
  from loguru import logger
@@ -124,8 +125,8 @@ async def resync_generator_wrapper(
124
125
  if items_to_parse:
125
126
  for data in result:
126
127
  data_path: str | None = None
127
- if isinstance(data, dict) and data.get("file") is not None:
128
- content = data["file"].get("content") if isinstance(data["file"].get("content"), dict) else {}
128
+ if isinstance(data, dict) and data.get("__type") == "path":
129
+ content = data.get("file", {}).get("content") if isinstance(data["file"].get("content"), dict) else {}
129
130
  data_path = content.get("path", None)
130
131
  bulks = get_items_to_parse_bulks(data, data_path, items_to_parse, items_to_parse_name, data.get("__base_jq", ".file.content"))
131
132
  async for bulk in bulks:
@@ -147,6 +148,62 @@ async def resync_generator_wrapper(
147
148
  "At least one of the resync generator iterations failed", errors
148
149
  )
149
150
 
151
+ def extract_jq_deletion_path_revised(jq_expression: str) -> str | None:
152
+ """
153
+ Revised function to extract a simple path suitable for del() by analyzing pipe segments.
154
+ """
155
+ expr = jq_expression.strip()
156
+
157
+ # 1. Handle surrounding parentheses and extract the main chain
158
+ if expr.startswith('('):
159
+ match_paren = re.match(r'\((.*?)\)', expr, re.DOTALL)
160
+ if match_paren:
161
+ chain = match_paren.group(1).strip()
162
+ else:
163
+ return None
164
+ else:
165
+ chain = expr
166
+
167
+ # 2. Split the chain by the main pipe operator (excluding pipes inside quotes or brackets,
168
+ # but for simplicity here, we split naively and check segments)
169
+ segments = chain.split('|')
170
+
171
+ # 3. Analyze each segment for a simple path
172
+ for segment in segments:
173
+ segment = segment.strip()
174
+
175
+ # Ignore variable assignment segments like '. as $root'
176
+ if re.match(r'^\.\s+as\s+\$\w+', segment):
177
+ continue
178
+
179
+ # Ignore identity and variable access like '.' or '$items'
180
+ if segment == '.' or segment.startswith('$'):
181
+ continue
182
+
183
+ # Look for the first genuine path accessor (e.g., .key, .[index], .key.nested, .key[0])
184
+ # This regex looks for a starting dot and follows it with path components
185
+ # (alphanumeric keys or bracketed accessors, where brackets can follow words directly)
186
+ # Pattern: Start with .word or .[index], then optionally more:
187
+ # - .word (dot followed by word)
188
+ # - [index] (bracket directly after word, no dot)
189
+ # - .[index] (dot followed by bracket)
190
+ path_match = re.match(r'(\.[\w]+|\.\[[^\]]+\])(\.[\w]+|\[[^\]]+\]|\.\[[^\]]+\])*', segment)
191
+
192
+ if path_match:
193
+ path = path_match.group(0).strip()
194
+
195
+ # If the path is immediately followed by a simple fallback (// value),
196
+ # we consider the path complete.
197
+ if re.search(r'\s*//\s*(\[\]|null|\.|\{.*?\})', segment):
198
+ return path
199
+
200
+ # If the path is just a path segment followed by nothing or the end of a complex
201
+ # expression (like .file.content.raw) we return it.
202
+ return path
203
+
204
+ # Default case: No suitable path found after checking all segments
205
+ return None
206
+
150
207
 
151
208
  def is_resource_supported(
152
209
  kind: str, resync_event_mapping: dict[str | None, list[RESYNC_EVENT_LISTENER]]
@@ -197,6 +254,7 @@ async def get_items_to_parse_bulks(raw_data: dict[Any, Any], data_path: str, ite
197
254
  temp_output_path = None
198
255
 
199
256
  try:
257
+ is_path_type = True
200
258
  # Create secure temporary files
201
259
  if not data_path:
202
260
  raw_data_serialized = json.dumps(raw_data)
@@ -204,17 +262,17 @@ async def get_items_to_parse_bulks(raw_data: dict[Any, Any], data_path: str, ite
204
262
  with open(temp_data_path, "w") as f:
205
263
  f.write(raw_data_serialized)
206
264
  data_path = temp_data_path
265
+ is_path_type = False
207
266
 
208
267
  temp_output_path = _create_secure_temp_file("_parsed.json")
209
268
 
210
- delete_target = items_to_parse.split('|', 1)[0].strip() if not items_to_parse.startswith('map(') else '.'
211
- base_jq_object_string = await _build_base_jq_object_string(raw_data, base_jq, delete_target)
269
+ delete_target = extract_jq_deletion_path_revised(items_to_parse) or '.'
212
270
 
213
271
  # Build jq expression safely
214
272
  jq_expression = f""". as $all
215
273
  | ($all | {items_to_parse}) as $items
216
274
  | $items
217
- | map({{{items_to_parse_name}: ., {base_jq_object_string}}})"""
275
+ | {_build_mapping_jq_expression(items_to_parse_name, base_jq, delete_target, is_path_type)}"""
218
276
 
219
277
  # Use subprocess with list arguments instead of shell=True
220
278
 
@@ -237,7 +295,7 @@ async def get_items_to_parse_bulks(raw_data: dict[Any, Any], data_path: str, ite
237
295
  with open(temp_output_path, "r") as f:
238
296
  events_stream = get_events_as_a_stream(f, 'item', ocean.config.yield_items_to_parse_batch_size)
239
297
  for items_bulk in events_stream:
240
- yield items_bulk
298
+ yield items_bulk if not is_path_type else [merge_raw_data_to_item(item, raw_data) for item in items_bulk]
241
299
 
242
300
  except ValueError as e:
243
301
  logger.error(f"Invalid jq expression: {e}")
@@ -260,13 +318,57 @@ def unsupported_kind_response(
260
318
  logger.error(f"Kind {kind} is not supported in this integration")
261
319
  return [], [KindNotImplementedException(kind, available_resync_kinds)]
262
320
 
263
- async def _build_base_jq_object_string(raw_data: dict[Any, Any], base_jq: str, delete_target: str) -> str:
264
- base_jq_object_before_parsing = await cast(JQEntityProcessor, ocean.app.integration.entity_processor)._search(raw_data, f"{base_jq} = {json.dumps("__all")}")
265
- base_jq_object_before_parsing_serialized = json.dumps(base_jq_object_before_parsing)
266
- base_jq_object_before_parsing_serialized = base_jq_object_before_parsing_serialized[1:-1] if len(base_jq_object_before_parsing_serialized) >= 2 else base_jq_object_before_parsing_serialized
267
- base_jq_object_before_parsing_serialized = base_jq_object_before_parsing_serialized.replace("\"__all\"", f"(($all | del({delete_target})) // {{}})")
268
- return base_jq_object_before_parsing_serialized
321
+ def _build_mapping_jq_expression(items_to_parse_name: str, base_jq: str, delete_target: str, is_path_type: bool = False) -> str:
322
+ if is_path_type:
323
+ return f"map({{{items_to_parse_name}: . }} | {base_jq} = (($all | del({delete_target})) // {{}}))"
324
+ return f"map(($all | del({delete_target})) + {{{items_to_parse_name}: . }})"
325
+
326
+ def merge_raw_data_to_item(item: dict[str, Any], raw_data: dict[str, Any]) -> dict[str, Any]:
327
+ return recursive_dict_merge(raw_data, item)
328
+
329
+ def recursive_dict_merge(d1: dict[Any, Any], d2: dict[Any, Any]) -> dict[Any, Any]:
330
+ """
331
+ Recursively merges dict d2 into dict d1.
332
+
333
+ If a key exists in both dictionaries:
334
+ 1. If the value in d2 is an empty dictionary (e.g., {}), it overwrites the value in d1.
335
+ 2. If both values are non-empty dictionaries, they are merged recursively.
336
+ 3. Otherwise, the value from d2 overwrites the value from d1.
337
+
338
+ The original dictionaries are not modified (d1 is copied).
339
+
340
+ Args:
341
+ d1: The base dictionary (will be copied and modified).
342
+ d2: The dictionary to merge into d1.
343
+
344
+ Returns:
345
+ The merged dictionary.
346
+ """
347
+ # Start with a copy of d1 to ensure d1 is not mutated
348
+ merged_dict = copy.deepcopy(d1)
349
+
350
+ for key, value in d2.items():
351
+ # Condition to trigger recursive deep merge:
352
+ # 1. Key exists in merged_dict
353
+ # 2. Both values are dictionaries
354
+ # 3. The value from d2 is NOT an empty dictionary ({}).
355
+ # If d2's value is {}, we treat it as an explicit instruction to overwrite/clear.
356
+ is_deep_merge = (
357
+ key in merged_dict and
358
+ isinstance(merged_dict[key], dict) and
359
+ isinstance(value, dict) and
360
+ value != {}
361
+ )
362
+
363
+ if is_deep_merge:
364
+ # If both are dictionaries and d2 is not empty, recurse
365
+ merged_dict[key] = recursive_dict_merge(merged_dict[key], value)
366
+ else:
367
+ # Otherwise (new key, non-dict value, or explicit {} overwrite),
368
+ # overwrite the value from d1 with the value from d2
369
+ merged_dict[key] = value
269
370
 
371
+ return merged_dict
270
372
 
271
373
  def get_events_as_a_stream(
272
374
  stream: Any,