port-ocean 0.27.5__py3-none-any.whl → 0.27.7__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.
- integrations/_infra/Dockerfile.Deb +4 -1
- integrations/_infra/Dockerfile.local +3 -1
- port_ocean/config/settings.py +9 -0
- port_ocean/helpers/async_client.py +7 -0
- port_ocean/helpers/stream.py +71 -0
- port_ocean/log/handlers.py +3 -4
- {port_ocean-0.27.5.dist-info → port_ocean-0.27.7.dist-info}/METADATA +4 -1
- {port_ocean-0.27.5.dist-info → port_ocean-0.27.7.dist-info}/RECORD +11 -10
- {port_ocean-0.27.5.dist-info → port_ocean-0.27.7.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.27.5.dist-info → port_ocean-0.27.7.dist-info}/WHEEL +0 -0
- {port_ocean-0.27.5.dist-info → port_ocean-0.27.7.dist-info}/entry_points.txt +0 -0
@@ -28,11 +28,14 @@ ARG INTEGRATION_VERSION
|
|
28
28
|
ARG BUILD_CONTEXT
|
29
29
|
ARG PROMETHEUS_MULTIPROC_DIR=/tmp/ocean/prometheus/metrics
|
30
30
|
ARG OAUTH_CONFIG_DIR=/app/.config
|
31
|
+
ARG STREAMING_LOCATION=/tmp/ocean/streaming
|
31
32
|
|
32
33
|
ENV LIBRDKAFKA_VERSION=2.8.2 \
|
33
|
-
PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR}
|
34
|
+
PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR} \
|
35
|
+
STREAMING_LOCATION=${STREAMING_LOCATION}
|
34
36
|
|
35
37
|
RUN mkdir -p ${PROMETHEUS_MULTIPROC_DIR}
|
38
|
+
RUN mkdir -p ${STREAMING_LOCATION}
|
36
39
|
RUN chown -R ocean:appgroup /tmp/ocean && chmod -R 755 /tmp/ocean
|
37
40
|
|
38
41
|
RUN mkdir -p ${OAUTH_CONFIG_DIR}
|
@@ -33,13 +33,15 @@ RUN apt-get update \
|
|
33
33
|
|
34
34
|
ARG BUILD_CONTEXT
|
35
35
|
ARG PROMETHEUS_MULTIPROC_DIR=/tmp/ocean/prometheus/metrics
|
36
|
+
ARG STREAMING_LOCATION=/tmp/ocean/streaming
|
36
37
|
|
37
38
|
ENV PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR}
|
38
|
-
|
39
|
+
ENV STREAMING_LOCATION=${STREAMING_LOCATION}
|
39
40
|
# Create /tmp/ocean directory and set permissions
|
40
41
|
|
41
42
|
|
42
43
|
RUN mkdir -p ${PROMETHEUS_MULTIPROC_DIR}
|
44
|
+
RUN mkdir -p ${STREAMING_LOCATION}
|
43
45
|
|
44
46
|
WORKDIR /app
|
45
47
|
|
port_ocean/config/settings.py
CHANGED
@@ -73,6 +73,13 @@ class MetricsSettings(BaseOceanModel, extra=Extra.allow):
|
|
73
73
|
webhook_url: str | None = Field(default=None)
|
74
74
|
|
75
75
|
|
76
|
+
class StreamingSettings(BaseOceanModel, extra=Extra.allow):
|
77
|
+
enabled: bool = Field(default=False)
|
78
|
+
max_buffer_size_mb: int = Field(default=1024 * 1024 * 20) # 20 mb
|
79
|
+
chunk_size: int = Field(default=1024 * 64) # 64 kb
|
80
|
+
location: str = Field(default="/tmp/ocean/streaming")
|
81
|
+
|
82
|
+
|
76
83
|
class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
|
77
84
|
_integration_config_model: BaseModel | None = None
|
78
85
|
|
@@ -114,6 +121,8 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
|
|
114
121
|
yield_items_to_parse: bool = False
|
115
122
|
yield_items_to_parse_batch_size: int = 10
|
116
123
|
|
124
|
+
streaming: StreamingSettings = Field(default_factory=lambda: StreamingSettings())
|
125
|
+
|
117
126
|
@validator("process_execution_mode")
|
118
127
|
def validate_process_execution_mode(
|
119
128
|
cls, process_execution_mode: ProcessExecutionMode
|
@@ -4,6 +4,7 @@ import httpx
|
|
4
4
|
from loguru import logger
|
5
5
|
|
6
6
|
from port_ocean.helpers.retry import RetryTransport
|
7
|
+
from port_ocean.helpers.stream import Stream
|
7
8
|
|
8
9
|
|
9
10
|
class OceanAsyncClient(httpx.AsyncClient):
|
@@ -50,3 +51,9 @@ class OceanAsyncClient(httpx.AsyncClient):
|
|
50
51
|
logger=logger,
|
51
52
|
**(self._transport_kwargs or {}),
|
52
53
|
)
|
54
|
+
|
55
|
+
async def get_stream(self, url: str, **kwargs: Any) -> Stream:
|
56
|
+
req = self.build_request("GET", url, **kwargs)
|
57
|
+
response = await self.send(req, stream=True)
|
58
|
+
response.raise_for_status()
|
59
|
+
return Stream(response)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import os
|
2
|
+
from typing import Any, AsyncGenerator
|
3
|
+
import uuid
|
4
|
+
|
5
|
+
import aiofiles
|
6
|
+
import httpx
|
7
|
+
import ijson # type: ignore[import-untyped]
|
8
|
+
from cryptography.fernet import Fernet
|
9
|
+
|
10
|
+
import port_ocean.context.ocean as ocean_context
|
11
|
+
|
12
|
+
|
13
|
+
class Stream:
|
14
|
+
def __init__(self, response: httpx.Response):
|
15
|
+
self.response = response
|
16
|
+
self.headers = response.headers
|
17
|
+
self.status_code = response.status_code
|
18
|
+
|
19
|
+
async def _byte_stream(
|
20
|
+
self, chunk_size: int | None = None
|
21
|
+
) -> AsyncGenerator[bytes, None]:
|
22
|
+
if chunk_size is None:
|
23
|
+
chunk_size = ocean_context.ocean.config.streaming.chunk_size
|
24
|
+
|
25
|
+
file_name = f"{ocean_context.ocean.config.streaming.location}/{uuid.uuid4()}"
|
26
|
+
|
27
|
+
crypt = Fernet(Fernet.generate_key())
|
28
|
+
|
29
|
+
try:
|
30
|
+
async for chunk in self.response.aiter_bytes(chunk_size=chunk_size):
|
31
|
+
async with aiofiles.open(f"{file_name}", "ab") as f:
|
32
|
+
if len(chunk) > 0:
|
33
|
+
await f.write(crypt.encrypt(chunk))
|
34
|
+
await f.write(b"\n")
|
35
|
+
finally:
|
36
|
+
await self.response.aclose()
|
37
|
+
|
38
|
+
try:
|
39
|
+
async with aiofiles.open(f"{file_name}", mode="rb") as f:
|
40
|
+
while True:
|
41
|
+
line = await f.readline()
|
42
|
+
if not line:
|
43
|
+
break
|
44
|
+
data = crypt.decrypt(line)
|
45
|
+
yield data
|
46
|
+
finally:
|
47
|
+
try:
|
48
|
+
os.remove(file_name)
|
49
|
+
except FileNotFoundError:
|
50
|
+
pass
|
51
|
+
|
52
|
+
async def get_json_stream(
|
53
|
+
self,
|
54
|
+
target_items: str = "",
|
55
|
+
max_buffer_size_mb: int | None = None,
|
56
|
+
) -> AsyncGenerator[list[dict[str, Any]], None]:
|
57
|
+
if max_buffer_size_mb is None:
|
58
|
+
max_buffer_size_mb = ocean_context.ocean.config.streaming.max_buffer_size_mb
|
59
|
+
|
60
|
+
events = ijson.sendable_list()
|
61
|
+
coro = ijson.items_coro(events, target_items)
|
62
|
+
current_buffer_size = 0
|
63
|
+
async for chunk in self._byte_stream():
|
64
|
+
coro.send(chunk)
|
65
|
+
current_buffer_size += len(chunk)
|
66
|
+
if current_buffer_size >= max_buffer_size_mb:
|
67
|
+
if len(events) > 0:
|
68
|
+
yield events
|
69
|
+
events.clear()
|
70
|
+
current_buffer_size = 0
|
71
|
+
yield events
|
port_ocean/log/handlers.py
CHANGED
@@ -3,16 +3,16 @@ import logging
|
|
3
3
|
import sys
|
4
4
|
import threading
|
5
5
|
import time
|
6
|
+
from copy import deepcopy
|
6
7
|
from datetime import datetime
|
7
8
|
from logging.handlers import MemoryHandler
|
9
|
+
from traceback import format_exception
|
8
10
|
from typing import Any
|
9
11
|
|
10
12
|
from loguru import logger
|
11
13
|
|
12
14
|
from port_ocean import Ocean
|
13
15
|
from port_ocean.context.ocean import ocean
|
14
|
-
from copy import deepcopy
|
15
|
-
from traceback import format_exception
|
16
16
|
|
17
17
|
|
18
18
|
def _serialize_record(record: logging.LogRecord) -> dict[str, Any]:
|
@@ -53,7 +53,6 @@ class HTTPMemoryHandler(MemoryHandler):
|
|
53
53
|
return None
|
54
54
|
|
55
55
|
def emit(self, record: logging.LogRecord) -> None:
|
56
|
-
|
57
56
|
self._serialized_buffer.append(_serialize_record(record))
|
58
57
|
super().emit(record)
|
59
58
|
|
@@ -106,4 +105,4 @@ class HTTPMemoryHandler(MemoryHandler):
|
|
106
105
|
try:
|
107
106
|
await _ocean.port_client.ingest_integration_logs(logs_to_send)
|
108
107
|
except Exception as e:
|
109
|
-
logger.
|
108
|
+
logger.error(f"Failed to send logs to Port with error: {e}")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: port-ocean
|
3
|
-
Version: 0.27.
|
3
|
+
Version: 0.27.7
|
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
|
@@ -22,12 +22,15 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
23
|
Classifier: Topic :: Utilities
|
24
24
|
Provides-Extra: cli
|
25
|
+
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
25
26
|
Requires-Dist: aiostream (>=0.5.2,<0.7.0)
|
26
27
|
Requires-Dist: click (>=8.1.3,<9.0.0) ; extra == "cli"
|
27
28
|
Requires-Dist: confluent-kafka (>=2.10.1,<3.0.0)
|
28
29
|
Requires-Dist: cookiecutter (>=2.1.1,<3.0.0) ; extra == "cli"
|
30
|
+
Requires-Dist: cryptography (>=44.0.1,<45.0.0)
|
29
31
|
Requires-Dist: fastapi (>=0.116.0,<0.117.0)
|
30
32
|
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
33
|
+
Requires-Dist: ijson (>=3.4.0,<4.0.0)
|
31
34
|
Requires-Dist: jinja2 (>=3.1.6)
|
32
35
|
Requires-Dist: jinja2-time (>=0.2.0,<0.3.0) ; extra == "cli"
|
33
36
|
Requires-Dist: jq (>=1.8.0,<2.0.0)
|
@@ -1,9 +1,9 @@
|
|
1
|
-
integrations/_infra/Dockerfile.Deb,sha256=
|
1
|
+
integrations/_infra/Dockerfile.Deb,sha256=weK3VrskDEsuvYaxerak-XIqDgTx_wPorAFQQjYH_FU,2493
|
2
2
|
integrations/_infra/Dockerfile.alpine,sha256=7E4Sb-8supsCcseerHwTkuzjHZoYcaHIyxiBZ-wewo0,3482
|
3
3
|
integrations/_infra/Dockerfile.base.builder,sha256=ESe1PKC6itp_AuXawbLI75k1Kruny6NTANaTinxOgVs,743
|
4
4
|
integrations/_infra/Dockerfile.base.runner,sha256=uAcs2IsxrAAUHGXt_qULA5INr-HFguf5a5fCKiqEzbY,384
|
5
5
|
integrations/_infra/Dockerfile.dockerignore,sha256=CM1Fxt3I2AvSvObuUZRmy5BNLSGC7ylnbpWzFgD4cso,1163
|
6
|
-
integrations/_infra/Dockerfile.local,sha256=
|
6
|
+
integrations/_infra/Dockerfile.local,sha256=FFX9RvFqlaHvhUrRnnzUl0zQp2oKDFVRGkXJQPMQ7cI,1650
|
7
7
|
integrations/_infra/Makefile,sha256=YgLKvuF_Dw4IA7X98Nus6zIW_3cJ60M1QFGs3imj5c4,2430
|
8
8
|
integrations/_infra/README.md,sha256=ZtJFSMCTU5zTeM8ddRuW1ZL1ga8z7Ic2F3mxmgOSjgo,1195
|
9
9
|
integrations/_infra/entry_local.sh,sha256=Sn2TexTEpruH2ixIAGsk-fZV6Y7pT3jd2Pi9TxBeFuw,633
|
@@ -70,7 +70,7 @@ port_ocean/clients/port/utils.py,sha256=osFyAjw7Y5Qf2uVSqC7_RTCQfijiL1zS74JJM0go
|
|
70
70
|
port_ocean/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
71
|
port_ocean/config/base.py,sha256=x1gFbzujrxn7EJudRT81C6eN9WsYAb3vOHwcpcpX8Tc,6370
|
72
72
|
port_ocean/config/dynamic.py,sha256=Lrk4JRGtR-0YKQ9DDGexX5NGFE7EJ6VoHya19YYhssM,2687
|
73
|
-
port_ocean/config/settings.py,sha256
|
73
|
+
port_ocean/config/settings.py,sha256=Zz_D40EXZEm0hzNdYgwdUy_s5LbJ6iMg3Zcl2n5NLUY,7686
|
74
74
|
port_ocean/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
75
|
port_ocean/consumers/kafka_consumer.py,sha256=N8KocjBi9aR0BOPG8hgKovg-ns_ggpEjrSxqSqF_BSo,4710
|
76
76
|
port_ocean/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -140,12 +140,13 @@ port_ocean/exceptions/port_defaults.py,sha256=2a7Koy541KxMan33mU-gbauUxsumG3NT4i
|
|
140
140
|
port_ocean/exceptions/utils.py,sha256=gjOqpi-HpY1l4WlMFsGA9yzhxDhajhoGGdDDyGbLnqI,197
|
141
141
|
port_ocean/exceptions/webhook_processor.py,sha256=4SnkVzVwiacH_Ip4qs1hRHa6GanhnojW_TLTdQQtm7Y,363
|
142
142
|
port_ocean/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
143
|
-
port_ocean/helpers/async_client.py,sha256=
|
143
|
+
port_ocean/helpers/async_client.py,sha256=LOgUlZ5Cs_WUSc8XujCVjPGvzZ_3AuFJNKPy0FKV3fA,1987
|
144
144
|
port_ocean/helpers/metric/metric.py,sha256=Aacz7bOd8ZCwEPpXAdwLbKRXf28Z4wiViG_GXiV_xWg,14529
|
145
145
|
port_ocean/helpers/metric/utils.py,sha256=1lAgrxnZLuR_wUNDyPOPzLrm32b8cDdioob2lvnPQ1A,1619
|
146
146
|
port_ocean/helpers/retry.py,sha256=VHAp6j9-Vid6aNR5sca3S0aW6b1S2oYw9vT9hi1N22U,18556
|
147
|
+
port_ocean/helpers/stream.py,sha256=_UwsThzXynxWzL8OlBT1pmb2evZBi9HaaqeAGNuTuOI,2338
|
147
148
|
port_ocean/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
148
|
-
port_ocean/log/handlers.py,sha256=
|
149
|
+
port_ocean/log/handlers.py,sha256=LJ1WAfq7wYCrBpeLPihMKmWjdSahKKXNHFMRYkbk0Co,3630
|
149
150
|
port_ocean/log/logger_setup.py,sha256=0K3zVG0YYrYOWEV8-rCGks1o-bMRxgHXlqawu9w_tSw,2656
|
150
151
|
port_ocean/log/sensetive.py,sha256=lVKiZH6b7TkrZAMmhEJRhcl67HNM94e56x12DwFgCQk,2920
|
151
152
|
port_ocean/middlewares.py,sha256=9wYCdyzRZGK1vjEJ28FY_DkfwDNENmXp504UKPf5NaQ,2727
|
@@ -206,8 +207,8 @@ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,32
|
|
206
207
|
port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
|
207
208
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
208
209
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
209
|
-
port_ocean-0.27.
|
210
|
-
port_ocean-0.27.
|
211
|
-
port_ocean-0.27.
|
212
|
-
port_ocean-0.27.
|
213
|
-
port_ocean-0.27.
|
210
|
+
port_ocean-0.27.7.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
211
|
+
port_ocean-0.27.7.dist-info/METADATA,sha256=hVAPvuv7qLhu-zJL8PTCzKljO3G_WK8QXHCW3P3OxBM,7015
|
212
|
+
port_ocean-0.27.7.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
213
|
+
port_ocean-0.27.7.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
214
|
+
port_ocean-0.27.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|