Skip to content

Commit 8814283

Browse files
continue refactoring
1 parent 5761e24 commit 8814283

File tree

6 files changed

+52
-79
lines changed

6 files changed

+52
-79
lines changed

api/specs/web-server/_auth_api_keys.py

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from models_library.rest_error import EnvelopedError
77
from simcore_service_webserver._meta import API_VTAG
88
from simcore_service_webserver.api_keys._exceptions_handlers import _TO_HTTP_ERROR_MAP
9-
from simcore_service_webserver.api_keys._models import ApiKeysPathParams
9+
from simcore_service_webserver.api_keys._rest import ApiKeysPathParams
1010

1111
router = APIRouter(
1212
prefix=f"/{API_VTAG}",
@@ -22,17 +22,6 @@
2222
operation_id="create_api_key",
2323
status_code=status.HTTP_201_CREATED,
2424
response_model=Envelope[ApiKeyGet],
25-
# responses={
26-
# status.HTTP_400_BAD_REQUEST: {
27-
# "description": "key name requested is invalid",
28-
# },
29-
# status.HTTP_401_UNAUTHORIZED: {
30-
# "description": "requires login to list keys",
31-
# },
32-
# status.HTTP_403_FORBIDDEN: {
33-
# "description": "not enough permissions to list keys",
34-
# },
35-
# },
3625
)
3726
async def create_api_key(_body: ApiKeyCreate):
3827
"""creates API keys to access public API"""
@@ -43,17 +32,6 @@ async def create_api_key(_body: ApiKeyCreate):
4332
operation_id="list_display_names",
4433
response_model=Envelope[list[str]],
4534
status_code=status.HTTP_200_OK,
46-
# responses={
47-
# status.HTTP_400_BAD_REQUEST: {
48-
# "description": "key name requested is invalid",
49-
# },
50-
# status.HTTP_401_UNAUTHORIZED: {
51-
# "description": "requires login to list keys",
52-
# },
53-
# status.HTTP_403_FORBIDDEN: {
54-
# "description": "not enough permissions to list keys",
55-
# },
56-
# },
5735
)
5836
async def list_api_keys():
5937
"""lists display names of API keys by this user"""
@@ -64,17 +42,6 @@ async def list_api_keys():
6442
operation_id="get_api_key",
6543
response_model=Envelope[ApiKeyGet],
6644
status_code=status.HTTP_200_OK,
67-
# responses={
68-
# status.HTTP_400_BAD_REQUEST: {
69-
# "description": "key name requested is invalid",
70-
# },
71-
# status.HTTP_401_UNAUTHORIZED: {
72-
# "description": "requires login to get the keu",
73-
# },
74-
# status.HTTP_403_FORBIDDEN: {
75-
# "description": "not enough permissions to get the keu",
76-
# },
77-
# },
7845
)
7946
async def get_api_key(_path: Annotated[ApiKeysPathParams, Depends()]):
8047
"""returns the key or None"""
@@ -84,14 +51,6 @@ async def get_api_key(_path: Annotated[ApiKeysPathParams, Depends()]):
8451
"/auth/api-keys",
8552
operation_id="delete_api_key",
8653
status_code=status.HTTP_204_NO_CONTENT,
87-
# responses={
88-
# status.HTTP_401_UNAUTHORIZED: {
89-
# "description": "requires login to delete a key",
90-
# },
91-
# status.HTTP_403_FORBIDDEN: {
92-
# "description": "not enough permissions to delete a key",
93-
# },
94-
# },
9554
)
9655
async def delete_api_key(_path: Annotated[ApiKeysPathParams, Depends()]):
9756
"""deletes API key by ID"""

services/web/server/src/simcore_service_webserver/api_keys/_api.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
from typing import Final
55

66
from aiohttp import web
7-
from models_library.api_schemas_webserver.auth import ApiKeyCreate, ApiKeyGet
7+
from models_library.api_schemas_webserver.auth import ApiKeyGet
88
from models_library.products import ProductName
99
from models_library.users import UserID
1010
from servicelib.utils_secrets import generate_token_secret_key
11+
from simcore_service_webserver.api_keys._models import ApiKey
1112

