webhook-platform 2.0.0__tar.gz → 2.2.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.
Files changed (18) hide show
  1. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/PKG-INFO +59 -1
  2. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/README.md +58 -0
  3. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/hookflow/__init__.py +1 -1
  4. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/hookflow/client.py +26 -1
  5. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/pyproject.toml +1 -1
  6. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/tests/test_client.py +29 -0
  7. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/webhook_platform.egg-info/PKG-INFO +59 -1
  8. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/hookflow/errors.py +0 -0
  9. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/hookflow/types.py +0 -0
  10. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/hookflow/webhooks.py +0 -0
  11. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/setup.cfg +0 -0
  12. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/tests/__init__.py +0 -0
  13. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/tests/test_incoming.py +0 -0
  14. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/tests/test_webhooks.py +0 -0
  15. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/webhook_platform.egg-info/SOURCES.txt +0 -0
  16. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/webhook_platform.egg-info/dependency_links.txt +0 -0
  17. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/webhook_platform.egg-info/requires.txt +0 -0
  18. {webhook_platform-2.0.0 → webhook_platform-2.2.1}/webhook_platform.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webhook-platform
3
- Version: 2.0.0
3
+ Version: 2.2.1
4
4
  Summary: Official Python SDK for Hookflow — reliable webhook infrastructure
5
5
  Author-email: Vadym Kykalo <vadymkykalo@gmail.com>
6
6
  License: MIT
@@ -338,6 +338,64 @@ except HookflowError as e:
338
338
  print(f"Error {e.status}: {e.message}")
339
339
  ```
340
340
 
341
+ ### Error Response Format
342
+
343
+ All API errors return a consistent JSON body:
344
+
345
+ ```json
346
+ {
347
+ "error": "error_code",
348
+ "message": "Human-readable description",
349
+ "status": 400,
350
+ "fieldErrors": { "field": "reason" }
351
+ }
352
+ ```
353
+
354
+ - **`error`** — machine-readable error code (`snake_case`), always present
355
+ - **`message`** — human-readable description, always present
356
+ - **`status`** — HTTP status code (integer), always present
357
+ - **`fieldErrors`** — field-level validation details (only present for `validation_error`)
358
+
359
+ ### Error Codes Reference
360
+
361
+ | HTTP Status | `error` Code | SDK Exception | Description |
362
+ |---|---|---|---|
363
+ | 400 | `validation_error` | `ValidationError` | Invalid request parameters; see `fieldErrors` |
364
+ | 400 | `invalid_request` | `HookflowError` | Malformed or semantically invalid request |
365
+ | 401 | `unauthorized` | `AuthenticationError` | Missing or invalid API key / expired token |
366
+ | 403 | `forbidden` | `HookflowError` | Insufficient permissions for the action |
367
+ | 404 | `not_found` | `NotFoundError` | Requested resource does not exist |
368
+ | 413 | `payload_too_large` | `HookflowError` | Request body exceeds maximum allowed size |
369
+ | 422 | `unprocessable_entity` | `HookflowError` | Valid syntax but violates business rules |
370
+ | 429 | `rate_limit_exceeded` | `RateLimitError` | Too many requests; check `X-RateLimit-*` headers |
371
+ | 500 | `internal_error` | `HookflowError` | Unexpected server error |
372
+
373
+ ## Generic Requests
374
+
375
+ As the API expands, you can call any endpoint directly without waiting for SDK updates:
376
+
377
+ ```python
378
+ # GET
379
+ schemas = client.get("/api/v1/projects/proj_123/schemas")
380
+
381
+ # GET with query params
382
+ items = client.get("/api/v1/projects/proj_123/items", params={"status": "active"})
383
+
384
+ # POST with body and idempotency key
385
+ result = client.post("/api/v1/some/new/endpoint", body={"key": "value"}, idempotency_key="unique-key")
386
+
387
+ # PUT
388
+ client.put("/api/v1/projects/proj_123/settings", body={"timezone": "UTC"})
389
+
390
+ # PATCH
391
+ client.patch("/api/v1/projects/proj_123/settings", body={"timezone": "UTC"})
392
+
393
+ # DELETE
394
+ client.delete("/api/v1/projects/proj_123/tags/old-tag")
395
+ ```
396
+
397
+ All generic methods use the same authentication, error handling, and rate-limit logic as the built-in methods.
398
+
341
399
  ## Configuration
342
400
 
343
401
  ```python
@@ -309,6 +309,64 @@ except HookflowError as e:
309
309
  print(f"Error {e.status}: {e.message}")
