hookbase 2.0.0__tar.gz → 2.1.1__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.
- hookbase-2.1.1/.github/workflows/python-publish.yml +86 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/PKG-INFO +2 -1
- {hookbase-2.0.0 → hookbase-2.1.1}/pyproject.toml +1 -0
- hookbase-2.1.1/src/hookbase/_version.py +1 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/deliveries.py +4 -2
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/events.py +1 -1
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/portal_tokens.py +0 -2
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/sources.py +4 -1
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/resources/test_sources.py +79 -0
- hookbase-2.0.0/src/hookbase/_version.py +0 -1
- {hookbase-2.0.0 → hookbase-2.1.1}/.gitignore +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/LICENSE +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/README.md +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/examples/async_usage.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/examples/basic_inbound.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/examples/error_handling.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/examples/send_webhooks.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/examples/verify_webhooks.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/__init__.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/_client.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/_constants.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/_pagination.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/client.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/errors.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/__init__.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/_base.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/analytics.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/api_keys.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/applications.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/common.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/cron_jobs.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/destinations.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/dlq.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/endpoints.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/event_types.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/filters.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/messages.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/organizations.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/routes.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/schemas.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/subscriptions.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/transforms.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/models/tunnels.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/py.typed +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/__init__.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/_base.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/analytics.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/api_keys.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/applications.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/cron_jobs.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/deliveries.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/destinations.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/dlq.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/endpoints.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/event_types.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/events.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/filters.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/message_log.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/messages.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/organizations.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/portal_tokens.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/routes.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/schemas.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/sources.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/subscriptions.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/transforms.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/resources/tunnels.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/src/hookbase/webhook.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/__init__.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/conftest.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/resources/__init__.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/resources/test_applications.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/resources/test_destinations.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/resources/test_dlq.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/resources/test_messages.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/test_client.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/test_errors.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/test_pagination.py +0 -0
- {hookbase-2.0.0 → hookbase-2.1.1}/tests/test_webhook.py +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: CI / Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
pip install -e ".[dev]"
|
|
32
|
+
|
|
33
|
+
- name: Lint with ruff
|
|
34
|
+
run: ruff check src/ tests/
|
|
35
|
+
|
|
36
|
+
- name: Type check with mypy
|
|
37
|
+
run: mypy src/hookbase
|
|
38
|
+
|
|
39
|
+
- name: Run tests
|
|
40
|
+
run: pytest -v
|
|
41
|
+
|
|
42
|
+
release-build:
|
|
43
|
+
if: github.event_name == 'release'
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
needs: test
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
|
|
50
|
+
- uses: actions/setup-python@v5
|
|
51
|
+
with:
|
|
52
|
+
python-version: "3.x"
|
|
53
|
+
|
|
54
|
+
- name: Build release distributions
|
|
55
|
+
run: |
|
|
56
|
+
python -m pip install build
|
|
57
|
+
python -m build
|
|
58
|
+
|
|
59
|
+
- name: Upload distributions
|
|
60
|
+
uses: actions/upload-artifact@v4
|
|
61
|
+
with:
|
|
62
|
+
name: release-dists
|
|
63
|
+
path: dist/
|
|
64
|
+
|
|
65
|
+
pypi-publish:
|
|
66
|
+
if: github.event_name == 'release'
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
needs: release-build
|
|
69
|
+
permissions:
|
|
70
|
+
id-token: write
|
|
71
|
+
|
|
72
|
+
environment:
|
|
73
|
+
name: pypi
|
|
74
|
+
url: https://pypi.org/project/hookbase/${{ github.event.release.tag_name }}
|
|
75
|
+
|
|
76
|
+
steps:
|
|
77
|
+
- name: Retrieve release distributions
|
|
78
|
+
uses: actions/download-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: release-dists
|
|
81
|
+
path: dist/
|
|
82
|
+
|
|
83
|
+
- name: Publish release distributions to PyPI
|
|
84
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
85
|
+
with:
|
|
86
|
+
packages-dir: dist/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hookbase
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
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
|
|
@@ -22,6 +22,7 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Classifier: Typing :: Typed
|
|
24
24
|
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: eval-type-backport>=0.2.0; python_version < '3.10'
|
|
25
26
|
Requires-Dist: httpx<1.0.0,>=0.25.0
|
|
26
27
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
27
28
|
Provides-Extra: dev
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.1"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from ._base import HookbaseModel
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# API may return: pending, success, failed, retrying, delivered,
|
|
8
|
+
# failed_over, schema_failed, exhausted, etc.
|
|
9
|
+
DeliveryStatus = str
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class Delivery(HookbaseModel):
|
|
@@ -34,12 +34,13 @@ class Source(HookbaseModel):
|
|
|
34
34
|
ip_denylist: list[str] | None = None
|
|
35
35
|
rate_limit: int | None = None
|
|
36
36
|
rate_limit_window: int | None = None
|
|
37
|
+
transient_mode: bool = False
|
|
37
38
|
event_count: int = 0
|
|
38
39
|
last_event_at: str | None = None
|
|
39
40
|
created_at: str = ""
|
|
40
41
|
updated_at: str = ""
|
|
41
42
|
|
|
42
|
-
@field_validator("is_active", "verify_signature", mode="before")
|
|
43
|
+
@field_validator("is_active", "verify_signature", "transient_mode", mode="before")
|
|
43
44
|
@classmethod
|
|
44
45
|
def parse_bool_from_int(cls, v: Any) -> Any:
|
|
45
46
|
if isinstance(v, int):
|
|
@@ -76,6 +77,7 @@ class CreateSourceParams(HookbaseModel):
|
|
|
76
77
|
ip_denylist: list[str] | None = None
|
|
77
78
|
rate_limit: int | None = None
|
|
78
79
|
rate_limit_window: int | None = None
|
|
80
|
+
transient_mode: bool | None = None
|
|
79
81
|
|
|
80
82
|
|
|
81
83
|
class UpdateSourceParams(HookbaseModel):
|
|
@@ -91,3 +93,4 @@ class UpdateSourceParams(HookbaseModel):
|
|
|
91
93
|
ip_denylist: list[str] | None = None
|
|
92
94
|
rate_limit: int | None = None
|
|
93
95
|
rate_limit_window: int | None = None
|
|
96
|
+
transient_mode: bool | None = None
|
|
@@ -5,6 +5,7 @@ import respx
|
|
|
5
5
|
|
|
6
6
|
from hookbase import Hookbase
|
|
7
7
|
from hookbase.models import Source, SourceWithSecret
|
|
8
|
+
from hookbase.models.sources import CreateSourceParams, UpdateSourceParams
|
|
8
9
|
|
|
9
10
|
from ..conftest import make_paginated_response
|
|
10
11
|
|
|
@@ -110,3 +111,81 @@ def test_import_sources(mock_api, client):
|
|
|
110
111
|
})
|
|
111
112
|
result = client.sources.import_sources([SOURCE_DATA])
|
|
112
113
|
assert result.imported == 1
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# --- transient_mode tests ---
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_source_transient_mode_defaults_false():
|
|
120
|
+
src = Source(**SOURCE_DATA)
|
|
121
|
+
assert src.transient_mode is False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_source_transient_mode_true():
|
|
125
|
+
data = {**SOURCE_DATA, "transientMode": True}
|
|
126
|
+
src = Source(**data)
|
|
127
|
+
assert src.transient_mode is True
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_source_transient_mode_from_int():
|
|
131
|
+
"""D1/SQLite returns booleans as 0/1 integers."""
|
|
132
|
+
data = {**SOURCE_DATA, "transientMode": 1}
|
|
133
|
+
src = Source(**data)
|
|
134
|
+
assert src.transient_mode is True
|
|
135
|
+
|
|
136
|
+
data_off = {**SOURCE_DATA, "transientMode": 0}
|
|
137
|
+
src_off = Source(**data_off)
|
|
138
|
+
assert src_off.transient_mode is False
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_source_transient_mode_serializes_as_camel():
|
|
142
|
+
src = Source(**{**SOURCE_DATA, "transientMode": True})
|
|
143
|
+
dumped = src.model_dump(by_alias=True)
|
|
144
|
+
assert "transientMode" in dumped
|
|
145
|
+
assert dumped["transientMode"] is True
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_create_source_with_transient_mode(mock_api, client):
|
|
149
|
+
resp_data = {**SOURCE_DATA, "transientMode": True, "signingSecret": "whsec_abc"}
|
|
150
|
+
mock_api.post("/api/sources").respond(200, json={"source": resp_data})
|
|
151
|
+
src = client.sources.create({
|
|
152
|
+
"name": "HIPAA Source",
|
|
153
|
+
"slug": "hipaa",
|
|
154
|
+
"transient_mode": True,
|
|
155
|
+
})
|
|
156
|
+
assert isinstance(src, SourceWithSecret)
|
|
157
|
+
assert src.transient_mode is True
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_create_source_params_serialization():
|
|
161
|
+
params = CreateSourceParams(name="test", transient_mode=True)
|
|
162
|
+
dumped = params.model_dump(by_alias=True, exclude_none=True)
|
|
163
|
+
assert dumped["transientMode"] is True
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_update_source_with_transient_mode(mock_api, client):
|
|
167
|
+
mock_api.patch("/api/sources/src_1").respond(204)
|
|
168
|
+
client.sources.update("src_1", {"transient_mode": True})
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_update_source_params_serialization():
|
|
172
|
+
params = UpdateSourceParams(transient_mode=True)
|
|
173
|
+
dumped = params.model_dump(by_alias=True, exclude_none=True)
|
|
174
|
+
assert dumped["transientMode"] is True
|
|
175
|
+
assert "name" not in dumped
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_get_source_with_transient_mode(mock_api, client):
|
|
179
|
+
data = {**SOURCE_DATA, "transientMode": True}
|
|
180
|
+
mock_api.get("/api/sources/src_1").respond(200, json={"source": data})
|
|
181
|
+
src = client.sources.get("src_1")
|
|
182
|
+
assert src.transient_mode is True
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_list_sources_with_transient_mode(mock_api, client):
|
|
186
|
+
data = {**SOURCE_DATA, "transientMode": True}
|
|
187
|
+
mock_api.get("/api/sources").respond(200, json=make_paginated_response(
|
|
188
|
+
[data], data_key="sources", total=1,
|
|
189
|
+
))
|
|
190
|
+
page = client.sources.list()
|
|
191
|
+
assert page.data[0].transient_mode is True
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.0.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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|