aidbox-python-sdk 0.1.9__tar.gz → 0.1.12__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.
- {aidbox_python_sdk-0.1.9/aidbox_python_sdk.egg-info → aidbox_python_sdk-0.1.12}/PKG-INFO +101 -10
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/README.md +100 -9
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/__init__.py +1 -1
- aidbox_python_sdk-0.1.12/aidbox_python_sdk/app_keys.py +11 -0
- aidbox_python_sdk-0.1.12/aidbox_python_sdk/db_migrations.py +38 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/handlers.py +7 -4
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/main.py +17 -12
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/pytest_plugin.py +13 -12
- aidbox_python_sdk-0.1.12/aidbox_python_sdk/types.py +13 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12/aidbox_python_sdk.egg-info}/PKG-INFO +101 -10
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/SOURCES.txt +1 -0
- aidbox_python_sdk-0.1.9/aidbox_python_sdk/db_migrations.py +0 -19
- aidbox_python_sdk-0.1.9/aidbox_python_sdk/types.py +0 -16
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/LICENSE.md +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/MANIFEST.in +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/aidboxpy.py +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/db.py +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/exceptions.py +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/sdk.py +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/settings.py +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/dependency_links.txt +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/not-zip-safe +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/requires.txt +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/top_level.txt +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/pyproject.toml +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/setup.cfg +0 -0
- {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/tests/test_sdk.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aidbox-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: Aidbox SDK for python
|
|
5
5
|
Author-email: "beda.software" <aidbox-python-sdk@beda.software>
|
|
6
6
|
License: MIT License
|
|
@@ -73,24 +73,34 @@ Requires-Dist: autohooks-plugin-black; extra == "dev"
|
|
|
73
73
|
4. You can then run example with `python example.py`.
|
|
74
74
|
|
|
75
75
|
# Getting started
|
|
76
|
+
|
|
76
77
|
## Minimal application
|
|
77
|
-
|
|
78
|
+
|
|
79
|
+
main.py
|
|
80
|
+
```python
|
|
78
81
|
from aidbox_python_sdk.main import create_app as _create_app
|
|
79
82
|
from aidbox_python_sdk.settings import Settings
|
|
80
83
|
from aidbox_python_sdk.sdk import SDK
|
|
81
84
|
|
|
85
|
+
|
|
82
86
|
settings = Settings(**{})
|
|
83
|
-
sdk = SDK(settings, resources=
|
|
87
|
+
sdk = SDK(settings, resources={}, seeds={})
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def create_app():
|
|
91
|
+
app = await _create_app(SDK)
|
|
92
|
+
return app
|
|
84
93
|
|
|
85
|
-
async def create_app():
|
|
86
|
-
return await _create_app(sdk)
|
|
87
94
|
|
|
95
|
+
async def create_gunicorn_app() -> web.Application:
|
|
96
|
+
return create_app()
|
|
88
97
|
```
|
|
89
98
|
|
|
90
99
|
## Register handler for operation
|
|
91
|
-
```
|
|
100
|
+
```python
|
|
92
101
|
import logging
|
|
93
102
|
from aiohttp import web
|
|
103
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
94
104
|
|
|
95
105
|
from yourappfolder import sdk
|
|
96
106
|
|
|
@@ -100,7 +110,7 @@ from yourappfolder import sdk
|
|
|
100
110
|
path=["signup", "register", {"name": "date"}, {"name": "test"}],
|
|
101
111
|
timeout=60000 ## Optional parameter to set a custom timeout for operation in milliseconds
|
|
102
112
|
)
|
|
103
|
-
def signup_register_op(
|
|
113
|
+
def signup_register_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
104
114
|
"""
|
|
105
115
|
POST /signup/register/21.02.19/testvalue
|
|
106
116
|
PATCH /signup/register/22.02.19/patchtestvalue
|
|
@@ -112,8 +122,86 @@ def signup_register_op(operation, request):
|
|
|
112
122
|
|
|
113
123
|
```
|
|
114
124
|
|
|
115
|
-
##
|
|
116
|
-
|
|
125
|
+
## Usage of AppKeys
|
|
126
|
+
|
|
127
|
+
To access Aidbox Client, SDK, settings, DB Proxy the `app` (`web.Application`) is extended by default with the following app keys that are defined in `aidbox_python_sdk.app_keys` module:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from aidbox_python_sdk import app_keys as ak
|
|
131
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
132
|
+
|
|
133
|
+
@sdk.operation(["POST"], ["example"])
|
|
134
|
+
async def update_organization_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
135
|
+
app = request.app
|
|
136
|
+
client = app[ak.client] # AsyncAidboxClient
|
|
137
|
+
sdk = app[ak.sdk] # SDK
|
|
138
|
+
settings = app[ak.settings] # Settings
|
|
139
|
+
db = app[ak.db] # DBProxy
|
|
140
|
+
return web.json_response()
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Usage of FHIR Client
|
|
144
|
+
|
|
145
|
+
FHIR Client is not plugged in by default, however, to use it you can extend the app by adding new AppKey
|
|
146
|
+
|
|
147
|
+
app/app_keys.py
|
|
148
|
+
```python
|
|
149
|
+
from fhirpy import AsyncFHIRClient
|
|
150
|
+
|
|
151
|
+
fhir_client: web.AppKey[AsyncFHIRClient] = web.AppKey("fhir_client", AsyncFHIRClient)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
main.py
|
|
155
|
+
```python
|
|
156
|
+
from collections.abc import AsyncGenerator
|
|
157
|
+
|
|
158
|
+
from aidbox_python_sdk.main import create_app as _create_app
|
|
159
|
+
from aidbox_python_sdk.settings import Settings
|
|
160
|
+
from aidbox_python_sdk.sdk import SDK
|
|
161
|
+
from aiohttp import BasicAuth, web
|
|
162
|
+
from fhirpy import AsyncFHIRClient
|
|
163
|
+
|
|
164
|
+
from app import app_keys as ak
|
|
165
|
+
|
|
166
|
+
settings = Settings(**{})
|
|
167
|
+
sdk = SDK(settings, resources={}, seeds={)
|
|
168
|
+
|
|
169
|
+
def create_app():
|
|
170
|
+
app = await _create_app(SDK)
|
|
171
|
+
app.cleanup_ctx.append(fhir_clients_ctx)
|
|
172
|
+
return app
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
async def create_gunicorn_app() -> web.Application:
|
|
176
|
+
return create_app()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def fhir_clients_ctx(app: web.Application) -> AsyncGenerator[None, None]:
|
|
180
|
+
app[ak.fhir_client] = await init_fhir_client(app[ak.settings], "/fhir")
|
|
181
|
+
|
|
182
|
+
yield
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async def init_fhir_client(settings: Settings, prefix: str = "") -> AsyncFHIRClient:
|
|
186
|
+
basic_auth = BasicAuth(
|
|
187
|
+
login=settings.APP_INIT_CLIENT_ID,
|
|
188
|
+
password=settings.APP_INIT_CLIENT_SECRET,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return AsyncFHIRClient(
|
|
192
|
+
f"{settings.APP_INIT_URL}{prefix}",
|
|
193
|
+
authorization=basic_auth.encode(),
|
|
194
|
+
dump_resource=lambda x: x.model_dump(),
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
After that, you can use `app[ak.fhir_client]` that has the type `AsyncFHIRClient` everywhere where the app is available.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
## Usage of request_schema
|
|
202
|
+
```python
|
|
203
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
204
|
+
|
|
117
205
|
schema = {
|
|
118
206
|
"required": ["params", "resource"],
|
|
119
207
|
"properties": {
|
|
@@ -137,10 +225,11 @@ schema = {
|
|
|
137
225
|
|
|
138
226
|
|
|
139
227
|
@sdk.operation(["POST"], ["Organization", {"name": "id"}, "$update"], request_schema=schema)
|
|
140
|
-
async def
|
|
228
|
+
async def update_organization_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
141
229
|
location = request["params"]["location"]
|
|
142
230
|
return web.json_response({"location": location})
|
|
143
231
|
```
|
|
232
|
+
|
|
144
233
|
### Valid request example
|
|
145
234
|
```shell
|
|
146
235
|
POST /Organization/org-1/$update?abc=xyz&location=us
|
|
@@ -148,3 +237,5 @@ POST /Organization/org-1/$update?abc=xyz&location=us
|
|
|
148
237
|
organizationType: non-profit
|
|
149
238
|
employeesCount: 10
|
|
150
239
|
```
|
|
240
|
+
|
|
241
|
+
|
|
@@ -12,24 +12,34 @@
|
|
|
12
12
|
4. You can then run example with `python example.py`.
|
|
13
13
|
|
|
14
14
|
# Getting started
|
|
15
|
+
|
|
15
16
|
## Minimal application
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
main.py
|
|
19
|
+
```python
|
|
17
20
|
from aidbox_python_sdk.main import create_app as _create_app
|
|
18
21
|
from aidbox_python_sdk.settings import Settings
|
|
19
22
|
from aidbox_python_sdk.sdk import SDK
|
|
20
23
|
|
|
24
|
+
|
|
21
25
|
settings = Settings(**{})
|
|
22
|
-
sdk = SDK(settings, resources=
|
|
26
|
+
sdk = SDK(settings, resources={}, seeds={})
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_app():
|
|
30
|
+
app = await _create_app(SDK)
|
|
31
|
+
return app
|
|
23
32
|
|
|
24
|
-
async def create_app():
|
|
25
|
-
return await _create_app(sdk)
|
|
26
33
|
|
|
34
|
+
async def create_gunicorn_app() -> web.Application:
|
|
35
|
+
return create_app()
|
|
27
36
|
```
|
|
28
37
|
|
|
29
38
|
## Register handler for operation
|
|
30
|
-
```
|
|
39
|
+
```python
|
|
31
40
|
import logging
|
|
32
41
|
from aiohttp import web
|
|
42
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
33
43
|
|
|
34
44
|
from yourappfolder import sdk
|
|
35
45
|
|
|
@@ -39,7 +49,7 @@ from yourappfolder import sdk
|
|
|
39
49
|
path=["signup", "register", {"name": "date"}, {"name": "test"}],
|
|
40
50
|
timeout=60000 ## Optional parameter to set a custom timeout for operation in milliseconds
|
|
41
51
|
)
|
|
42
|
-
def signup_register_op(
|
|
52
|
+
def signup_register_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
43
53
|
"""
|
|
44
54
|
POST /signup/register/21.02.19/testvalue
|
|
45
55
|
PATCH /signup/register/22.02.19/patchtestvalue
|
|
@@ -51,8 +61,86 @@ def signup_register_op(operation, request):
|
|
|
51
61
|
|
|
52
62
|
```
|
|
53
63
|
|
|
54
|
-
##
|
|
55
|
-
|
|
64
|
+
## Usage of AppKeys
|
|
65
|
+
|
|
66
|
+
To access Aidbox Client, SDK, settings, DB Proxy the `app` (`web.Application`) is extended by default with the following app keys that are defined in `aidbox_python_sdk.app_keys` module:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from aidbox_python_sdk import app_keys as ak
|
|
70
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
71
|
+
|
|
72
|
+
@sdk.operation(["POST"], ["example"])
|
|
73
|
+
async def update_organization_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
74
|
+
app = request.app
|
|
75
|
+
client = app[ak.client] # AsyncAidboxClient
|
|
76
|
+
sdk = app[ak.sdk] # SDK
|
|
77
|
+
settings = app[ak.settings] # Settings
|
|
78
|
+
db = app[ak.db] # DBProxy
|
|
79
|
+
return web.json_response()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage of FHIR Client
|
|
83
|
+
|
|
84
|
+
FHIR Client is not plugged in by default, however, to use it you can extend the app by adding new AppKey
|
|
85
|
+
|
|
86
|
+
app/app_keys.py
|
|
87
|
+
```python
|
|
88
|
+
from fhirpy import AsyncFHIRClient
|
|
89
|
+
|
|
90
|
+
fhir_client: web.AppKey[AsyncFHIRClient] = web.AppKey("fhir_client", AsyncFHIRClient)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
main.py
|
|
94
|
+
```python
|
|
95
|
+
from collections.abc import AsyncGenerator
|
|
96
|
+
|
|
97
|
+
from aidbox_python_sdk.main import create_app as _create_app
|
|
98
|
+
from aidbox_python_sdk.settings import Settings
|
|
99
|
+
from aidbox_python_sdk.sdk import SDK
|
|
100
|
+
from aiohttp import BasicAuth, web
|
|
101
|
+
from fhirpy import AsyncFHIRClient
|
|
102
|
+
|
|
103
|
+
from app import app_keys as ak
|
|
104
|
+
|
|
105
|
+
settings = Settings(**{})
|
|
106
|
+
sdk = SDK(settings, resources={}, seeds={)
|
|
107
|
+
|
|
108
|
+
def create_app():
|
|
109
|
+
app = await _create_app(SDK)
|
|
110
|
+
app.cleanup_ctx.append(fhir_clients_ctx)
|
|
111
|
+
return app
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def create_gunicorn_app() -> web.Application:
|
|
115
|
+
return create_app()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def fhir_clients_ctx(app: web.Application) -> AsyncGenerator[None, None]:
|
|
119
|
+
app[ak.fhir_client] = await init_fhir_client(app[ak.settings], "/fhir")
|
|
120
|
+
|
|
121
|
+
yield
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def init_fhir_client(settings: Settings, prefix: str = "") -> AsyncFHIRClient:
|
|
125
|
+
basic_auth = BasicAuth(
|
|
126
|
+
login=settings.APP_INIT_CLIENT_ID,
|
|
127
|
+
password=settings.APP_INIT_CLIENT_SECRET,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return AsyncFHIRClient(
|
|
131
|
+
f"{settings.APP_INIT_URL}{prefix}",
|
|
132
|
+
authorization=basic_auth.encode(),
|
|
133
|
+
dump_resource=lambda x: x.model_dump(),
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
After that, you can use `app[ak.fhir_client]` that has the type `AsyncFHIRClient` everywhere where the app is available.
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
## Usage of request_schema
|
|
141
|
+
```python
|
|
142
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
143
|
+
|
|
56
144
|
schema = {
|
|
57
145
|
"required": ["params", "resource"],
|
|
58
146
|
"properties": {
|
|
@@ -76,10 +164,11 @@ schema = {
|
|
|
76
164
|
|
|
77
165
|
|
|
78
166
|
@sdk.operation(["POST"], ["Organization", {"name": "id"}, "$update"], request_schema=schema)
|
|
79
|
-
async def
|
|
167
|
+
async def update_organization_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
80
168
|
location = request["params"]["location"]
|
|
81
169
|
return web.json_response({"location": location})
|
|
82
170
|
```
|
|
171
|
+
|
|
83
172
|
### Valid request example
|
|
84
173
|
```shell
|
|
85
174
|
POST /Organization/org-1/$update?abc=xyz&location=us
|
|
@@ -87,3 +176,5 @@ POST /Organization/org-1/$update?abc=xyz&location=us
|
|
|
87
176
|
organizationType: non-profit
|
|
88
177
|
employeesCount: 10
|
|
89
178
|
```
|
|
179
|
+
|
|
180
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
from aidbox_python_sdk.aidboxpy import AsyncAidboxClient
|
|
4
|
+
from aidbox_python_sdk.db import DBProxy
|
|
5
|
+
from aidbox_python_sdk.sdk import SDK
|
|
6
|
+
from aidbox_python_sdk.settings import Settings
|
|
7
|
+
|
|
8
|
+
db: web.AppKey[DBProxy] = web.AppKey("db", DBProxy)
|
|
9
|
+
client: web.AppKey[AsyncAidboxClient] = web.AppKey("client", AsyncAidboxClient)
|
|
10
|
+
sdk: web.AppKey[SDK] = web.AppKey("sdk", SDK)
|
|
11
|
+
settings: web.AppKey[Settings] = web.AppKey("settings", Settings)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
sdk_migrations = [
|
|
2
|
+
{
|
|
3
|
+
"id": "20190909_add_drop_before",
|
|
4
|
+
"sql": """
|
|
5
|
+
DROP FUNCTION IF EXISTS drop_before_all(integer);
|
|
6
|
+
|
|
7
|
+
CREATE FUNCTION drop_before_all(integer) RETURNS VOID AS $$
|
|
8
|
+
declare
|
|
9
|
+
e record;
|
|
10
|
+
BEGIN
|
|
11
|
+
FOR e IN (select LOWER(entity.id) as t_name from entity where resource#>>'{type}' = 'resource' and id != 'OperationOutcome') LOOP
|
|
12
|
+
EXECUTE 'delete from "' || e.t_name || '" where txid > ' || $1 ;
|
|
13
|
+
END LOOP;
|
|
14
|
+
END;
|
|
15
|
+
|
|
16
|
+
$$ LANGUAGE plpgsql;""",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "20240913_change_drop_before_all",
|
|
20
|
+
"sql": """
|
|
21
|
+
DROP FUNCTION IF EXISTS drop_before_all(integer);
|
|
22
|
+
|
|
23
|
+
CREATE FUNCTION drop_before_all(integer) RETURNS VOID AS $$
|
|
24
|
+
declare
|
|
25
|
+
e record;
|
|
26
|
+
BEGIN
|
|
27
|
+
FOR e IN (
|
|
28
|
+
SELECT table_name
|
|
29
|
+
FROM information_schema.columns
|
|
30
|
+
WHERE column_name = 'txid' AND table_schema = 'public' AND table_name NOT LIKE '%_history'
|
|
31
|
+
) LOOP
|
|
32
|
+
EXECUTE 'DELETE FROM "' || e.table_name || '" WHERE txid > ' || $1 ;
|
|
33
|
+
END LOOP;
|
|
34
|
+
END;
|
|
35
|
+
|
|
36
|
+
$$ LANGUAGE plpgsql;""",
|
|
37
|
+
},
|
|
38
|
+
]
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
from aiohttp import web
|
|
5
6
|
from fhirpy.base.exceptions import OperationOutcome
|
|
6
7
|
|
|
8
|
+
from . import app_keys as ak
|
|
9
|
+
|
|
7
10
|
logger = logging.getLogger("aidbox_sdk")
|
|
8
11
|
routes = web.RouteTableDef()
|
|
9
12
|
|
|
10
13
|
|
|
11
|
-
async def subscription(request, data):
|
|
14
|
+
async def subscription(request: web.Request, data: dict):
|
|
12
15
|
logger.debug("Subscription handler: %s", data["handler"])
|
|
13
16
|
if "handler" not in data or "event" not in data:
|
|
14
17
|
logger.error("`handler` and/or `event` param is missing, data: %s", data)
|
|
15
18
|
raise web.HTTPBadRequest()
|
|
16
|
-
handler = request.app[
|
|
19
|
+
handler = request.app[ak.sdk].get_subscription_handler(data["handler"])
|
|
17
20
|
if not handler:
|
|
18
21
|
logger.error("Subscription handler `%s` was not found", "handler")
|
|
19
22
|
raise web.HTTPNotFound()
|
|
@@ -23,12 +26,12 @@ async def subscription(request, data):
|
|
|
23
26
|
return web.json_response({})
|
|
24
27
|
|
|
25
28
|
|
|
26
|
-
async def operation(request, data):
|
|
29
|
+
async def operation(request: web.Request, data: dict[str, Any]):
|
|
27
30
|
logger.debug("Operation handler: %s", data["operation"]["id"])
|
|
28
31
|
if "operation" not in data or "id" not in data["operation"]:
|
|
29
32
|
logger.error("`operation` or `operation[id]` param is missing, data: %s", data)
|
|
30
33
|
raise web.HTTPBadRequest()
|
|
31
|
-
handler = request.app[
|
|
34
|
+
handler = request.app[ak.sdk].get_operation_handler(data["operation"]["id"])
|
|
32
35
|
if not handler:
|
|
33
36
|
logger.error("Operation handler `%s` was not found", data["handler"])
|
|
34
37
|
raise web.HTTPNotFound()
|
|
@@ -3,10 +3,12 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import sys
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
from typing import cast
|
|
6
7
|
|
|
7
8
|
from aiohttp import BasicAuth, client_exceptions, web
|
|
8
9
|
from fhirpy.base.exceptions import OperationOutcome
|
|
9
10
|
|
|
11
|
+
from . import app_keys as ak
|
|
10
12
|
from .aidboxpy import AsyncAidboxClient
|
|
11
13
|
from .db import DBProxy
|
|
12
14
|
from .handlers import routes
|
|
@@ -47,28 +49,31 @@ async def register_app(sdk: SDK, client: AsyncAidboxClient):
|
|
|
47
49
|
async def init_client(settings: Settings):
|
|
48
50
|
aidbox_client_cls = settings.AIDBOX_CLIENT_CLASS
|
|
49
51
|
basic_auth = BasicAuth(
|
|
50
|
-
login=settings.APP_INIT_CLIENT_ID,
|
|
51
|
-
password=settings.APP_INIT_CLIENT_SECRET,
|
|
52
|
+
login=cast(str, settings.APP_INIT_CLIENT_ID),
|
|
53
|
+
password=cast(str, settings.APP_INIT_CLIENT_SECRET),
|
|
52
54
|
)
|
|
53
55
|
|
|
54
56
|
return aidbox_client_cls(f"{settings.APP_INIT_URL}", authorization=basic_auth.encode())
|
|
55
57
|
|
|
56
58
|
|
|
57
|
-
async def init(app):
|
|
58
|
-
app[
|
|
59
|
-
app["
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
async def init(app: web.Application):
|
|
60
|
+
app[ak.client] = await init_client(app[ak.settings])
|
|
61
|
+
app["client"] = app[ak.client] # For backwards compatibility
|
|
62
|
+
app[ak.db] = DBProxy(app[ak.settings])
|
|
63
|
+
app["db"] = app[ak.db] # For backwards compatibility
|
|
64
|
+
await register_app(app[ak.sdk], app[ak.client])
|
|
65
|
+
await app[ak.db].initialize()
|
|
62
66
|
yield
|
|
63
|
-
await app[
|
|
67
|
+
await app[ak.db].deinitialize()
|
|
64
68
|
|
|
65
69
|
|
|
66
70
|
def create_app(sdk: SDK):
|
|
67
71
|
app = web.Application()
|
|
68
72
|
app.cleanup_ctx.append(init)
|
|
69
|
-
app.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
app[ak.sdk] = sdk
|
|
74
|
+
app["sdk"] = app[ak.sdk] # For backwards compatibility
|
|
75
|
+
app[ak.settings] = sdk.settings
|
|
76
|
+
app["settings"] = app[ak.settings] # For backwards compatibility
|
|
77
|
+
|
|
73
78
|
setup_routes(app)
|
|
74
79
|
return app
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import cast
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
4
5
|
import pytest_asyncio
|
|
5
|
-
from aiohttp import BasicAuth, ClientSession
|
|
6
|
+
from aiohttp import BasicAuth, ClientSession, web
|
|
6
7
|
from yarl import URL
|
|
7
8
|
|
|
8
9
|
from main import create_app as _create_app
|
|
9
10
|
|
|
11
|
+
from . import app_keys as ak
|
|
12
|
+
|
|
10
13
|
|
|
11
14
|
async def start_app(aiohttp_client):
|
|
12
15
|
app = await aiohttp_client(_create_app(), server_kwargs={"host": "0.0.0.0", "port": 8081})
|
|
13
|
-
sdk = app.server.app[
|
|
16
|
+
sdk = cast(web.Application, app.server.app)[ak.sdk]
|
|
14
17
|
sdk._test_start_txid = -1
|
|
15
18
|
|
|
16
19
|
return app
|
|
@@ -40,20 +43,18 @@ class AidboxSession(ClientSession):
|
|
|
40
43
|
@pytest_asyncio.fixture
|
|
41
44
|
async def aidbox(client):
|
|
42
45
|
"""HTTP client for making requests to Aidbox"""
|
|
43
|
-
app = client.server.app
|
|
46
|
+
app = cast(web.Application, client.server.app)
|
|
44
47
|
basic_auth = BasicAuth(
|
|
45
|
-
login=app[
|
|
46
|
-
password=app[
|
|
48
|
+
login=app[ak.settings].APP_INIT_CLIENT_ID,
|
|
49
|
+
password=app[ak.settings].APP_INIT_CLIENT_SECRET,
|
|
47
50
|
)
|
|
48
|
-
session = AidboxSession(auth=basic_auth)
|
|
51
|
+
session = AidboxSession(auth=basic_auth, base_url=app[ak.settings].APP_INIT_URL)
|
|
49
52
|
yield session
|
|
50
53
|
await session.close()
|
|
51
54
|
|
|
52
55
|
|
|
53
56
|
@pytest_asyncio.fixture
|
|
54
|
-
async def safe_db(aidbox, client):
|
|
55
|
-
sdk = client.server.app["sdk"]
|
|
56
|
-
|
|
57
|
+
async def safe_db(aidbox, client, sdk):
|
|
57
58
|
resp = await aidbox.post(
|
|
58
59
|
"/$psql",
|
|
59
60
|
json={"query": "SELECT last_value from transaction_id_seq;"},
|
|
@@ -76,14 +77,14 @@ async def safe_db(aidbox, client):
|
|
|
76
77
|
|
|
77
78
|
@pytest.fixture()
|
|
78
79
|
def sdk(client):
|
|
79
|
-
return client.server.app[
|
|
80
|
+
return cast(web.Application, client.server.app)[ak.sdk]
|
|
80
81
|
|
|
81
82
|
|
|
82
83
|
@pytest.fixture()
|
|
83
84
|
def aidbox_client(client):
|
|
84
|
-
return client.server.app[
|
|
85
|
+
return cast(web.Application, client.server.app)[ak.client]
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
@pytest.fixture()
|
|
88
89
|
def aidbox_db(client):
|
|
89
|
-
return client.server.app[
|
|
90
|
+
return cast(web.Application, client.server.app)[ak.db]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from aiohttp import web
|
|
4
|
+
from typing_extensions import TypedDict
|
|
5
|
+
|
|
6
|
+
SDKOperationRequest = TypedDict(
|
|
7
|
+
"SDKOperationRequest",
|
|
8
|
+
{"app": web.Application, "params": dict, "route-params": dict, "headers": dict, "resource": Any},
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SDKOperation(TypedDict):
|
|
13
|
+
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aidbox-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: Aidbox SDK for python
|
|
5
5
|
Author-email: "beda.software" <aidbox-python-sdk@beda.software>
|
|
6
6
|
License: MIT License
|
|
@@ -73,24 +73,34 @@ Requires-Dist: autohooks-plugin-black; extra == "dev"
|
|
|
73
73
|
4. You can then run example with `python example.py`.
|
|
74
74
|
|
|
75
75
|
# Getting started
|
|
76
|
+
|
|
76
77
|
## Minimal application
|
|
77
|
-
|
|
78
|
+
|
|
79
|
+
main.py
|
|
80
|
+
```python
|
|
78
81
|
from aidbox_python_sdk.main import create_app as _create_app
|
|
79
82
|
from aidbox_python_sdk.settings import Settings
|
|
80
83
|
from aidbox_python_sdk.sdk import SDK
|
|
81
84
|
|
|
85
|
+
|
|
82
86
|
settings = Settings(**{})
|
|
83
|
-
sdk = SDK(settings, resources=
|
|
87
|
+
sdk = SDK(settings, resources={}, seeds={})
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def create_app():
|
|
91
|
+
app = await _create_app(SDK)
|
|
92
|
+
return app
|
|
84
93
|
|
|
85
|
-
async def create_app():
|
|
86
|
-
return await _create_app(sdk)
|
|
87
94
|
|
|
95
|
+
async def create_gunicorn_app() -> web.Application:
|
|
96
|
+
return create_app()
|
|
88
97
|
```
|
|
89
98
|
|
|
90
99
|
## Register handler for operation
|
|
91
|
-
```
|
|
100
|
+
```python
|
|
92
101
|
import logging
|
|
93
102
|
from aiohttp import web
|
|
103
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
94
104
|
|
|
95
105
|
from yourappfolder import sdk
|
|
96
106
|
|
|
@@ -100,7 +110,7 @@ from yourappfolder import sdk
|
|
|
100
110
|
path=["signup", "register", {"name": "date"}, {"name": "test"}],
|
|
101
111
|
timeout=60000 ## Optional parameter to set a custom timeout for operation in milliseconds
|
|
102
112
|
)
|
|
103
|
-
def signup_register_op(
|
|
113
|
+
def signup_register_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
104
114
|
"""
|
|
105
115
|
POST /signup/register/21.02.19/testvalue
|
|
106
116
|
PATCH /signup/register/22.02.19/patchtestvalue
|
|
@@ -112,8 +122,86 @@ def signup_register_op(operation, request):
|
|
|
112
122
|
|
|
113
123
|
```
|
|
114
124
|
|
|
115
|
-
##
|
|
116
|
-
|
|
125
|
+
## Usage of AppKeys
|
|
126
|
+
|
|
127
|
+
To access Aidbox Client, SDK, settings, DB Proxy the `app` (`web.Application`) is extended by default with the following app keys that are defined in `aidbox_python_sdk.app_keys` module:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from aidbox_python_sdk import app_keys as ak
|
|
131
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
132
|
+
|
|
133
|
+
@sdk.operation(["POST"], ["example"])
|
|
134
|
+
async def update_organization_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
135
|
+
app = request.app
|
|
136
|
+
client = app[ak.client] # AsyncAidboxClient
|
|
137
|
+
sdk = app[ak.sdk] # SDK
|
|
138
|
+
settings = app[ak.settings] # Settings
|
|
139
|
+
db = app[ak.db] # DBProxy
|
|
140
|
+
return web.json_response()
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Usage of FHIR Client
|
|
144
|
+
|
|
145
|
+
FHIR Client is not plugged in by default, however, to use it you can extend the app by adding new AppKey
|
|
146
|
+
|
|
147
|
+
app/app_keys.py
|
|
148
|
+
```python
|
|
149
|
+
from fhirpy import AsyncFHIRClient
|
|
150
|
+
|
|
151
|
+
fhir_client: web.AppKey[AsyncFHIRClient] = web.AppKey("fhir_client", AsyncFHIRClient)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
main.py
|
|
155
|
+
```python
|
|
156
|
+
from collections.abc import AsyncGenerator
|
|
157
|
+
|
|
158
|
+
from aidbox_python_sdk.main import create_app as _create_app
|
|
159
|
+
from aidbox_python_sdk.settings import Settings
|
|
160
|
+
from aidbox_python_sdk.sdk import SDK
|
|
161
|
+
from aiohttp import BasicAuth, web
|
|
162
|
+
from fhirpy import AsyncFHIRClient
|
|
163
|
+
|
|
164
|
+
from app import app_keys as ak
|
|
165
|
+
|
|
166
|
+
settings = Settings(**{})
|
|
167
|
+
sdk = SDK(settings, resources={}, seeds={)
|
|
168
|
+
|
|
169
|
+
def create_app():
|
|
170
|
+
app = await _create_app(SDK)
|
|
171
|
+
app.cleanup_ctx.append(fhir_clients_ctx)
|
|
172
|
+
return app
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
async def create_gunicorn_app() -> web.Application:
|
|
176
|
+
return create_app()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def fhir_clients_ctx(app: web.Application) -> AsyncGenerator[None, None]:
|
|
180
|
+
app[ak.fhir_client] = await init_fhir_client(app[ak.settings], "/fhir")
|
|
181
|
+
|
|
182
|
+
yield
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async def init_fhir_client(settings: Settings, prefix: str = "") -> AsyncFHIRClient:
|
|
186
|
+
basic_auth = BasicAuth(
|
|
187
|
+
login=settings.APP_INIT_CLIENT_ID,
|
|
188
|
+
password=settings.APP_INIT_CLIENT_SECRET,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return AsyncFHIRClient(
|
|
192
|
+
f"{settings.APP_INIT_URL}{prefix}",
|
|
193
|
+
authorization=basic_auth.encode(),
|
|
194
|
+
dump_resource=lambda x: x.model_dump(),
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
After that, you can use `app[ak.fhir_client]` that has the type `AsyncFHIRClient` everywhere where the app is available.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
## Usage of request_schema
|
|
202
|
+
```python
|
|
203
|
+
from aidbox_python_sdk.types import SDKOperation, SDKOperationRequest
|
|
204
|
+
|
|
117
205
|
schema = {
|
|
118
206
|
"required": ["params", "resource"],
|
|
119
207
|
"properties": {
|
|
@@ -137,10 +225,11 @@ schema = {
|
|
|
137
225
|
|
|
138
226
|
|
|
139
227
|
@sdk.operation(["POST"], ["Organization", {"name": "id"}, "$update"], request_schema=schema)
|
|
140
|
-
async def
|
|
228
|
+
async def update_organization_op(_operation: SDKOperation, request: SDKOperationRequest):
|
|
141
229
|
location = request["params"]["location"]
|
|
142
230
|
return web.json_response({"location": location})
|
|
143
231
|
```
|
|
232
|
+
|
|
144
233
|
### Valid request example
|
|
145
234
|
```shell
|
|
146
235
|
POST /Organization/org-1/$update?abc=xyz&location=us
|
|
@@ -148,3 +237,5 @@ POST /Organization/org-1/$update?abc=xyz&location=us
|
|
|
148
237
|
organizationType: non-profit
|
|
149
238
|
employeesCount: 10
|
|
150
239
|
```
|
|
240
|
+
|
|
241
|
+
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
sdk_migrations = [
|
|
2
|
-
{
|
|
3
|
-
"id": "20190909_add_drop_before",
|
|
4
|
-
"sql": """
|
|
5
|
-
DROP FUNCTION IF EXISTS drop_before_all(integer);
|
|
6
|
-
|
|
7
|
-
CREATE FUNCTION drop_before_all(integer) RETURNS VOID AS $$
|
|
8
|
-
declare
|
|
9
|
-
e record;
|
|
10
|
-
BEGIN
|
|
11
|
-
FOR e IN (select LOWER(entity.id) as t_name from entity where resource#>>'{type}' = 'resource' and id != 'OperationOutcome') LOOP
|
|
12
|
-
EXECUTE 'delete from "' || e.t_name || '" where txid > ' || $1 ;
|
|
13
|
-
END LOOP;
|
|
14
|
-
END;
|
|
15
|
-
|
|
16
|
-
$$ LANGUAGE plpgsql;
|
|
17
|
-
""",
|
|
18
|
-
}
|
|
19
|
-
]
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from typing_extensions import TypedDict
|
|
2
|
-
|
|
3
|
-
from aidbox_python_sdk.aidboxpy import AsyncAidboxClient
|
|
4
|
-
from aidbox_python_sdk.db import DBProxy
|
|
5
|
-
from aidbox_python_sdk.sdk import SDK
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class SDKOperationRequestApp(TypedDict):
|
|
9
|
-
client: AsyncAidboxClient
|
|
10
|
-
db: DBProxy
|
|
11
|
-
sdk: SDK
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
SDKOperationRequest = TypedDict(
|
|
15
|
-
"SDKOperationRequest", {"app": SDKOperationRequestApp, "route-params": dict, "resource": dict}
|
|
16
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/requires.txt
RENAMED
|
File without changes
|
{aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|