kuahr 0.3.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.
- kuahr-0.3.0/.gitignore +9 -0
- kuahr-0.3.0/LICENSE +21 -0
- kuahr-0.3.0/PKG-INFO +84 -0
- kuahr-0.3.0/README.md +65 -0
- kuahr-0.3.0/pyproject.toml +29 -0
- kuahr-0.3.0/src/kua/__init__.py +67 -0
- kuahr-0.3.0/src/kua/_http.py +80 -0
- kuahr-0.3.0/src/kua/client.py +65 -0
- kuahr-0.3.0/src/kua/errors.py +22 -0
- kuahr-0.3.0/src/kua/models.py +326 -0
- kuahr-0.3.0/src/kua/py.typed +0 -0
- kuahr-0.3.0/src/kua/resources.py +425 -0
- kuahr-0.3.0/src/kua/webhooks_verify.py +61 -0
- kuahr-0.3.0/tests/test_kua.py +264 -0
kuahr-0.3.0/.gitignore
ADDED
kuahr-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kua
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
kuahr-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: kuahr
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Official Python client for Kua company people, payroll drafts, events and webhooks.
|
|
5
|
+
Project-URL: Homepage, https://docs.kuahr.com
|
|
6
|
+
Project-URL: Repository, https://github.com/kuahr/kua-sdk/tree/main/python
|
|
7
|
+
Author: Kua
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: africa,api,kua,nigeria,payments,payroll,wallet
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Requires-Dist: httpx>=0.24
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
<picture>
|
|
21
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kuahr/.github/main/profile/dark.svg">
|
|
22
|
+
<img src="https://raw.githubusercontent.com/kuahr/.github/main/profile/light.svg" alt="Kua" width="150">
|
|
23
|
+
</picture>
|
|
24
|
+
|
|
25
|
+
# Kua Python SDK
|
|
26
|
+
|
|
27
|
+
Official client for Kua company people, payroll drafts, workforce resources,
|
|
28
|
+
events and webhooks. [Documentation](https://docs.kuahr.com/sdks/python).
|
|
29
|
+
|
|
30
|
+
## Install from public source
|
|
31
|
+
|
|
32
|
+
Source version 0.3.0 is available here; registry publication is pending. The
|
|
33
|
+
distribution name is `kuahr`, with `import kua`. The unrelated PyPI package
|
|
34
|
+
named `kua` is not this SDK.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python -m venv .venv
|
|
38
|
+
source .venv/bin/activate
|
|
39
|
+
git clone --branch main https://github.com/kuahr/kua-sdk.git
|
|
40
|
+
python -m pip install ./kua-sdk/python
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Requires Python 3.9+. On Windows activate `.venv\Scripts\activate` instead.
|
|
44
|
+
Store a sandbox-issued Test key with `payroll:read` in `KUA_API_KEY`:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import os
|
|
48
|
+
from kua import Kua
|
|
49
|
+
with Kua(api_key=os.environ["KUA_API_KEY"], base_url="https://sandbox-api.kuahr.com") as kua:
|
|
50
|
+
print(kua.payroll_runs.list())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python -m pip install -e ".[dev]"
|
|
57
|
+
python -m pytest -q
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Expected result: an array of this company's Test payroll runs, including `[]`.
|
|
61
|
+
Choose the host explicitly: both clients otherwise default to production.
|
|
62
|
+
|
|
63
|
+
## Supported boundaries
|
|
64
|
+
|
|
65
|
+
Employee creation and payroll draft/list operations support Test mode. Events,
|
|
66
|
+
webhooks and workforce add-ons require a separately authorised Live key with the
|
|
67
|
+
matching resource scopes. Draft creation is not approval or payment; salary setup
|
|
68
|
+
is outside this public SDK. Registered-client Kola OIDC uses HTTP separately.
|
|
69
|
+
|
|
70
|
+
The client does not retry automatically. Persist an operation-specific idempotency
|
|
71
|
+
key and exact request before writes. Credential rotation changes that namespace;
|
|
72
|
+
reconcile uncertain outcomes before retrying under another credential. Webhook
|
|
73
|
+
replay uses a separate stable request key/reason. Queued is not delivered.
|
|
74
|
+
|
|
75
|
+
## Webhooks and production readiness
|
|
76
|
+
|
|
77
|
+
Use the included raw-body signature helper and timestamp tolerance, then
|
|
78
|
+
deduplicate the verified body's event ID. Commit durable acceptance before
|
|
79
|
+
acknowledging a delivery. Follow setup, recovery, rotation and go-live guidance at
|
|
80
|
+
https://docs.kuahr.com. Never log credentials or signing secrets.
|
|
81
|
+
|
|
82
|
+
## Licence
|
|
83
|
+
|
|
84
|
+
MIT. See LICENSE.
|
kuahr-0.3.0/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kuahr/.github/main/profile/dark.svg">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/kuahr/.github/main/profile/light.svg" alt="Kua" width="150">
|
|
4
|
+
</picture>
|
|
5
|
+
|
|
6
|
+
# Kua Python SDK
|
|
7
|
+
|
|
8
|
+
Official client for Kua company people, payroll drafts, workforce resources,
|
|
9
|
+
events and webhooks. [Documentation](https://docs.kuahr.com/sdks/python).
|
|
10
|
+
|
|
11
|
+
## Install from public source
|
|
12
|
+
|
|
13
|
+
Source version 0.3.0 is available here; registry publication is pending. The
|
|
14
|
+
distribution name is `kuahr`, with `import kua`. The unrelated PyPI package
|
|
15
|
+
named `kua` is not this SDK.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python -m venv .venv
|
|
19
|
+
source .venv/bin/activate
|
|
20
|
+
git clone --branch main https://github.com/kuahr/kua-sdk.git
|
|
21
|
+
python -m pip install ./kua-sdk/python
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires Python 3.9+. On Windows activate `.venv\Scripts\activate` instead.
|
|
25
|
+
Store a sandbox-issued Test key with `payroll:read` in `KUA_API_KEY`:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import os
|
|
29
|
+
from kua import Kua
|
|
30
|
+
with Kua(api_key=os.environ["KUA_API_KEY"], base_url="https://sandbox-api.kuahr.com") as kua:
|
|
31
|
+
print(kua.payroll_runs.list())
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Development
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python -m pip install -e ".[dev]"
|
|
38
|
+
python -m pytest -q
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Expected result: an array of this company's Test payroll runs, including `[]`.
|
|
42
|
+
Choose the host explicitly: both clients otherwise default to production.
|
|
43
|
+
|
|
44
|
+
## Supported boundaries
|
|
45
|
+
|
|
46
|
+
Employee creation and payroll draft/list operations support Test mode. Events,
|
|
47
|
+
webhooks and workforce add-ons require a separately authorised Live key with the
|
|
48
|
+
matching resource scopes. Draft creation is not approval or payment; salary setup
|
|
49
|
+
is outside this public SDK. Registered-client Kola OIDC uses HTTP separately.
|
|
50
|
+
|
|
51
|
+
The client does not retry automatically. Persist an operation-specific idempotency
|
|
52
|
+
key and exact request before writes. Credential rotation changes that namespace;
|
|
53
|
+
reconcile uncertain outcomes before retrying under another credential. Webhook
|
|
54
|
+
replay uses a separate stable request key/reason. Queued is not delivered.
|
|
55
|
+
|
|
56
|
+
## Webhooks and production readiness
|
|
57
|
+
|
|
58
|
+
Use the included raw-body signature helper and timestamp tolerance, then
|
|
59
|
+
deduplicate the verified body's event ID. Commit durable acceptance before
|
|
60
|
+
acknowledging a delivery. Follow setup, recovery, rotation and go-live guidance at
|
|
61
|
+
https://docs.kuahr.com. Never log credentials or signing secrets.
|
|
62
|
+
|
|
63
|
+
## Licence
|
|
64
|
+
|
|
65
|
+
MIT. See LICENSE.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kuahr"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "Official Python client for Kua company people, payroll drafts, events and webhooks."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{ name = "Kua" }]
|
|
13
|
+
keywords = ["kua", "payroll", "payments", "wallet", "api", "nigeria", "africa"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
]
|
|
19
|
+
dependencies = ["httpx>=0.24"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://docs.kuahr.com"
|
|
23
|
+
Repository = "https://github.com/kuahr/kua-sdk/tree/main/python"
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = ["pytest>=7"]
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/kua"]
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Official Python client for the Kua API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .client import Kua
|
|
6
|
+
from .errors import KuaError
|
|
7
|
+
from .models import (
|
|
8
|
+
Advance,
|
|
9
|
+
AppraisalCycle,
|
|
10
|
+
AppraisalReview,
|
|
11
|
+
CheckIn,
|
|
12
|
+
EmployeeCreated,
|
|
13
|
+
EventItem,
|
|
14
|
+
KeyResult,
|
|
15
|
+
LeaveAllocation,
|
|
16
|
+
LeaveBalance,
|
|
17
|
+
LeaveRequest,
|
|
18
|
+
LeaveType,
|
|
19
|
+
Objective,
|
|
20
|
+
ObjectiveDetail,
|
|
21
|
+
PayrollRunCreated,
|
|
22
|
+
PayrollRunSummary,
|
|
23
|
+
Promotion,
|
|
24
|
+
TestDataCleared,
|
|
25
|
+
WebhookCreated,
|
|
26
|
+
WebhookHistory,
|
|
27
|
+
WebhookReplayAccepted,
|
|
28
|
+
WebhookDeliveryAttempt,
|
|
29
|
+
WebhookReplayRecord,
|
|
30
|
+
WebhookDeliveryItem,
|
|
31
|
+
WebhookPingAccepted,
|
|
32
|
+
WebhookSummary,
|
|
33
|
+
)
|
|
34
|
+
from .webhooks_verify import verify_signature
|
|
35
|
+
|
|
36
|
+
__version__ = "0.3.0"
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"Kua",
|
|
40
|
+
"KuaError",
|
|
41
|
+
"verify_signature",
|
|
42
|
+
"PayrollRunSummary",
|
|
43
|
+
"PayrollRunCreated",
|
|
44
|
+
"EventItem",
|
|
45
|
+
"EmployeeCreated",
|
|
46
|
+
"TestDataCleared",
|
|
47
|
+
"WebhookCreated",
|
|
48
|
+
"WebhookHistory",
|
|
49
|
+
"WebhookReplayAccepted",
|
|
50
|
+
"WebhookDeliveryAttempt",
|
|
51
|
+
"WebhookReplayRecord",
|
|
52
|
+
"WebhookSummary",
|
|
53
|
+
"WebhookDeliveryItem",
|
|
54
|
+
"WebhookPingAccepted",
|
|
55
|
+
"LeaveType",
|
|
56
|
+
"LeaveRequest",
|
|
57
|
+
"LeaveBalance",
|
|
58
|
+
"LeaveAllocation",
|
|
59
|
+
"Advance",
|
|
60
|
+
"Objective",
|
|
61
|
+
"KeyResult",
|
|
62
|
+
"CheckIn",
|
|
63
|
+
"ObjectiveDetail",
|
|
64
|
+
"AppraisalCycle",
|
|
65
|
+
"AppraisalReview",
|
|
66
|
+
"Promotion",
|
|
67
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Internal HTTP transport."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from .errors import KuaError
|
|
10
|
+
|
|
11
|
+
DEFAULT_BASE_URL = "https://api.kuahr.com"
|
|
12
|
+
CLIENT_VERSION = "0.3.0"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _Transport:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
api_key: str,
|
|
19
|
+
base_url: Optional[str] = None,
|
|
20
|
+
timeout: float = 30.0,
|
|
21
|
+
_client: Optional[httpx.Client] = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
if not api_key:
|
|
24
|
+
raise ValueError("Kua: `api_key` is required.")
|
|
25
|
+
self._client = _client or httpx.Client(
|
|
26
|
+
base_url=(base_url or DEFAULT_BASE_URL).rstrip("/"),
|
|
27
|
+
timeout=timeout,
|
|
28
|
+
)
|
|
29
|
+
# Apply auth/identity headers to the client regardless of whether it was
|
|
30
|
+
# constructed here or injected (e.g. in tests).
|
|
31
|
+
self._client.headers.update(
|
|
32
|
+
{
|
|
33
|
+
"X-Api-Key": api_key,
|
|
34
|
+
"Accept": "application/json",
|
|
35
|
+
"User-Agent": f"kua-python/{CLIENT_VERSION}",
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def request(
|
|
40
|
+
self,
|
|
41
|
+
method: str,
|
|
42
|
+
path: str,
|
|
43
|
+
*,
|
|
44
|
+
params: Optional[Dict[str, Any]] = None,
|
|
45
|
+
json: Optional[Any] = None,
|
|
46
|
+
idempotency_key: Optional[str] = None,
|
|
47
|
+
) -> Any:
|
|
48
|
+
headers = {}
|
|
49
|
+
if idempotency_key:
|
|
50
|
+
headers["Idempotency-Key"] = idempotency_key
|
|
51
|
+
|
|
52
|
+
clean_params = {k: v for k, v in (params or {}).items() if v is not None}
|
|
53
|
+
|
|
54
|
+
res = self._client.request(
|
|
55
|
+
method, path, params=clean_params or None, json=json, headers=headers or None
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if res.status_code == 204:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
data: Any = None
|
|
62
|
+
if res.content:
|
|
63
|
+
try:
|
|
64
|
+
data = res.json()
|
|
65
|
+
except ValueError:
|
|
66
|
+
data = None
|
|
67
|
+
|
|
68
|
+
if res.status_code >= 400:
|
|
69
|
+
code = (data or {}).get("code") if isinstance(data, dict) else None
|
|
70
|
+
message = (data or {}).get("message") if isinstance(data, dict) else None
|
|
71
|
+
raise KuaError(
|
|
72
|
+
res.status_code,
|
|
73
|
+
code or f"HTTP_{res.status_code}",
|
|
74
|
+
message or f"Kua request failed with status {res.status_code}.",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return data
|
|
78
|
+
|
|
79
|
+
def close(self) -> None:
|
|
80
|
+
self._client.close()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""The Kua API client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from ._http import _Transport
|
|
10
|
+
from .resources import (
|
|
11
|
+
Advances,
|
|
12
|
+
Employees,
|
|
13
|
+
Events,
|
|
14
|
+
Leave,
|
|
15
|
+
PayrollRuns,
|
|
16
|
+
Performance,
|
|
17
|
+
TestData,
|
|
18
|
+
Webhooks,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Kua:
|
|
23
|
+
"""The Kua API client.
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from kua import Kua
|
|
27
|
+
|
|
28
|
+
kua = Kua(api_key="kua_live_…")
|
|
29
|
+
runs = kua.payroll_runs.list()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Can be used as a context manager to close the underlying HTTP connection::
|
|
33
|
+
|
|
34
|
+
with Kua(api_key=...) as kua:
|
|
35
|
+
kua.payroll_runs.list()
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
api_key: str,
|
|
41
|
+
*,
|
|
42
|
+
base_url: Optional[str] = None,
|
|
43
|
+
timeout: float = 30.0,
|
|
44
|
+
_http_client: Optional[httpx.Client] = None,
|
|
45
|
+
) -> None:
|
|
46
|
+
self._transport = _Transport(
|
|
47
|
+
api_key, base_url=base_url, timeout=timeout, _client=_http_client
|
|
48
|
+
)
|
|
49
|
+
self.payroll_runs = PayrollRuns(self._transport)
|
|
50
|
+
self.employees = Employees(self._transport)
|
|
51
|
+
self.events = Events(self._transport)
|
|
52
|
+
self.webhooks = Webhooks(self._transport)
|
|
53
|
+
self.test_data = TestData(self._transport)
|
|
54
|
+
self.leave = Leave(self._transport)
|
|
55
|
+
self.advances = Advances(self._transport)
|
|
56
|
+
self.performance = Performance(self._transport)
|
|
57
|
+
|
|
58
|
+
def close(self) -> None:
|
|
59
|
+
self._transport.close()
|
|
60
|
+
|
|
61
|
+
def __enter__(self) -> "Kua":
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
def __exit__(self, *_exc: object) -> None:
|
|
65
|
+
self.close()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Error types for the Kua SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class KuaError(Exception):
|
|
7
|
+
"""Raised when the Kua API returns a non-2xx response.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
code: A stable, machine-readable identifier you can branch on
|
|
11
|
+
(e.g. ``"INSUFFICIENT_SCOPE"``).
|
|
12
|
+
status: The HTTP status code.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, status: int, code: str, message: str) -> None:
|
|
16
|
+
super().__init__(message)
|
|
17
|
+
self.status = status
|
|
18
|
+
self.code = code
|
|
19
|
+
self.message = message
|
|
20
|
+
|
|
21
|
+
def __repr__(self) -> str: # pragma: no cover - repr convenience
|
|
22
|
+
return f"KuaError(status={self.status!r}, code={self.code!r}, message={self.message!r})"
|