port-ocean 0.10.5__tar.gz → 0.10.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (140) hide show
  1. {port_ocean-0.10.5 → port_ocean-0.10.8}/PKG-INFO +1 -1
  2. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/bootstrap.py +1 -1
  3. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +12 -7
  4. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/mixins/entities.py +9 -2
  5. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/mixins/integrations.py +1 -1
  6. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/utils.py +1 -1
  7. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/config/settings.py +1 -1
  8. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/defaults/initialize.py +1 -1
  9. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entities_state_applier/port/applier.py +17 -8
  10. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/port_app_config/models.py +8 -4
  11. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/mixins/sync_raw.py +8 -1
  12. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/models.py +4 -0
  13. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/utils.py +3 -1
  14. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/helpers/retry.py +5 -0
  15. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/middlewares.py +10 -2
  16. port_ocean-0.10.8/port_ocean/tests/clients/port/mixins/test_entities.py +53 -0
  17. {port_ocean-0.10.5 → port_ocean-0.10.8}/pyproject.toml +2 -2
  18. {port_ocean-0.10.5 → port_ocean-0.10.8}/LICENSE.md +0 -0
  19. {port_ocean-0.10.5 → port_ocean-0.10.8}/README.md +0 -0
  20. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/__init__.py +0 -0
  21. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/__init__.py +0 -0
  22. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cli.py +0 -0
  23. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/__init__.py +0 -0
  24. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/defaults/__init___.py +0 -0
  25. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/defaults/clean.py +0 -0
  26. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/defaults/dock.py +0 -0
  27. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/defaults/group.py +0 -0
  28. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/list_integrations.py +0 -0
  29. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/main.py +0 -0
  30. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/new.py +0 -0
  31. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/pull.py +0 -0
  32. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/sail.py +0 -0
  33. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/commands/version.py +0 -0
  34. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/__init__.py +0 -0
  35. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
  36. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/extensions.py +0 -0
  37. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
  38. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.dockerignore +0 -0
  39. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
  40. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
  41. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
  42. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
  43. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
  44. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
  45. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
  46. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
  47. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile +0 -0
  48. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Makefile +0 -0
  49. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
  50. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
  51. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
  52. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
  53. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
  54. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
  55. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
  56. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
  57. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/cli/utils.py +0 -0
  58. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/__init__.py +0 -0
  59. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/__init__.py +0 -0
  60. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/authentication.py +0 -0
  61. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/client.py +0 -0
  62. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/mixins/__init__.py +0 -0
  63. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/mixins/blueprints.py +0 -0
  64. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/mixins/migrations.py +0 -0
  65. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/retry_transport.py +0 -0
  66. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/clients/port/types.py +0 -0
  67. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/config/__init__.py +0 -0
  68. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/config/base.py +0 -0
  69. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/config/dynamic.py +0 -0
  70. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/consumers/__init__.py +0 -0
  71. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/consumers/kafka_consumer.py +0 -0
  72. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/context/__init__.py +0 -0
  73. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/context/event.py +0 -0
  74. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/context/ocean.py +0 -0
  75. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/context/resource.py +0 -0
  76. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/__init__.py +0 -0
  77. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/defaults/__init__.py +0 -0
  78. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/defaults/clean.py +0 -0
  79. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/defaults/common.py +0 -0
  80. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/event_listener/__init__.py +0 -0
  81. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/event_listener/base.py +0 -0
  82. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/event_listener/factory.py +0 -0
  83. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/event_listener/http.py +0 -0
  84. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/event_listener/kafka.py +0 -0
  85. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/event_listener/once.py +0 -0
  86. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/event_listener/polling.py +0 -0
  87. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/__init__.py +0 -0
  88. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/base.py +0 -0
  89. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
  90. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
  91. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
  92. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
  93. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
  94. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
  95. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entity_processor/base.py +0 -0
  96. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
  97. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
  98. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/port_app_config/api.py +0 -0
  99. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/port_app_config/base.py +0 -0
  100. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
  101. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
  102. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/__init__.py +0 -0
  103. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/base.py +0 -0
  104. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/mixins/__init__.py +0 -0
  105. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/mixins/events.py +0 -0
  106. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/mixins/handler.py +0 -0
  107. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/mixins/sync.py +0 -0
  108. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/integrations/mixins/utils.py +0 -0
  109. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/core/ocean_types.py +0 -0
  110. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/__init__.py +0 -0
  111. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/api.py +0 -0
  112. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/base.py +0 -0
  113. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/clients.py +0 -0
  114. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/context.py +0 -0
  115. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/core.py +0 -0
  116. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/port_defaults.py +0 -0
  117. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/exceptions/utils.py +0 -0
  118. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/helpers/__init__.py +0 -0
  119. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/helpers/async_client.py +0 -0
  120. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/log/__init__.py +0 -0
  121. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/log/handlers.py +0 -0
  122. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/log/logger_setup.py +0 -0
  123. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/log/sensetive.py +0 -0
  124. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/ocean.py +0 -0
  125. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/py.typed +0 -0
  126. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/run.py +0 -0
  127. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/sonar-project.properties +0 -0
  128. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/tests/__init__.py +0 -0
  129. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/tests/helpers/__init__.py +0 -0
  130. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/tests/test_sample.py +0 -0
  131. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/__init__.py +0 -0
  132. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/async_http.py +0 -0
  133. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/async_iterators.py +0 -0
  134. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/cache.py +0 -0
  135. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/misc.py +0 -0
  136. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/queue_utils.py +0 -0
  137. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/repeat.py +0 -0
  138. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/signal.py +0 -0
  139. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/utils/time.py +0 -0
  140. {port_ocean-0.10.5 → port_ocean-0.10.8}/port_ocean/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.10.5
