hookbase 2.3.0__tar.gz → 2.5.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.3.0 → hookbase-2.5.0}/PKG-INFO +2 -2
  2. {hookbase-2.3.0 → hookbase-2.5.0}/pyproject.toml +1 -1
  3. hookbase-2.5.0/src/hookbase/_version.py +1 -0
  4. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/cron_jobs.py +3 -0
  5. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/destinations.py +6 -0
  6. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/sources.py +23 -0
  7. {hookbase-2.3.0 → hookbase-2.5.0}/tests/resources/test_sources.py +46 -0
  8. hookbase-2.3.0/src/hookbase/_version.py +0 -1
  9. {hookbase-2.3.0 → hookbase-2.5.0}/.github/workflows/python-publish.yml +0 -0
  10. {hookbase-2.3.0 → hookbase-2.5.0}/.gitignore +0 -0
  11. {hookbase-2.3.0 → hookbase-2.5.0}/LICENSE +0 -0
  12. {hookbase-2.3.0 → hookbase-2.5.0}/README.md +0 -0
  13. {hookbase-2.3.0 → hookbase-2.5.0}/examples/async_usage.py +0 -0
  14. {hookbase-2.3.0 → hookbase-2.5.0}/examples/basic_inbound.py +0 -0
  15. {hookbase-2.3.0 → hookbase-2.5.0}/examples/error_handling.py +0 -0
  16. {hookbase-2.3.0 → hookbase-2.5.0}/examples/send_webhooks.py +0 -0
  17. {hookbase-2.3.0 → hookbase-2.5.0}/examples/verify_webhooks.py +0 -0
  18. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/__init__.py +0 -0
  19. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/_client.py +0 -0
  20. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/_constants.py +0 -0
  21. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/_pagination.py +0 -0
  22. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/client.py +0 -0
  23. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/errors.py +0 -0
  24. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/__init__.py +0 -0
  25. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/_base.py +0 -0
  26. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/analytics.py +0 -0
  27. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/api_keys.py +0 -0
  28. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/applications.py +0 -0
  29. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/common.py +0 -0
  30. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/deliveries.py +0 -0
  31. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/dlq.py +0 -0
  32. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/endpoints.py +0 -0
  33. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/event_types.py +0 -0
  34. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/events.py +0 -0
  35. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/filters.py +0 -0
  36. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/messages.py +0 -0
  37. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/organizations.py +0 -0
  38. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/portal_tokens.py +0 -0
  39. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/routes.py +0 -0
  40. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/schemas.py +0 -0
  41. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/subscriptions.py +0 -0
  42. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/transforms.py +0 -0
  43. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/models/tunnels.py +0 -0
  44. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/py.typed +0 -0
  45. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/__init__.py +0 -0
  46. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/_base.py +0 -0
  47. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/analytics.py +0 -0
  48. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/api_keys.py +0 -0
  49. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/applications.py +0 -0
  50. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/cron_jobs.py +0 -0
  51. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/deliveries.py +0 -0
  52. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/destinations.py +0 -0
  53. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/dlq.py +0 -0
  54. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/endpoints.py +0 -0
  55. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/event_types.py +0 -0
  56. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/events.py +0 -0
  57. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/filters.py +0 -0
  58. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/message_log.py +0 -0
  59. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/messages.py +0 -0
  60. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/organizations.py +0 -0
  61. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/portal_tokens.py +0 -0
  62. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/routes.py +0 -0
  63. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/schemas.py +0 -0
  64. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/sources.py +0 -0
  65. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/subscriptions.py +0 -0
  66. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/transforms.py +0 -0
  67. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/resources/tunnels.py +0 -0
  68. {hookbase-2.3.0 → hookbase-2.5.0}/src/hookbase/webhook.py +0 -0
  69. {hookbase-2.3.0 → hookbase-2.5.0}/tests/__init__.py +0 -0
  70. {hookbase-2.3.0 → hookbase-2.5.0}/tests/conftest.py +0 -0
  71. {hookbase-2.3.0 → hookbase-2.5.0}/tests/resources/__init__.py +0 -0
  72. {hookbase-2.3.0 → hookbase-2.5.0}/tests/resources/test_applications.py +0 -0
  73. {hookbase-2.3.0 → hookbase-2.5.0}/tests/resources/test_destinations.py +0 -0
  74. {hookbase-2.3.0 → hookbase-2.5.0}/tests/resources/test_dlq.py +0 -0
  75. {hookbase-2.3.0 → hookbase-2.5.0}/tests/resources/test_messages.py +0 -0
  76. {hookbase-2.3.0 → hookbase-2.5.0}/tests/test_client.py +0 -0
  77. {hookbase-2.3.0 → hookbase-2.5.0}/tests/test_errors.py +0 -0
  78. {hookbase-2.3.0 → hookbase-2.5.0}/tests/test_pagination.py +0 -0
  79. {hookbase-2.3.0 → hookbase-2.5.0}/tests/test_webhook.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hookbase
3
- Version: 2.3.0
3
+ Version: 2.5.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.5.0"
@@ -19,6 +19,7 @@ class CronJob(HookbaseModel):
19
19
  schedule: str = ""
20
20
  timezone: str = "UTC"
21
21
  is_active: bool = True
22
+ use_static_ip: bool = True
22
23
  group_id: str | None = None
23
24
  last_run_at: str | None = None
24
25
  next_run_at: str | None = None
@@ -49,6 +50,7 @@ class CreateCronJobParams(HookbaseModel):
49
50
  schedule: str
50
51
  timezone: str | None = None
51
52
  is_active: bool | None = None
53
+ use_static_ip: bool | None = None
52
54
  group_id: str | None = None
53
55
 
54
56
 
@@ -62,6 +64,7 @@ class UpdateCronJobParams(HookbaseModel):
62
64
  schedule: str | None = None
63
65
  timezone: str | None = None
64
66
  is_active: bool | None = None
67
+ use_static_ip: bool | None = None
65
68
  group_id: str | None = None
66
69
 
67
70
 
@@ -78,6 +78,8 @@ class Destination(HookbaseModel):
78
78
  use_static_ip: bool = True
79
79
  config: dict[str, Any] | None = None
80
80
  field_mapping: list[FieldMapping] | None = None
81
+ batch_size: int | None = None
82
+ batch_window_seconds: int | None = None
81
83
  delivery_count: int = 0
82
84
  last_delivery_at: str | None = None
83
85
  created_at: str = ""
@@ -149,6 +151,8 @@ class CreateDestinationParams(HookbaseModel):
149
151
  config: dict[str, Any] | None = None
150
152
  field_mapping: list[FieldMapping] | None = None
151
153
  use_static_ip: bool | None = None
154
+ batch_size: int | None = None
155
+ batch_window_seconds: int | None = None
152
156
 
153
157
 
154
158
  class UpdateDestinationParams(HookbaseModel):
@@ -168,6 +172,8 @@ class UpdateDestinationParams(HookbaseModel):
168
172
  config: dict[str, Any] | None = None
169
173
  field_mapping: list[FieldMapping] | None = None
170
174
  use_static_ip: bool | None = None
175
+ batch_size: int | None = None
176
+ batch_window_seconds: int | None = None
171
177
 
172
178
 
173
179
  class TestResult(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.3.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