port-ocean 0.5.6__py3-none-any.whl → 0.17.8__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.

Files changed (111) hide show
  1. integrations/_infra/Dockerfile.Deb +56 -0
  2. integrations/_infra/Dockerfile.alpine +108 -0
  3. integrations/_infra/Dockerfile.base.builder +26 -0
  4. integrations/_infra/Dockerfile.base.runner +13 -0
  5. integrations/_infra/Dockerfile.dockerignore +94 -0
  6. {port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}} → integrations/_infra}/Makefile +21 -8
  7. integrations/_infra/grpcio.sh +18 -0
  8. integrations/_infra/init.sh +5 -0
  9. port_ocean/bootstrap.py +1 -1
  10. port_ocean/cli/commands/defaults/clean.py +3 -1
  11. port_ocean/cli/commands/new.py +42 -7
  12. port_ocean/cli/commands/sail.py +7 -1
  13. port_ocean/cli/cookiecutter/cookiecutter.json +3 -0
  14. port_ocean/cli/cookiecutter/hooks/post_gen_project.py +20 -3
  15. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +6 -0
  16. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +41 -0
  17. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +16 -0
  18. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +6 -7
  19. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +1 -1
  20. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +7 -0
  21. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +1 -0
  22. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +16 -1
  23. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +21 -10
  24. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +2 -0
  25. port_ocean/clients/port/authentication.py +16 -4
  26. port_ocean/clients/port/client.py +17 -0
  27. port_ocean/clients/port/mixins/blueprints.py +7 -8
  28. port_ocean/clients/port/mixins/entities.py +108 -53
  29. port_ocean/clients/port/mixins/integrations.py +23 -34
  30. port_ocean/clients/port/retry_transport.py +0 -5
  31. port_ocean/clients/port/utils.py +9 -3
  32. port_ocean/config/base.py +16 -16
  33. port_ocean/config/settings.py +79 -11
  34. port_ocean/context/event.py +18 -5
  35. port_ocean/context/ocean.py +14 -3
  36. port_ocean/core/defaults/clean.py +10 -3
  37. port_ocean/core/defaults/common.py +25 -9
  38. port_ocean/core/defaults/initialize.py +111 -100
  39. port_ocean/core/event_listener/__init__.py +8 -0
  40. port_ocean/core/event_listener/base.py +49 -10
  41. port_ocean/core/event_listener/factory.py +9 -1
  42. port_ocean/core/event_listener/http.py +11 -3
  43. port_ocean/core/event_listener/kafka.py +24 -5
  44. port_ocean/core/event_listener/once.py +96 -4
  45. port_ocean/core/event_listener/polling.py +16 -14
  46. port_ocean/core/event_listener/webhooks_only.py +41 -0
  47. port_ocean/core/handlers/__init__.py +1 -2
  48. port_ocean/core/handlers/entities_state_applier/base.py +4 -1
  49. port_ocean/core/handlers/entities_state_applier/port/applier.py +29 -87
  50. port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +5 -2
  51. port_ocean/core/handlers/entity_processor/base.py +26 -22
  52. port_ocean/core/handlers/entity_processor/jq_entity_processor.py +253 -45
  53. port_ocean/core/handlers/port_app_config/base.py +55 -15
  54. port_ocean/core/handlers/port_app_config/models.py +24 -5
  55. port_ocean/core/handlers/resync_state_updater/__init__.py +5 -0
  56. port_ocean/core/handlers/resync_state_updater/updater.py +84 -0
  57. port_ocean/core/integrations/base.py +5 -7
  58. port_ocean/core/integrations/mixins/events.py +3 -1
  59. port_ocean/core/integrations/mixins/sync.py +4 -2
  60. port_ocean/core/integrations/mixins/sync_raw.py +209 -74
  61. port_ocean/core/integrations/mixins/utils.py +1 -1
  62. port_ocean/core/models.py +44 -0
  63. port_ocean/core/ocean_types.py +29 -11
  64. port_ocean/core/utils/entity_topological_sorter.py +90 -0
  65. port_ocean/core/utils/utils.py +109 -0
  66. port_ocean/debug_cli.py +5 -0
  67. port_ocean/exceptions/core.py +4 -0
  68. port_ocean/exceptions/port_defaults.py +0 -2
  69. port_ocean/helpers/retry.py +85 -24
  70. port_ocean/log/handlers.py +23 -2
  71. port_ocean/log/logger_setup.py +8 -1
  72. port_ocean/log/sensetive.py +25 -10
  73. port_ocean/middlewares.py +10 -2
  74. port_ocean/ocean.py +57 -24
  75. port_ocean/run.py +10 -5
  76. port_ocean/tests/__init__.py +0 -0
  77. port_ocean/tests/clients/port/mixins/test_entities.py +53 -0
  78. port_ocean/tests/conftest.py +4 -0
  79. port_ocean/tests/core/defaults/test_common.py +166 -0
  80. port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +350 -0
  81. port_ocean/tests/core/handlers/mixins/test_sync_raw.py +552 -0
  82. port_ocean/tests/core/test_utils.py +73 -0
  83. port_ocean/tests/core/utils/test_entity_topological_sorter.py +99 -0
  84. port_ocean/tests/helpers/__init__.py +0 -0
  85. port_ocean/tests/helpers/fake_port_api.py +191 -0
  86. port_ocean/tests/helpers/fixtures.py +46 -0
  87. port_ocean/tests/helpers/integration.py +31 -0
  88. port_ocean/tests/helpers/ocean_app.py +66 -0
  89. port_ocean/tests/helpers/port_client.py +21 -0
  90. port_ocean/tests/helpers/smoke_test.py +82 -0
  91. port_ocean/tests/log/test_handlers.py +71 -0
  92. port_ocean/tests/test_smoke.py +74 -0
  93. port_ocean/tests/utils/test_async_iterators.py +45 -0
  94. port_ocean/tests/utils/test_cache.py +189 -0
  95. port_ocean/utils/async_iterators.py +109 -0
  96. port_ocean/utils/cache.py +37 -1
  97. port_ocean/utils/misc.py +22 -4
  98. port_ocean/utils/queue_utils.py +88 -0
  99. port_ocean/utils/signal.py +1 -4
  100. port_ocean/utils/time.py +54 -0
  101. {port_ocean-0.5.6.dist-info → port_ocean-0.17.8.dist-info}/METADATA +27 -19
  102. port_ocean-0.17.8.dist-info/RECORD +164 -0
  103. {port_ocean-0.5.6.dist-info → port_ocean-0.17.8.dist-info}/WHEEL +1 -1
  104. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.dockerignore +0 -94
  105. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile +0 -15
  106. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/config.yaml +0 -17
  107. port_ocean/core/handlers/entities_state_applier/port/validate_entity_relations.py +0 -40
  108. port_ocean/core/utils.py +0 -65
  109. port_ocean-0.5.6.dist-info/RECORD +0 -129
  110. {port_ocean-0.5.6.dist-info → port_ocean-0.17.8.dist-info}/LICENSE.md +0 -0
  111. {port_ocean-0.5.6.dist-info → port_ocean-0.17.8.dist-info}/entry_points.txt +0 -0