3
+ Version: 0.10.8
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
@@ -16,7 +16,7 @@ def _get_base_integration_class_from_module(
16
16
  for name, obj in getmembers(module):
17
17
  if (
18
18
  isclass(obj)
19
- and type(obj) == type
19
+ and type(obj) is type
20
20
  and issubclass(obj, BaseIntegration)
21
21
  and obj != BaseIntegration
22
22
  ):
@@ -11,14 +11,14 @@ port_ocean = { version = "^{% version %}", extras = ["cli"] }
11
11
  [tool.poetry.group.dev.dependencies]
12
12
  # Uncomment this if you want to debug the ocean core together with your integration
13
13
  # port_ocean = { path = '../../', develop = true, extras = ['all'] }
14
- pytest = "^7.2"
15
- pytest-xdist = "^3.6.1"
16
- pre-commit = "^3.7.1"
17
- requests = "^2.32.3"
18
- black = "^23.3.0"
14
+ black = "^24.4.2"
19
15
  mypy = "^1.3.0"
20
- ruff = "^0.0.278"
21
- pylint = "^2.17.4"
16
+ pylint = ">=2.17.4,<4.0.0"
17
+ pytest = ">=8.2,<9.0"
18
+ pytest-asyncio = ">=0.24.0"
19
+ pytest-httpx = ">=0.30.0"
20
+ pytest-xdist = "^3.6.1"
21
+ ruff = "^0.6.3"
22
22
  towncrier = "^23.6.0"
23
23
 
24
24
  [tool.towncrier]
@@ -106,3 +106,8 @@ exclude = '''
106
106
  |\.venv
107
107
  )/
108
108
  '''
109
+
110
+ [tool.pytest.ini_options]
111
+ asyncio_mode = "auto"
112
+ asyncio_default_fixture_loop_scope = "function"
113
+ addopts = "-vv -n auto ./tests"
@@ -29,7 +29,7 @@ class EntityClientMixin:
29
29
  request_options: RequestOptions,
30
30
  user_agent_type: UserAgentType | None = None,
31
31
  should_raise: bool = True,
32
- ) -> Entity:
32
+ ) -> Entity | None:
33
33
  validation_only = request_options["validation_only"]
