port-ocean 0.18.9__py3-none-any.whl → 0.19.2__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.
File without changes
@@ -0,0 +1,10 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ import httpx
4
+
5
+
6
+ class AuthClient(ABC):
7
+
8
+ @abstractmethod
9
+ def refresh_request_auth_creds(self, request: httpx.Request) -> httpx.Request:
10
+ pass
@@ -0,0 +1,22 @@
1
+ from port_ocean.clients.auth.auth_client import AuthClient
2
+ from port_ocean.context.ocean import ocean
3
+ from port_ocean.helpers.retry import register_on_retry_callback
4
+
5
+
6
+ class OAuthClient(AuthClient):
7
+ def __init__(self) -> None:
8
+ """
9
+ A client that can refresh a request using an access token.
10
+ """
11
+ if self.is_oauth_enabled():
12
+ register_on_retry_callback(self.refresh_request_auth_creds)
13
+
14
+ def is_oauth_enabled(self) -> bool:
15
+ return ocean.app.load_external_oauth_access_token() is not None
16
+
17
+ @property
18
+ def external_access_token(self) -> str:
19
+ access_token = ocean.app.load_external_oauth_access_token()
20
+ if access_token is None:
21
+ raise ValueError("No external access token found")
22
+ return access_token
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- from typing import Any, Dict, TYPE_CHECKING, Optional, TypedDict
2
+ from typing import Any, Dict, List, TYPE_CHECKING, Optional, TypedDict
3
3
  from urllib.parse import quote_plus
4
4
 
5
5
  import httpx
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
14
14
 
15
15
 
16
16
  INTEGRATION_POLLING_INTERVAL_INITIAL_SECONDS = 3
17
- INTEGRATION_POLLING_INTERVAL_BACKOFF_FACTOR = 1.15
17
+ INTEGRATION_POLLING_INTERVAL_BACKOFF_FACTOR = 1.55
18
18
  INTEGRATION_POLLING_RETRY_LIMIT = 30
19
19
  CREATE_RESOURCES_PARAM_NAME = "integration_modes"
20
20
  CREATE_RESOURCES_PARAM_VALUE = ["create_resources"]
@@ -38,6 +38,27 @@ class IntegrationClientMixin:
38
38
  self.client = client
39
39
  self._log_attributes: LogAttributes | None = None
40
40
 
41
+ async def is_integration_provision_enabled(
42
+ self, integration_type: str, should_raise: bool = True, should_log: bool = True
43
+ ) -> bool:
44
+ enabled_integrations = await self.get_provision_enabled_integrations(
45
+ should_raise, should_log
46
+ )
47
+ return integration_type in enabled_integrations
48
+
49
+ async def get_provision_enabled_integrations(
50
+ self, should_raise: bool = True, should_log: bool = True
51
+ ) -> List[str]:
52
+ logger.info("Fetching provision enabled integrations")
53
+ response = await self.client.get(
54
+ f"{self.auth.api_url}/integration/provision-enabled",
55
+ headers=await self.auth.headers(),
56
+ )
57
+
58
+ handle_status_code(response, should_raise, should_log)
59
+
60
+ return response.json().get("integrations", [])
61
+
41
62
  async def _get_current_integration(self) -> httpx.Response:
42
63
  logger.info(f"Fetching integration with id: {self.integration_identifier}")
43
64
  response = await self.client.get(
@@ -51,7 +72,27 @@ class IntegrationClientMixin:
51
72
  ) -> dict[str, Any]:
52
73
  response = await self._get_current_integration()
53
74
  handle_status_code(response, should_raise, should_log)
54
- return response.json().get("integration", {})
75
+ integration = response.json().get("integration", {})
76
+ if integration.get("config", None) or not integration:
77
+ return integration
78
+ is_provision_enabled_for_integration = integration.get(
79
+ "installationAppType", None
80
+ ) and (
81
+ await self.is_integration_provision_enabled(
82
+ integration.get("installationAppType", ""),
83
+ should_raise,
84
+ should_log,
85
+ )
86
+ )
87
+
88
+ if is_provision_enabled_for_integration:
89
+ logger.info(
90
+ "integration type is enabled, polling until provisioning is complete"
91
+ )
92
+ integration = (
93
+ await self._poll_integration_until_default_provisioning_is_complete()
94
+ )
95
+ return integration
55
96
 