@@ -10,6 +10,7 @@ from port_ocean.core.event_listener.base import (
10
10
  EventListenerSettings,
11
11
  )
12
12
  from port_ocean.utils.repeat import repeat_every
13
+ from port_ocean.utils.signal import signal_handler
13
14
 
14
15
 
15
16
  class PollingEventListenerSettings(EventListenerSettings):
@@ -26,9 +27,6 @@ class PollingEventListenerSettings(EventListenerSettings):
26
27
  resync_on_start: bool = True
27
28
  interval: int = 60
28
29
 
29
- def to_request(self) -> dict[str, Any]:
30
- return {}
31
-
32
30
 
33
31
  class PollingEventListener(BaseEventListener):
34
32
  """
@@ -48,7 +46,16 @@ class PollingEventListener(BaseEventListener):
48
46
  ):
49
47
  super().__init__(events)
50
48
  self.event_listener_config = event_listener_config
51
- self._last_updated_at = None
49
+
50
+ def should_resync(self, last_updated_at: str) -> bool:
51
+ _last_updated_at = (
52
+ ocean.app.resync_state_updater.last_integration_state_updated_at
53
+ )
54
+
55
+ if _last_updated_at is None:
56
+ return self.event_listener_config.resync_on_start
57
+
58
+ return _last_updated_at != last_updated_at
52
59
 
53
60
  async def _start(self) -> None:
54
61
  """
