hookbase 2.1.1__tar.gz → 2.2.0__tar.gz

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.
Files changed (80) hide show
  1. {hookbase-2.1.1 → hookbase-2.2.0}/PKG-INFO +1 -1
  2. hookbase-2.2.0/src/hookbase/_version.py +1 -0
  3. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/__init__.py +12 -0
  4. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/destinations.py +76 -2
  5. hookbase-2.2.0/tests/resources/test_destinations.py +193 -0
  6. hookbase-2.1.1/src/hookbase/_version.py +0 -1
  7. hookbase-2.1.1/tests/resources/test_destinations.py +0 -72
  8. {hookbase-2.1.1 → hookbase-2.2.0}/.github/workflows/python-publish.yml +0 -0
  9. {hookbase-2.1.1 → hookbase-2.2.0}/.gitignore +0 -0
  10. {hookbase-2.1.1 → hookbase-2.2.0}/LICENSE +0 -0
  11. {hookbase-2.1.1 → hookbase-2.2.0}/README.md +0 -0
  12. {hookbase-2.1.1 → hookbase-2.2.0}/examples/async_usage.py +0 -0
  13. {hookbase-2.1.1 → hookbase-2.2.0}/examples/basic_inbound.py +0 -0
  14. {hookbase-2.1.1 → hookbase-2.2.0}/examples/error_handling.py +0 -0
  15. {hookbase-2.1.1 → hookbase-2.2.0}/examples/send_webhooks.py +0 -0
  16. {hookbase-2.1.1 → hookbase-2.2.0}/examples/verify_webhooks.py +0 -0
  17. {hookbase-2.1.1 → hookbase-2.2.0}/pyproject.toml +0 -0
  18. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/__init__.py +0 -0
  19. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/_client.py +0 -0
  20. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/_constants.py +0 -0
  21. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/_pagination.py +0 -0
  22. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/client.py +0 -0
  23. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/errors.py +0 -0
  24. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/_base.py +0 -0
  25. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/analytics.py +0 -0
  26. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/api_keys.py +0 -0
  27. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/applications.py +0 -0
  28. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/common.py +0 -0
  29. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/cron_jobs.py +0 -0
  30. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/deliveries.py +0 -0
  31. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/dlq.py +0 -0
  32. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/endpoints.py +0 -0
  33. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/event_types.py +0 -0
  34. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/events.py +0 -0
  35. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/filters.py +0 -0
  36. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/messages.py +0 -0
  37. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/organizations.py +0 -0
  38. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/portal_tokens.py +0 -0
  39. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/routes.py +0 -0
  40. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/schemas.py +0 -0
  41. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/sources.py +0 -0
  42. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/subscriptions.py +0 -0
  43. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/transforms.py +0 -0
  44. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/models/tunnels.py +0 -0
  45. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/py.typed +0 -0
  46. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/__init__.py +0 -0
  47. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/_base.py +0 -0
  48. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/analytics.py +0 -0
  49. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/api_keys.py +0 -0
  50. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/applications.py +0 -0
  51. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/cron_jobs.py +0 -0
  52. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/deliveries.py +0 -0
  53. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/destinations.py +0 -0
  54. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/dlq.py +0 -0
  55. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/endpoints.py +0 -0
  56. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/event_types.py +0 -0
  57. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/events.py +0 -0
  58. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/filters.py +0 -0
  59. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/message_log.py +0 -0
  60. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/messages.py +0 -0
  61. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/organizations.py +0 -0
  62. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/portal_tokens.py +0 -0
  63. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/routes.py +0 -0
  64. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/schemas.py +0 -0
  65. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/sources.py +0 -0
  66. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/subscriptions.py +0 -0
  67. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/transforms.py +0 -0
  68. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/resources/tunnels.py +0 -0
  69. {hookbase-2.1.1 → hookbase-2.2.0}/src/hookbase/webhook.py +0 -0
  70. {hookbase-2.1.1 → hookbase-2.2.0}/tests/__init__.py +0 -0
  71. {hookbase-2.1.1 → hookbase-2.2.0}/tests/conftest.py +0 -0
  72. {hookbase-2.1.1 → hookbase-2.2.0}/tests/resources/__init__.py +0 -0
  73. {hookbase-2.1.1 → hookbase-2.2.0}/tests/resources/test_applications.py +0 -0
  74. {hookbase-2.1.1 → hookbase-2.2.0}/tests/resources/test_dlq.py +0 -0
  75. {hookbase-2.1.1 → hookbase-2.2.0}/tests/resources/test_messages.py +0 -0
  76. {hookbase-2.1.1 → hookbase-2.2.0}/tests/resources/test_sources.py +0 -0
  77. {hookbase-2.1.1 → hookbase-2.2.0}/tests/test_client.py +0 -0
  78. {hookbase-2.1.1 → hookbase-2.2.0}/tests/test_errors.py +0 -0
  79. {hookbase-2.1.1 → hookbase-2.2.0}/tests/test_pagination.py +0 -0
  80. {hookbase-2.1.1 → hookbase-2.2.0}/tests/test_webhook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hookbase
