diff --git a/src/apify_client/_models.py b/src/apify_client/_models.py index 19f5cf6d..57b1db8e 100644 --- a/src/apify_client/_models.py +++ b/src/apify_client/_models.py @@ -3766,6 +3766,18 @@ class WebhookRepresentation(BaseModel): """ Optional template for the HTTP headers sent by the webhook. """ + idempotency_key: Annotated[str | None, Field(alias='idempotencyKey', examples=['fdSJmdP3nfs7sfk3y'])] = None + """ + Optional key that prevents creating duplicate webhooks, e.g. when the run-starting request is retried. + """ + ignore_ssl_errors: Annotated[bool | None, Field(alias='ignoreSslErrors', examples=[False])] = None + """ + Optional flag to ignore SSL errors when the webhook sends the request. + """ + do_not_retry: Annotated[bool | None, Field(alias='doNotRetry', examples=[False])] = None + """ + Optional flag to skip retrying the webhook request on failure. + """ @docs_group('Models') diff --git a/src/apify_client/_typeddicts.py b/src/apify_client/_typeddicts.py index fa430ab7..e3e3bbb1 100644 --- a/src/apify_client/_typeddicts.py +++ b/src/apify_client/_typeddicts.py @@ -336,6 +336,18 @@ class WebhookRepresentationDict(TypedDict): """ Optional template for the HTTP headers sent by the webhook. """ + idempotency_key: NotRequired[str | None] + """ + Optional key that prevents creating duplicate webhooks, e.g. when the run-starting request is retried. + """ + ignore_ssl_errors: NotRequired[bool | None] + """ + Optional flag to ignore SSL errors when the webhook sends the request. + """ + do_not_retry: NotRequired[bool | None] + """ + Optional flag to skip retrying the webhook request on failure. + """ @docs_group('Typed dicts') @@ -374,3 +386,15 @@ class WebhookRepresentationCamelDict(TypedDict): """ Optional template for the HTTP headers sent by the webhook. """ + idempotencyKey: NotRequired[str | None] + """ + Optional key that prevents creating duplicate webhooks, e.g. when the run-starting request is retried. + """ + ignoreSslErrors: NotRequired[bool | None] + """ + Optional flag to ignore SSL errors when the webhook sends the request. + """ + doNotRetry: NotRequired[bool | None] + """ + Optional flag to skip retrying the webhook request on failure. + """ diff --git a/src/apify_client/_utils.py b/src/apify_client/_utils.py index e488b5be..9d8e128e 100644 --- a/src/apify_client/_utils.py +++ b/src/apify_client/_utils.py @@ -307,6 +307,9 @@ def encode_webhooks_to_base64(webhooks: WebhooksList | None) -> str | None: request_url=webhook.request_url, payload_template=webhook.payload_template, headers_template=webhook.headers_template, + idempotency_key=webhook.idempotency_key, + ignore_ssl_errors=webhook.ignore_ssl_errors, + do_not_retry=webhook.do_not_retry, ) ) else: diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 7ecf037c..0fcfe08c 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,6 +1,8 @@ from __future__ import annotations import io +import json +from base64 import b64decode from datetime import timedelta from http import HTTPStatus from typing import TYPE_CHECKING @@ -58,6 +60,33 @@ def test_encode_webhooks_to_base64() -> None: ) +def test_encode_webhooks_to_base64_keeps_adhoc_fields() -> None: + """Test that the idempotency key and the SSL/retry flags survive the projection onto `WebhookRepresentation`.""" + result = encode_webhooks_to_base64( + [ + WebhookCreate( + event_types=['ACTOR.RUN.SUCCEEDED'], + condition=WebhookCondition(), + request_url='https://example.com/run-succeeded', + idempotency_key='some-key', + ignore_ssl_errors=True, + do_not_retry=True, + ), + ] + ) + + assert result is not None + assert json.loads(b64decode(result)) == [ + { + 'eventTypes': ['ACTOR.RUN.SUCCEEDED'], + 'requestUrl': 'https://example.com/run-succeeded', + 'idempotencyKey': 'some-key', + 'ignoreSslErrors': True, + 'doNotRetry': True, + } + ] + + def test_encode_webhooks_to_base64_from_dicts() -> None: """Test that encode_webhooks_to_base64 accepts plain dicts typed as WebhookRepresentationDict.""" webhooks: list[WebhookRepresentationDict] = [