@@ -68,18 +75,13 @@ class PollingEventListener(BaseEventListener):
68
75
  integration = await ocean.app.port_client.get_current_integration()
69
76
  last_updated_at = integration["updatedAt"]
70
77
 
71
- should_resync = (
72
- self._last_updated_at is not None
73
- or self.event_listener_config.resync_on_start
74
- ) and self._last_updated_at != last_updated_at
75
-
76
- if should_resync:
78
+ if self.should_resync(last_updated_at):
77
79
  logger.info("Detected change in integration, resyncing")
78
- self._last_updated_at = last_updated_at
79
- running_task: Task[Any] = get_event_loop().create_task(
80
- self.events["on_resync"]({}) # type: ignore
80
+ ocean.app.resync_state_updater.last_integration_state_updated_at = (
81
+ last_updated_at
81
82
  )
82
- self._tasks_to_close.append(running_task)
83
+ running_task: Task[Any] = get_event_loop().create_task(self._resync({}))
84
+ signal_handler.register(running_task.cancel)
83
85
 
84
86
  await running_task
85
87
 
@@ -0,0 +1,41 @@
1
+ from typing import Literal
2
+
3
+ from loguru import logger
4
+
5
+ from port_ocean.core.event_listener.base import (
6
+ BaseEventListener,
7
+ EventListenerEvents,
8
+ EventListenerSettings,
9
+ )
10
+
11
+
12
+ class WebhooksOnlyEventListenerSettings(EventListenerSettings):
13
+ """
14
+ This class inherits from `EventListenerSettings`, which provides a foundation for creating event listener settings.
15
+ """
16
+
17
+ type: Literal["WEBHOOKS_ONLY"]
18
+ should_resync: bool = False
19
+
20
+
21
+ class WebhooksOnlyEventListener(BaseEventListener):
22
+ """
23
+ No resync event listener.
24
+
25
+ It is used to handle events exclusively through webhooks without supporting resync events.
26
+
27
+ Parameters:
28
+ events (EventListenerEvents): A dictionary containing event types and their corresponding event handlers.
29
+ event_listener_config (OnceEventListenerSettings): The event listener configuration settings.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ events: EventListenerEvents,
35
+ event_listener_config: WebhooksOnlyEventListenerSettings,
36
+ ):
37
+ super().__init__(events)
38
+ self.event_listener_config = event_listener_config
39
+
40
+ async def _start(self) -> None:
41
+ logger.info("Starting Webhooks-only event listener")
@@ -4,7 +4,7 @@ from .entities_state_applier.base import (
4
4
  from .entities_state_applier.port.applier import (
5
5
  HttpEntitiesStateApplier,
6
6
  )
7
- from .entity_processor.base import BaseEntityProcessor, EntityPortDiff
7
+ from .entity_processor.base import BaseEntityProcessor
8
8
  from .entity_processor.jq_entity_processor import (
9
9
  JQEntityProcessor,
10
10
  )
@@ -12,7 +12,6 @@ from .port_app_config.api import APIPortAppConfig
12
12
  from .port_app_config.base import BasePortAppConfig
13
13
 
14
14
  __all__ = [
15
- "EntityPortDiff",
16
15
  "BaseEntityProcessor",
17
16
  "JQEntityProcessor",
18
17
  "BasePortAppConfig",
@@ -47,12 +47,15 @@ class BaseEntitiesStateApplier(BaseHandler):
47
47
  @abstractmethod
48
48
  async def upsert(
49
49
  self, entities: list[Entity], user_agent_type: UserAgentType
50
- ) -> None:
50
+ ) -> list[Entity]:
51
51
  """Upsert (insert or update) the given entities into the state.
52
52
 
53
53
  Args:
54
54
  entities (list[Entity]): The entities to be upserted.
55
55
  user_agent_type (UserAgentType): The user agent responsible for the upsert.
56
+
57
+ Returns:
58
+ list[Entity]: The upserted entities.
56
59
  """
57
60
  pass
58
61
 
@@ -1,6 +1,3 @@
1
- import asyncio
2
- from itertools import chain
3
-
4
1
  from loguru import logger
5
2
 
6
3
  from port_ocean.clients.port.types import UserAgentType
@@ -11,17 +8,11 @@ from port_ocean.core.handlers.entities_state_applier.base import (
11
8
  from port_ocean.core.handlers.entities_state_applier.port.get_related_entities import (
12
9
  get_related_entities,
13
10
  )
14
- from port_ocean.core.handlers.entities_state_applier.port.order_by_entities_dependencies import (
15
- order_by_entities_dependencies,
16
- )
17
- from port_ocean.core.handlers.entities_state_applier.port.validate_entity_relations import (
18
- validate_entity_relations,
19
- )
20
- from port_ocean.core.handlers.entity_processor.base import EntityPortDiff
11
+
21
12
  from port_ocean.core.models import Entity
22
13
  from port_ocean.core.ocean_types import EntityDiff
23
- from port_ocean.core.utils import is_same_entity, get_unique, get_port_diff
24
- from port_ocean.exceptions.core import RelationValidationException
14
+ from port_ocean.core.utils.entity_topological_sorter import EntityTopologicalSorter
15
+ from port_ocean.core.utils.utils import is_same_entity, get_port_diff
25
16
 
26
17
 
27
18
  class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
@@ -32,63 +23,17 @@ class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
32
23
  through HTTP requests.
33
24
  """
34
25
 
35
- async def _validate_delete_dependent_entities(self, entities: list[Entity]) -> None:
36
- logger.info("Validated deleted entities")
37
- if not event.port_app_config.delete_dependent_entities:
38
- dependent_entities = await asyncio.gather(
39
- *(
40
- self.context.port_client.search_dependent_entities(entity)
41
- for entity in entities
42
- )
43
- )
44
- new_dependent_entities = get_unique(
45
- [
46
- entity
47
- for entity in chain.from_iterable(dependent_entities)
48
- if not any(is_same_entity(item, entity) for item in entities)
49
- ]
50
- )
51
-
52
- if new_dependent_entities:
53
- raise RelationValidationException(
54
- f"Must enable delete_dependent_entities flag or delete all dependent entities: "
55
- f" {[(dep.blueprint, dep.identifier) for dep in new_dependent_entities]}"
56
- )
57
-
58
- async def _validate_entity_diff(self, diff: EntityPortDiff) -> None:
59
- config = event.port_app_config
60
- await self._validate_delete_dependent_entities(diff.deleted)
61
- modified_or_created_entities = diff.modified + diff.created
62
-
63
- if modified_or_created_entities and not config.create_missing_related_entities:
64
- logger.info("Validating modified or created entities")
65
-
66
- await asyncio.gather(
67
- *(
68
- self.context.port_client.validate_entity_payload(
69
- entity,
70
- config.enable_merge_entity,
71
- create_missing_related_entities=config.create_missing_related_entities,
72
- )
73
- for entity in modified_or_created_entities
74
- )
75
- )
76
-
77
- if not event.port_app_config.delete_dependent_entities:
78
- logger.info("Validating no relation blocks the operation")
79
- await validate_entity_relations(diff, self.context.port_client)
80
-
81
- async def _delete_diff(
26
+ async def _safe_delete(
82
27
  self,
83
28
  entities_to_delete: list[Entity],
84
- created_entities: list[Entity],
29
+ entities_to_protect: list[Entity],
85
30
  user_agent_type: UserAgentType,
86
31
  ) -> None:
87
32
  if not entities_to_delete:
88
33
  return
89
34
 
90
35
  related_entities = await get_related_entities(
91
- created_entities, self.context.port_client
36
+ entities_to_protect, self.context.port_client
92
37
  )
93
38
 
94
39
  allowed_entities_to_delete = []
@@ -98,7 +43,8 @@ class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
98
43
  is_same_entity(entity, entity_to_delete) for entity in related_entities
99
44
  )
100
45
  is_part_of_created = any(
101
- is_same_entity(entity, entity_to_delete) for entity in created_entities
46
+ is_same_entity(entity, entity_to_delete)
47
+ for entity in entities_to_protect
102
48
  )
103
49
  if is_part_of_related:
104
50
  if event.port_app_config.create_missing_related_entities:
@@ -119,21 +65,14 @@ class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
119
65
  user_agent_type: UserAgentType,
120
66
  ) -> None:
