port-ocean 0.12.2.dev5__py3-none-any.whl → 0.12.2.dev6__py3-none-any.whl
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.
- port_ocean/clients/port/mixins/entities.py +39 -40
- port_ocean/core/handlers/entities_state_applier/port/applier.py +31 -32
- port_ocean/core/integrations/mixins/sync_raw.py +48 -47
- {port_ocean-0.12.2.dev5.dist-info → port_ocean-0.12.2.dev6.dist-info}/METADATA +1 -1
- {port_ocean-0.12.2.dev5.dist-info → port_ocean-0.12.2.dev6.dist-info}/RECORD +8 -8
- {port_ocean-0.12.2.dev5.dist-info → port_ocean-0.12.2.dev6.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.12.2.dev5.dist-info → port_ocean-0.12.2.dev6.dist-info}/WHEEL +0 -0
- {port_ocean-0.12.2.dev5.dist-info → port_ocean-0.12.2.dev6.dist-info}/entry_points.txt +0 -0
|
@@ -56,33 +56,33 @@ class EntityClientMixin:
|
|
|
56
56
|
f"entity: {entity.identifier} of "
|
|
57
57
|
f"blueprint: {entity.blueprint}"
|
|
58
58
|
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return
|
|
59
|
+
handle_status_code(response, should_raise)
|
|
60
|
+
result = response.json()
|
|
61
|
+
|
|
62
|
+
result_entity = (
|
|
63
|
+
Entity.parse_obj(result["entity"]) if result.get("entity") else entity
|
|
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
|
+
|
|
71
|
+
# In order to save memory we'll keep only the identifier, blueprint and relations of the
|
|
72
|
+
# upserted entity result for later calculations
|
|
73
|
+
reduced_entity = Entity(
|
|
74
|
+
identifier=result_entity.identifier, blueprint=result_entity.blueprint
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Turning dict typed relations (raw search relations) is required
|
|
78
|
+
# for us to be able to successfully calculate the participation related entities
|
|
79
|
+
# and ignore the ones that don't as they weren't upserted
|
|
80
|
+
reduced_entity.relations = {
|
|
81
|
+
key: None if isinstance(relation, dict) else relation
|
|
82
|
+
for key, relation in result_entity.relations.items()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return reduced_entity
|
|
86
86
|
|
|
87
87
|
async def batch_upsert_entities(
|
|
88
88
|
self,
|
|
@@ -91,7 +91,7 @@ class EntityClientMixin:
|
|
|
91
91
|
user_agent_type: UserAgentType | None = None,
|
|
92
92
|
should_raise: bool = True,
|
|
93
93
|
) -> list[Entity]:
|
|
94
|
-
await asyncio.gather(
|
|
94
|
+
modified_entities_results = await asyncio.gather(
|
|
95
95
|
*(
|
|
96
96
|
self.upsert_entity(
|
|
97
97
|
entity,
|
|
@@ -103,18 +103,17 @@ class EntityClientMixin:
|
|
|
103
103
|
),
|
|
104
104
|
return_exceptions=True,
|
|
105
105
|
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
# return entity_results
|
|
106
|
+
entity_results = [
|
|
107
|
+
entity for entity in modified_entities_results if isinstance(entity, Entity)
|
|
108
|
+
]
|
|
109
|
+
if not should_raise:
|
|
110
|
+
return entity_results
|
|
111
|
+
|
|
112
|
+
for entity_result in modified_entities_results:
|
|
113
|
+
if isinstance(entity_result, Exception):
|
|
114
|
+
raise entity_result
|
|
115
|
+
|
|
116
|
+
return entity_results
|
|
118
117
|
|
|
119
118
|
async def delete_entity(
|
|
120
119
|
self,
|
|
@@ -97,38 +97,37 @@ class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
|
|
|
97
97
|
self, entities: list[Entity], user_agent_type: UserAgentType
|
|
98
98
|
) -> list[Entity]:
|
|
99
99
|
logger.info(f"Upserting {len(entities)} entities")
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return []
|
|
100
|
+
modified_entities: list[Entity] = []
|
|
101
|
+
if event.port_app_config.create_missing_related_entities:
|
|
102
|
+
modified_entities = await self.context.port_client.batch_upsert_entities(
|
|
103
|
+
entities,
|
|
104
|
+
event.port_app_config.get_port_request_options(),
|
|
105
|
+
user_agent_type,
|
|
106
|
+
should_raise=False,
|
|
107
|
+
)
|
|
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
|
+
|
|
117
|
+
ordered_created_entities = reversed(
|
|
118
|
+
entities_with_search_identifier
|
|
119
|
+
+ order_by_entities_dependencies(entities_without_search_identifier)
|
|
120
|
+
)
|
|
121
|
+
for entity in ordered_created_entities:
|
|
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,
|
|
127
|
+
)
|
|
128
|
+
if upsertedEntity:
|
|
129
|
+
modified_entities.append(upsertedEntity)
|
|
130
|
+
return modified_entities
|
|
132
131
|
|
|
133
132
|
async def delete(
|
|
134
133
|
self, entities: list[Entity], user_agent_type: UserAgentType
|
|
@@ -3,6 +3,7 @@ import inspect
|
|
|
3
3
|
import typing
|
|
4
4
|
from typing import Callable, Awaitable, Any
|
|
5
5
|
|
|
6
|
+
import httpx
|
|
6
7
|
from loguru import logger
|
|
7
8
|
|
|
8
9
|
from port_ocean.clients.port.types import UserAgentType
|
|
@@ -425,20 +426,20 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
425
426
|
use_cache=False
|
|
426
427
|
)
|
|
427
428
|
logger.info(f"Resync will use the following mappings: {app_config.dict()}")
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
429
|
+
try:
|
|
430
|
+
did_fetched_current_state = True
|
|
431
|
+
entities_at_port = await ocean.port_client.search_entities(
|
|
432
|
+
user_agent_type
|
|
433
|
+
)
|
|
434
|
+
except httpx.HTTPError as e:
|
|
435
|
+
logger.warning(
|
|
436
|
+
"Failed to fetch the current state of entities at Port. "
|
|
437
|
+
"Skipping delete phase due to unknown initial state. "
|
|
438
|
+
f"Error: {e}\n"
|
|
439
|
+
f"Response status code: {e.response.status_code if isinstance(e, httpx.HTTPStatusError) else None}\n"
|
|
440
|
+
f"Response content: {e.response.text if isinstance(e, httpx.HTTPStatusError) else None}\n"
|
|
441
|
+
)
|
|
442
|
+
did_fetched_current_state = False
|
|
442
443
|
|
|
443
444
|
creation_results: list[tuple[list[Entity], list[Exception]]] = []
|
|
444
445
|
|
|
@@ -457,36 +458,36 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
457
458
|
except asyncio.CancelledError as e:
|
|
458
459
|
logger.warning("Resync aborted successfully, skipping delete phase. This leads to an incomplete state")
|
|
459
460
|
raise
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
461
|
+
else:
|
|
462
|
+
if not did_fetched_current_state:
|
|
463
|
+
logger.warning(
|
|
464
|
+
"Due to an error before the resync, the previous state of entities at Port is unknown."
|
|
465
|
+
" Skipping delete phase due to unknown initial state."
|
|
466
|
+
)
|
|
467
|
+
return
|
|
468
|
+
|
|
469
|
+
logger.info("Starting resync diff calculation")
|
|
470
|
+
flat_created_entities, errors = zip_and_sum(creation_results) or [
|
|
471
|
+
[],
|
|
472
|
+
[],
|
|
473
|
+
]
|
|
474
|
+
|
|
475
|
+
if errors:
|
|
476
|
+
message = f"Resync failed with {len(errors)}. Skipping delete phase due to incomplete state"
|
|
477
|
+
error_group = ExceptionGroup(
|
|
478
|
+
f"Resync failed with {len(errors)}. Skipping delete phase due to incomplete state",
|
|
479
|
+
errors,
|
|
480
|
+
)
|
|
481
|
+
if not silent:
|
|
482
|
+
raise error_group
|
|
483
|
+
|
|
484
|
+
logger.error(message, exc_info=error_group)
|
|
485
|
+
else:
|
|
486
|
+
logger.info(
|
|
487
|
+
f"Running resync diff calculation, number of entities at Port before resync: {len(entities_at_port)}, number of entities created during sync: {len(flat_created_entities)}"
|
|
488
|
+
)
|
|
489
|
+
await self.entities_state_applier.delete_diff(
|
|
490
|
+
{"before": entities_at_port, "after": flat_created_entities},
|
|
491
|
+
user_agent_type,
|
|
492
|
+
)
|
|
493
|
+
logger.info("Resync finished successfully")
|
|
@@ -43,7 +43,7 @@ port_ocean/clients/port/authentication.py,sha256=t3z6h4vld-Tzkpth15sstaMJg0rccX-
|
|
|
43
43
|
port_ocean/clients/port/client.py,sha256=Xd8Jk25Uh4WXY_WW-z1Qbv6F3ZTBFPoOolsxHMfozKw,3366
|
|
44
44
|
port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
45
|
port_ocean/clients/port/mixins/blueprints.py,sha256=POBl4uDocrgJBw4rvCAzwRcD4jk-uBL6pDAuKMTajdg,4633
|
|
46
|
-
port_ocean/clients/port/mixins/entities.py,sha256=
|
|
46
|
+
port_ocean/clients/port/mixins/entities.py,sha256=WdqT1gyS81pByUl9xIfZz_xEHRaBfDuZ-ekKX53oBSE,8870
|
|
47
47
|
port_ocean/clients/port/mixins/integrations.py,sha256=HnWXaJt41SUcha-bhvLdJW07j-l7xIo91GUzzwl2f_E,4859
|
|
48
48
|
port_ocean/clients/port/mixins/migrations.py,sha256=A6896oJF6WbFL2WroyTkMzr12yhVyWqGoq9dtLNSKBY,1457
|
|
49
49
|
port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
|
|
@@ -76,7 +76,7 @@ port_ocean/core/handlers/base.py,sha256=cTarblazu8yh8xz2FpB-dzDKuXxtoi143XJgPbV_
|
|
|
76
76
|
port_ocean/core/handlers/entities_state_applier/__init__.py,sha256=kgLZDCeCEzi4r-0nzW9k78haOZNf6PX7mJOUr34A4c8,173
|
|
77
77
|
port_ocean/core/handlers/entities_state_applier/base.py,sha256=5wHL0icfFAYRPqk8iV_wN49GdJ3aRUtO8tumSxBi4Wo,2268
|
|
78
78
|
port_ocean/core/handlers/entities_state_applier/port/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
-
port_ocean/core/handlers/entities_state_applier/port/applier.py,sha256=
|
|
79
|
+
port_ocean/core/handlers/entities_state_applier/port/applier.py,sha256=EirgWhT_TNeEwfdCElEDGkJ2tSOz9HsaUJ1i2uD7z28,5922
|
|
80
80
|
port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py,sha256=1zncwCbE-Gej0xaWKlzZgoXxOBe9bgs_YxlZ8QW3NdI,1751
|
|
81
81
|
port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py,sha256=82BvU8t5w9uhsxX8hbnwuRPuWhW3cMeuT_5sVIkip1I,1550
|
|
82
82
|
port_ocean/core/handlers/entity_processor/__init__.py,sha256=FvFCunFg44wNQoqlybem9MthOs7p1Wawac87uSXz9U8,156
|
|
@@ -94,7 +94,7 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
|
|
|
94
94
|
port_ocean/core/integrations/mixins/events.py,sha256=Ddfx2L4FpghV38waF8OfVeOV0bHBxNIgjU-q5ffillI,2341
|
|
95
95
|
port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
|
|
96
96
|
port_ocean/core/integrations/mixins/sync.py,sha256=B9fEs8faaYLLikH9GBjE_E61vo0bQDjIGQsQ1SRXOlA,3931
|
|
97
|
-
port_ocean/core/integrations/mixins/sync_raw.py,sha256=
|
|
97
|
+
port_ocean/core/integrations/mixins/sync_raw.py,sha256=hWaO1XbW7m2pUTGXlPJE30gOfbVhYhPXz0uNKEuDaNs,18849
|
|
98
98
|
port_ocean/core/integrations/mixins/utils.py,sha256=7y1rGETZIjOQadyIjFJXIHKkQFKx_SwiP-TrAIsyyLY,2303
|
|
99
99
|
port_ocean/core/models.py,sha256=dJ2_olTdbjUpObQJNmg7e7EENU_zZiX6XOaknNp54B0,1342
|
|
100
100
|
port_ocean/core/ocean_types.py,sha256=3_d8-n626f1kWLQ_Jxw194LEyrOVupz05qs_Y1pvB-A,990
|
|
@@ -140,8 +140,8 @@ port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,32
|
|
|
140
140
|
port_ocean/utils/signal.py,sha256=K-6kKFQTltcmKDhtyZAcn0IMa3sUpOHGOAUdWKgx0_E,1369
|
|
141
141
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
|
142
142
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
143
|
-
port_ocean-0.12.2.
|
|
144
|
-
port_ocean-0.12.2.
|
|
145
|
-
port_ocean-0.12.2.
|
|
146
|
-
port_ocean-0.12.2.
|
|
147
|
-
port_ocean-0.12.2.
|
|
143
|
+
port_ocean-0.12.2.dev6.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
144
|
+
port_ocean-0.12.2.dev6.dist-info/METADATA,sha256=2jHYgbU6PsEbwOZIXbqyF9PfomB4mCoGmsBcdsr0XZU,6619
|
|
145
|
+
port_ocean-0.12.2.dev6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
146
|
+
port_ocean-0.12.2.dev6.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
147
|
+
port_ocean-0.12.2.dev6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|