34
34
  async with self.semaphore:
35
35
  logger.debug(
@@ -58,9 +58,16 @@ class EntityClientMixin:
58
58
  )
59
59
  handle_status_code(response, should_raise)
60
60
  result = response.json()
61
+
61
62
  result_entity = (
62
63
  Entity.parse_obj(result["entity"]) if result.get("entity") else entity
63
64
  )
65
+
66
+ # Happens when upsert fails and search identifier is defined.
67
+ # We return None to ignore the entity later in the delete process
68
+ if result_entity.is_using_search_identifier:
69
+ return None
70
+
64
71
  # In order to save memory we'll keep only the identifier, blueprint and relations of the
65
72
  # upserted entity result for later calculations
66
73
  reduced_entity = Entity(
@@ -94,7 +101,7 @@ class EntityClientMixin:
94
101
  )
95
102
  for entity in entities
96
103
  ),
97
- return_exceptions=should_raise,
104
+ return_exceptions=True,
98
105
  )
99
106
  entity_results = [
100
107
  entity for entity in modified_entities_results if isinstance(entity, Entity)
@@ -110,7 +110,7 @@ class IntegrationClientMixin:
110
110
  "logs": logs,
111
111
  },
112
112
  )
113
- handle_status_code(response)
113
+ handle_status_code(response, should_log=False)
114
114
  logger.debug("Logs successfully ingested")
115
115
 