3
- Version: 2.1.1
3
+ Version: 2.2.0
4
4
  Summary: Official Python SDK for the Hookbase webhook management platform
5
5
  Project-URL: Homepage, https://hookbase.app
6
6
  Project-URL: Documentation, https://docs.hookbase.app/sdk/python
@@ -0,0 +1 @@
1
+ __version__ = "2.2.0"
@@ -18,9 +18,15 @@ from .deliveries import (
18
18
  )
19
19
  from .destinations import (
20
20
  AuthType,
21
+ AzureBlobConfig,
21
22
  CreateDestinationParams,
22
23
  Destination,
24
+ DestinationType,
25
+ FieldMapping,
26
+ GCSConfig,
23
27
  HttpMethod,
28
+ R2Config,
29
+ S3Config,
24
30
  TestResult,
25
31
  UpdateDestinationParams,
26
32
  )
@@ -123,9 +129,15 @@ __all__ = [
123
129
  "ReplayResult",
124
130
  # Destinations
125
131
  "AuthType",
132
+ "AzureBlobConfig",
126
133
  "CreateDestinationParams",
127
134
  "Destination",
135
+ "DestinationType",
136
+ "FieldMapping",
137
+ "GCSConfig",
128
138
  "HttpMethod",
139
+ "R2Config",
140
+ "S3Config",
129
141
  "TestResult",
130
142
  "UpdateDestinationParams",
131
143
  # DLQ
@@ -9,6 +9,52 @@ from ._base import HookbaseModel
9
9
 
10
10
  HttpMethod = Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
11
11
  AuthType = Literal["none", "basic", "bearer", "api_key", "custom_header"]
12
+ DestinationType = Literal["http", "s3", "r2", "gcs", "azure_blob"]
13
+ FileFormat = Literal["json", "jsonl"]
14
+ PartitionBy = Literal["date", "hour", "source"]
15
+ FieldMappingType = Literal["string", "number", "boolean", "timestamp", "json"]
16
+
17
+
18
+ class FieldMapping(HookbaseModel):
19
+ source: str
20
+ target: str
21
+ type: FieldMappingType
22
+ default: str | None = None
23
+
24
+
25
+ class S3Config(HookbaseModel):
26
+ bucket: str
27
+ region: str
28
+ access_key_id: str
29
+ secret_access_key: str
30
+ prefix: str | None = None
31
+ file_format: FileFormat | None = None
32
+ partition_by: PartitionBy | None = None
33
+
34
+
35
+ class R2Config(HookbaseModel):
36
+ bucket: str
37
+ prefix: str | None = None
38
+ file_format: FileFormat | None = None
39
+ partition_by: PartitionBy | None = None
40
+
41
+
42
+ class GCSConfig(HookbaseModel):
43
+ bucket: str
44
+ project_id: str
45
+ service_account_key: str
46
+ prefix: str | None = None
47
+ file_format: FileFormat | None = None
48
+ partition_by: PartitionBy | None = None
49
+
50
+
51
+ class AzureBlobConfig(HookbaseModel):
52
+ account_name: str
53
+ account_key: str
54
+ container_name: str
55
+ prefix: str | None = None
56
+ file_format: FileFormat | None = None
57
+ partition_by: PartitionBy | None = None
12
58
 
13
59
 
14
60
  class Destination(HookbaseModel):
@@ -17,7 +63,8 @@ class Destination(HookbaseModel):
17
63
  name: str
18
64
  slug: str
19
65
  description: str | None = None
20
- url: str
66
+ type: DestinationType = "http"
67
+ url: str = ""
21
68
  method: HttpMethod = "POST"
22
69
  headers: dict[str, str] | None = None
23
70
  auth_type: str | None = "none"
@@ -28,6 +75,8 @@ class Destination(HookbaseModel):
28
75
  rate_limit: int | None = None
29
76
  rate_limit_window: int | None = None
30
77
  is_active: bool = True
78
+ config: dict[str, Any] | None = None
79
+ field_mapping: list[FieldMapping] | None = None
31
80
  delivery_count: int = 0
32
81
  last_delivery_at: str | None = None
33
82
  created_at: str = ""
@@ -53,6 +102,26 @@ class Destination(HookbaseModel):
53
102
  return None
54
103
  return v
55
104
 
105
+ @field_validator("config", mode="before")
106
+ @classmethod
107
+ def parse_config(cls, v: Any) -> Any:
108
+ if isinstance(v, str):
109
+ try:
110
+ return json.loads(v)
111
+ except (json.JSONDecodeError, ValueError):
112
+ return None
113
+ return v
114
+
115
+ @field_validator("field_mapping", mode="before")
116
+ @classmethod
117
+ def parse_field_mapping(cls, v: Any) -> Any:
118
+ if isinstance(v, str):
119
+ try:
120
+ return json.loads(v)
121
+ except (json.JSONDecodeError, ValueError):
122
+ return None
123
+ return v
124
+
56
125
  @field_validator("is_active", mode="before")
57
126
  @classmethod
58
127
  def parse_is_active(cls, v: Any) -> Any:
@@ -65,7 +134,8 @@ class CreateDestinationParams(HookbaseModel):
65
134
  name: str
66
135
  slug: str | None = None
67
136
  description: str | None = None
68
- url: str
137
+ type: DestinationType | None = None
138
+ url: str | None = None
69
139
  method: HttpMethod | None = None
70
140
  headers: dict[str, str] | None = None
71
141
  auth_type: AuthType | None = None
@@ -75,6 +145,8 @@ class CreateDestinationParams(HookbaseModel):
75
145
  retry_interval: int | None = None
76
146
  rate_limit: int | None = None
77
147
  rate_limit_window: int | None = None
148
+ config: dict[str, Any] | None = None
149
+ field_mapping: list[FieldMapping] | None = None
78
150
 
79
151
 
80
152
  class UpdateDestinationParams(HookbaseModel):
@@ -91,6 +163,8 @@ class UpdateDestinationParams(HookbaseModel):
91
163
  rate_limit: int | None = None
92
164
  rate_limit_window: int | None = None
93
165
  is_active: bool | None = None
166
+ config: dict[str, Any] | None = None
167
+ field_mapping: list[FieldMapping] | None = None
94
168
 
95
169
 
96
170
  class TestResult(HookbaseModel):
@@ -0,0 +1,193 @@
1
+ from __future__ import annotations
2
+
3
+ import pytest
4
+ import respx
5
+
6
+ from hookbase import Hookbase
7
+ from hookbase.models import Destination, FieldMapping, TestResult
8
+
9
+ from ..conftest import make_paginated_response
10
+
11
+
12
+ @pytest.fixture
13
+ def mock_api():
14
+ with respx.mock(base_url="https://api.hookbase.app") as mock:
15
+ yield mock
16
+
17
+
18
+ @pytest.fixture
19
+ def client(mock_api):
20
+ c = Hookbase(api_key="whr_test")
21
+ yield c
22
+ c.close()
23
+
24
+
25
+ DEST_DATA = {
26
+ "id": "dst_1",
27
+ "organizationId": "org_1",
28
+ "name": "Backend",
29
+ "slug": "backend",
30
+ "type": "http",
31
+ "url": "https://example.com/webhooks",
32
+ "method": "POST",
33
+ "authType": "none",
34
+ "timeout": 30,
35
+ "retryCount": 3,
36
+ "retryInterval": 60,
37
+ "isActive": True,
38
+ "config": None,
39
+ "fieldMapping": None,
40
+ "deliveryCount": 100,
41
+ "createdAt": "2024-01-01T00:00:00Z",
42
+ "updatedAt": "2024-01-01T00:00:00Z",
43
+ }
44
+
45
+ S3_DEST_DATA = {
46
+ "id": "dst_s3",
47
+ "organizationId": "org_1",
48
+ "name": "S3 Archive",
49
+ "slug": "s3-archive",
50
+ "type": "s3",
51
+ "url": "",
52
+ "method": "POST",
53
+ "authType": "none",
54
+ "timeout": 30,
55
+ "retryCount": 3,
56
+ "retryInterval": 60,
57
+ "isActive": True,
58
+ "config": {
59
+ "bucket": "my-webhooks",
60
+ "region": "us-east-1",
61
+ "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
62
+ "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
63
+ "prefix": "events/",
64
+ "fileFormat": "jsonl",
65
+ "partitionBy": "date",
66
+ },
67
+ "fieldMapping": [
68
+ {"source": "$.id", "target": "event_id", "type": "string"},
69
+ {"source": "$.payload", "target": "data", "type": "json"},
70
+ {"source": "$.timestamp", "target": "received_at", "type": "timestamp", "default": "now()"},
71
+ ],
72
+ "deliveryCount": 0,
73
+ "createdAt": "2024-01-01T00:00:00Z",
74
+ "updatedAt": "2024-01-01T00:00:00Z",
75
+ }
76
+
77
+
78
+ def test_list_destinations(mock_api, client):
79
+ mock_api.get("/api/destinations").respond(200, json=make_paginated_response(
80
+ [DEST_DATA], data_key="destinations", total=1,
81
+ ))
82
+ page = client.destinations.list()
83
+ assert len(page) == 1
84
+ assert page.data[0].name == "Backend"
85
+
86
+
87
+ def test_get_destination(mock_api, client):
88
+ mock_api.get("/api/destinations/dst_1").respond(200, json={"destination": DEST_DATA})
89
+ dest = client.destinations.get("dst_1")
90
+ assert isinstance(dest, Destination)
91
+ assert dest.url == "https://example.com/webhooks"
92
+ assert dest.type == "http"
93
+ assert dest.config is None
94
+ assert dest.field_mapping is None
95
+
96
+
97
+ def test_get_warehouse_destination(mock_api, client):
98
+ mock_api.get("/api/destinations/dst_s3").respond(200, json={"destination": S3_DEST_DATA})
99
+ dest = client.destinations.get("dst_s3")
100
+ assert isinstance(dest, Destination)
101
+ assert dest.type == "s3"
102
+ assert dest.config is not None
103
+ assert dest.config["bucket"] == "my-webhooks"
104
+ assert dest.config["region"] == "us-east-1"
105
+ assert dest.field_mapping is not None
106
+ assert len(dest.field_mapping) == 3
107
+ assert dest.field_mapping[0].source == "$.id"
108
+ assert dest.field_mapping[0].target == "event_id"
109
+ assert dest.field_mapping[0].type == "string"
110
+ assert dest.field_mapping[2].default == "now()"
111
+
112
+
113
+ def test_create_destination(mock_api, client):
114
+ mock_api.post("/api/destinations").respond(200, json={"destination": DEST_DATA})
115
+ dest = client.destinations.create({"name": "Backend", "url": "https://example.com/webhooks"})
116
+ assert dest.id == "dst_1"
117
+ assert dest.type == "http"
118
+
119
+
120
+ def test_create_warehouse_destination(mock_api, client):
121
+ mock_api.post("/api/destinations").respond(200, json={"destination": S3_DEST_DATA})
122
+ dest = client.destinations.create({
123
+ "name": "S3 Archive",
124
+ "type": "s3",
125
+ "config": {
126
+ "bucket": "my-webhooks",
127
+ "region": "us-east-1",
128
+ "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
129
+ "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
130
+ },
131
+ "fieldMapping": [
132
+ {"source": "$.id", "target": "event_id", "type": "string"},
133
+ ],
134
+ })
135
+ assert dest.id == "dst_s3"
136
+ assert dest.type == "s3"
137
+ assert dest.config is not None
138
+ assert dest.field_mapping is not None
139
+
140
+
141
+ def test_update_warehouse_destination_config(mock_api, client):
142
+ updated = {**S3_DEST_DATA, "config": {**S3_DEST_DATA["config"], "bucket": "new-bucket"}}
143
+ mock_api.patch("/api/destinations/dst_s3").respond(200, json={"destination": updated})
144
+ client.destinations.update("dst_s3", {
145
+ "config": {
146
+ "bucket": "new-bucket",
147
+ "region": "us-east-1",
148
+ "accessKeyId": "AKIA",
149
+ "secretAccessKey": "secret",
150
+ },
151
+ "fieldMapping": [
152
+ {"source": "$.body", "target": "payload", "type": "json"},
153
+ ],
154
+ })
155
+
156
+
157
+ def test_field_mapping_model():
158
+ fm = FieldMapping(source="$.id", target="event_id", type="string")
159
+ assert fm.source == "$.id"
160
+ assert fm.target == "event_id"
161
+ assert fm.type == "string"
162
+ assert fm.default is None
163
+
164
+ fm_with_default = FieldMapping(
165
+ source="$.ts", target="timestamp", type="timestamp", default="now()",
166
+ )
167
+ assert fm_with_default.default == "now()"
168
+
169
+
170
+ def test_destination_without_type_defaults():
171
+ """Destinations without type field should default to http."""
172
+ data = {
173
+ "id": "dst_old",
174
+ "name": "Old Dest",
175
+ "slug": "old-dest",
176
+ "url": "https://example.com",
177
+ "method": "POST",
178
+ "isActive": True,
179
+ }
180
+ dest = Destination(**data)
181
+ assert dest.type == "http"
182
+ assert dest.config is None
183
+ assert dest.field_mapping is None
184
+
185
+
186
+ def test_test_destination(mock_api, client):
187
+ mock_api.post("/api/destinations/dst_1/test").respond(200, json={
188
+ "success": True, "statusCode": 200, "duration": 150.5,
189
+ })
190
+ result = client.destinations.test("dst_1")
191
+ assert isinstance(result, TestResult)
192
+ assert result.success is True
193
+ assert result.status_code == 200
@@ -1 +0,0 @@
1
- __version__ = "2.1.1"
@@ -1,72 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import pytest
4
- import respx
5
-
6
- from hookbase import Hookbase
7
- from hookbase.models import Destination, TestResult
8
-
9
- from ..conftest import make_paginated_response
10
-
11
-
12
- @pytest.fixture
13
- def mock_api():
14
- with respx.mock(base_url="https://api.hookbase.app") as mock:
15
- yield mock
16
-
17
-
18
- @pytest.fixture
19
- def client(mock_api):
20
- c = Hookbase(api_key="whr_test")
21
- yield c
22
- c.close()
23
-
24
-
25
- DEST_DATA = {
26
- "id": "dst_1",
27
- "organizationId": "org_1",
28
- "name": "Backend",
29
- "slug": "backend",
30
- "url": "https://example.com/webhooks",
31
- "method": "POST",
32
- "authType": "none",
33
- "timeout": 30,
34
- "retryCount": 3,
35
- "retryInterval": 60,
36
- "isActive": True,
37
- "deliveryCount": 100,
38
- "createdAt": "2024-01-01T00:00:00Z",
39
- "updatedAt": "2024-01-01T00:00:00Z",
40
- }
41
-
42
-
43
- def test_list_destinations(mock_api, client):
44
- mock_api.get("/api/destinations").respond(200, json=make_paginated_response(
45
- [DEST_DATA], data_key="destinations", total=1,
46
- ))
47
- page = client.destinations.list()
48
- assert len(page) == 1
49
- assert page.data[0].name == "Backend"
50
-
51
-
52
- def test_get_destination(mock_api, client):
53
- mock_api.get("/api/destinations/dst_1").respond(200, json={"destination": DEST_DATA})
54
- dest = client.destinations.get("dst_1")
55
- assert isinstance(dest, Destination)
56
- assert dest.url == "https://example.com/webhooks"
57
-
58
-
59
- def test_create_destination(mock_api, client):
60
- mock_api.post("/api/destinations").respond(200, json={"destination": DEST_DATA})
61
- dest = client.destinations.create({"name": "Backend", "url": "https://example.com/webhooks"})
62
- assert dest.id == "dst_1"
63
-
64
-
65
- def test_test_destination(mock_api, client):
66
- mock_api.post("/api/destinations/dst_1/test").respond(200, json={
67
- "success": True, "statusCode": 200, "duration": 150.5,
68
- })
69
- result = client.destinations.test("dst_1")
70
- assert isinstance(result, TestResult)
71
- assert result.success is True
72
- assert result.status_code == 200
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes