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.
Files changed (27) hide show
  1. {aidbox_python_sdk-0.1.9/aidbox_python_sdk.egg-info → aidbox_python_sdk-0.1.12}/PKG-INFO +101 -10
  2. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/README.md +100 -9
  3. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/__init__.py +1 -1
  4. aidbox_python_sdk-0.1.12/aidbox_python_sdk/app_keys.py +11 -0
  5. aidbox_python_sdk-0.1.12/aidbox_python_sdk/db_migrations.py +38 -0
  6. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/handlers.py +7 -4
  7. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/main.py +17 -12
  8. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/pytest_plugin.py +13 -12
  9. aidbox_python_sdk-0.1.12/aidbox_python_sdk/types.py +13 -0
  10. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12/aidbox_python_sdk.egg-info}/PKG-INFO +101 -10
  11. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/SOURCES.txt +1 -0
  12. aidbox_python_sdk-0.1.9/aidbox_python_sdk/db_migrations.py +0 -19
  13. aidbox_python_sdk-0.1.9/aidbox_python_sdk/types.py +0 -16
  14. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/LICENSE.md +0 -0
  15. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/MANIFEST.in +0 -0
  16. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/aidboxpy.py +0 -0
  17. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/db.py +0 -0
  18. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/exceptions.py +0 -0
  19. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/sdk.py +0 -0
  20. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk/settings.py +0 -0
  21. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/dependency_links.txt +0 -0
  22. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/not-zip-safe +0 -0
  23. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/requires.txt +0 -0
  24. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/aidbox_python_sdk.egg-info/top_level.txt +0 -0
  25. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/pyproject.toml +0 -0
  26. {aidbox_python_sdk-0.1.9 → aidbox_python_sdk-0.1.12}/setup.cfg +0 -0
  27. {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.9
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
- ```Python
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=resources, seeds=seeds)
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
- ```Python
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(operation, request):
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
- ## Validate request
116
- ```Python
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 update_organization_handler(operation, request):
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
- ```Python
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=resources, seeds=seeds)
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
- ```Python
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(operation, request):
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
- ## Validate request
55
- ```Python
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 update_organization_handler(operation, request):
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
+
@@ -1,5 +1,5 @@
1
1
  __title__ = "aidbox-python-sdk"
2
- __version__ = "0.1.9"
2
+ __version__ = "0.1.12"
3
3
  __author__ = "beda.software"
4
4
  __license__ = "None"
5
5
  __copyright__ = "Copyright 2024 beda.software"
@@ -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["sdk"].get_subscription_handler(data["handler"])
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["sdk"].get_operation_handler(data["operation"]["id"])
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["client"] = await init_client(app["settings"])
59
- app["db"] = DBProxy(app["settings"])
60
- await register_app(app["sdk"], app["client"])
61
- await app["db"].initialize()
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["db"].deinitialize()
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.update(
70
- settings=sdk.settings,
71
- sdk=sdk,
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["sdk"]
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["settings"].APP_INIT_CLIENT_ID,
46
- password=app["settings"].APP_INIT_CLIENT_SECRET,
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["sdk"]
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["client"]
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["db"]
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.9
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
- ```Python
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=resources, seeds=seeds)
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
- ```Python
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(operation, request):
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
- ## Validate request
116
- ```Python
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 update_organization_handler(operation, request):
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
+
@@ -4,6 +4,7 @@ README.md
4
4
  pyproject.toml
5
5
  aidbox_python_sdk/__init__.py
6
6
  aidbox_python_sdk/aidboxpy.py
7
+ aidbox_python_sdk/app_keys.py
7
8
  aidbox_python_sdk/db.py
8
9
  aidbox_python_sdk/db_migrations.py
9
10
  aidbox_python_sdk/exceptions.py
@@ -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
- )