pyawe 0.1.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.
- pyawe-0.1.0/PKG-INFO +166 -0
- pyawe-0.1.0/README.md +139 -0
- pyawe-0.1.0/pyawe/__init__.py +77 -0
- pyawe-0.1.0/pyawe/_http.py +103 -0
- pyawe-0.1.0/pyawe/client.py +1837 -0
- pyawe-0.1.0/pyawe/exceptions.py +23 -0
- pyawe-0.1.0/pyawe/models.py +620 -0
- pyawe-0.1.0/pyawe.egg-info/PKG-INFO +166 -0
- pyawe-0.1.0/pyawe.egg-info/SOURCES.txt +16 -0
- pyawe-0.1.0/pyawe.egg-info/dependency_links.txt +1 -0
- pyawe-0.1.0/pyawe.egg-info/requires.txt +9 -0
- pyawe-0.1.0/pyawe.egg-info/top_level.txt +1 -0
- pyawe-0.1.0/pyproject.toml +62 -0
- pyawe-0.1.0/setup.cfg +4 -0
- pyawe-0.1.0/tests/test_client.py +991 -0
- pyawe-0.1.0/tests/test_exceptions.py +65 -0
- pyawe-0.1.0/tests/test_http.py +336 -0
- pyawe-0.1.0/tests/test_models.py +637 -0
pyawe-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyawe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client library for the AWE (Advanced Workflow Engine) API
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/colinmanning/pyawe
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/colinmanning/pyawe/issues
|
|
8
|
+
Keywords: awe,workflow,automation,ullav
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: requests>=2.28
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-cov>=4; extra == "dev"
|
|
23
|
+
Requires-Dist: responses>=0.23; extra == "dev"
|
|
24
|
+
Requires-Dist: ruff; extra == "dev"
|
|
25
|
+
Requires-Dist: mypy; extra == "dev"
|
|
26
|
+
Requires-Dist: types-requests; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# pyawe
|
|
29
|
+
|
|
30
|
+
Python client library for the [AWE (Advanced Workflow Engine)](https://github.com/colinmanning/awe-server) API.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install pyawe
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from pyawe import AweClient
|
|
42
|
+
|
|
43
|
+
client = AweClient(
|
|
44
|
+
api_url="http://localhost:8080",
|
|
45
|
+
auth_url="http://localhost:8081", # ullav-user-management service
|
|
46
|
+
)
|
|
47
|
+
client.login(email="user@example.com", password="secret")
|
|
48
|
+
|
|
49
|
+
# Workflows
|
|
50
|
+
workflows = client.workflows.list()
|
|
51
|
+
wf = client.workflows.create("Q1 Onboarding", is_template=False)
|
|
52
|
+
detail = client.workflows.get(wf.id) # WorkflowWithTasks
|
|
53
|
+
|
|
54
|
+
# Tasks
|
|
55
|
+
task = client.tasks.create("Review docs", workflow_id=wf.id, is_start=True)
|
|
56
|
+
client.tasks.update(task.id, status="In Progress")
|
|
57
|
+
mine = client.tasks.list_mine() # tasks assigned to me / my roles
|
|
58
|
+
|
|
59
|
+
# Jobs
|
|
60
|
+
job = client.jobs.create("Q1 Campaign")
|
|
61
|
+
client.jobs.clone_workflow(job.id, workflow_template_id)
|
|
62
|
+
|
|
63
|
+
# Notes
|
|
64
|
+
note = client.notes.create("task", task.id, "Looks good", is_shared=True)
|
|
65
|
+
client.notes.create_reply(note.id, "Agreed")
|
|
66
|
+
|
|
67
|
+
# Automated tasks
|
|
68
|
+
client.task_scripts.upsert(
|
|
69
|
+
task.id,
|
|
70
|
+
script_type="python",
|
|
71
|
+
script_body="print('hello')",
|
|
72
|
+
timeout_secs=60,
|
|
73
|
+
)
|
|
74
|
+
runs = client.task_runs.list(task.id)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Example: list your tasks
|
|
78
|
+
|
|
79
|
+
Print every task assigned to the authenticated user, along with its status and
|
|
80
|
+
the workflow (and job, if any) it belongs to:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import os
|
|
84
|
+
from pyawe import AweClient
|
|
85
|
+
|
|
86
|
+
client = AweClient(
|
|
87
|
+
api_url=os.environ["AWE_API_URL"],
|
|
88
|
+
auth_url=os.environ.get("AWE_AUTH_URL", os.environ["AWE_API_URL"]),
|
|
89
|
+
)
|
|
90
|
+
client.login(email=os.environ["AWE_EMAIL"], password=os.environ["AWE_PASSWORD"])
|
|
91
|
+
|
|
92
|
+
for twc in client.tasks.list_mine():
|
|
93
|
+
context = twc.workflow_name
|
|
94
|
+
if twc.job_name:
|
|
95
|
+
context = f"{twc.job_name} / {twc.workflow_name}"
|
|
96
|
+
print(f"[{twc.task.status}] {twc.task.name} ({context})")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A runnable version of this script is at `examples/list_my_tasks.py`.
|
|
100
|
+
|
|
101
|
+
## Authentication
|
|
102
|
+
|
|
103
|
+
`AweClient` authenticates against the `ullav-user-management` service, which
|
|
104
|
+
issues the JWT accepted by the AWE server.
|
|
105
|
+
|
|
106
|
+
- `api_url` — AWE server base URL (e.g. `http://awe-server:8080`)
|
|
107
|
+
- `auth_url` — auth service base URL (e.g. `http://ullav-user-management:8081`);
|
|
108
|
+
omit if both services are behind the same proxy
|
|
109
|
+
|
|
110
|
+
Call `client.login(email, password)` before any other method. Tokens expire
|
|
111
|
+
according to the server's configuration; call `login()` again to refresh.
|
|
112
|
+
|
|
113
|
+
## Resource clients
|
|
114
|
+
|
|
115
|
+
| Attribute | Resource |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `client.workflows` | Workflow CRUD, merge, duplicate, team |
|
|
118
|
+
| `client.tasks` | Task CRUD, mine, decide, rework |
|
|
119
|
+
| `client.task_links` | Link create/delete, next/previous tasks, data bindings |
|
|
120
|
+
| `client.task_ports` | Port spec CRUD, input/output values |
|
|
121
|
+
| `client.task_scripts` | Automated task script upsert/delete |
|
|
122
|
+
| `client.task_runs` | Execution run history |
|
|
123
|
+
| `client.task_team_roles` | Team-role assignment |
|
|
124
|
+
| `client.jobs` | Job CRUD, clone workflow, team |
|
|
125
|
+
| `client.execution_profiles` | Kubernetes execution profile CRUD |
|
|
126
|
+
| `client.loop_blocks` | Loop block CRUD |
|
|
127
|
+
| `client.notes` | Note CRUD, replies, folder move |
|
|
128
|
+
| `client.note_folders` | Note folder CRUD |
|
|
129
|
+
|
|
130
|
+
## Error handling
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from pyawe import AweAuthError, AweNotFoundError, AweValidationError
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
wf = client.workflows.get("non-existent-id")
|
|
137
|
+
except AweNotFoundError:
|
|
138
|
+
print("not found")
|
|
139
|
+
except AweAuthError:
|
|
140
|
+
client.login(email, password) # token expired — re-authenticate
|
|
141
|
+
except AweValidationError as e:
|
|
142
|
+
print("bad request:", e)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Exception | HTTP status |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `AweAuthError` | 401 / 403, or `login()` not called |
|
|
148
|
+
| `AweNotFoundError` | 404 |
|
|
149
|
+
| `AweValidationError` | 400 |
|
|
150
|
+
| `AweServerError` | 5xx |
|
|
151
|
+
| `AweError` | base class |
|
|
152
|
+
|
|
153
|
+
## Status values
|
|
154
|
+
|
|
155
|
+
Use the `Status` and `ScheduleStatus` enumerations or plain strings:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from pyawe import Status, ScheduleStatus
|
|
159
|
+
|
|
160
|
+
client.tasks.update(task_id, status=Status.IN_PROGRESS)
|
|
161
|
+
client.tasks.update(task_id, status="In Progress") # equivalent
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Licence
|
|
165
|
+
|
|
166
|
+
MIT
|
pyawe-0.1.0/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# pyawe
|
|
2
|
+
|
|
3
|
+
Python client library for the [AWE (Advanced Workflow Engine)](https://github.com/colinmanning/awe-server) API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pyawe
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from pyawe import AweClient
|
|
15
|
+
|
|
16
|
+
client = AweClient(
|
|
17
|
+
api_url="http://localhost:8080",
|
|
18
|
+
auth_url="http://localhost:8081", # ullav-user-management service
|
|
19
|
+
)
|
|
20
|
+
client.login(email="user@example.com", password="secret")
|
|
21
|
+
|
|
22
|
+
# Workflows
|
|
23
|
+
workflows = client.workflows.list()
|
|
24
|
+
wf = client.workflows.create("Q1 Onboarding", is_template=False)
|
|
25
|
+
detail = client.workflows.get(wf.id) # WorkflowWithTasks
|
|
26
|
+
|
|
27
|
+
# Tasks
|
|
28
|
+
task = client.tasks.create("Review docs", workflow_id=wf.id, is_start=True)
|
|
29
|
+
client.tasks.update(task.id, status="In Progress")
|
|
30
|
+
mine = client.tasks.list_mine() # tasks assigned to me / my roles
|
|
31
|
+
|
|
32
|
+
# Jobs
|
|
33
|
+
job = client.jobs.create("Q1 Campaign")
|
|
34
|
+
client.jobs.clone_workflow(job.id, workflow_template_id)
|
|
35
|
+
|
|
36
|
+
# Notes
|
|
37
|
+
note = client.notes.create("task", task.id, "Looks good", is_shared=True)
|
|
38
|
+
client.notes.create_reply(note.id, "Agreed")
|
|
39
|
+
|
|
40
|
+
# Automated tasks
|
|
41
|
+
client.task_scripts.upsert(
|
|
42
|
+
task.id,
|
|
43
|
+
script_type="python",
|
|
44
|
+
script_body="print('hello')",
|
|
45
|
+
timeout_secs=60,
|
|
46
|
+
)
|
|
47
|
+
runs = client.task_runs.list(task.id)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Example: list your tasks
|
|
51
|
+
|
|
52
|
+
Print every task assigned to the authenticated user, along with its status and
|
|
53
|
+
the workflow (and job, if any) it belongs to:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import os
|
|
57
|
+
from pyawe import AweClient
|
|
58
|
+
|
|
59
|
+
client = AweClient(
|
|
60
|
+
api_url=os.environ["AWE_API_URL"],
|
|
61
|
+
auth_url=os.environ.get("AWE_AUTH_URL", os.environ["AWE_API_URL"]),
|
|
62
|
+
)
|
|
63
|
+
client.login(email=os.environ["AWE_EMAIL"], password=os.environ["AWE_PASSWORD"])
|
|
64
|
+
|
|
65
|
+
for twc in client.tasks.list_mine():
|
|
66
|
+
context = twc.workflow_name
|
|
67
|
+
if twc.job_name:
|
|
68
|
+
context = f"{twc.job_name} / {twc.workflow_name}"
|
|
69
|
+
print(f"[{twc.task.status}] {twc.task.name} ({context})")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A runnable version of this script is at `examples/list_my_tasks.py`.
|
|
73
|
+
|
|
74
|
+
## Authentication
|
|
75
|
+
|
|
76
|
+
`AweClient` authenticates against the `ullav-user-management` service, which
|
|
77
|
+
issues the JWT accepted by the AWE server.
|
|
78
|
+
|
|
79
|
+
- `api_url` — AWE server base URL (e.g. `http://awe-server:8080`)
|
|
80
|
+
- `auth_url` — auth service base URL (e.g. `http://ullav-user-management:8081`);
|
|
81
|
+
omit if both services are behind the same proxy
|
|
82
|
+
|
|
83
|
+
Call `client.login(email, password)` before any other method. Tokens expire
|
|
84
|
+
according to the server's configuration; call `login()` again to refresh.
|
|
85
|
+
|
|
86
|
+
## Resource clients
|
|
87
|
+
|
|
88
|
+
| Attribute | Resource |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `client.workflows` | Workflow CRUD, merge, duplicate, team |
|
|
91
|
+
| `client.tasks` | Task CRUD, mine, decide, rework |
|
|
92
|
+
| `client.task_links` | Link create/delete, next/previous tasks, data bindings |
|
|
93
|
+
| `client.task_ports` | Port spec CRUD, input/output values |
|
|
94
|
+
| `client.task_scripts` | Automated task script upsert/delete |
|
|
95
|
+
| `client.task_runs` | Execution run history |
|
|
96
|
+
| `client.task_team_roles` | Team-role assignment |
|
|
97
|
+
| `client.jobs` | Job CRUD, clone workflow, team |
|
|
98
|
+
| `client.execution_profiles` | Kubernetes execution profile CRUD |
|
|
99
|
+
| `client.loop_blocks` | Loop block CRUD |
|
|
100
|
+
| `client.notes` | Note CRUD, replies, folder move |
|
|
101
|
+
| `client.note_folders` | Note folder CRUD |
|
|
102
|
+
|
|
103
|
+
## Error handling
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from pyawe import AweAuthError, AweNotFoundError, AweValidationError
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
wf = client.workflows.get("non-existent-id")
|
|
110
|
+
except AweNotFoundError:
|
|
111
|
+
print("not found")
|
|
112
|
+
except AweAuthError:
|
|
113
|
+
client.login(email, password) # token expired — re-authenticate
|
|
114
|
+
except AweValidationError as e:
|
|
115
|
+
print("bad request:", e)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
| Exception | HTTP status |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `AweAuthError` | 401 / 403, or `login()` not called |
|
|
121
|
+
| `AweNotFoundError` | 404 |
|
|
122
|
+
| `AweValidationError` | 400 |
|
|
123
|
+
| `AweServerError` | 5xx |
|
|
124
|
+
| `AweError` | base class |
|
|
125
|
+
|
|
126
|
+
## Status values
|
|
127
|
+
|
|
128
|
+
Use the `Status` and `ScheduleStatus` enumerations or plain strings:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from pyawe import Status, ScheduleStatus
|
|
132
|
+
|
|
133
|
+
client.tasks.update(task_id, status=Status.IN_PROGRESS)
|
|
134
|
+
client.tasks.update(task_id, status="In Progress") # equivalent
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Licence
|
|
138
|
+
|
|
139
|
+
MIT
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""pyawe — Python client library for the AWE (Advanced Workflow Engine) API."""
|
|
2
|
+
|
|
3
|
+
from .client import AweClient
|
|
4
|
+
from .exceptions import (
|
|
5
|
+
AweAuthError,
|
|
6
|
+
AweError,
|
|
7
|
+
AweNotFoundError,
|
|
8
|
+
AweServerError,
|
|
9
|
+
AweValidationError,
|
|
10
|
+
)
|
|
11
|
+
from .models import (
|
|
12
|
+
CreateLoopBlockResponse,
|
|
13
|
+
DataBinding,
|
|
14
|
+
ExecutionProfile,
|
|
15
|
+
Job,
|
|
16
|
+
JobWithWorkflows,
|
|
17
|
+
LoginInfo,
|
|
18
|
+
LoopBlock,
|
|
19
|
+
LoopType,
|
|
20
|
+
Note,
|
|
21
|
+
NoteFolder,
|
|
22
|
+
PortDirection,
|
|
23
|
+
PortValueType,
|
|
24
|
+
ScheduleStatus,
|
|
25
|
+
ScriptType,
|
|
26
|
+
Status,
|
|
27
|
+
Task,
|
|
28
|
+
TaskLink,
|
|
29
|
+
TaskPortSpec,
|
|
30
|
+
TaskPortValues,
|
|
31
|
+
TaskRun,
|
|
32
|
+
TaskScript,
|
|
33
|
+
TaskTeamRole,
|
|
34
|
+
TaskType,
|
|
35
|
+
TaskWithContext,
|
|
36
|
+
Workflow,
|
|
37
|
+
WorkflowWithTasks,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
# Client
|
|
42
|
+
"AweClient",
|
|
43
|
+
# Exceptions
|
|
44
|
+
"AweError",
|
|
45
|
+
"AweAuthError",
|
|
46
|
+
"AweNotFoundError",
|
|
47
|
+
"AweValidationError",
|
|
48
|
+
"AweServerError",
|
|
49
|
+
# Enumerations
|
|
50
|
+
"Status",
|
|
51
|
+
"ScheduleStatus",
|
|
52
|
+
"TaskType",
|
|
53
|
+
"PortDirection",
|
|
54
|
+
"PortValueType",
|
|
55
|
+
"ScriptType",
|
|
56
|
+
"LoopType",
|
|
57
|
+
# Models
|
|
58
|
+
"LoginInfo",
|
|
59
|
+
"Workflow",
|
|
60
|
+
"WorkflowWithTasks",
|
|
61
|
+
"Task",
|
|
62
|
+
"TaskWithContext",
|
|
63
|
+
"TaskLink",
|
|
64
|
+
"TaskPortSpec",
|
|
65
|
+
"TaskPortValues",
|
|
66
|
+
"DataBinding",
|
|
67
|
+
"TaskScript",
|
|
68
|
+
"TaskRun",
|
|
69
|
+
"TaskTeamRole",
|
|
70
|
+
"Job",
|
|
71
|
+
"JobWithWorkflows",
|
|
72
|
+
"ExecutionProfile",
|
|
73
|
+
"LoopBlock",
|
|
74
|
+
"CreateLoopBlockResponse",
|
|
75
|
+
"Note",
|
|
76
|
+
"NoteFolder",
|
|
77
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Internal HTTP session: request dispatch and error mapping."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from typing import Any, Dict, Optional
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from .exceptions import AweAuthError, AweError, AweNotFoundError, AweServerError, AweValidationError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _compact(d: Dict[str, Any]) -> Dict[str, Any]:
|
|
14
|
+
"""Return a copy of *d* with all ``None``-valued keys removed."""
|
|
15
|
+
return {k: v for k, v in d.items() if v is not None}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _str_id(value: Any) -> Optional[str]:
|
|
19
|
+
"""Coerce a ``uuid.UUID`` or string identifier to ``str``, pass ``None`` through."""
|
|
20
|
+
if value is None:
|
|
21
|
+
return None
|
|
22
|
+
return str(value) if isinstance(value, uuid.UUID) else value
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _HttpSession:
|
|
26
|
+
"""Thin wrapper around :class:`requests.Session` that handles auth headers and
|
|
27
|
+
maps non-2xx responses to typed exceptions."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, api_url: str, auth_url: str) -> None:
|
|
30
|
+
self._api_url = api_url.rstrip("/")
|
|
31
|
+
self._auth_url = auth_url.rstrip("/")
|
|
32
|
+
self._session = requests.Session()
|
|
33
|
+
self._session.headers.update({"Content-Type": "application/json"})
|
|
34
|
+
self._token: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
def set_token(self, token: str) -> None:
|
|
37
|
+
"""Store the JWT and attach it to all subsequent requests."""
|
|
38
|
+
self._token = token
|
|
39
|
+
self._session.headers["Authorization"] = f"Bearer {token}"
|
|
40
|
+
|
|
41
|
+
def _require_auth(self) -> None:
|
|
42
|
+
if self._token is None:
|
|
43
|
+
raise AweAuthError("Not authenticated — call AweClient.login() first.")
|
|
44
|
+
|
|
45
|
+
def _raise_for_status(self, response: requests.Response) -> None:
|
|
46
|
+
if response.ok:
|
|
47
|
+
return
|
|
48
|
+
try:
|
|
49
|
+
body = response.json()
|
|
50
|
+
message = body.get("error") or body.get("message") or response.text
|
|
51
|
+
except Exception:
|
|
52
|
+
message = response.text
|
|
53
|
+
if response.status_code == 401:
|
|
54
|
+
raise AweAuthError(message)
|
|
55
|
+
if response.status_code == 403:
|
|
56
|
+
raise AweAuthError(f"Forbidden: {message}")
|
|
57
|
+
if response.status_code == 404:
|
|
58
|
+
raise AweNotFoundError(message)
|
|
59
|
+
if response.status_code == 400:
|
|
60
|
+
raise AweValidationError(message)
|
|
61
|
+
if response.status_code >= 500:
|
|
62
|
+
raise AweServerError(f"Server error {response.status_code}: {message}")
|
|
63
|
+
raise AweError(f"HTTP {response.status_code}: {message}")
|
|
64
|
+
|
|
65
|
+
# ── auth service ──────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
def post_auth(self, path: str, json: Any) -> Any:
|
|
68
|
+
"""POST to the authentication service (no JWT header required)."""
|
|
69
|
+
response = self._session.post(f"{self._auth_url}{path}", json=json)
|
|
70
|
+
self._raise_for_status(response)
|
|
71
|
+
return response.json()
|
|
72
|
+
|
|
73
|
+
# ── AWE API ───────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
|
|
76
|
+
self._require_auth()
|
|
77
|
+
response = self._session.get(f"{self._api_url}{path}", params=params)
|
|
78
|
+
self._raise_for_status(response)
|
|
79
|
+
return response.json()
|
|
80
|
+
|
|
81
|
+
def post(self, path: str, json: Any = None) -> Any:
|
|
82
|
+
self._require_auth()
|
|
83
|
+
response = self._session.post(f"{self._api_url}{path}", json=json)
|
|
84
|
+
self._raise_for_status(response)
|
|
85
|
+
return response.json() if response.status_code != 204 else None
|
|
86
|
+
|
|
87
|
+
def put(self, path: str, json: Any = None) -> Any:
|
|
88
|
+
self._require_auth()
|
|
89
|
+
response = self._session.put(f"{self._api_url}{path}", json=json)
|
|
90
|
+
self._raise_for_status(response)
|
|
91
|
+
return response.json() if response.status_code != 204 else None
|
|
92
|
+
|
|
93
|
+
def patch(self, path: str, json: Any = None) -> Any:
|
|
94
|
+
self._require_auth()
|
|
95
|
+
response = self._session.patch(f"{self._api_url}{path}", json=json)
|
|
96
|
+
self._raise_for_status(response)
|
|
97
|
+
return response.json() if response.status_code != 204 else None
|
|
98
|
+
|
|
99
|
+
def delete(self, path: str, json: Any = None) -> Any:
|
|
100
|
+
self._require_auth()
|
|
101
|
+
response = self._session.delete(f"{self._api_url}{path}", json=json)
|
|
102
|
+
self._raise_for_status(response)
|
|
103
|
+
return response.json() if response.status_code != 204 else None
|