hookbase 2.2.0__tar.gz → 2.4.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 (79) hide show
  1. {hookbase-2.2.0 → hookbase-2.4.0}/PKG-INFO +2 -2
  2. {hookbase-2.2.0 → hookbase-2.4.0}/pyproject.toml +1 -1
  3. hookbase-2.4.0/src/hookbase/_version.py +1 -0
  4. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/destinations.py +9 -0
  5. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/endpoints.py +3 -0
  6. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/sources.py +23 -0
  7. {hookbase-2.2.0 → hookbase-2.4.0}/tests/resources/test_sources.py +46 -0
  8. hookbase-2.2.0/src/hookbase/_version.py +0 -1
  9. {hookbase-2.2.0 → hookbase-2.4.0}/.github/workflows/python-publish.yml +0 -0
  10. {hookbase-2.2.0 → hookbase-2.4.0}/.gitignore +0 -0
  11. {hookbase-2.2.0 → hookbase-2.4.0}/LICENSE +0 -0
  12. {hookbase-2.2.0 → hookbase-2.4.0}/README.md +0 -0
  13. {hookbase-2.2.0 → hookbase-2.4.0}/examples/async_usage.py +0 -0
  14. {hookbase-2.2.0 → hookbase-2.4.0}/examples/basic_inbound.py +0 -0
  15. {hookbase-2.2.0 → hookbase-2.4.0}/examples/error_handling.py +0 -0
  16. {hookbase-2.2.0 → hookbase-2.4.0}/examples/send_webhooks.py +0 -0
  17. {hookbase-2.2.0 → hookbase-2.4.0}/examples/verify_webhooks.py +0 -0
  18. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/__init__.py +0 -0
  19. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/_client.py +0 -0
  20. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/_constants.py +0 -0
  21. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/_pagination.py +0 -0
  22. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/client.py +0 -0
  23. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/errors.py +0 -0
  24. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/__init__.py +0 -0
  25. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/_base.py +0 -0
  26. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/analytics.py +0 -0
  27. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/api_keys.py +0 -0
  28. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/applications.py +0 -0
  29. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/common.py +0 -0
  30. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/cron_jobs.py +0 -0
  31. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/deliveries.py +0 -0
  32. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/dlq.py +0 -0
  33. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/event_types.py +0 -0
  34. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/events.py +0 -0
  35. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/filters.py +0 -0
  36. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/messages.py +0 -0
  37. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/organizations.py +0 -0
  38. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/portal_tokens.py +0 -0
  39. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/routes.py +0 -0
  40. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/schemas.py +0 -0
  41. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/subscriptions.py +0 -0
  42. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/transforms.py +0 -0
  43. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/models/tunnels.py +0 -0
  44. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/py.typed +0 -0
  45. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/__init__.py +0 -0
  46. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/_base.py +0 -0
  47. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/analytics.py +0 -0
  48. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/api_keys.py +0 -0
  49. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/applications.py +0 -0
  50. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/cron_jobs.py +0 -0
  51. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/deliveries.py +0 -0
  52. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/destinations.py +0 -0
  53. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/dlq.py +0 -0
  54. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/endpoints.py +0 -0
  55. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/event_types.py +0 -0
  56. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/events.py +0 -0
  57. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/filters.py +0 -0
  58. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/message_log.py +0 -0
  59. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/messages.py +0 -0
  60. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/organizations.py +0 -0
  61. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/portal_tokens.py +0 -0
  62. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/routes.py +0 -0
  63. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/schemas.py +0 -0
  64. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/sources.py +0 -0
  65. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/subscriptions.py +0 -0
  66. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/transforms.py +0 -0
  67. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/resources/tunnels.py +0 -0
  68. {hookbase-2.2.0 → hookbase-2.4.0}/src/hookbase/webhook.py +0 -0
  69. {hookbase-2.2.0 → hookbase-2.4.0}/tests/__init__.py +0 -0
  70. {hookbase-2.2.0 → hookbase-2.4.0}/tests/conftest.py +0 -0
  71. {hookbase-2.2.0 → hookbase-2.4.0}/tests/resources/__init__.py +0 -0
  72. {hookbase-2.2.0 → hookbase-2.4.0}/tests/resources/test_applications.py +0 -0
  73. {hookbase-2.2.0 → hookbase-2.4.0}/tests/resources/test_destinations.py +0 -0
  74. {hookbase-2.2.0 → hookbase-2.4.0}/tests/resources/test_dlq.py +0 -0
  75. {hookbase-2.2.0 → hookbase-2.4.0}/tests/resources/test_messages.py +0 -0
  76. {hookbase-2.2.0 → hookbase-2.4.0}/tests/test_client.py +0 -0
  77. {hookbase-2.2.0 → hookbase-2.4.0}/tests/test_errors.py +0 -0
  78. {hookbase-2.2.0 → hookbase-2.4.0}/tests/test_pagination.py +0 -0
  79. {hookbase-2.2.0 → hookbase-2.4.0}/tests/test_webhook.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hookbase
