Skip to content

Commit 0f349a4

Browse files
committed
Drafting client api sdk
1 parent b3f5f20 commit 0f349a4

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# pylint: disable=unused-variable
2+
# pylint: disable=unused-argument
3+
# pylint: disable=redefined-outer-name
4+
# pylint: disable=protected-access
5+
6+
7+
from pprint import pprint
8+
from typing import Dict, List
9+
10+
import pytest
11+
from starlette.testclient import TestClient
12+
13+
from simcore_service_api_gateway import application, endpoints_check
14+
from simcore_service_api_gateway.__version__ import api_vtag
15+
from simcore_service_api_gateway.settings import AppSettings
16+
17+
18+
@pytest.fixture
19+
def client(monkeypatch) -> TestClient:
20+
monkeypatch.setenv("POSTGRES_USER", "test")
21+
monkeypatch.setenv("POSTGRES_PASSWORD", "test")
22+
monkeypatch.setenv("POSTGRES_DB", "test")
23+
monkeypatch.setenv("LOGLEVEL", "debug")
24+
monkeypatch.setenv("SC_BOOT_MODE", "production")
25+
26+
# app
27+
test_settings = AppSettings()
28+
app = application.create(settings=test_settings)
29+
30+
# routes
31+
app.include_router(endpoints_check.router, tags=["check"])
32+
33+
# test client:
34+
# Context manager to trigger events: https://fastapi.tiangolo.com/advanced/testing-events/
35+
with TestClient(app) as cli:
36+
yield cli
37+
38+
39+
# DEV ---------------------------------------------------------------------
40+
import attr
41+
import aiohttp
42+
43+
from yarl import URL
44+
from typing import Optional, Dict, Any
45+
import json
46+
47+
48+
# simcore_api_sdk.abc.py
49+
import abc as _abc
50+
51+
52+
@attr.s(auto_attribs=True)
53+
class ApiResponse:
54+
status: int
55+
headers: Dict
56+
body: Dict
57+
58+
59+
@attr.s(auto_attribs=True)
60+
class ApiConfig:
61+
session: aiohttp.ClientSession
62+
api_key: str = attr.ib(repr=False)
63+
api_secret: str = attr.ib(repr=False)
64+
base_url: URL = URL(f"https://api.osparc.io/{api_vtag}/")
65+
66+
# TODO: add validation here
67+
68+
69+
class API(_abc.ABC):
70+
def __init__(self, cfg: ApiConfig, *, parent=None):
71+
self._cfg = cfg
72+
73+
async def _make_request(
74+
self,
75+
method: str,
76+
url: str,
77+
*,
78+
url_params: Optional[Dict] = None,
79+
body_params: Optional[Dict] = None,
80+
headers: Optional[Dict] = None,
81+
body: bytes = b"",
82+
**requester_params: Any,
83+
) -> ApiResponse:
84+
filled_url = self._cfg.base_url # format_url(url, url_params)
85+
86+
# TODO: it is always json !!
87+
if body_params is not None:
88+
body = json.dumps(body_params)
89+
90+
resp: aiohttp.ClientResponse = await self._cfg.session.request(
91+
method, filled_url, body, headers, **requester_params
92+
)
93+
94+
response = ApiResponse(
95+
status=resp.status, headers=resp.headers, body=await resp.json()
96+
)
97+
return response
98+
99+
100+
# simcore_api_sdk/v0/__init__.py
101+
# from ._openapi import ApiSession
102+
103+
# simcore_api_sdk/v0/me_api.py
104+
from attr import NOTHING
105+
106+
107+
class MeAPI(API):
108+
async def get(self):
109+
pass
110+
111+
async def update(self, *, name: str = NOTHING, full_name: str = NOTHING):
112+
"""
113+
Only writable fields can be updated
114+
"""
115+
116+
117+
@attr.s(auto_attribs=True)
118+
class StudiesAPI(API):
119+
_next_page_token: int = NOTHING
120+
121+
async def list(
122+
self,
123+
*,
124+
page_size: int = NOTHING,
125+
keep_page_token: bool = False,
126+
order_by: str = NOTHING,
127+
filter_fields: str = NOTHING,
128+
):
129+
pass
130+
131+
async def get(self, uid: str):
132+
pass
133+
134+
async def create(self):
135+
pass
136+
137+
async def update(self, uid: str, *, from_other=None, **study_fields):
138+
# TODO: how update fields like a.b.c.??
139+
pass
140+
141+
async def remove(self, uid: str) -> None:
142+
# wait ??
143+
pass
144+
145+
146+
# simcore_api_sdk/v0/_openapi.py
147+
class ApiSession:
148+
def __init__(
149+
self, api_key: str, api_secret: str, base_url: URL = NOTHING,
150+
):
151+
# TODO: setup auth here
152+
self.session = aiohttp.ClientSession(auth=None)
153+
154+
cfg = ApiConfig(self.session, api_key, api_secret, base_url)
155+
self._cfg = cfg
156+
157+
# API
158+
self.me = MeAPI(cfg)
159+
self.studies = StudiesAPI(cfg)
160+
161+
async def __aenter__(self):
162+
return self
163+
164+
async def __aexit__(self, exc_type, exc, tb):
165+
await self.session.close()
166+
167+
168+
# ----------------------------------------------------
169+
170+
async def test_client_sdk():
171+
# TODO: design SDK for these calls
172+
# TODO: these examples should run test tests and automaticaly added to redoc
173+
174+
# from simcore_api_sdk.v0 import ApiSession
175+
176+
async with ApiSession(api_key="1234", api_secret="secret") as api:
177+
178+
# GET /me is a special resource that is unique
179+
me: Dict = await api.me.get()
180+
pprint(me)
181+
182+
await api.me.update(name="pcrespov", full_name="Pedro Crespo")
183+
184+
# corresponds to the studies I have access ??
185+
186+
## https://cloud.google.com/apis/design/standard_methods
187+
188+
# GET /studies
189+
studies: List[Dict] = await api.studies.list()
190+
191+
# Implements Pagination: https://cloud.google.com/apis/design/design_patterns#list_pagination
192+
first_studies = await api.studies.list(page_size=3, keep_page_token=True)
193+
assert api.studies._next_page_token != NOTHING
194+
195+
next_5_studies = await api.studies.list(page_size=5)
196+
197+
# Results ordering: https://cloud.google.com/apis/design/design_patterns#sorting_order
198+
sorted_studies: List[Dict] = await api.studies.list(order_by="foo desc,bar")
199+
200+
# List filter field: https://cloud.google.com/apis/design/naming_convention#list_filter_field
201+
studies: List[Dict] = await api.studies.list(filter_fields="foo.zoo, bar")
202+
assert studies[0]
203+
204+
# GET /studies/{prj_id}
205+
prj: Dict = await api.studies.get("1234")
206+
207+
# POST /studies
208+
new_prj: Dict = await api.studies.create()
209+
210+
# PUT or PATCH /studies/{prj_id}
211+
# this is a patch
212+
await api.studies.update(prj.id, description="Bar")
213+
214+
# this is a put: using copy_from
215+
await api.studies.update(prj.id, copy_from=new_prj)
216+
217+
# DELETE /studies/{prj_id}
218+
await api.studies.remove(prj.id)

0 commit comments

Comments
 (0)