port-ocean 0.24.10__py3-none-any.whl → 0.24.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of port-ocean might be problematic. Click here for more details.
- port_ocean/core/handlers/webhook/processor_manager.py +19 -1
- port_ocean/exceptions/webhook_processor.py +13 -0
- port_ocean/tests/core/handlers/webhook/test_processor_manager.py +87 -3
- {port_ocean-0.24.10.dist-info → port_ocean-0.24.11.dist-info}/METADATA +1 -1
- {port_ocean-0.24.10.dist-info → port_ocean-0.24.11.dist-info}/RECORD +8 -8
- {port_ocean-0.24.10.dist-info → port_ocean-0.24.11.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.24.10.dist-info → port_ocean-0.24.11.dist-info}/WHEEL +0 -0
- {port_ocean-0.24.10.dist-info → port_ocean-0.24.11.dist-info}/entry_points.txt +0 -0
|
@@ -8,6 +8,7 @@ from port_ocean.context.event import EventType, event_context
|
|
|
8
8
|
from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
9
9
|
from port_ocean.core.integrations.mixins.events import EventsMixin
|
|
10
10
|
from port_ocean.core.integrations.mixins.live_events import LiveEventsMixin
|
|
11
|
+
from port_ocean.exceptions.webhook_processor import WebhookEventNotSupportedError
|
|
11
12
|
from .webhook_event import WebhookEvent, WebhookEventRawResults, LiveEventTimestamp
|
|
12
13
|
from port_ocean.context.event import event
|
|
13
14
|
|
|
@@ -53,10 +54,12 @@ class LiveEventsProcessorManager(LiveEventsMixin, EventsMixin):
|
|
|
53
54
|
"""Find and extract the matching processor for an event"""
|
|
54
55
|
|
|
55
56
|
created_processors: list[tuple[ResourceConfig, AbstractWebhookProcessor]] = []
|
|
57
|
+
event_processor_names = []
|
|
56
58
|
|
|
57
59
|
for processor_class in self._processors_classes[path]:
|
|
58
60
|
processor = processor_class(webhook_event.clone())
|
|
59
61
|
if await processor.should_process_event(webhook_event):
|
|
62
|
+
event_processor_names.append(processor.__class__.__name__)
|
|
60
63
|
kinds = await processor.get_matching_kinds(webhook_event)
|
|
61
64
|
for kind in kinds:
|
|
62
65
|
for resource in event.port_app_config.resources:
|
|
@@ -64,7 +67,22 @@ class LiveEventsProcessorManager(LiveEventsMixin, EventsMixin):
|
|
|
64
67
|
created_processors.append((resource, processor))
|
|
65
68
|
|
|
66
69
|
if not created_processors:
|
|
67
|
-
|
|
70
|
+
if event_processor_names:
|
|
71
|
+
logger.info(
|
|
72
|
+
"Webhook processors are available to handle this webhook event, but the corresponding kinds are not configured in the integration's mapping",
|
|
73
|
+
processors_available=event_processor_names,
|
|
74
|
+
webhook_path=path,
|
|
75
|
+
)
|
|
76
|
+
return []
|
|
77
|
+
else:
|
|
78
|
+
logger.warning(
|
|
79
|
+
"Unknown webhook event type received",
|
|
80
|
+
webhook_path=path,
|
|
81
|
+
message="No processors registered to handle this webhook event type.",
|
|
82
|
+
)
|
|
83
|
+
raise WebhookEventNotSupportedError(
|
|
84
|
+
"No matching processors found for webhook event"
|
|
85
|
+
)
|
|
68
86
|
|
|
69
87
|
logger.info(
|
|
70
88
|
"Found matching processors for webhook event",
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
from port_ocean.exceptions.base import BaseOceanException
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
class RetryableError(Exception):
|
|
2
5
|
"""Base exception class for errors that should trigger a retry."""
|
|
3
6
|
|
|
4
7
|
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WebhookProcessingError(BaseOceanException):
|
|
11
|
+
"""Base exception for webhook processing errors"""
|
|
12
|
+
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WebhookEventNotSupportedError(WebhookProcessingError):
|
|
17
|
+
pass
|
|
@@ -37,7 +37,10 @@ from port_ocean.core.handlers.port_app_config.models import (
|
|
|
37
37
|
)
|
|
38
38
|
from port_ocean.core.integrations.mixins.live_events import LiveEventsMixin
|
|
39
39
|
from port_ocean.core.models import Entity
|
|
40
|
-
from port_ocean.exceptions.webhook_processor import
|
|
40
|
+
from port_ocean.exceptions.webhook_processor import (
|
|
41
|
+
RetryableError,
|
|
42
|
+
WebhookEventNotSupportedError,
|
|
43
|
+
)
|
|
41
44
|
from port_ocean.core.handlers.queue import LocalQueue
|
|
42
45
|
|
|
43
46
|
|
|
@@ -354,7 +357,9 @@ async def test_extractMatchingProcessors_noMatch(
|
|
|
354
357
|
test_path = "/test"
|
|
355
358
|
processor_manager.register_processor(test_path, MockProcessorFalse)
|
|
356
359
|
|
|
357
|
-
with pytest.raises(
|
|
360
|
+
with pytest.raises(
|
|
361
|
+
WebhookEventNotSupportedError, match="No matching processors found"
|
|
362
|
+
):
|
|
358
363
|
async with event_context(
|
|
359
364
|
EventType.HTTP_REQUEST, trigger_type="request"
|
|
360
365
|
) as event:
|
|
@@ -409,6 +414,82 @@ async def test_extractMatchingProcessors_onlyOneMatches(
|
|
|
409
414
|
assert processor.event.payload == webhook_event.payload
|
|
410
415
|
|
|
411
416
|
|
|
417
|
+
@pytest.mark.asyncio
|
|
418
|
+
async def test_extractMatchingProcessors_noProcessorsRegistered(
|
|
419
|
+
processor_manager: LiveEventsProcessorManager,
|
|
420
|
+
webhook_event: WebhookEvent,
|
|
421
|
+
mock_port_app_config: PortAppConfig,
|
|
422
|
+
) -> None:
|
|
423
|
+
"""Test that WebhookEventNotSupportedError is raised for unknown events without any registered processors"""
|
|
424
|
+
test_path = "/unknown_path"
|
|
425
|
+
# No processors registered for this path
|
|
426
|
+
|
|
427
|
+
# Manually add the path to _processors_classes to simulate a path with no processors
|
|
428
|
+
processor_manager._processors_classes[test_path] = []
|
|
429
|
+
|
|
430
|
+
with pytest.raises(
|
|
431
|
+
WebhookEventNotSupportedError, match="No matching processors found"
|
|
432
|
+
):
|
|
433
|
+
async with event_context(
|
|
434
|
+
EventType.HTTP_REQUEST, trigger_type="request"
|
|
435
|
+
) as event:
|
|
436
|
+
event.port_app_config = mock_port_app_config
|
|
437
|
+
await processor_manager._extract_matching_processors(
|
|
438
|
+
webhook_event, test_path
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
@pytest.mark.asyncio
|
|
443
|
+
async def test_extractMatchingProcessors_processorsAvailableButKindsNotConfigured(
|
|
444
|
+
processor_manager: LiveEventsProcessorManager,
|
|
445
|
+
webhook_event: WebhookEvent,
|
|
446
|
+
) -> None:
|
|
447
|
+
"""Test that processors available but kinds not configured returns empty list"""
|
|
448
|
+
test_path = "/test"
|
|
449
|
+
|
|
450
|
+
from port_ocean.core.handlers.port_app_config.models import (
|
|
451
|
+
PortAppConfig,
|
|
452
|
+
ResourceConfig,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Create a mock processor that will match the event but return a kind not in the port app config
|
|
456
|
+
class MockProcessorWithUnmappedKind(AbstractWebhookProcessor):
|
|
457
|
+
async def authenticate(
|
|
458
|
+
self, payload: Dict[str, Any], headers: Dict[str, str]
|
|
459
|
+
) -> bool:
|
|
460
|
+
return True
|
|
461
|
+
|
|
462
|
+
async def validate_payload(self, payload: Dict[str, Any]) -> bool:
|
|
463
|
+
return True
|
|
464
|
+
|
|
465
|
+
async def handle_event(
|
|
466
|
+
self, payload: EventPayload, resource: ResourceConfig
|
|
467
|
+
) -> WebhookEventRawResults:
|
|
468
|
+
return WebhookEventRawResults(
|
|
469
|
+
updated_raw_results=[], deleted_raw_results=[]
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
async def should_process_event(self, event: WebhookEvent) -> bool:
|
|
473
|
+
return True # This processor will match
|
|
474
|
+
|
|
475
|
+
async def get_matching_kinds(self, event: WebhookEvent) -> list[str]:
|
|
476
|
+
return ["unmapped_kind"] # This kind is not in the mock_port_app_config
|
|
477
|
+
|
|
478
|
+
processor_manager.register_processor(test_path, MockProcessorWithUnmappedKind)
|
|
479
|
+
|
|
480
|
+
empty_port_app_config = PortAppConfig(
|
|
481
|
+
resources=[],
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
async with event_context(EventType.HTTP_REQUEST, trigger_type="request") as event:
|
|
485
|
+
event.port_app_config = empty_port_app_config
|
|
486
|
+
processors = await processor_manager._extract_matching_processors(
|
|
487
|
+
webhook_event, test_path
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
assert len(processors) == 0
|
|
491
|
+
|
|
492
|
+
|
|
412
493
|
def test_registerProcessor_registrationWorks(
|
|
413
494
|
processor_manager: LiveEventsProcessorManager,
|
|
414
495
|
) -> None:
|
|
@@ -853,7 +934,10 @@ async def test_integrationTest_postRequestSent_noMatchingHandlers_entityNotUpser
|
|
|
853
934
|
except asyncio.TimeoutError:
|
|
854
935
|
pytest.fail("Event processing timed out")
|
|
855
936
|
|
|
856
|
-
assert
|
|
937
|
+
assert (
|
|
938
|
+
isinstance(test_state["exception_thrown"], WebhookEventNotSupportedError)
|
|
939
|
+
is True
|
|
940
|
+
)
|
|
857
941
|
|
|
858
942
|
mock_upsert.assert_not_called()
|
|
859
943
|
mock_delete.assert_not_called()
|
|
@@ -112,7 +112,7 @@ port_ocean/core/handlers/resync_state_updater/__init__.py,sha256=kG6y-JQGpPfuTHh
|
|
|
112
112
|
port_ocean/core/handlers/resync_state_updater/updater.py,sha256=TRYq6QnTtPlJg6MvgZPtQdZPvkAhkvpcmWjtkxCnkg4,3764
|
|
113
113
|
port_ocean/core/handlers/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
114
114
|
port_ocean/core/handlers/webhook/abstract_webhook_processor.py,sha256=5KwZkdkDd5HdVkXPzKiqabodZKl-hOtMypkTKd8Hq3M,3891
|
|
115
|
-
port_ocean/core/handlers/webhook/processor_manager.py,sha256=
|
|
115
|
+
port_ocean/core/handlers/webhook/processor_manager.py,sha256=4u9Q_djZAzxgwGHlHBmVBG26svEigeSka6GajcETd20,12976
|
|
116
116
|
port_ocean/core/handlers/webhook/webhook_event.py,sha256=9wHXLY6IGgjuqrwXXvZm_RbYEd-a9qIFNxWnGbfPv6o,3877
|
|
117
117
|
port_ocean/core/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
118
118
|
port_ocean/core/integrations/base.py,sha256=dUhytVM9uUbcDRzG1QWyvBvEJOWZY0dPVV3hXuukOfg,3587
|
|
@@ -136,7 +136,7 @@ port_ocean/exceptions/context.py,sha256=mA8HII6Rl4QxKUz98ppy1zX3kaziaen21h1ZWuU3
|
|
|
136
136
|
port_ocean/exceptions/core.py,sha256=3LpQrOWdZ-xZ8zm90DmTnFnk0Nms2OgrVIzZBK0Xw5M,931
|
|
137
137
|
port_ocean/exceptions/port_defaults.py,sha256=2a7Koy541KxMan33mU-gbauUxsumG3NT4itVxSpQqfw,666
|
|
138
138
|
port_ocean/exceptions/utils.py,sha256=gjOqpi-HpY1l4WlMFsGA9yzhxDhajhoGGdDDyGbLnqI,197
|
|
139
|
-
port_ocean/exceptions/webhook_processor.py,sha256=
|
|
139
|
+
port_ocean/exceptions/webhook_processor.py,sha256=4SnkVzVwiacH_Ip4qs1hRHa6GanhnojW_TLTdQQtm7Y,363
|
|
140
140
|
port_ocean/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
141
141
|
port_ocean/helpers/async_client.py,sha256=SRlP6o7_FCSY3UHnRlZdezppePVxxOzZ0z861vE3K40,1783
|
|
142
142
|
port_ocean/helpers/metric/metric.py,sha256=Aacz7bOd8ZCwEPpXAdwLbKRXf28Z4wiViG_GXiV_xWg,14529
|
|
@@ -171,7 +171,7 @@ port_ocean/tests/core/handlers/port_app_config/test_api.py,sha256=eJZ6SuFBLz71y4
|
|
|
171
171
|
port_ocean/tests/core/handlers/port_app_config/test_base.py,sha256=hSh556bJM9zuELwhwnyKSfd9z06WqWXIfe-6hCl5iKI,9799
|
|
172
172
|
port_ocean/tests/core/handlers/queue/test_local_queue.py,sha256=9Ly0HzZXbs6Rbl_bstsIdInC3h2bgABU3roP9S_PnJM,2582
|
|
173
173
|
port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py,sha256=zKwHhPAYEZoZ5Z2UETp1t--mbkS8uyvlXThB0obZTTc,3340
|
|
174
|
-
port_ocean/tests/core/handlers/webhook/test_processor_manager.py,sha256=
|
|
174
|
+
port_ocean/tests/core/handlers/webhook/test_processor_manager.py,sha256=rqNFc-S_ZnPyDTSFTdiGcRFKbeDGfWQCH_f2UPbfcAA,52310
|
|
175
175
|
port_ocean/tests/core/handlers/webhook/test_webhook_event.py,sha256=oR4dEHLO65mp6rkfNfszZcfFoRZlB8ZWee4XetmsuIk,3181
|
|
176
176
|
port_ocean/tests/core/test_utils.py,sha256=Z3kdhb5V7Svhcyy3EansdTpgHL36TL6erNtU-OPwAcI,2647
|
|
177
177
|
port_ocean/tests/core/utils/test_entity_topological_sorter.py,sha256=zuq5WSPy_88PemG3mOUIHTxWMR_js1R7tOzUYlgBd68,3447
|
|
@@ -200,8 +200,8 @@ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,32
|
|
|
200
200
|
port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
|
|
201
201
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
|
202
202
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
203
|
-
port_ocean-0.24.
|
|
204
|
-
port_ocean-0.24.
|
|
205
|
-
port_ocean-0.24.
|
|
206
|
-
port_ocean-0.24.
|
|
207
|
-
port_ocean-0.24.
|
|
203
|
+
port_ocean-0.24.11.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
204
|
+
port_ocean-0.24.11.dist-info/METADATA,sha256=5SPsmnSpVV2tiRRGdbP0SeRJZ3SJ2EUiPR_t-4oGupI,6853
|
|
205
|
+
port_ocean-0.24.11.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
206
|
+
port_ocean-0.24.11.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
207
|
+
port_ocean-0.24.11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|