56
97
  async def get_log_attributes(self) -> LogAttributes:
57
98
  if self._log_attributes is None:
@@ -72,7 +113,7 @@ class IntegrationClientMixin:
72
113
  )
73
114
  response = await self._get_current_integration()
74
115
  integration_json = response.json()
75
- if integration_json.get("integration", {}).get("config", {}):
116
+ if integration_json.get("integration", {}).get("config", {}) != {}:
76
117
  return integration_json
77
118
 
78
119
  logger.info(
@@ -1,12 +1,12 @@
1
1
  from typing import Any, Literal, Type, cast
2
2
 
3
- from pydantic import Extra, AnyHttpUrl, parse_obj_as, parse_raw_as
3
+ from pydantic import AnyHttpUrl, Extra, parse_obj_as, parse_raw_as
4
4
  from pydantic.class_validators import root_validator, validator
5
- from pydantic.env_settings import InitSettingsSource, EnvSettingsSource, BaseSettings
5
+ from pydantic.env_settings import BaseSettings, EnvSettingsSource, InitSettingsSource
6
6
  from pydantic.fields import Field
7
7
  from pydantic.main import BaseModel
8
8
 
9
- from port_ocean.config.base import BaseOceanSettings, BaseOceanModel
9
+ from port_ocean.config.base import BaseOceanModel, BaseOceanSettings
10
10
  from port_ocean.core.event_listener import EventListenerSettingsType
11
11
  from port_ocean.core.models import CreatePortResourcesOrigin, Runtime
12
12
  from port_ocean.utils.misc import get_integration_name, get_spec_file
@@ -71,6 +71,7 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
71
71
  # Determines if Port should generate resources such as blueprints and pages instead of ocean
72
72
  create_port_resources_origin: CreatePortResourcesOrigin | None = None
73
73
  send_raw_data_examples: bool = True
74
+ oauth_access_token_file_path: str | None = None
74
75
  port: PortSettings
75
76
  event_listener: EventListenerSettingsType = Field(
76
77
  default=cast(EventListenerSettingsType, {"type": "POLLING"})
@@ -75,7 +75,7 @@ async def _initialize_required_integration_settings(
75
75
  create_port_resources_origin_in_port=integration_config.create_port_resources_origin
76
76
  == CreatePortResourcesOrigin.Port,
77
77
  )
78
- elif not integration.get("config"):
78
+ elif not integration.get("config", None):
79
79
  logger.info(
80
80
  "Encountered that the integration's mapping is empty, Initializing to default mapping"
81
81
  )
@@ -213,11 +213,20 @@ async def _initialize_defaults(
213
213
  config_class, integration_config.resources_path
214
214
  )
215
215
 
216
+ is_integration_provision_enabled = (
217
+ await port_client.is_integration_provision_enabled(
218
+ integration_config.integration.type
219
+ )
220
+ )
221
+
216
222
  if (
217
223
  not integration_config.create_port_resources_origin
218
- and integration_config.runtime.is_saas_runtime
224
+ and is_integration_provision_enabled
219
225
  ):
220
- logger.info("Setting resources origin to be Port")
226
+ # Need to set default since spec is missing
227
+ logger.info(
228
+ f"Setting resources origin to be Port (integration {integration_config.integration.type} is supported)"
229
+ )
221
230
  integration_config.create_port_resources_origin = CreatePortResourcesOrigin.Port
222
231
 
223
232
  if (
@@ -228,7 +237,10 @@ async def _initialize_defaults(
228
237
  "Resources origin is set to be Port, verifying integration is supported"
229
238
  )
230
239
  org_feature_flags = await port_client.get_organization_feature_flags()
231
- if ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG not in org_feature_flags:
240
+ if (
241
+ not is_integration_provision_enabled
242
+ or ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG not in org_feature_flags
243
+ ):
232
244
  logger.info(
233
245
  "Port origin for Integration is not supported, changing resources origin to use Ocean"
234
246
  )
@@ -9,6 +9,15 @@ from typing import Any, Callable, Coroutine, Iterable, Mapping, Union
9
9
  import httpx
10
10
  from dateutil.parser import isoparse
11
11
 
12
+ _ON_RETRY_CALLBACK: Callable[[httpx.Request], httpx.Request] | None = None
13
+
14
+
15
+ def register_on_retry_callback(
16
+ _on_retry_callback: Callable[[httpx.Request], httpx.Request]
17
+ ) -> None:
18
+ global _ON_RETRY_CALLBACK
19
+ _ON_RETRY_CALLBACK = _on_retry_callback
20
+
12
21
 
13
22
  # Adapted from https://github.com/encode/httpx/issues/108#issuecomment-1434439481
14
23
  class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
@@ -43,7 +52,6 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
43
52
  _retry_status_codes (frozenset): The HTTP status codes that can be retried.
44
53
  _jitter_ratio (float): The amount of jitter to add to the backoff time.
45
54
  _max_backoff_wait (float): The maximum time to wait between retries in seconds.
46
-
47
55
  """
48
56
 
49
57
  RETRYABLE_METHODS = frozenset(["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"])
@@ -53,6 +61,8 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
53
61
  HTTPStatus.BAD_GATEWAY,
54
62
  HTTPStatus.SERVICE_UNAVAILABLE,
55
63
  HTTPStatus.GATEWAY_TIMEOUT,
64
+ HTTPStatus.UNAUTHORIZED,
65
+ HTTPStatus.BAD_REQUEST,
56
66
  ]
57
67
  )
58
68
  MAX_BACKOFF_WAIT_IN_SECONDS = 60
@@ -316,6 +326,8 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
316
326
  if remaining_attempts < 1:
317
327
  self._log_error(request, error)
318
328
  raise
329
+ if _ON_RETRY_CALLBACK:
330
+ request = _ON_RETRY_CALLBACK(request)
319
331
  attempts_made += 1
320
332
  remaining_attempts -= 1
321
333
 
@@ -357,5 +369,7 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
357
369
  if remaining_attempts < 1:
358
370
  self._log_error(request, error)
359
371
  raise
372
+ if _ON_RETRY_CALLBACK:
373
+ request = _ON_RETRY_CALLBACK(request)
360
374
  attempts_made += 1
361
375
  remaining_attempts -= 1
port_ocean/ocean.py CHANGED
@@ -1,31 +1,31 @@
1
1
  import asyncio
2
2
  import sys
3
- import threading
4
3
  from contextlib import asynccontextmanager
5
- from typing import Callable, Any, Dict, AsyncIterator, Type
4
+ import threading
5
+ from typing import Any, AsyncIterator, Callable, Dict, Type
6
6
 
7
- from fastapi import FastAPI, APIRouter
7
+ from fastapi import APIRouter, FastAPI
8
8
  from loguru import logger
9
9
  from pydantic import BaseModel
10
- from starlette.types import Scope, Receive, Send
10
+ from starlette.types import Receive, Scope, Send
11
11
 
12
- from port_ocean.core.handlers.resync_state_updater import ResyncStateUpdater
13
12
  from port_ocean.clients.port.client import PortClient
14
13
  from port_ocean.config.settings import (
15
14
  IntegrationConfiguration,
16
15
  )
17
16
  from port_ocean.context.ocean import (
18
17
  PortOceanContext,
19
- ocean,
20
18
  initialize_port_ocean_context,
19
+ ocean,
21
20
  )
21
+ from port_ocean.core.handlers.resync_state_updater import ResyncStateUpdater
22
22
  from port_ocean.core.integrations.base import BaseIntegration
23
23
  from port_ocean.log.sensetive import sensitive_log_filter
24
24
  from port_ocean.middlewares import request_handler
25
+ from port_ocean.utils.misc import IntegrationStateStatus
25
26
  from port_ocean.utils.repeat import repeat_every
26
27
  from port_ocean.utils.signal import signal_handler
27
28
  from port_ocean.version import __integration_version__
28
- from port_ocean.utils.misc import IntegrationStateStatus
29
29
  from port_ocean.core.handlers.webhook.processor_manager import WebhookProcessorManager
30
30
 
31
31
 
@@ -118,6 +118,18 @@ class Ocean:
118
118
  )
119
119
  await repeated_function()
120
120
 
121
+ def load_external_oauth_access_token(self) -> str | None:
122
+ if self.config.oauth_access_token_file_path is not None:
123
+ try:
124
+ with open(self.config.oauth_access_token_file_path, "r") as f:
125
+ return f.read()
126
+ except Exception:
127
+ logger.exception(
128
+ "Failed to load external oauth access token from file",
129
+ file_path=self.config.oauth_access_token_file_path,
130
+ )
131
+ return None
132
+
121
133
  def initialize_app(self) -> None:
122
134
  self.fast_api_app.include_router(self.integration_router, prefix="/integration")
123
135
 
File without changes
File without changes
@@ -0,0 +1,96 @@
1
+ import pytest
2
+ import httpx
3
+ from unittest.mock import MagicMock, patch
4
+ from port_ocean.ocean import Ocean
5
+
6
+ from port_ocean.clients.auth.oauth_client import OAuthClient
7
+ from port_ocean.config.settings import IntegrationConfiguration
8
+
9
+
10
+ @pytest.fixture
11
+ def mock_ocean() -> Ocean:
12
+ with patch("port_ocean.ocean.Ocean.__init__", return_value=None):
13
+ ocean_mock = Ocean()
14
+ ocean_mock.config = MagicMock(spec=IntegrationConfiguration)
15
+ return ocean_mock
16
+
17
+
18
+ class MockOAuthClient(OAuthClient):
19
+ def __init__(self, is_oauth_enabled_value: bool = True):
20
+ self._is_oauth_enabled = is_oauth_enabled_value
21
+ self._access_token = "mock_access_token"
22
+ super().__init__()
23
+
24
+ def is_oauth_enabled(self) -> bool:
25
+ return self._is_oauth_enabled
26
+
27
+ def refresh_request_auth_creds(self, request: httpx.Request) -> httpx.Request:
28
+ headers = dict(request.headers)
29
+ headers["Authorization"] = f"Bearer {self.access_token}"
30
+ return httpx.Request(
31
+ method=request.method,
32
+ url=request.url,
33
+ headers=headers,
34
+ content=request.content,
35
+ )
36
+
37
+ @property
38
+ def access_token(self) -> str:
39
+ return self._access_token
40
+
41
+
42
+ @pytest.fixture
43
+ def mock_oauth_client() -> MockOAuthClient:
44
+ return MockOAuthClient()
45
+
46
+
47
+ @pytest.fixture
48
+ def disabled_oauth_client() -> MockOAuthClient:
49
+ return MockOAuthClient(is_oauth_enabled_value=False)
50
+
51
+
52
+ def test_oauth_client_initialization(mock_oauth_client: MockOAuthClient) -> None:
53
+ assert isinstance(mock_oauth_client, OAuthClient)
54
+ assert mock_oauth_client.is_oauth_enabled() is True
55
+
56
+
57
+ def test_oauth_client_disabled_initialization(
58
+ disabled_oauth_client: MockOAuthClient,
59
+ ) -> None:
60
+ assert isinstance(disabled_oauth_client, OAuthClient)
61
+ assert disabled_oauth_client.is_oauth_enabled() is False
62
+
63
+
64
+ def test_refresh_request_auth_creds(mock_oauth_client: MockOAuthClient) -> None:
65
+ # Create request with some content and existing headers
66
+ original_headers = {"Accept": "application/json", "X-Custom": "value"}
67
+ original_content = b'{"key": "value"}'
68
+ original_request = httpx.Request(
69
+ "GET",
70
+ "https://api.example.com",
71
+ headers=original_headers,
72
+ content=original_content,
73
+ )
74
+
75
+ refreshed_request = mock_oauth_client.refresh_request_auth_creds(original_request)
76
+
77
+ # Verify all attributes are identical except headers
78
+ assert refreshed_request.method == original_request.method
79
+ assert refreshed_request.url == original_request.url
80
+ assert refreshed_request.content == original_request.content
81
+
82
+ # Verify headers: should contain all original headers plus the new Authorization
83
+ for key, value in original_headers.items():
84
+ assert refreshed_request.headers[key] == value
85
+ assert refreshed_request.headers["Authorization"] == "Bearer mock_access_token"
86
+ # New headers should be:
87
+ # {'host': 'api.example.com',
88
+ # 'accept': 'application/json',
89
+ # 'x-custom': 'value',
90
+ # 'content-length': '16',
91
+ # 'authorization': '[secure]'}
92
+ assert len(refreshed_request.headers) == 5
93
+
94
+
95
+ def test_access_token_property(mock_oauth_client: MockOAuthClient) -> None:
96
+ assert mock_oauth_client.access_token == "mock_access_token"
@@ -0,0 +1,49 @@
1
+ import pytest
2
+ from unittest.mock import MagicMock, mock_open, patch
3
+ from port_ocean.ocean import Ocean
4
+ from port_ocean.config.settings import IntegrationConfiguration
5
+
6
+
7
+ @pytest.fixture
8
+ def mock_ocean() -> Ocean:
9
+ with patch("port_ocean.ocean.Ocean.__init__", return_value=None):
10
+ ocean_mock = Ocean()
11
+ ocean_mock.config = MagicMock(spec=IntegrationConfiguration)
12
+ return ocean_mock
13
+
14
+
15
+ def test_load_external_oauth_access_token_no_file(mock_ocean: Ocean) -> None:
16
+ # Setup
17
+ mock_ocean.config.oauth_access_token_file_path = None
18
+
19
+ # Execute
20
+ result = mock_ocean.load_external_oauth_access_token()
21
+
22
+ # Assert
23
+ assert result is None
24
+
25
+
26
+ def test_load_external_oauth_access_token_with_file(mock_ocean: Ocean) -> None:
27
+ # Setup
28
+ mock_ocean.config.oauth_access_token_file_path = "/path/to/token.txt"
29
+ mock_file_content = "test_access_token"
30
+
31
+ with patch("builtins.open", mock_open(read_data=mock_file_content)):
32
+ # Execute
33
+ result = mock_ocean.load_external_oauth_access_token()
34
+
35
+ # Assert
36
+ assert result == "test_access_token"
37
+
38
+
39
+ def test_load_external_oauth_access_token_with_empty_file(mock_ocean: Ocean) -> None:
40
+ # Setup
41
+ mock_ocean.config.oauth_access_token_file_path = "/path/to/token.txt"
42
+ mock_file_content = ""
43
+
44
+ with patch("builtins.open", mock_open(read_data=mock_file_content)):
45
+ # Execute
46
+ result = mock_ocean.load_external_oauth_access_token()
47
+
48
+ # Assert
49
+ assert result == ""
@@ -18,7 +18,7 @@ def _get_http_client_context() -> httpx.AsyncClient:
18
18
 
19
19
 
20
20
  """
21
- Utilize this client for all outbound integration requests to the third-party application. It functions as a wrapper
21
+ Utilize this client for all outbound integration requests to the third-party application. It functions as a wrapper
22
22
  around the httpx.AsyncClient, incorporating retry logic at the transport layer for handling retries on 5xx errors and
23
23
  connection errors.
24
24
 
@@ -22,10 +22,8 @@ def repeat_every(
22
22
  ) -> NoArgsNoReturnDecorator:
23
23
  """
24
24
  This function returns a decorator that modifies a function so it is periodically re-executed after its first call.
25
-
26
25
  The function it decorates should accept no arguments and return nothing. If necessary, this can be accomplished
27
26
  by using `functools.partial` or otherwise wrapping the target function prior to decoration.
28
-
29
27
  Parameters
30
28
  ----------
31
29
  seconds: float
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.18.9
3
+ Version: 0.19.2
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
@@ -44,13 +44,16 @@ port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py,
44
44
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py,sha256=Ew5LA_G1k6DC5a2ygU2FoyjZQa0fRmPy73N0bio0d14,46
45
45
  port_ocean/cli/utils.py,sha256=IUK2UbWqjci-lrcDdynZXqVP5B5TcjF0w5CpEVUks-k,54
46
46
  port_ocean/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ port_ocean/clients/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
+ port_ocean/clients/auth/auth_client.py,sha256=scxx7AYqvXoRAd8_K-Ww26oErzi5l8ZCGPc0sVKgIfA,192
49
+ port_ocean/clients/auth/oauth_client.py,sha256=RGMigXP8XOQECvEccXjF-EYPBXaxFDpA2aniN_vM3d0,794
47
50
  port_ocean/clients/port/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
51
  port_ocean/clients/port/authentication.py,sha256=6-uDMWsJ0xLe1-9IoYXHWmwtufj8rJR4BCRXJlSkCSQ,3447
49
52
  port_ocean/clients/port/client.py,sha256=OaNeN3U7Hw0tK4jYE6ESJEPKbTf9nGp2jcJVq00gnf8,3546
50
53
  port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
54
  port_ocean/clients/port/mixins/blueprints.py,sha256=POBl4uDocrgJBw4rvCAzwRcD4jk-uBL6pDAuKMTajdg,4633
52
55
  port_ocean/clients/port/mixins/entities.py,sha256=PJzVZTBW_OheFRGPRCZ6yPbVGEAKsMO9CNDNJUI1l48,10770
53
- port_ocean/clients/port/mixins/integrations.py,sha256=R7ZTdU7UKh1IHOqbOJzK76c_JwAyEosRvIWWs4PO_8k,7543
56
+ port_ocean/clients/port/mixins/integrations.py,sha256=0ht8nfjsRBu_dbnlMZxSH0jqKv0PF8U8EkllRXgnrWA,9122
54
57
  port_ocean/clients/port/mixins/migrations.py,sha256=A6896oJF6WbFL2WroyTkMzr12yhVyWqGoq9dtLNSKBY,1457
55
58
  port_ocean/clients/port/mixins/organization.py,sha256=fCo_ZS8UlQXsyIx-odTuWkbnfcYmVnQfIsSyJuPOPjM,1031
56
59
  port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
@@ -59,7 +62,7 @@ port_ocean/clients/port/utils.py,sha256=SjhgmJXAqH2JqXfGy8GoGwzUYiJvUhWDrJyxQcen
59
62
  port_ocean/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
63
  port_ocean/config/base.py,sha256=x1gFbzujrxn7EJudRT81C6eN9WsYAb3vOHwcpcpX8Tc,6370
61
64
  port_ocean/config/dynamic.py,sha256=qOFkRoJsn_BW7581omi_AoMxoHqasf_foxDQ_G11_SI,2030
62
- port_ocean/config/settings.py,sha256=q5KgDIr8snIxejoKyWSyf21R_AYEEXiEd3-ry9qf3ss,5227
65
+ port_ocean/config/settings.py,sha256=C1hQJsvHz-411-CtT0DvMGkImcO-E_zgmbzpI3TUO64,5279
63
66
  port_ocean/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
67
  port_ocean/consumers/kafka_consumer.py,sha256=N8KocjBi9aR0BOPG8hgKovg-ns_ggpEjrSxqSqF_BSo,4710
65
68
  port_ocean/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -70,7 +73,7 @@ port_ocean/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
73
  port_ocean/core/defaults/__init__.py,sha256=8qCZg8n06WAdMu9s_FiRtDYLGPGHbOuS60vapeUoAks,142
71
74
  port_ocean/core/defaults/clean.py,sha256=_rL-NCl6Q_x3lUxDW5ACOM27IYilTCWl6ISUfRleuL0,2891
72
75
  port_ocean/core/defaults/common.py,sha256=zJsj7jvlqIMLGXhdASUlbKS8GIAf-FDKKB0O7jB6nx0,4166
73
- port_ocean/core/defaults/initialize.py,sha256=IYgc6XS5ARdcPM4IQrCSi4cdAomHxJVnP_OWOU9f05U,10382
76
+ port_ocean/core/defaults/initialize.py,sha256=3Ezn63YwxVnByegRPqNXt057Tx9D0IxqrZQHgkrbKWA,10760
74
77
  port_ocean/core/event_listener/__init__.py,sha256=T3E52MKs79fNEW381p7zU9F2vOMvIiiTYWlqRUqnsg0,1135
75
78
  port_ocean/core/event_listener/base.py,sha256=VdIdp7RLOSxH3ICyV-wCD3NiJoUzsh2KkJ0a9B29GeI,2847
76
79
  port_ocean/core/event_listener/factory.py,sha256=M4Qi05pI840sjDIbdjUEgYe9Gp5ckoCkX-KgLBxUpZg,4096
@@ -127,17 +130,20 @@ port_ocean/exceptions/utils.py,sha256=gjOqpi-HpY1l4WlMFsGA9yzhxDhajhoGGdDDyGbLnq
127
130
  port_ocean/exceptions/webhook_processor.py,sha256=yQYazg53Y-ohb7HfViwq1opH_ZUuUdhHSRxcUNveFpI,114
128
131
  port_ocean/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
132
  port_ocean/helpers/async_client.py,sha256=SRlP6o7_FCSY3UHnRlZdezppePVxxOzZ0z861vE3K40,1783
130
- port_ocean/helpers/retry.py,sha256=fmvaUFLIW6PICgYH4RI5rrKBXDxCAhR1Gtl0dsb2kMs,15625
133
+ port_ocean/helpers/retry.py,sha256=1SxeRPkaH3K1BDvcdZbze2ila7SyOMYD8DQ3CwrruYk,16135
131
134
  port_ocean/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
132
135
  port_ocean/log/handlers.py,sha256=ncVjgqrZRh6BhyRrA6DQG86Wsbxph1yWYuEC0cWfe-Q,3631
133
136
  port_ocean/log/logger_setup.py,sha256=CoEDowe5OwNOL_5clU6Z4faktfh0VWaOTS0VLmyhHjw,2404
134
137
  port_ocean/log/sensetive.py,sha256=lVKiZH6b7TkrZAMmhEJRhcl67HNM94e56x12DwFgCQk,2920
135
138
  port_ocean/middlewares.py,sha256=9wYCdyzRZGK1vjEJ28FY_DkfwDNENmXp504UKPf5NaQ,2727
136
- port_ocean/ocean.py,sha256=MmLGxx0EC3Q6FEpbnZc9_5A7DculsvN2nAz3aZkj2kI,5536
139
+ port_ocean/ocean.py,sha256=jFNVykBy01b4A7er16bzQoiMYZqfRgcs8TR0Snd3FKY,6046
137
140
  port_ocean/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
138
141
  port_ocean/run.py,sha256=COoRSmLG4hbsjIW5DzhV0NYVegI9xHd1POv6sg4U1No,2217
139
142
  port_ocean/sonar-project.properties,sha256=X_wLzDOkEVmpGLRMb2fg9Rb0DxWwUFSvESId8qpvrPI,73
140
143
  port_ocean/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
+ port_ocean/tests/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
+ port_ocean/tests/clients/oauth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
146
+ port_ocean/tests/clients/oauth/test_oauth_client.py,sha256=2XVMQUalDpiD539Z7_dk5BK_ngXQzsTmb2lNBsfEm9c,3266
141
147
  port_ocean/tests/clients/port/mixins/test_entities.py,sha256=A9myrnkLhKSQrnOLv1Zz2wiOVSxW65Q9RIUIRbn_V7w,1586
142
148
  port_ocean/tests/clients/port/mixins/test_organization_mixin.py,sha256=-8iHM33Oe8PuyEfj3O_6Yob8POp4fSmB0hnIT0Gv-8Y,868
143
149
  port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,101
@@ -162,21 +168,22 @@ port_ocean/tests/helpers/ocean_app.py,sha256=N06vcNI1klqdcNFq-PXL5vm77u-hODsOSXn
162
168
  port_ocean/tests/helpers/port_client.py,sha256=5d6GNr8vNNSOkrz1AdOhxBUKuusr_-UPDP7AVpHasQw,599
163
169
  port_ocean/tests/helpers/smoke_test.py,sha256=_9aJJFRfuGJEg2D2YQJVJRmpreS6gEPHHQq8Q01x4aQ,2697
164
170
  port_ocean/tests/log/test_handlers.py,sha256=uxgYCEQLP9U5qf-zUN9SgWFogMbYdnBeOVzXZ7E_yFw,2119
171
+ port_ocean/tests/test_ocean.py,sha256=bsXKGTVEjwLSbR7-qSmI4GZ-EzDo0eBE3TNSMsWzYxM,1502
165
172
  port_ocean/tests/test_smoke.py,sha256=uix2uIg_yOm8BHDgHw2hTFPy1fiIyxBGW3ENU_KoFlo,2557
166
173
  port_ocean/tests/utils/test_async_iterators.py,sha256=3PLk1emEXekb8LcC5GgVh3OicaX15i5WyaJT_eFnu_4,1336
167
174
  port_ocean/tests/utils/test_cache.py,sha256=GzoS8xGCBDbBcPwSDbdimsMMkRvJATrBC7UmFhdW3fw,4906
168
175
  port_ocean/utils/__init__.py,sha256=KMGnCPXZJbNwtgxtyMycapkDz8tpSyw23MSYT3iVeHs,91
169
- port_ocean/utils/async_http.py,sha256=arnH458TExn2Dju_Sy6pHas_vF5RMWnOp-jBz5WAAcE,1226
176
+ port_ocean/utils/async_http.py,sha256=pdXUs5VxvWcX0i6wJXb_LjN0OrunEkCGdoM_TNXcSO0,1225
170
177
  port_ocean/utils/async_iterators.py,sha256=CPXskYWkhkZtAG-ducEwM8537t3z5usPEqXR9vcivzw,3715
171
178
  port_ocean/utils/cache.py,sha256=RgfN4SjjHrEkbqUChyboeD1mrXomolUUjsJtvbkmr3U,3353
172
179
  port_ocean/utils/misc.py,sha256=0q2cJ5psqxn_5u_56pT7vOVQ3shDM02iC1lzyWQ_zl0,2098
173
180
  port_ocean/utils/queue_utils.py,sha256=KWWl8YVnG-glcfIHhM6nefY-2sou_C6DVP1VynQwzB4,2762
174
- port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,3231
181
+ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,3229
175
182
  port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
176
183
  port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
177
184
  port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
178
- port_ocean-0.18.9.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
179
- port_ocean-0.18.9.dist-info/METADATA,sha256=wNsXnfPpKx_dLVdrt9CSVl8oLrrHNM4-T0y6j27iHM4,6669
180
- port_ocean-0.18.9.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
181
- port_ocean-0.18.9.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
182
- port_ocean-0.18.9.dist-info/RECORD,,
185
+ port_ocean-0.19.2.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
186
+ port_ocean-0.19.2.dist-info/METADATA,sha256=-jaMpn-0NtgOvDlvVbIwTPuRwbRMh2kDmbGYRLzdEqQ,6669
187
+ port_ocean-0.19.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
188
+ port_ocean-0.19.2.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
189
+ port_ocean-0.19.2.dist-info/RECORD,,