1213
from . import _db
1314
from .errors import ApiKeyNotFoundError
@@ -42,27 +43,20 @@ def _generate_api_key_and_secret(name: str):
4243
async def create_api_key(
4344
app: web.Application,
4445
*,
45-
new: ApiKeyCreate,
46+
display_name=str,
47+
expiration=dt.timedelta,
4648
user_id: UserID,
4749
product_name: ProductName,
48-
) -> ApiKeyGet:
50+
) -> ApiKey:
4951
# generate key and secret
50-
api_key, api_secret = _generate_api_key_and_secret(new.display_name)
52+
api_key, api_secret = _generate_api_key_and_secret(display_name)
5153

52-
# raises if name exists already!
53-
api_key_id = await _db.create(
54+
return await _db.create(
5455
app,
5556
user_id=user_id,
5657
product_name=product_name,
57-
display_name=new.display_name,
58-
expiration=new.expiration,
59-
api_key=api_key,
60-
api_secret=api_secret,
61-
)
62-
63-
return ApiKeyGet(
64-
id_=api_key_id,
65-
display_name=new.display_name,
58+
display_name=display_name,
59+
expiration=expiration,
6660
api_key=api_key,
6761
api_secret=api_secret,
6862
)

services/web/server/src/simcore_service_webserver/api_keys/_db.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
import sqlalchemy as sa
55
from aiohttp import web
66
from models_library.api_schemas_api_server.api_keys import ApiKeyInDB
7-
from models_library.basic_types import IdInt
87
from models_library.products import ProductName
98
from models_library.users import UserID
10-
from pydantic import TypeAdapter
119
from simcore_postgres_database.models.api_keys import api_keys
1210
from simcore_postgres_database.utils_repos import transaction_context
11+
from simcore_service_webserver.api_keys._models import ApiKey
1312
from sqlalchemy.dialects.postgresql import insert as pg_insert
1413
from sqlalchemy.ext.asyncio import AsyncConnection
1514

@@ -45,7 +44,7 @@ async def create(
4544
expiration: timedelta | None,
4645
api_key: str,
4746
api_secret: str,
48-
) -> IdInt:
47+
) -> ApiKey:
4948
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
5049
stmt = (
5150
api_keys.insert()
@@ -62,7 +61,14 @@ async def create(
6261

6362
result = await conn.stream(stmt)
6463
row = await result.first()
65-
return TypeAdapter(IdInt).validate_python(row.id)
64+
65+
return ApiKey(
66+
id=row.id,
67+
display_name=display_name,
68+
expiration=expiration,
69+
api_key=api_key,
70+
api_secret=api_secret,
71+
)
6672

6773

6874
async def get(
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
from models_library.rest_base import StrictRequestParameters
1+
import datetime as dt
2+
from dataclasses import dataclass
23

34

4-
class ApiKeysPathParams(StrictRequestParameters):
5-
api_key_id: int
5+
@dataclass
6+
class ApiKey:
7+
id: str
8+
display_name: str
9+
expiration: dt.timedelta
10+
api_key: str
11+
api_secret: str

services/web/server/src/simcore_service_webserver/api_keys/_rest.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from aiohttp import web
44
from aiohttp.web import RouteTableDef
55
from models_library.api_schemas_webserver.auth import ApiKeyCreate, ApiKeyGet
6+
from models_library.rest_base import StrictRequestParameters
67
from servicelib.aiohttp import status
78
from servicelib.aiohttp.requests_validation import (
89
parse_request_body_as,
@@ -13,7 +14,6 @@
1314
from simcore_service_webserver.api_keys._exceptions_handlers import (
1415
handle_plugin_requests_exceptions,
1516
)
16-
from simcore_service_webserver.api_keys._models import ApiKeysPathParams
1717

1818
from .._meta import API_VTAG
1919
from ..login.decorators import login_required
@@ -28,6 +28,10 @@
2828
routes = RouteTableDef()
2929

3030

31+
class ApiKeysPathParams(StrictRequestParameters):
32+
api_key_id: int
33+
34+
3135
@routes.get(f"/{API_VTAG}/auth/api-keys", name="list_api_keys")
3236
@login_required
3337
@permission_required("user.apikey.*")
@@ -64,22 +68,25 @@ async def api_key_get(request: web.Request):
6468
@handle_plugin_requests_exceptions
6569
async def create_api_key(request: web.Request):
6670
req_ctx = RequestContext.model_validate(request)
67-
new = await parse_request_body_as(ApiKeyCreate, request)
71+
new_api_key = await parse_request_body_as(ApiKeyCreate, request)
6872
try:
69-
resp: ApiKeyGet = await _api.create_api_key(
73+
created_api_key = await _api.create_api_key(
7074
request.app,
71-
new=new,
75+
display_name=new_api_key.display_name,
76+
expiration=new_api_key.expiration,
7277
user_id=req_ctx.user_id,
7378
product_name=req_ctx.product_name,
7479
)
75-
# resp.api_base_url = TODO: https://github.com/ITISFoundation/osparc-simcore/issues/6340
80+
81+
api_key = ApiKeyGet.model_validate(created_api_key)
82+
# api_key.api_base_url = TODO: https://github.com/ITISFoundation/osparc-simcore/issues/6340
7683
except DatabaseError as err:
7784
raise web.HTTPBadRequest(
7885
reason="Invalid API key name: already exists",
7986
content_type=MIMETYPE_APPLICATION_JSON,
8087
) from err
8188

82-
return envelope_json_response(resp)
89+
return envelope_json_response(api_key)
8390

8491

8592
@routes.delete(f"/{API_VTAG}/auth/api-keys/{{api_key_id}}", name="delete_api_key")

services/web/server/tests/unit/with_dbs/01/test_api_keys.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@
1010
from http.client import HTTPException
1111

1212
import pytest
13-
import simcore_service_webserver.api_keys._db as db
1413
from aiohttp.test_utils import TestClient
1514
from faker import Faker
1615
from models_library.products import ProductName
1716
from pytest_simcore.helpers.assert_checks import assert_status
1817
from pytest_simcore.helpers.webserver_login import NewUser, UserInfoDict
1918
from servicelib.aiohttp import status
19+
from simcore_service_webserver.api_keys import _db
2020
from simcore_service_webserver.api_keys._api import (
2121
get_or_create_api_key,
2222
prune_expired_api_keys,
2323
)
24+
from simcore_service_webserver.api_keys._models import ApiKey
2425
from simcore_service_webserver.db.models import UserRole
2526

2627

@@ -32,8 +33,8 @@ async def fake_user_api_keys(
3233
faker: Faker,
3334
) -> AsyncIterable[list[int]]:
3435
assert client.app
35-
api_key_ids: list[int] = [
36-
await db.create(
36+
api_keys: list[ApiKey] = [
37+
await _db.create(
3738
client.app,
3839
user_id=logged_user["id"],
3940
product_name=osparc_product_name,
@@ -45,12 +46,12 @@ async def fake_user_api_keys(
4546
for _ in range(5)
4647
]
4748

48-
yield api_key_ids
49+
yield api_keys
4950

50-
for api_key_id in api_key_ids:
51-
await db.delete(
51+
for api_key in api_keys:
52+
await _db.delete(
5253
client.app,
53-
api_key_id=api_key_id,
54+
api_key_id=api_key.id,
5455
user_id=logged_user["id"],
5556
product_name=osparc_product_name,
5657
)
@@ -127,8 +128,8 @@ async def test_delete_api_keys(
127128
resp = await client.delete("/v0/auth/api-keys/0")
128129
await assert_status(resp, expected)
129130

130-
for api_key_id in fake_user_api_keys:
131-
resp = await client.delete(f"/v0/auth/api-keys/{api_key_id}")
131+
for api_key in fake_user_api_keys:
132+
resp = await client.delete(f"/v0/auth/api-keys/{api_key.id}")
132133
await assert_status(resp, expected)
133134

134135

0 commit comments

Comments
 (0)