port-ocean 0.21.5__py3-none-any.whl → 0.22.0__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 (29) hide show
  1. integrations/_infra/Makefile +2 -0
  2. port_ocean/cli/cookiecutter/cookiecutter.json +2 -2
  3. port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +1 -1
  4. port_ocean/clients/port/mixins/entities.py +1 -2
  5. port_ocean/config/settings.py +22 -0
  6. port_ocean/context/event.py +1 -0
  7. port_ocean/context/ocean.py +5 -0
  8. port_ocean/context/resource.py +3 -4
  9. port_ocean/core/event_listener/base.py +6 -3
  10. port_ocean/core/handlers/entities_state_applier/port/applier.py +9 -1
  11. port_ocean/core/handlers/entity_processor/base.py +0 -2
  12. port_ocean/core/handlers/resync_state_updater/updater.py +9 -0
  13. port_ocean/core/integrations/mixins/sync_raw.py +61 -10
  14. port_ocean/core/ocean_types.py +1 -0
  15. port_ocean/helpers/metric/metric.py +238 -0
  16. port_ocean/helpers/metric/utils.py +30 -0
  17. port_ocean/helpers/retry.py +2 -1
  18. port_ocean/ocean.py +17 -4
  19. port_ocean/tests/clients/port/mixins/test_entities.py +11 -9
  20. port_ocean/tests/core/conftest.py +186 -0
  21. port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +86 -6
  22. port_ocean/tests/core/handlers/mixins/test_sync_raw.py +5 -164
  23. port_ocean/tests/test_metric.py +180 -0
  24. port_ocean/utils/async_http.py +4 -1
  25. {port_ocean-0.21.5.dist-info → port_ocean-0.22.0.dist-info}/METADATA +2 -1
  26. {port_ocean-0.21.5.dist-info → port_ocean-0.22.0.dist-info}/RECORD +29 -25
  27. {port_ocean-0.21.5.dist-info → port_ocean-0.22.0.dist-info}/LICENSE.md +0 -0
  28. {port_ocean-0.21.5.dist-info → port_ocean-0.22.0.dist-info}/WHEEL +0 -0
  29. {port_ocean-0.21.5.dist-info → port_ocean-0.22.0.dist-info}/entry_points.txt +0 -0
@@ -1,9 +1,6 @@
1
- from contextlib import asynccontextmanager
2
1
  from graphlib import CycleError
3
- from typing import Any, AsyncGenerator
2
+ from typing import Any
4
3
 
5
- from httpx import Response
6
- from port_ocean.clients.port.client import PortClient
7
4
  from port_ocean.core.utils.entity_topological_sorter import EntityTopologicalSorter
8
5
  from port_ocean.exceptions.core import OceanAbortException
9
6
  import pytest
@@ -11,12 +8,8 @@ from unittest.mock import MagicMock, AsyncMock, patch
11
8
  from port_ocean.ocean import Ocean
12
9
  from port_ocean.context.ocean import PortOceanContext
13
10
  from port_ocean.core.handlers.port_app_config.models import (
14
- EntityMapping,
15
- MappingsConfig,
16
11
  PortAppConfig,
17
- PortResourceConfig,
18
12
  ResourceConfig,
19
- Selector,
20
13
  )
21
14
  from port_ocean.core.integrations.mixins import SyncRawMixin