121
67
  diff = get_port_diff(entities["before"], entities["after"])
68
+ kept_entities = diff.created + diff.modified
122
69
 
123
70
  logger.info(
124
71
  f"Updating entity diff (created: {len(diff.created)}, deleted: {len(diff.deleted)}, modified: {len(diff.modified)})"
125
72
  )
126
- await self._validate_entity_diff(diff)
73
+ modified_entities = await self.upsert(kept_entities, user_agent_type)
127
74
 
128
- logger.info("Upserting new entities")
129
- await self.upsert(diff.created, user_agent_type)
130
- logger.info("Upserting modified entities")
131
- await self.upsert(diff.modified, user_agent_type)
132
-
133
- logger.info("Deleting diff entities")
134
- await self._delete_diff(
135
- diff.deleted, diff.created + diff.modified, user_agent_type
136
- )
75
+ await self._safe_delete(diff.deleted, modified_entities, user_agent_type)
137
76
 
138
77
  async def delete_diff(
139
78
  self,
@@ -145,39 +84,40 @@ class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
145
84
  if not diff.deleted:
146
85
  return
147
86
 
87
+ kept_entities = diff.created + diff.modified
88
+
148
89
  logger.info(
149
- f"Updating entity diff (created: {len(diff.created)}, deleted: {len(diff.deleted)}, modified: {len(diff.modified)})"
90
+ f"Determining entities to delete ({len(diff.deleted)}/{len(kept_entities)})"
150
91
  )
151
- await self._validate_entity_diff(diff)
152
92
 
153
- logger.info("Deleting diff entities")
154
- await self._delete_diff(
155
- diff.deleted, diff.created + diff.modified, user_agent_type
156
- )
93
+ await self._safe_delete(diff.deleted, kept_entities, user_agent_type)
157
94
 
158
95
  async def upsert(
159
96
  self, entities: list[Entity], user_agent_type: UserAgentType
160
- ) -> None:
97
+ ) -> list[Entity]:
161
98
  logger.info(f"Upserting {len(entities)} entities")
99
+ modified_entities: list[Entity] = []
162
100
  if event.port_app_config.create_missing_related_entities:
163
- await self.context.port_client.batch_upsert_entities(
101
+ modified_entities = await self.context.port_client.batch_upsert_entities(
164
102
  entities,
165
103
  event.port_app_config.get_port_request_options(),
166
104
  user_agent_type,
167
105
  should_raise=False,
168
106
  )
169
107
  else:
170
- ordered_created_entities = reversed(
171
- order_by_entities_dependencies(entities)
172
- )
173
-
174
- for entity in ordered_created_entities:
175
- await self.context.port_client.upsert_entity(
108
+ for entity in entities:
109
+ upsertedEntity = await self.context.port_client.upsert_entity(
176
110
  entity,
177
111
  event.port_app_config.get_port_request_options(),
178
112
  user_agent_type,
179
113
  should_raise=False,
180
114
  )
115
+ if upsertedEntity:
116
+ modified_entities.append(upsertedEntity)
117
+ # condition to false to differentiate from `result_entity.is_using_search_identifier`
118
+ if upsertedEntity is False:
119
+ event.entity_topological_sorter.register_entity(entity)
120
+ return modified_entities
181
121
 
182
122
  async def delete(
183
123
  self, entities: list[Entity], user_agent_type: UserAgentType
@@ -191,7 +131,9 @@ class HttpEntitiesStateApplier(BaseEntitiesStateApplier):
191
131
  should_raise=False,
192
132
  )
193
133
  else:
194
- ordered_deleted_entities = order_by_entities_dependencies(entities)
134
+ ordered_deleted_entities = (
135
+ EntityTopologicalSorter.order_by_entities_dependencies(entities)
136
+ )
195
137
 
196
138
  for entity in ordered_deleted_entities:
197
139
  await self.context.port_client.delete_entity(
@@ -14,7 +14,6 @@ def node(entity: Entity) -> Node:
14
14
  def order_by_entities_dependencies(entities: list[Entity]) -> list[Entity]:
15
15
  nodes: dict[Node, Set[Node]] = {}
16
16
  entities_map = {}
17
-
18
17
  for entity in entities:
19
18
  nodes[node(entity)] = set()
20
19
  entities_map[node(entity)] = entity
@@ -33,7 +32,11 @@ def order_by_entities_dependencies(entities: list[Entity]) -> list[Entity]:
33
32
  ]
34
33
 
35
34
  for related_entity in related_entities:
36
- nodes[node(entity)].add(node(related_entity))
35
+ if (
36
+ entity.blueprint is not related_entity.blueprint
37
+ or entity.identifier is not related_entity.identifier
38
+ ):
39
+ nodes[node(entity)].add(node(related_entity))
37
40
 
38
41
  sort_op = TopologicalSorter(nodes)
39
42
  try:
@@ -1,25 +1,14 @@
1
1
  from abc import abstractmethod
2
- from dataclasses import dataclass, field
3
2
 
4
3
  from loguru import logger
5
4
 
6
5
  from port_ocean.core.handlers.base import BaseHandler
7
6
  from port_ocean.core.handlers.port_app_config.models import ResourceConfig
8
- from port_ocean.core.models import Entity
9
- from port_ocean.core.ocean_types import RawEntityDiff, EntityDiff
10
-
11
-
12
- @dataclass
13
- class EntityPortDiff:
14
- """Represents the differences between entities for porting.
15
-
16
- This class holds the lists of deleted, modified, and created entities as part
17
- of the porting process.
18
- """
19
-
20
- deleted: list[Entity] = field(default_factory=list)
21
- modified: list[Entity] = field(default_factory=list)
22
- created: list[Entity] = field(default_factory=list)
7
+ from port_ocean.core.ocean_types import (
8
+ RAW_ITEM,
9
+ CalculationResult,
10
+ EntitySelectorDiff,
11
+ )
23
12
 
24
13
 
25
14
  class BaseEntityProcessor(BaseHandler):
@@ -33,21 +22,36 @@ class BaseEntityProcessor(BaseHandler):
33
22
 
34
23
  @abstractmethod
35
24
  async def _parse_items(
36
- self, mapping: ResourceConfig, raw_data: RawEntityDiff
37
- ) -> EntityDiff:
25
+ self,
26
+ mapping: ResourceConfig,
27
+ raw_data: list[RAW_ITEM],
28
+ parse_all: bool = False,
29
+ send_raw_data_examples_amount: int = 0,
30
+ ) -> CalculationResult:
38
31
  pass
39
32
 
40
33
  async def parse_items(
41
- self, mapping: ResourceConfig, raw_data: RawEntityDiff
42
- ) -> EntityDiff:
34
+ self,
35
+ mapping: ResourceConfig,
36
+ raw_data: list[RAW_ITEM],
37
+ parse_all: bool = False,
38
+ send_raw_data_examples_amount: int = 0,
39
+ ) -> CalculationResult:
43
40
  """Public method to parse raw entity data and map it to an EntityDiff.
44
41
 
45
42
  Args:
46
43
  mapping (ResourceConfig): The configuration for entity mapping.
47
- raw_data (RawEntityDiff): The raw data to be parsed.
44
+ raw_data (list[RawEntity]): The raw data to be parsed.
45
+ parse_all (bool): Whether to parse all data or just data that passed the selector.
46
+ send_raw_data_examples_amount (bool): Whether to send example data to the integration service.
48
47
 
49
48
  Returns:
50
49
  EntityDiff: The parsed entity differences.
51
50
  """
52
51
  with logger.contextualize(kind=mapping.kind):
53
- return await self._parse_items(mapping, raw_data)
52
+ if not raw_data:
53
+ return CalculationResult(EntitySelectorDiff([], []), [])
54
+
55
+ return await self._parse_items(
56
+ mapping, raw_data, parse_all, send_raw_data_examples_amount
57
+ )