3
- Version: 2.2.0
3
+ Version: 2.4.0
4
4
  Summary: Official Python SDK for the Hookbase webhook management platform
5
5
  Project-URL: Homepage, https://hookbase.app
6
- Project-URL: Documentation, https://docs.hookbase.app/sdk/python
6
+ Project-URL: Documentation, https://www.hookbase.app/docs/send/sdk
7
7
  Project-URL: Repository, https://github.com/hookbase/hookbase-python
8
8
  Project-URL: Issues, https://github.com/hookbase/hookbase-python/issues
9
9
  Author-email: Hookbase <support@hookbase.app>
@@ -43,7 +43,7 @@ dev = [
43
43
 
44
44
  [project.urls]
45
45
  Homepage = "https://hookbase.app"
46
- Documentation = "https://docs.hookbase.app/sdk/python"
46
+ Documentation = "https://www.hookbase.app/docs/send/sdk"
47
47
  Repository = "https://github.com/hookbase/hookbase-python"
48
48
  Issues = "https://github.com/hookbase/hookbase-python/issues"
49
49
 
@@ -0,0 +1 @@
1
+ __version__ = "2.4.0"
@@ -75,8 +75,11 @@ class Destination(HookbaseModel):
75
75
  rate_limit: int | None = None
76
76
  rate_limit_window: int | None = None
77
77
  is_active: bool = True
78
+ use_static_ip: bool = True
78
79
  config: dict[str, Any] | None = None
79
80
  field_mapping: list[FieldMapping] | None = None
81
+ batch_size: int | None = None
82
+ batch_window_seconds: int | None = None
80
83
  delivery_count: int = 0
81
84
  last_delivery_at: str | None = None
82
85
  created_at: str = ""
@@ -147,6 +150,9 @@ class CreateDestinationParams(HookbaseModel):
147
150
  rate_limit_window: int | None = None
148
151
  config: dict[str, Any] | None = None
149
152
  field_mapping: list[FieldMapping] | None = None
153
+ use_static_ip: bool | None = None
154
+ batch_size: int | None = None
155
+ batch_window_seconds: int | None = None
150
156
 
151
157
 
152
158
  class UpdateDestinationParams(HookbaseModel):
@@ -165,6 +171,9 @@ class UpdateDestinationParams(HookbaseModel):
165
171
  is_active: bool | None = None
166
172
  config: dict[str, Any] | None = None
167
173
  field_mapping: list[FieldMapping] | None = None
174
+ use_static_ip: bool | None = None
175
+ batch_size: int | None = None
176
+ batch_window_seconds: int | None = None
168
177
 
169
178
 
170
179
  class TestResult(HookbaseModel):
@@ -30,6 +30,7 @@ class WebhookEndpoint(HookbaseModel):
30
30
  return {}
31
31
  return v
32
32
  metadata: dict[str, Any] | None = None
33
+ use_static_ip: bool = True
33
34
  total_messages: int = 0
34
35
  total_successes: int = 0
35
36
  total_failures: int = 0
@@ -49,6 +50,7 @@ class CreateEndpointParams(HookbaseModel):
49
50
  rate_limit_period: int | None = None
50
51
  headers: dict[str, str] | None = None
51
52
  metadata: dict[str, Any] | None = None
53
+ use_static_ip: bool | None = None
52
54
 
53
55
 
54
56
  class UpdateEndpointParams(HookbaseModel):
@@ -60,6 +62,7 @@ class UpdateEndpointParams(HookbaseModel):
60
62
  rate_limit_period: int | None = None
61
63
  headers: dict[str, str] | None = None
62
64
  metadata: dict[str, Any] | None = None
65
+ use_static_ip: bool | None = None
63
66
 
64
67
 
65
68
  class EndpointStats(HookbaseModel):
@@ -13,6 +13,9 @@ SourceProvider = Literal[
13
13
  ]
14
14
  DedupStrategy = Literal["none", "header", "payload_hash", "event_id"]
15
15
  IpFilterMode = Literal["none", "allowlist", "denylist"]
16
+ # HTTP verbs an ingest endpoint can be restricted to. OPTIONS is excluded: CORS preflight is
17
+ # answered before ingest runs, so it is never gateable.
18
+ IngestMethod = Literal["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]
16
19
 
17
20
 
18
21
  class Source(HookbaseModel):
@@ -35,6 +38,8 @@ class Source(HookbaseModel):
35
38
  rate_limit: int | None = None
36
39
  rate_limit_window: int | None = None
37
40
  transient_mode: bool = False
41
+ #: HTTP verbs the ingest endpoint accepts. Empty list means any method.
42
+ allowed_methods: list[str] = []
38
43
  event_count: int = 0
39
44
  last_event_at: str | None = None
40
45
  created_at: str = ""
@@ -58,6 +63,20 @@ class Source(HookbaseModel):
58
63
  return None
59
64
  return v
60
65
 
66
+ @field_validator("allowed_methods", mode="before")
67
+ @classmethod
68
+ def parse_allowed_methods(cls, v: Any) -> Any:
69
+ """Coerce null/JSON-string forms to a list; both mean "any method" when empty."""
70
+ if v is None:
71
+ return []
72
+ if isinstance(v, str):
73
+ try:
74
+ parsed = json.loads(v)
75
+ return parsed if isinstance(parsed, list) else []
76
+ except (json.JSONDecodeError, ValueError):
77
+ return []
78
+ return v
79
+
61
80
 
62
81
  class SourceWithSecret(Source):
63
82
  signing_secret: str = ""
@@ -78,6 +97,8 @@ class CreateSourceParams(HookbaseModel):
78
97
  rate_limit: int | None = None
79
98
  rate_limit_window: int | None = None
80
99
  transient_mode: bool | None = None
100
+ #: Restrict the ingest endpoint to these verbs. None/[] accepts any method.
101
+ allowed_methods: list[IngestMethod] | None = None
81
102
 
82
103
 
83
104
  class UpdateSourceParams(HookbaseModel):
@@ -94,3 +115,5 @@ class UpdateSourceParams(HookbaseModel):
94
115
  rate_limit: int | None = None
95
116
  rate_limit_window: int | None = None
96
117
  transient_mode: bool | None = None
118
+ #: Restrict the ingest endpoint to these verbs. Pass [] to accept any method again.
119
+ allowed_methods: list[IngestMethod] | None = None
@@ -189,3 +189,49 @@ def test_list_sources_with_transient_mode(mock_api, client):
189
189
  ))
190
190
  page = client.sources.list()
191
191
  assert page.data[0].transient_mode is True
192
+
193
+
194
+ # --- allowed_methods tests ---
195
+
196
+
197
+ def test_source_allowed_methods_defaults_empty():
198
+ """No restriction is the default; [] means the endpoint accepts any HTTP method."""
199
+ src = Source(**SOURCE_DATA)
200
+ assert src.allowed_methods == []
201
+
202
+
203
+ def test_source_allowed_methods_from_list():
204
+ data = {**SOURCE_DATA, "allowedMethods": ["GET", "POST"]}
205
+ src = Source(**data)
206
+ assert src.allowed_methods == ["GET", "POST"]
207
+
208
+
209
+ def test_source_allowed_methods_from_json_string():
210
+ """The column is JSONB projected as text, so it can arrive as a JSON string."""
211
+ data = {**SOURCE_DATA, "allowedMethods": '["POST"]'}
212
+ src = Source(**data)
213
+ assert src.allowed_methods == ["POST"]
214
+
215
+
216
+ def test_source_allowed_methods_null_becomes_empty():
217
+ """NULL and [] both mean "any method" — normalize so callers only handle a list."""
218
+ data = {**SOURCE_DATA, "allowedMethods": None}
219
+ src = Source(**data)
220
+ assert src.allowed_methods == []
221
+
222
+
223
+ def test_source_allowed_methods_malformed_fails_open():
224
+ data = {**SOURCE_DATA, "allowedMethods": "not json"}
225
+ src = Source(**data)
226
+ assert src.allowed_methods == []
227
+
228
+
229
+ def test_create_source_params_serializes_allowed_methods():
230
+ params = CreateSourceParams(name="s", allowed_methods=["GET", "POST"])
231
+ assert params.model_dump(by_alias=True, exclude_none=True)["allowedMethods"] == ["GET", "POST"]
232
+
233
+
234
+ def test_update_source_params_empty_list_is_sent():
235
+ """[] must survive serialization — it is how a source reverts to accepting any method."""
236
+ params = UpdateSourceParams(allowed_methods=[])
237
+ assert params.model_dump(by_alias=True, exclude_none=True)["allowedMethods"] == []
@@ -1 +0,0 @@
1
- __version__ = "2.2.0"
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