22
15
  from port_ocean.core.handlers.entities_state_applier.port.applier import (
@@ -26,148 +19,11 @@ from port_ocean.core.handlers.entity_processor.jq_entity_processor import (
26
19
  JQEntityProcessor,
27
20
  )
28
21
  from port_ocean.core.models import Entity
29
- from port_ocean.context.event import EventContext, event_context, EventType
22
+ from port_ocean.context.event import event_context, EventType
30
23
  from port_ocean.clients.port.types import UserAgentType
31
- from port_ocean.context.ocean import ocean
32
24
  from dataclasses import dataclass
33
25
  from typing import List, Optional
34
-
35
-
36
- @pytest.fixture
37
- def mock_port_client(mock_http_client: MagicMock) -> PortClient:
38
- mock_port_client = PortClient(
39
- MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock()
40
- )
41
- mock_port_client.auth = AsyncMock()
42
- mock_port_client.auth.headers = AsyncMock(
43
- return_value={
44
- "Authorization": "test",
45
- "User-Agent": "test",
46
- }
47
- )
48
-
49
- mock_port_client.search_entities = AsyncMock(return_value=[]) # type: ignore
50
- mock_port_client.client = mock_http_client
51
- return mock_port_client
52
-
53
-
54
- @pytest.fixture
55
- def mock_http_client() -> MagicMock:
56
- mock_http_client = MagicMock()
57
- mock_upserted_entities = []
58
-
59
- async def post(url: str, *args: Any, **kwargs: Any) -> Response:
60
- entity = kwargs.get("json", {})
61
- if entity.get("properties", {}).get("mock_is_to_fail", {}):
62
- return Response(
63
- 404, headers=MagicMock(), json={"ok": False, "error": "not_found"}
64
- )
65
-
66
- mock_upserted_entities.append(
67
- f"{entity.get('identifier')}-{entity.get('blueprint')}"
68
- )
69
- return Response(
70
- 200,
71
- json={
72
- "entity": {
73
- "identifier": entity.get("identifier"),
74
- "blueprint": entity.get("blueprint"),
75
- }
76
- },
77
- )
78
-
79
- mock_http_client.post = AsyncMock(side_effect=post)
80
- return mock_http_client
81
-
82
-
83
- @pytest.fixture
84
- def mock_ocean(mock_port_client: PortClient) -> Ocean:
85
- with patch("port_ocean.ocean.Ocean.__init__", return_value=None):
86
- ocean_mock = Ocean(
87
- MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock()
88
- )
89
- ocean_mock.config = MagicMock()
90
- ocean_mock.config.port = MagicMock()
91
- ocean_mock.config.port.port_app_config_cache_ttl = 60
92
- ocean_mock.port_client = mock_port_client
93
-
94
- return ocean_mock
95
-
96
-
97
- @pytest.fixture
98
- def mock_context(mock_ocean: Ocean) -> PortOceanContext:
99
- context = PortOceanContext(mock_ocean)
100
- ocean._app = context.app
101
- return context
102
-
103
-
104
- @pytest.fixture
105
- def mock_port_app_config() -> PortAppConfig:
106
- return PortAppConfig(
107
- enable_merge_entity=True,
108
- delete_dependent_entities=True,
109
- create_missing_related_entities=False,
110
- resources=[
111
- ResourceConfig(
112
- kind="project",
113
- selector=Selector(query="true"),
114
- port=PortResourceConfig(
115
- entity=MappingsConfig(
116
- mappings=EntityMapping(
117
- identifier=".id | tostring",
118
- title=".name",
119
- blueprint='"service"',
120
- properties={"url": ".web_url"},
121
- relations={},
122
- )
123
- )
124
- ),
125
- )
126
- ],
127
- )
128
-
129
-
130
- @pytest.fixture
131
- def mock_port_app_config_handler(mock_port_app_config: PortAppConfig) -> MagicMock:
132
- handler = MagicMock()
133
-
134
- async def get_config(use_cache: bool = True) -> Any:
135
- return mock_port_app_config
136
-
137
- handler.get_port_app_config = get_config
138
- return handler
139
-
140
-
141
- @pytest.fixture
142
- def mock_entity_processor(mock_context: PortOceanContext) -> JQEntityProcessor:
143
- return JQEntityProcessor(mock_context)
144
-
145
-
146
- @pytest.fixture
147
- def mock_resource_config() -> ResourceConfig:
148
- resource = ResourceConfig(
149
- kind="service",
150
- selector=Selector(query="true"),
151
- port=PortResourceConfig(
152
- entity=MappingsConfig(
153
- mappings=EntityMapping(
154
- identifier=".id",
155
- title=".name",
156
- blueprint='"service"',
157
- properties={"url": ".web_url"},
158
- relations={},
159
- )
160
- )
161
- ),
162
- )
163
- return resource
164
-
165
-
166
- @pytest.fixture
167
- def mock_entities_state_applier(
168
- mock_context: PortOceanContext,
169
- ) -> HttpEntitiesStateApplier:
170
- return HttpEntitiesStateApplier(mock_context)
26
+ from port_ocean.tests.core.conftest import create_entity, no_op_event_context
171
27
 
172
28
 
173
29
  @pytest.fixture
@@ -189,27 +45,12 @@ def mock_sync_raw_mixin(
189
45
  @pytest.fixture
190
46
  def mock_sync_raw_mixin_with_jq_processor(
191
47
  mock_sync_raw_mixin: SyncRawMixin,
48
+ mock_context: PortOceanContext,
192
49
  ) -> SyncRawMixin:
193
- mock_sync_raw_mixin._entity_processor = JQEntityProcessor(mock_context) # type: ignore
50
+ mock_sync_raw_mixin._entity_processor = JQEntityProcessor(mock_context)
194
51
  return mock_sync_raw_mixin
195
52
 
196
53
 
197
- @asynccontextmanager
198
- async def no_op_event_context(
199
- existing_event: EventContext,
200
- ) -> AsyncGenerator[EventContext, None]:
201
- yield existing_event
202
-
203
-
204
- def create_entity(
205
- id: str, blueprint: str, relation: dict[str, str], is_to_fail: bool
206
- ) -> Entity:
207
- entity = Entity(identifier=id, blueprint=blueprint)
208
- entity.relations = relation
209
- entity.properties = {"mock_is_to_fail": is_to_fail}
210
- return entity
211
-
212
-
213
54
  @pytest.mark.asyncio
214
55
  async def test_sync_raw_mixin_self_dependency(
215
56
  mock_sync_raw_mixin: SyncRawMixin,
@@ -0,0 +1,180 @@
1
+ import ast
2
+ import pytest
3
+
4
+
5
+ @pytest.mark.metric
6
+ @pytest.mark.skip(reason="Skipping metric test until we have a way to test the metrics")
7
+ def test_metrics() -> None:
8
+ """
9
+ Test that the metrics logged in /tmp/ocean/metric.log match expected values.
10
+ """
11
+
12
+ log_path = "/tmp/ocean/metric.log"
13
+ delay = 2
14
+ batch_size = 400
15
+ total_objects = 2000
16
+ magic_string = "prometheus metrics |"
17
+
18
+ # Read the file
19
+ with open(log_path, "r") as file:
20
+ content = file.read()
21
+
22
+ # Ensure the magic string is present in the content
23
+ assert magic_string in content, f"'{magic_string}' not found in {log_path}"
24
+
25
+ # Isolate and parse the JSON object after the magic string
26
+ start_idx = content.rfind(magic_string)
27
+ content_after_magic = content[start_idx + len(magic_string) :]
28
+ obj = ast.literal_eval(content_after_magic)
29
+
30
+ # ----------------------------------------------------------------------------
31
+ # 1. Validate Extract Duration (using original delay/batch_size logic)
32
+ # ----------------------------------------------------------------------------
33
+ num_batches = total_objects / batch_size # e.g., 2000 / 400 = 5
34
+ expected_min_extract_duration = num_batches * delay # e.g., 5 * 2 = 10
35
+
36
+ # Check "fake-person-1" extract duration is > expected_min_extract_duration
37
+ actual_extract_duration = obj.get("duration_seconds__fake-person-1__extract", 0)
38
+ assert round(actual_extract_duration) > round(expected_min_extract_duration), (
39
+ f"Extract duration {actual_extract_duration} not greater than "
40
+ f"{expected_min_extract_duration}"
41
+ )
42
+
43
+ # ----------------------------------------------------------------------------
44
+ # 2. Check Durations for Both "fake-person-1" and "fake-department-0"
45
+ # ----------------------------------------------------------------------------
46
+ # -- fake-person-1
47
+ transform_duration_p1 = obj.get("duration_seconds__fake-person-1__transform", 0)
48
+ load_duration_p1 = obj.get("duration_seconds__fake-person-1__load", 0)
49
+ assert (
50
+ transform_duration_p1 > 0
51
+ ), f"Expected transform duration > 0, got {transform_duration_p1}"
52
+ assert load_duration_p1 > 0, f"Expected load duration > 0, got {load_duration_p1}"
53
+
54
+ # -- fake-department-0
55
+ extract_duration_dept0 = obj.get("duration_seconds__fake-department-0__extract", 0)
56
+ transform_duration_dept0 = obj.get(
57
+ "duration_seconds__fake-department-0__transform", 0
58
+ )
59
+ load_duration_dept0 = obj.get("duration_seconds__fake-department-0__load", 0)
60
+
61
+ assert (
62
+ extract_duration_dept0 > 0
63
+ ), f"Expected department extract duration > 0, got {extract_duration_dept0}"
64
+ assert (
65
+ transform_duration_dept0 > 0
66
+ ), f"Expected department transform duration > 0, got {transform_duration_dept0}"
67
+ assert (
68
+ load_duration_dept0 > 0
69
+ ), f"Expected department load duration > 0, got {load_duration_dept0}"
70
+
71
+ # Optionally, check the "init__top_sort" duration too, if it's relevant:
72
+ init_top_sort = obj.get("duration_seconds__init__top_sort", 0)
73
+ assert init_top_sort >= 0, f"Expected init__top_sort >= 0, got {init_top_sort}"
74
+
75
+ # ----------------------------------------------------------------------------
76
+ # 3. Check Object Counts
77
+ # ----------------------------------------------------------------------------
78
+ # -- fake-person-1
79
+ person_extract_count = obj.get("object_count__fake-person-1__extract", 0)
80
+ person_load_count = obj.get("object_count__fake-person-1__load", 0)
81
+ assert person_extract_count == 2000.0, (
82
+ f"Expected object_count__fake-person-1__extract=2000.0, "
83
+ f"got {person_extract_count}"
84
+ )
85
+ assert person_load_count == 4000.0, (
86
+ f"Expected object_count__fake-person-1__load=4000.0, "
87
+ f"got {person_load_count}"
88
+ )
89
+
90
+ # -- fake-department-0
91
+ dept_extract_count = obj.get("object_count__fake-department-0__extract", 0)
92
+ dept_load_count = obj.get("object_count__fake-department-0__load", 0)
93
+ assert dept_extract_count == 5.0, (
94
+ f"Expected object_count__fake-department-0__extract=5.0, "
95
+ f"got {dept_extract_count}"
96
+ )
97
+ assert dept_load_count == 10.0, (
98
+ f"Expected object_count__fake-department-0__load=10.0, "
99
+ f"got {dept_load_count}"
100
+ )
101
+
102
+ # ----------------------------------------------------------------------------
103
+ # 4. Check Input/Upserted Counts
104
+ # ----------------------------------------------------------------------------
105
+ # -- fake-person-1
106
+ input_count_p1 = obj.get("input_count__fake-person-1__load", 0)
107
+ upserted_count_p1 = obj.get("upserted_count__fake-person-1__load", 0)
108
+ assert (
109
+ input_count_p1 == 2000.0
110
+ ), f"Expected input_count__fake-person-1__load=2000.0, got {input_count_p1}"
111
+ assert (
112
+ upserted_count_p1 == 2000.0
113
+ ), f"Expected upserted_count__fake-person-1__load=2000.0, got {upserted_count_p1}"
114
+
115
+ # -- fake-department-0
116
+ input_count_dept0 = obj.get("input_count__fake-department-0__load", 0)
117
+ upserted_count_dept0 = obj.get("upserted_count__fake-department-0__load", 0)
118
+ assert (
119
+ input_count_dept0 == 5.0
120
+ ), f"Expected input_count__fake-department-0__load=5.0, got {input_count_dept0}"
121
+ assert (
122
+ upserted_count_dept0 == 5.0
123
+ ), f"Expected upserted_count__fake-department-0__load=5.0, got {upserted_count_dept0}"
124
+
125
+ # ----------------------------------------------------------------------------
126
+ # 5. Check Error and Failed Counts
127
+ # ----------------------------------------------------------------------------
128
+ # -- fake-person-1
129
+ error_count_p1 = obj.get("error_count__fake-person-1__load", 0)
130
+ failed_count_p1 = obj.get("failed_count__fake-person-1__load", 0)
131
+ assert (
132
+ error_count_p1 == 0.0
133
+ ), f"Expected error_count__fake-person-1__load=0.0, got {error_count_p1}"
134
+ assert (
135
+ failed_count_p1 == 0.0
136
+ ), f"Expected failed_count__fake-person-1__load=0.0, got {failed_count_p1}"
137
+
138
+ # -- fake-department-0
139
+ error_count_dept0 = obj.get("error_count__fake-department-0__load", 0)
140
+ failed_count_dept0 = obj.get("failed_count__fake-department-0__load", 0)
141
+ assert (
142
+ error_count_dept0 == 0.0
143
+ ), f"Expected error_count__fake-department-0__load=0.0, got {error_count_dept0}"
144
+ assert (
145
+ failed_count_dept0 == 0.0
146
+ ), f"Expected failed_count__fake-department-0__load=0.0, got {failed_count_dept0}"
147
+
148
+ # ----------------------------------------------------------------------------
149
+ # 6. Check HTTP Request Counts (200s)
150
+ # ----------------------------------------------------------------------------
151
+ # Example: we confirm certain request counters match the sample data provided:
152
+ assert (
153
+ obj.get(
154
+ "http_requests_count__http://host.docker.internal:5555/v1/auth/access_token__init__load__200",
155
+ 0,
156
+ )
157
+ == 1.0
158
+ ), "Expected 1.0 for auth access_token 200 requests"
159
+ assert (
160
+ obj.get(
161
+ "http_requests_count__http://host.docker.internal:5555/v1/integration/smoke-test-integration__init__load__200",
162
+ 0,
163
+ )
164
+ == 5.0
165
+ ), "Expected 5.0 for integration/smoke-test-integration 200 requests"
166
+ assert (
167
+ obj.get(
168
+ "http_requests_count__http://localhost:8000/integration/department/hr/employees?limit=-1&entity_kb_size=1&latency=2000__fake-person-1__extract__200",
169
+ 0,
170
+ )
171
+ == 1.0
172
+ ), "Expected 1.0 for hr/employees?limit=-1 extract 200 requests"
173
+ expected_requests = {
174
+ "http_requests_count__http://localhost:8000/integration/department/marketing/employees?limit=-1&entity_kb_size=1&latency=2000__fake-person-1__extract__200": 1.0,
175
+ "http_requests_count__http://localhost:8000/integration/department/finance/employees?limit=-1&entity_kb_size=1&latency=2000__fake-person-1__extract__200": 1.0,
176
+ }
177
+ for key, expected_val in expected_requests.items():
178
+ assert (
179
+ obj.get(key, 0) == expected_val
180
+ ), f"Expected {expected_val} for '{key}', got {obj.get(key)}"
@@ -11,7 +11,10 @@ _http_client: LocalStack[httpx.AsyncClient] = LocalStack()
11
11
  def _get_http_client_context() -> httpx.AsyncClient:
12
12
  client = _http_client.top
13
13
  if client is None:
14
- client = OceanAsyncClient(RetryTransport, timeout=ocean.config.client_timeout)
14
+ client = OceanAsyncClient(
15
+ RetryTransport,
16
+ timeout=ocean.config.client_timeout,
17
+ )
15
18
  _http_client.push(client)
16
19
 
17
20
  return client
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.21.5
3
+ Version: 0.22.0
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
@@ -31,6 +31,7 @@ Requires-Dist: httpx (>=0.24.1,<0.28.0)
31
31
  Requires-Dist: jinja2-time (>=0.2.0,<0.3.0) ; extra == "cli"
32
32
  Requires-Dist: jq (>=1.8.0,<2.0.0)
33
33
  Requires-Dist: loguru (>=0.7.0,<0.8.0)
34
+ Requires-Dist: prometheus-client (>=0.21.1,<0.22.0)
34
35
  Requires-Dist: pydantic[dotenv] (>=1.10.8,<2.0.0)
35
36
  Requires-Dist: pydispatcher (>=2.0.7,<3.0.0)
36
37
  Requires-Dist: pyhumps (>=3.8.0,<4.0.0)
@@ -3,7 +3,7 @@ integrations/_infra/Dockerfile.alpine,sha256=7E4Sb-8supsCcseerHwTkuzjHZoYcaHIyxi
3
3
  integrations/_infra/Dockerfile.base.builder,sha256=Ogp_fodUE-lD-HgcfcFJd7pr520LPoEU9GCNk6HQBxk,619
4
4
  integrations/_infra/Dockerfile.base.runner,sha256=T7Tis9EjmcOl2jUXPMSENbRXMqwoHnnFPhgRmfEMO5Y,357
5
5
  integrations/_infra/Dockerfile.dockerignore,sha256=CM1Fxt3I2AvSvObuUZRmy5BNLSGC7ylnbpWzFgD4cso,1163
6
- integrations/_infra/Makefile,sha256=NWX1QLd429KyCzvwNwpkWtSm5QTVarnh5SYO822-ea4,2360
6
+ integrations/_infra/Makefile,sha256=Pzfac3IA1eRxSNDkdOx1spQFxmyRZwqswTI8WGsFYnU,2420
7
7
  integrations/_infra/grpcio.sh,sha256=m924poYznoRZ6Tt7Ct8Cs5AV_cmmOx598yIZ3z4DvZE,616
8
8
  integrations/_infra/init.sh,sha256=nN8lTrOhB286UfFvD6sJ9YJ-9asT9zVSddQB-RAb7Z4,99
9
9
  port_ocean/__init__.py,sha256=J3Mqp7d-CkEe9eMigGG8gSEiVKICY2bf7csNEwVOXk0,294
@@ -22,7 +22,7 @@ port_ocean/cli/commands/pull.py,sha256=VvrRjLNlfPuLIf7KzeIcbzzdi98Z0M9wCRpXC3QPx
22
22
  port_ocean/cli/commands/sail.py,sha256=rY7rEMjfy_KXiWvtL0T72TTLgeQ3HW4SOzKkz9wL9nI,2282
23
23
  port_ocean/cli/commands/version.py,sha256=hEuIEIcm6Zkamz41Z9nxeSM_4g3oNlAgWwQyDGboh-E,536
24
24
  port_ocean/cli/cookiecutter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- port_ocean/cli/cookiecutter/cookiecutter.json,sha256=ie-LJjg-ek3lP2RRosY2u_q2W4y2TykXm_Gynjjt6Es,814
25
+ port_ocean/cli/cookiecutter/cookiecutter.json,sha256=LoZvXhhjVevMUiXVO8fcKi5JHLis6nG3wtwh6hwLoXg,808
26
26
  port_ocean/cli/cookiecutter/extensions.py,sha256=eQNjZvy2enDkJpvMbBGil77Xk9-38f862wfnmCjdoBc,446
27
27
  port_ocean/cli/cookiecutter/hooks/post_gen_project.py,sha256=7DBdSv_vDI1YKW-y7y4wHenu4iF4zV1MukoUT1EgzXI,1182
28
28
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example,sha256=ywAmZto6YBGXyhEmpG1uYsgaHr2N1ZBRjdtRNt6Vkpw,388
@@ -33,7 +33,7 @@ port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/po
33
33
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml,sha256=ie8bI_QOZnJJVG-N1e4KlMebdYts4LUNO_kKw8nGdhA,531
34
34
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md,sha256=XVSgyxfXJZoZmtwaGbQ8XmCapIthe4E7flfuJub-m_s,338
35
35
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md,sha256=ZQwD3K35q0wugHZmb1z5wnynmn0uuzwGFSpjm7GieZU,259
36
- port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md,sha256=5VZmgDRW9gO4d8UuzkujslOIDfIDBiAGL2Hd74HK770,468
36
+ port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md,sha256=D5apUFQBGwMsvPTf1DxEqNWW_gludB2RrlrLgdN8j9E,465
37
37
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore,sha256=JAo-DTfS6GthQGP1NH6wLU-ZymwlTea4KHH_jZVTKn0,14
38
38
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py,sha256=_TRsA2s6GV2E3CTI8CHcsH-ZuH4_Eh5-juDXWaET0ho,65
39
39
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py,sha256=XUwo9yroqSKxAdwVrTKGNGSWvec9n1Rh9Cqvep4HIuE,2257
@@ -52,7 +52,7 @@ port_ocean/clients/port/authentication.py,sha256=6-uDMWsJ0xLe1-9IoYXHWmwtufj8rJR
52
52
  port_ocean/clients/port/client.py,sha256=OaNeN3U7Hw0tK4jYE6ESJEPKbTf9nGp2jcJVq00gnf8,3546
53
53
  port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  port_ocean/clients/port/mixins/blueprints.py,sha256=POBl4uDocrgJBw4rvCAzwRcD4jk-uBL6pDAuKMTajdg,4633
55
- port_ocean/clients/port/mixins/entities.py,sha256=PJzVZTBW_OheFRGPRCZ6yPbVGEAKsMO9CNDNJUI1l48,10770
55
+ port_ocean/clients/port/mixins/entities.py,sha256=ACBq99r2ssc-8ybr6THE3bE4jRx7d_loAPeq1BfnZBQ,10769
56
56
  port_ocean/clients/port/mixins/integrations.py,sha256=nqz1gvQnR6Nzv62ZfbXEyli7FTKW5DCFpI1x1KoJJ_E,9341
57
57
  port_ocean/clients/port/mixins/migrations.py,sha256=A6896oJF6WbFL2WroyTkMzr12yhVyWqGoq9dtLNSKBY,1457
58
58
  port_ocean/clients/port/mixins/organization.py,sha256=fCo_ZS8UlQXsyIx-odTuWkbnfcYmVnQfIsSyJuPOPjM,1031
@@ -62,20 +62,20 @@ port_ocean/clients/port/utils.py,sha256=SjhgmJXAqH2JqXfGy8GoGwzUYiJvUhWDrJyxQcen
62
62
  port_ocean/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
63
  port_ocean/config/base.py,sha256=x1gFbzujrxn7EJudRT81C6eN9WsYAb3vOHwcpcpX8Tc,6370
64
64
  port_ocean/config/dynamic.py,sha256=qOFkRoJsn_BW7581omi_AoMxoHqasf_foxDQ_G11_SI,2030
65
- port_ocean/config/settings.py,sha256=n9aZXkGcyNCk9PBqlMDhk8tuvEMPimNgX0fEoaKNa1o,5408
65
+ port_ocean/config/settings.py,sha256=pqDn733zNJYxUH-y2oTofHz8_WKekw-4QwhovAzO1nw,6207
66
66
  port_ocean/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
67
  port_ocean/consumers/kafka_consumer.py,sha256=N8KocjBi9aR0BOPG8hgKovg-ns_ggpEjrSxqSqF_BSo,4710
68
68
  port_ocean/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
- port_ocean/context/event.py,sha256=pdLBnHl9Ue5Qyyxk_NLVnIizsj9rjFgAt5qzpXq-2yw,6492
70
- port_ocean/context/ocean.py,sha256=Yt0KP3Rgc4MrLSo3dF0a40ww6ny0r2hDADZ42vTj22M,7750
71
- port_ocean/context/resource.py,sha256=yDj63URzQelj8zJPh4BAzTtPhpKr9Gw9DRn7I_0mJ1s,1692
69
+ port_ocean/context/event.py,sha256=z7DBNOPOL9P3s-SR8jpgwoyaQ6IL9vZxLaAxIjv1Faw,6493
70
+ port_ocean/context/ocean.py,sha256=xWD30AbStQK44QFW9ad5pO4i5dQG7uouvjuHScKnJOM,7884
71
+ port_ocean/context/resource.py,sha256=WQlPzplxWic0hvvMxWKu8xPfQQC0VjrsJVeA6yZytw0,1712
72
72
  port_ocean/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
73
  port_ocean/core/defaults/__init__.py,sha256=8qCZg8n06WAdMu9s_FiRtDYLGPGHbOuS60vapeUoAks,142
74
74
  port_ocean/core/defaults/clean.py,sha256=_rL-NCl6Q_x3lUxDW5ACOM27IYilTCWl6ISUfRleuL0,2891
75
75
  port_ocean/core/defaults/common.py,sha256=zJsj7jvlqIMLGXhdASUlbKS8GIAf-FDKKB0O7jB6nx0,4166
76
76
  port_ocean/core/defaults/initialize.py,sha256=wdc3UdhRTu_tRZuCnt9ZRtqbl4dSLa8u6E_dyiAuEWg,10980
77
77
  port_ocean/core/event_listener/__init__.py,sha256=T3E52MKs79fNEW381p7zU9F2vOMvIiiTYWlqRUqnsg0,1135
78
- port_ocean/core/event_listener/base.py,sha256=VdIdp7RLOSxH3ICyV-wCD3NiJoUzsh2KkJ0a9B29GeI,2847
78
+ port_ocean/core/event_listener/base.py,sha256=GFBTHiYhCzps50phzopQFUlTGAluQkCRlyaRqOG4g1Y,2995
79
79
  port_ocean/core/event_listener/factory.py,sha256=M4Qi05pI840sjDIbdjUEgYe9Gp5ckoCkX-KgLBxUpZg,4096
80
80
  port_ocean/core/event_listener/http.py,sha256=_hkQmi9nNh8YG6hbfLrhkATsmGVO8y3qBWvrBHX5Nhk,2992
81
81
  port_ocean/core/event_listener/kafka.py,sha256=pVQfGtElIJN2P5lPBrMxyl05B8c2Q2wp5WvA6Pqjnyc,7501
@@ -87,11 +87,11 @@ port_ocean/core/handlers/base.py,sha256=cTarblazu8yh8xz2FpB-dzDKuXxtoi143XJgPbV_
87
87
  port_ocean/core/handlers/entities_state_applier/__init__.py,sha256=kgLZDCeCEzi4r-0nzW9k78haOZNf6PX7mJOUr34A4c8,173
88
88
  port_ocean/core/handlers/entities_state_applier/base.py,sha256=5wHL0icfFAYRPqk8iV_wN49GdJ3aRUtO8tumSxBi4Wo,2268
89
89
  port_ocean/core/handlers/entities_state_applier/port/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- port_ocean/core/handlers/entities_state_applier/port/applier.py,sha256=F9TxWJWtFOtgslaAB37WCKaGNkr_al0HDXFqNrEMbaU,6271
90
+ port_ocean/core/handlers/entities_state_applier/port/applier.py,sha256=hgZyFuJab-WNQfSLHwGHtKarxMVrvLkFXoiZsLmAGQA,6703
91
91
  port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py,sha256=1zncwCbE-Gej0xaWKlzZgoXxOBe9bgs_YxlZ8QW3NdI,1751
92
92
  port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py,sha256=lyv6xKzhYfd6TioUgR3AVRSJqj7JpAaj1LxxU2xAqeo,1720
93
93
  port_ocean/core/handlers/entity_processor/__init__.py,sha256=FvFCunFg44wNQoqlybem9MthOs7p1Wawac87uSXz9U8,156
94
- port_ocean/core/handlers/entity_processor/base.py,sha256=udR0w5TstTOS5xOfTjAZIEdldn4xr6Oyb3DylatYX3Q,1869
94
+ port_ocean/core/handlers/entity_processor/base.py,sha256=PsnpNRqjHth9xwOvDRe7gKu8cjnVV0XGmTIHGvOelX0,1867
95
95
  port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=C6zJbS3miKyDeXiEV-0t5vJvkEznOeXRZFFOnwJcNdA,11714
96
96
  port_ocean/core/handlers/port_app_config/__init__.py,sha256=8AAT5OthiVM7KCcM34iEgEeXtn2pRMrT4Dze5r1Ixbk,134
97
97
  port_ocean/core/handlers/port_app_config/api.py,sha256=r_Th66NEw38IpRdnXZcRvI8ACfvxW_A6V62WLwjWXlQ,1044
@@ -101,7 +101,7 @@ port_ocean/core/handlers/queue/__init__.py,sha256=1fICM0ZLATmmj6f7cdq_eV2kmw0_jy
101
101
  port_ocean/core/handlers/queue/abstract_queue.py,sha256=q_gpaWFFZHxM3XovEbgsDn8jEOLM45iAZWVC81Paxto,620
102
102
  port_ocean/core/handlers/queue/local_queue.py,sha256=EzqsGIX43xbVAcePwTcCg5QDrXATQpy-VzWxxN_OyAM,574
103
103
  port_ocean/core/handlers/resync_state_updater/__init__.py,sha256=kG6y-JQGpPfuTHh912L_bctIDCzAK4DN-d00S7rguWU,81
104
- port_ocean/core/handlers/resync_state_updater/updater.py,sha256=Yg9ET6ZV5B9GW7u6zZA6GlB_71kmvxvYX2FWgQNzMvo,3182
104
+ port_ocean/core/handlers/resync_state_updater/updater.py,sha256=K3E7MfO7SDO6SYRb_MPb4Cu3LTEumfYLHnhYV8IE0kY,3606
105
105
  port_ocean/core/handlers/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  port_ocean/core/handlers/webhook/abstract_webhook_processor.py,sha256=5KwZkdkDd5HdVkXPzKiqabodZKl-hOtMypkTKd8Hq3M,3891
107
107
  port_ocean/core/handlers/webhook/processor_manager.py,sha256=ipyAXoFtF84EGczyzRcZCzQG4Ippjo4eMsnVxMVz12A,12072
@@ -113,10 +113,10 @@ port_ocean/core/integrations/mixins/events.py,sha256=2L7P3Jhp8XBqddh2_o9Cn4N261n
113
113
  port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
114
114
  port_ocean/core/integrations/mixins/live_events.py,sha256=8HklZmlyffYY_LeDe8xbt3Tb08rlLkqVhFF-2NQeJP4,4126
115
115
  port_ocean/core/integrations/mixins/sync.py,sha256=GHiFbnw0XrBfl7aCTH_w67f_N7EZbcUgssc-0fPujNU,4047
116
- port_ocean/core/integrations/mixins/sync_raw.py,sha256=jKbdDEypGJ86uc579anrPLBa_dETQlphi3gvpu9917U,25427
116
+ port_ocean/core/integrations/mixins/sync_raw.py,sha256=eUupD__avxQZKsgEvGWa7ibTidYShwsKWBq_wakiQAc,27481
117
117
  port_ocean/core/integrations/mixins/utils.py,sha256=oN4Okz6xlaefpid1_Pud8HPSw9BwwjRohyNsknq-Myg,2309
118
118
  port_ocean/core/models.py,sha256=FvTp-BlpbvLbMbngE0wsiimsCfmIhUR1PvsE__Z--1I,2206
119
- port_ocean/core/ocean_types.py,sha256=onwYMsvdd2_9QmZ7qU6h-t2uF_PTIivpEro0ahevhdw,1354
119
+ port_ocean/core/ocean_types.py,sha256=4VipWFOHEh_d9LmWewQccwx1p2dtrRYW0YURVgNsAjo,1398
120
120
  port_ocean/core/utils/entity_topological_sorter.py,sha256=MDUjM6OuDy4Xj68o-7InNN0w1jqjxeDfeY8U02vySNI,3081
121
121
  port_ocean/core/utils/utils.py,sha256=HmumOeH27N0NX1_OP3t4oGKt074ht9XyXhvfZ5I05s4,6474
122
122
  port_ocean/debug_cli.py,sha256=gHrv-Ey3cImKOcGZpjoHlo4pa_zfmyOl6TUM4o9VtcA,96
@@ -131,13 +131,15 @@ port_ocean/exceptions/utils.py,sha256=gjOqpi-HpY1l4WlMFsGA9yzhxDhajhoGGdDDyGbLnq
131
131
  port_ocean/exceptions/webhook_processor.py,sha256=yQYazg53Y-ohb7HfViwq1opH_ZUuUdhHSRxcUNveFpI,114
132
132
  port_ocean/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
133
  port_ocean/helpers/async_client.py,sha256=SRlP6o7_FCSY3UHnRlZdezppePVxxOzZ0z861vE3K40,1783
134
- port_ocean/helpers/retry.py,sha256=1SxeRPkaH3K1BDvcdZbze2ila7SyOMYD8DQ3CwrruYk,16135
134
+ port_ocean/helpers/metric/metric.py,sha256=EQstAxlsC-lsp8VFw4GgxCh3SGV9t2Y-j1Z1qbXyy-I,8014
135
+ port_ocean/helpers/metric/utils.py,sha256=1DlLR9FmUOpSlWSfY_tUEGcxRQY6ckAfbD3dnMnxd4c,874
136
+ port_ocean/helpers/retry.py,sha256=gmS4YxM6N4fboFp7GSgtOzyBJemxs46bnrz4L4rDS6Y,16136
135
137
  port_ocean/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
136
138
  port_ocean/log/handlers.py,sha256=ncVjgqrZRh6BhyRrA6DQG86Wsbxph1yWYuEC0cWfe-Q,3631
137
139
  port_ocean/log/logger_setup.py,sha256=CoEDowe5OwNOL_5clU6Z4faktfh0VWaOTS0VLmyhHjw,2404
138
140
  port_ocean/log/sensetive.py,sha256=lVKiZH6b7TkrZAMmhEJRhcl67HNM94e56x12DwFgCQk,2920
139
141
  port_ocean/middlewares.py,sha256=9wYCdyzRZGK1vjEJ28FY_DkfwDNENmXp504UKPf5NaQ,2727
140
- port_ocean/ocean.py,sha256=IL6jIgmfOmvUpGuM9zgv0Cvvuh4Ek-zn4gb9kOy18B8,6900
142
+ port_ocean/ocean.py,sha256=XmPmnjo-2oMZGvCLwIxPxwVNzXXzACzVR4JmlkXEOKY,7418
141
143
  port_ocean/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
144
  port_ocean/run.py,sha256=COoRSmLG4hbsjIW5DzhV0NYVegI9xHd1POv6sg4U1No,2217
143
145
  port_ocean/sonar-project.properties,sha256=X_wLzDOkEVmpGLRMb2fg9Rb0DxWwUFSvESId8qpvrPI,73
@@ -145,14 +147,15 @@ port_ocean/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
145
147
  port_ocean/tests/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
146
148
  port_ocean/tests/clients/oauth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
149
  port_ocean/tests/clients/oauth/test_oauth_client.py,sha256=2XVMQUalDpiD539Z7_dk5BK_ngXQzsTmb2lNBsfEm9c,3266
148
- port_ocean/tests/clients/port/mixins/test_entities.py,sha256=A9myrnkLhKSQrnOLv1Zz2wiOVSxW65Q9RIUIRbn_V7w,1586
150
+ port_ocean/tests/clients/port/mixins/test_entities.py,sha256=vm2COG5pFC4mbJis9fjWE1zvGuNKD2kskPbcDEMxF2w,1751
149
151
  port_ocean/tests/clients/port/mixins/test_organization_mixin.py,sha256=-8iHM33Oe8PuyEfj3O_6Yob8POp4fSmB0hnIT0Gv-8Y,868
150
152
  port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,101
153
+ port_ocean/tests/core/conftest.py,sha256=2MNOmQ3C8rNAOlkryiV604_S7FmcpVNApHrpY378OPU,5448
151
154
  port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcGsYZ2SCe_PYX5vLYM,5560
152
- port_ocean/tests/core/handlers/entities_state_applier/test_applier.py,sha256=R9bqyJocUWTh0NW0s-5ttD_SYYeM5EbYILgVmgWa7qA,2776
155
+ port_ocean/tests/core/handlers/entities_state_applier/test_applier.py,sha256=nx2apKGDzKbckJQV-if8XzNSaldZwbzDBlZBNdVtMGg,5705
153
156
  port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=FnEnaDjuoAbKvKyv6xJ46n3j0ZcaT70Sg2zc7oy7HAA,13596
154
157
  port_ocean/tests/core/handlers/mixins/test_live_events.py,sha256=iAwVpr3n3PIkXQLw7hxd-iB_SR_vyfletVXJLOmyz28,12480
155
- port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=S1m6SQtlQVr3qD_otHEojj4pR87vICyO9ELNyAEwZRs,37428
158
+ port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=91H2GfHqxIDFd4zbmv8WWp8rR6SVy1Sm0IvhqTvm7p0,32783
156
159
  port_ocean/tests/core/handlers/port_app_config/test_api.py,sha256=eJZ6SuFBLz71y4ca3DNqKag6d6HUjNJS0aqQPwiLMTI,1999
157
160
  port_ocean/tests/core/handlers/port_app_config/test_base.py,sha256=tdjpFUnUZ6TNMxc3trKkzmMTGTb7oKIeu3rRXv_fV3g,6872
158
161
  port_ocean/tests/core/handlers/queue/test_local_queue.py,sha256=9Ly0HzZXbs6Rbl_bstsIdInC3h2bgABU3roP9S_PnJM,2582
@@ -170,12 +173,13 @@ port_ocean/tests/helpers/ocean_app.py,sha256=N06vcNI1klqdcNFq-PXL5vm77u-hODsOSXn
170
173
  port_ocean/tests/helpers/port_client.py,sha256=5d6GNr8vNNSOkrz1AdOhxBUKuusr_-UPDP7AVpHasQw,599
171
174
  port_ocean/tests/helpers/smoke_test.py,sha256=_9aJJFRfuGJEg2D2YQJVJRmpreS6gEPHHQq8Q01x4aQ,2697
172
175
  port_ocean/tests/log/test_handlers.py,sha256=uxgYCEQLP9U5qf-zUN9SgWFogMbYdnBeOVzXZ7E_yFw,2119
176
+ port_ocean/tests/test_metric.py,sha256=gDdeJcqJDQ_o3VrYrW23iZyw2NuUsyATdrygSXhcDuQ,8096
173
177
  port_ocean/tests/test_ocean.py,sha256=bsXKGTVEjwLSbR7-qSmI4GZ-EzDo0eBE3TNSMsWzYxM,1502
174
178
  port_ocean/tests/test_smoke.py,sha256=uix2uIg_yOm8BHDgHw2hTFPy1fiIyxBGW3ENU_KoFlo,2557
175
179
  port_ocean/tests/utils/test_async_iterators.py,sha256=3PLk1emEXekb8LcC5GgVh3OicaX15i5WyaJT_eFnu_4,1336
176
180
  port_ocean/tests/utils/test_cache.py,sha256=GzoS8xGCBDbBcPwSDbdimsMMkRvJATrBC7UmFhdW3fw,4906
177
181
  port_ocean/utils/__init__.py,sha256=KMGnCPXZJbNwtgxtyMycapkDz8tpSyw23MSYT3iVeHs,91
178
- port_ocean/utils/async_http.py,sha256=pdXUs5VxvWcX0i6wJXb_LjN0OrunEkCGdoM_TNXcSO0,1225
182
+ port_ocean/utils/async_http.py,sha256=aDsw3gQIMwt6qLegbZtkHqD8em48tKvbITnblsrTY3g,1260
179
183
  port_ocean/utils/async_iterators.py,sha256=CPXskYWkhkZtAG-ducEwM8537t3z5usPEqXR9vcivzw,3715
180
184
  port_ocean/utils/cache.py,sha256=RgfN4SjjHrEkbqUChyboeD1mrXomolUUjsJtvbkmr3U,3353
181
185
  port_ocean/utils/misc.py,sha256=0q2cJ5psqxn_5u_56pT7vOVQ3shDM02iC1lzyWQ_zl0,2098
@@ -184,8 +188,8 @@ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,32
184
188
  port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
185
189
  port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
186
190
  port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
187
- port_ocean-0.21.5.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
188
- port_ocean-0.21.5.dist-info/METADATA,sha256=fKeRch6wpkatHhgqYcaoEvUvI9V6JV8AnsppmVLOsuI,6669
189
- port_ocean-0.21.5.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
190
- port_ocean-0.21.5.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
191
- port_ocean-0.21.5.dist-info/RECORD,,
191
+ port_ocean-0.22.0.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
192
+ port_ocean-0.22.0.dist-info/METADATA,sha256=Dm3UD58bqEM3h5bX-Oeb7sjzKViKWfrN9sH-jl8AxUE,6721
193
+ port_ocean-0.22.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
194
+ port_ocean-0.22.0.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
195
+ port_ocean-0.22.0.dist-info/RECORD,,