310
310
  ```
311
311
 
312
+ ### Error Response Format
313
+
314
+ All API errors return a consistent JSON body:
315
+
316
+ ```json
317
+ {
318
+ "error": "error_code",
319
+ "message": "Human-readable description",
320
+ "status": 400,
321
+ "fieldErrors": { "field": "reason" }
322
+ }
323
+ ```
324
+
325
+ - **`error`** — machine-readable error code (`snake_case`), always present
326
+ - **`message`** — human-readable description, always present
327
+ - **`status`** — HTTP status code (integer), always present
328
+ - **`fieldErrors`** — field-level validation details (only present for `validation_error`)
329
+
330
+ ### Error Codes Reference
331
+
332
+ | HTTP Status | `error` Code | SDK Exception | Description |
333
+ |---|---|---|---|
334
+ | 400 | `validation_error` | `ValidationError` | Invalid request parameters; see `fieldErrors` |
335
+ | 400 | `invalid_request` | `HookflowError` | Malformed or semantically invalid request |
336
+ | 401 | `unauthorized` | `AuthenticationError` | Missing or invalid API key / expired token |
337
+ | 403 | `forbidden` | `HookflowError` | Insufficient permissions for the action |
338
+ | 404 | `not_found` | `NotFoundError` | Requested resource does not exist |
339
+ | 413 | `payload_too_large` | `HookflowError` | Request body exceeds maximum allowed size |
340
+ | 422 | `unprocessable_entity` | `HookflowError` | Valid syntax but violates business rules |
341
+ | 429 | `rate_limit_exceeded` | `RateLimitError` | Too many requests; check `X-RateLimit-*` headers |
342
+ | 500 | `internal_error` | `HookflowError` | Unexpected server error |
343
+
344
+ ## Generic Requests
345
+
346
+ As the API expands, you can call any endpoint directly without waiting for SDK updates:
347
+
348
+ ```python
349
+ # GET
350
+ schemas = client.get("/api/v1/projects/proj_123/schemas")
351
+
352
+ # GET with query params
353
+ items = client.get("/api/v1/projects/proj_123/items", params={"status": "active"})
354
+
355
+ # POST with body and idempotency key
356
+ result = client.post("/api/v1/some/new/endpoint", body={"key": "value"}, idempotency_key="unique-key")
357
+
358
+ # PUT
359
+ client.put("/api/v1/projects/proj_123/settings", body={"timezone": "UTC"})
360
+
361
+ # PATCH
362
+ client.patch("/api/v1/projects/proj_123/settings", body={"timezone": "UTC"})
363
+
364
+ # DELETE
365
+ client.delete("/api/v1/projects/proj_123/tags/old-tag")
366
+ ```
367
+
368
+ All generic methods use the same authentication, error handling, and rate-limit logic as the built-in methods.
369
+
312
370
  ## Configuration
313
371
 
314
372
  ```python
@@ -36,7 +36,7 @@ from .types import (
36
36
  ReplayEventResponse,
37
37
  )
38
38
 
39
- __version__ = "2.0.0"
39
+ __version__ = "2.2.1"
40
40
 
41
41
  # Backward-compatible aliases
42
42
  WebhookPlatform = Hookflow
@@ -37,7 +37,7 @@ from .errors import (
37
37
 
38
38
  DEFAULT_BASE_URL = "http://localhost:8080"
39
39
  DEFAULT_TIMEOUT = 30
40
- SDK_VERSION = "2.0.0"
40
+ SDK_VERSION = "2.2.1"
41
41
 
42
42
 
43
43
  class Hookflow:
@@ -63,6 +63,31 @@ class Hookflow:
63
63
  self.incoming_sources = IncomingSources(self)
64
64
  self.incoming_events = IncomingEventsApi(self)
65
65
 
66
+ def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
67
+ """Generic GET request. Use for endpoints not yet covered by the SDK."""
68
+ return self._request("GET", path, params=params)
69
+
70
+ def post(
71
+ self,
72
+ path: str,
73
+ body: Optional[Dict[str, Any]] = None,
74
+ idempotency_key: Optional[str] = None,
75
+ ) -> Any:
76
+ """Generic POST request. Use for endpoints not yet covered by the SDK."""
77
+ return self._request("POST", path, body=body, idempotency_key=idempotency_key)
78
+
79
+ def put(self, path: str, body: Optional[Dict[str, Any]] = None) -> Any:
80
+ """Generic PUT request. Use for endpoints not yet covered by the SDK."""
81
+ return self._request("PUT", path, body=body)
82
+
83
+ def patch(self, path: str, body: Optional[Dict[str, Any]] = None) -> Any:
84
+ """Generic PATCH request. Use for endpoints not yet covered by the SDK."""
85
+ return self._request("PATCH", path, body=body)
86
+
87
+ def delete(self, path: str) -> Any:
88
+ """Generic DELETE request. Use for endpoints not yet covered by the SDK."""
89
+ return self._request("DELETE", path)
90
+
66
91
  def _request(
67
92
  self,
68
93
  method: str,
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "webhook-platform"
7
- version = "2.0.0"
7
+ version = "2.2.1"
8
8
  description = "Official Python SDK for Hookflow — reliable webhook infrastructure"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -74,6 +74,35 @@ class TestHookflowClient:
74
74
  assert client.deliveries is not None
75
75
 
76
76
 
77
+ class TestGenericRequestMethods:
78
+ """Tests for generic request methods."""
79
+
80
+ def test_exposes_get_method(self):
81
+ """Should expose public get method."""
82
+ client = Hookflow(api_key="test_api_key")
83
+ assert callable(client.get)
84
+
85
+ def test_exposes_post_method(self):
86
+ """Should expose public post method."""
87
+ client = Hookflow(api_key="test_api_key")
88
+ assert callable(client.post)
89
+
90
+ def test_exposes_put_method(self):
91
+ """Should expose public put method."""
92
+ client = Hookflow(api_key="test_api_key")
93
+ assert callable(client.put)
94
+
95
+ def test_exposes_patch_method(self):
96
+ """Should expose public patch method."""
97
+ client = Hookflow(api_key="test_api_key")
98
+ assert callable(client.patch)
99
+
100
+ def test_exposes_delete_method(self):
101
+ """Should expose public delete method."""
102
+ client = Hookflow(api_key="test_api_key")
103
+ assert callable(client.delete)
104
+
105
+
77
106
  class TestErrorClasses:
78
107
  """Tests for error classes."""
79
108
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webhook-platform
3
- Version: 2.0.0
3
+ Version: 2.2.1
4
4
  Summary: Official Python SDK for Hookflow — reliable webhook infrastructure
5
5
  Author-email: Vadym Kykalo <vadymkykalo@gmail.com>
6
6
  License: MIT
@@ -338,6 +338,64 @@ except HookflowError as e:
338
338
  print(f"Error {e.status}: {e.message}")
339
339
  ```
340
340
 
341
+ ### Error Response Format
342
+
343
+ All API errors return a consistent JSON body:
344
+
345
+ ```json
346
+ {
347
+ "error": "error_code",
348
+ "message": "Human-readable description",
349
+ "status": 400,
350
+ "fieldErrors": { "field": "reason" }
351
+ }
352
+ ```
353
+
354
+ - **`error`** — machine-readable error code (`snake_case`), always present
355
+ - **`message`** — human-readable description, always present
356
+ - **`status`** — HTTP status code (integer), always present
357
+ - **`fieldErrors`** — field-level validation details (only present for `validation_error`)
358
+
359
+ ### Error Codes Reference
360
+
361
+ | HTTP Status | `error` Code | SDK Exception | Description |
362
+ |---|---|---|---|
363
+ | 400 | `validation_error` | `ValidationError` | Invalid request parameters; see `fieldErrors` |
364
+ | 400 | `invalid_request` | `HookflowError` | Malformed or semantically invalid request |
365
+ | 401 | `unauthorized` | `AuthenticationError` | Missing or invalid API key / expired token |
366
+ | 403 | `forbidden` | `HookflowError` | Insufficient permissions for the action |
367
+ | 404 | `not_found` | `NotFoundError` | Requested resource does not exist |
368
+ | 413 | `payload_too_large` | `HookflowError` | Request body exceeds maximum allowed size |
369
+ | 422 | `unprocessable_entity` | `HookflowError` | Valid syntax but violates business rules |
370
+ | 429 | `rate_limit_exceeded` | `RateLimitError` | Too many requests; check `X-RateLimit-*` headers |
371
+ | 500 | `internal_error` | `HookflowError` | Unexpected server error |
372
+
373
+ ## Generic Requests
374
+
375
+ As the API expands, you can call any endpoint directly without waiting for SDK updates:
376
+
377
+ ```python
378
+ # GET
379
+ schemas = client.get("/api/v1/projects/proj_123/schemas")
380
+
381
+ # GET with query params
382
+ items = client.get("/api/v1/projects/proj_123/items", params={"status": "active"})
383
+
384
+ # POST with body and idempotency key
385
+ result = client.post("/api/v1/some/new/endpoint", body={"key": "value"}, idempotency_key="unique-key")
386
+
387
+ # PUT
388
+ client.put("/api/v1/projects/proj_123/settings", body={"timezone": "UTC"})
389
+
390
+ # PATCH
391
+ client.patch("/api/v1/projects/proj_123/settings", body={"timezone": "UTC"})
392
+
393
+ # DELETE
394
+ client.delete("/api/v1/projects/proj_123/tags/old-tag")
395
+ ```
396
+
397
+ All generic methods use the same authentication, error handling, and rate-limit logic as the built-in methods.
398
+
341
399
  ## Configuration
342
400
 
343
401
  ```python