116
116
  async def ingest_integration_kind_examples(
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
18
18
  # The max_keepalive_connections can't be too high, as it will cause the application to run out of available connections.
19
19
  PORT_HTTP_MAX_CONNECTIONS_LIMIT = 200
20
20
  PORT_HTTP_MAX_KEEP_ALIVE_CONNECTIONS = 50
21
- PORT_HTTP_TIMEOUT = 10.0
21
+ PORT_HTTP_TIMEOUT = 60.0
22
22
 
23
23
  PORT_HTTPX_TIMEOUT = httpx.Timeout(PORT_HTTP_TIMEOUT)
24
24
  PORT_HTTPX_LIMITS = httpx.Limits(
@@ -67,7 +67,7 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
67
67
  allow_environment_variables_jq_access: bool = True
68
68
  initialize_port_resources: bool = True
69
69
  scheduled_resync_interval: int | None = None
70
- client_timeout: int = 30
70
+ client_timeout: int = 60
71
71
  send_raw_data_examples: bool = True
72
72
  port: PortSettings
73
73
  event_listener: EventListenerSettingsType = Field(
@@ -64,7 +64,7 @@ async def _initialize_required_integration_settings(
64
64
  )
65
65
  if not integration:
66
66
  logger.info(
67
- "Integration does not exist, Creating new integration with default default mapping"
67
+ "Integration does not exist, Creating new integration with default mapping"
68
68
  )
69
69
  integration = await port_client.create_integration(
70
70
  integration_config.integration.type,
@@ -106,18 +106,27 @@ class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
106
106
  should_raise=False,
107
107
  )
108
108
  else:
109
+ entities_with_search_identifier: list[Entity] = []
110
+ entities_without_search_identifier: list[Entity] = []
111
+ for entity in entities:
112
+ if entity.is_using_search_identifier:
113
+ entities_with_search_identifier.append(entity)
114
+ else:
115
+ entities_without_search_identifier.append(entity)
116
+
109
117
  ordered_created_entities = reversed(
110
- order_by_entities_dependencies(entities)
118
+ entities_with_search_identifier
119
+ + order_by_entities_dependencies(entities_without_search_identifier)
111
120
  )
112
121
  for entity in ordered_created_entities:
113
- modified_entities.append(
114
- await self.context.port_client.upsert_entity(
115
- entity,
116
- event.port_app_config.get_port_request_options(),
117
- user_agent_type,
118
- should_raise=False,
119
- )
122
+ upsertedEntity = await self.context.port_client.upsert_entity(
123
+ entity,
124
+ event.port_app_config.get_port_request_options(),
125
+ user_agent_type,
126
+ should_raise=False,
120
127
  )
128
+ if upsertedEntity:
129
+ modified_entities.append(upsertedEntity)
121
130
  return modified_entities
122
131
 
123
132
  async def delete(
@@ -13,18 +13,22 @@ class Rule(BaseModel):
13
13
  value: str
14
14
 
15
15
 
16
- class SearchRelation(BaseModel):
16
+ class IngestSearchQuery(BaseModel):
17
17
  combinator: str
18
- rules: list[Rule | SearchRelation]
18
+ rules: list[Rule | IngestSearchQuery]
19
19
 
20
20
 
21
21
  class EntityMapping(BaseModel):
22
- identifier: str
22
+ identifier: str | IngestSearchQuery
23
23
  title: str | None
24
24
  blueprint: str
25
25
  team: str | None
26
26
  properties: dict[str, str] = Field(default_factory=dict)
27
- relations: dict[str, str | SearchRelation] = Field(default_factory=dict)
27
+ relations: dict[str, str | IngestSearchQuery] = Field(default_factory=dict)
28
+
29
+ @property
30
+ def is_using_search_identifier(self) -> bool:
31
+ return isinstance(self.identifier, dict)
28
32
 
29
33
 
30
34
  class MappingsConfig(BaseModel):
@@ -154,6 +154,12 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
154
154
  results: list[RAW_ITEM],
155
155
  user_agent_type: UserAgentType,
156
156
  ) -> tuple[list[Entity], list[Exception]]:
157
+ if resource.port.entity.mappings.is_using_search_identifier:
158
+ logger.info(
159
+ f"Skip unregistering resource of kind {resource.kind}, as mapping defined with search identifier"
160
+ )
161
+ return [], []
162
+
157
163
  objects_diff = await self._calculate_raw([(resource, results)])
158
164
  entities_selector_diff, errors = objects_diff[0]
159
165
 
@@ -272,7 +278,8 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
272
278
  [
273
279
  entity
274
280
  for entity in entities_to_delete
275
- if (entity.identifier, entity.blueprint)
281
+ if not entity.is_using_search_identifier
282
+ and (entity.identifier, entity.blueprint)
276
283
  not in registered_entities_attributes
277
284
  ],
278
285
  )
@@ -19,6 +19,10 @@ class Entity(BaseModel):
19
19
  properties: dict[str, Any] = {}
20
20
  relations: dict[str, Any] = {}
21
21
 
22
+ @property
23
+ def is_using_search_identifier(self) -> bool:
24
+ return isinstance(self.identifier, dict)
25
+
22
26
 
23
27
  class BlueprintRelation(BaseModel):
24
28
  many: bool
@@ -39,7 +39,9 @@ async def validate_integration_runtime(
39
39
  requested_runtime: Runtime,
40
40
  ) -> None:
41
41
  logger.debug("Validating integration runtime")
42
- current_integration = await port_client.get_current_integration(should_raise=False)
42
+ current_integration = await port_client.get_current_integration(
43
+ should_raise=False, should_log=False
44
+ )
43
45
  current_runtime = current_integration.get("installationType", "OnPrem")
44
46
  if current_integration and current_runtime != requested_runtime.value:
45
47
  raise IntegrationRuntimeException(
@@ -290,6 +290,11 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
290
290
  if remaining_attempts < 1:
291
291
  self._log_error(request, error)
292
292
  raise
293
+ except httpx.ReadTimeout as e:
294
+ error = e
295
+ if remaining_attempts < 1:
296
+ self._log_error(request, error)
297
+ raise
293
298
  except httpx.TimeoutException as e:
294
299
  error = e
295
300
  if remaining_attempts < 1:
@@ -52,7 +52,14 @@ async def request_handler(
52
52
  request_id = generate_uuid()
53
53
 
54
54
  with logger.contextualize(request_id=request_id):
55
- logger.bind(url=str(request.url), method=request.method).info("Request started")
55
+ log_level = (
56
+ "DEBUG"
57
+ if request.url.path == "/docs" or request.url.path == "/openapi.json"
58
+ else "INFO"
59
+ )
60
+ logger.bind(url=str(request.url), method=request.method).log(
61
+ log_level, f"Request to {request.url.path} started"
62
+ )
56
63
  response = await _handle_silently(call_next, request)
57
64
 
58
65
  end_time = get_time(seconds_precision=False)
@@ -61,5 +68,6 @@ async def request_handler(
61
68
  response.headers["X-Process-Time"] = str(time_elapsed)
62
69
  logger.bind(
63
70
  time_elapsed=time_elapsed, response_status=response.status_code
64
- ).info("Request ended")
71
+ ).log(log_level, f"Request to {request.url.path} ended")
72
+
65
73
  return response
@@ -0,0 +1,53 @@
1
+ from typing import Any
2
+ from unittest.mock import MagicMock
3
+
4
+ import pytest
5
+
6
+ from port_ocean.clients.port.mixins.entities import EntityClientMixin
7
+ from port_ocean.core.models import Entity
8
+ from httpx import ReadTimeout
9
+
10
+
11
+ errored_entity_identifier: str = "a"
12
+ expected_result_entities = [
13
+ Entity(identifier="b", blueprint="b"),
14
+ Entity(identifier="c", blueprint="c"),
15
+ ]
16
+ all_entities = [
17
+ Entity(identifier=errored_entity_identifier, blueprint="a")
18
+ ] + expected_result_entities
19
+
20
+
21
+ async def mock_upsert_entity(entity: Entity, *args: Any, **kwargs: Any) -> Entity:
22
+ if entity.identifier == errored_entity_identifier:
23
+ raise ReadTimeout("")
24
+ else:
25
+ return entity
26
+
27
+
28
+ @pytest.fixture
29
+ async def entity_client(monkeypatch: Any) -> EntityClientMixin:
30
+ # Arrange
31
+ entity_client = EntityClientMixin(auth=MagicMock(), client=MagicMock())
32
+ monkeypatch.setattr(entity_client, "upsert_entity", mock_upsert_entity)
33
+
34
+ return entity_client
35
+
36
+
37
+ async def test_batch_upsert_entities_read_timeout_should_raise_false(
38
+ entity_client: EntityClientMixin,
39
+ ) -> None:
40
+ result_entities = await entity_client.batch_upsert_entities(
41
+ entities=all_entities, request_options=MagicMock(), should_raise=False
42
+ )
43
+
44
+ assert result_entities == expected_result_entities
45
+
46
+
47
+ async def test_batch_upsert_entities_read_timeout_should_raise_true(
48
+ entity_client: EntityClientMixin,
49
+ ) -> None:
50
+ with pytest.raises(ReadTimeout):
51
+ await entity_client.batch_upsert_entities(
52
+ entities=all_entities, request_options=MagicMock(), should_raise=True
53
+ )
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "port-ocean"
3
- version = "0.10.5"
3
+ version = "0.10.8"
4
4
  description = "Port Ocean is a CLI tool for managing your Port projects."
5
5
  readme = "README.md"
6
6
  homepage = "https://app.getport.io"
@@ -72,7 +72,7 @@ pytest = ">=8.2,<9.0"
72
72
  pytest-asyncio = ">=0.24.0"
73
73
  pytest-httpx = ">=0.30.0"
74
74
  pytest-xdist = "^3.6.1"
75
- ruff = ">=0.0.278,<0.4.11"
75
+ ruff = "^0.6.3"
76
76
  towncrier = "^23.6.0"
77
77
  types-python-dateutil = "^2.9.0.20240316"
78
78
  types-pyyaml = "^6.0.12.10"
File without changes
File without changes