Skip to content

Commit a8aaea0

Browse files
committed
oas
1 parent 1a5dee9 commit a8aaea0

File tree

5 files changed

+46
-53
lines changed

5 files changed

+46
-53
lines changed

api/specs/web-server/_auth.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from typing import Any
88

9-
from _common import Error, Log
9+
from _common import EnvelopeE
1010
from fastapi import APIRouter, status
1111
from models_library.api_schemas_webserver.auth import (
1212
AccountRequestInfo,
@@ -15,6 +15,7 @@
1515
UnregisterCheck,
1616
)
1717
from models_library.generics import Envelope
18+
from models_library.rest_error import ErrorGet, Log
1819
from pydantic import BaseModel, Field, confloat
1920
from simcore_service_webserver._meta import API_VTAG
2021
from simcore_service_webserver.login._2fa_handlers import Resend2faBody
@@ -75,7 +76,7 @@ async def register(_body: RegisterBody):
7576
"/auth/unregister",
7677
response_model=Envelope[Log],
7778
status_code=status.HTTP_200_OK,
78-
responses={status.HTTP_409_CONFLICT: {"model": Envelope[Error]}},
79+
responses={status.HTTP_409_CONFLICT: {"model": EnvelopeE[ErrorGet]}},
7980
)
8081
async def unregister_account(_body: UnregisterCheck):
8182
...
@@ -107,7 +108,7 @@ async def phone_confirmation(_body: PhoneConfirmationBody):
107108
responses={
108109
# status.HTTP_503_SERVICE_UNAVAILABLE
109110
status.HTTP_401_UNAUTHORIZED: {
110-
"model": Envelope[Error],
111+
"model": EnvelopeE[ErrorGet],
111112
"description": "unauthorized reset due to invalid token code",
112113
}
113114
},
@@ -122,7 +123,7 @@ async def login(_body: LoginBody):
122123
operation_id="auth_login_2fa",
123124
responses={
124125
status.HTTP_401_UNAUTHORIZED: {
125-
"model": Envelope[Error],
126+
"model": EnvelopeE[ErrorGet],
126127
"description": "unauthorized reset due to invalid token code",
127128
}
128129
},
@@ -137,7 +138,7 @@ async def login_2fa(_body: LoginTwoFactorAuthBody):
137138
operation_id="auth_resend_2fa_code",
138139
responses={
139140
status.HTTP_401_UNAUTHORIZED: {
140-
"model": Envelope[Error],
141+
"model": EnvelopeE[ErrorGet],
141142
"description": "unauthorized reset due to invalid token code",
142143
}
143144
},
@@ -161,7 +162,7 @@ async def logout(_body: LogoutBody):
161162
status_code=status.HTTP_204_NO_CONTENT,
162163
responses={
163164
status.HTTP_401_UNAUTHORIZED: {
164-
"model": Envelope[Error],
165+
"model": EnvelopeE[ErrorGet],
165166
"description": "unauthorized reset due to invalid token code",
166167
}
167168
},
@@ -174,7 +175,7 @@ async def check_auth():
174175
"/auth/reset-password",
175176
response_model=Envelope[Log],
176177
operation_id="auth_reset_password",
177-
responses={status.HTTP_503_SERVICE_UNAVAILABLE: {"model": Envelope[Error]}},
178+
responses={status.HTTP_503_SERVICE_UNAVAILABLE: {"model": EnvelopeE[ErrorGet]}},
178179
)
179180
async def reset_password(_body: ResetPasswordBody):
180181
"""a non logged-in user requests a password reset"""
@@ -186,7 +187,7 @@ async def reset_password(_body: ResetPasswordBody):
186187
operation_id="auth_reset_password_allowed",
187188
responses={
188189
status.HTTP_401_UNAUTHORIZED: {
189-
"model": Envelope[Error],
190+
"model": EnvelopeE[ErrorGet],
190191
"description": "unauthorized reset due to invalid token code",
191192
}
192193
},
@@ -201,11 +202,11 @@ async def reset_password_allowed(code: str, _body: ResetPasswordConfirmation):
201202
operation_id="auth_change_email",
202203
responses={
203204
status.HTTP_401_UNAUTHORIZED: {
204-
"model": Envelope[Error],
205+
"model": EnvelopeE[ErrorGet],
205206
"description": "unauthorized user. Login required",
206207
},
207208
status.HTTP_503_SERVICE_UNAVAILABLE: {
208-
"model": Envelope[Error],
209+
"model": EnvelopeE[ErrorGet],
209210
"description": "unable to send confirmation email",
210211
},
211212
},
@@ -233,15 +234,15 @@ class PasswordCheckSchema(BaseModel):
233234
operation_id="auth_change_password",
234235
responses={
235236
status.HTTP_401_UNAUTHORIZED: {
236-
"model": Envelope[Error],
237+
"model": EnvelopeE[ErrorGet],
237238
"description": "unauthorized user. Login required",
238239
},
239240
status.HTTP_409_CONFLICT: {
240-
"model": Envelope[Error],
241+
"model": EnvelopeE[ErrorGet],
241242
"description": "mismatch between new and confirmation passwords",
242243
},
243244
status.HTTP_422_UNPROCESSABLE_ENTITY: {
244-
"model": Envelope[Error],
245+
"model": EnvelopeE[ErrorGet],
245246
"description": "current password is invalid",
246247
},
247248
},

api/specs/web-server/_common.py

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,22 @@
55
import sys
66
from collections.abc import Callable
77
from pathlib import Path
8-
from typing import Annotated, NamedTuple, Optional, Union, get_args, get_origin
8+
from typing import (
9+
Annotated,
10+
Any,
11+
Generic,
12+
NamedTuple,
13+
Optional,
14+
TypeVar,
15+
Union,
16+
get_args,
17+
get_origin,
18+
)
919

1020
from common_library.json_serialization import json_dumps
1121
from common_library.pydantic_fields_extension import get_type
1222
from fastapi import Query
13-
from models_library.basic_types import LogLevel
14-
from pydantic import BaseModel, ConfigDict, Field, Json, create_model
23+
from pydantic import BaseModel, Json, create_model
1524
from pydantic.fields import FieldInfo
1625

1726
CURRENT_DIR = Path(sys.argv[0] if __name__ == "__main__" else __file__).resolve().parent
@@ -78,43 +87,14 @@ def as_query(model_class: type[BaseModel]) -> type[BaseModel]:
7887
return create_model(new_model_name, **fields)
7988

8089

81-
class Log(BaseModel):
82-
level: LogLevel | None = Field("INFO", description="log level")
83-
message: str = Field(
84-
...,
85-
description="log message. If logger is USER, then it MUST be human readable",
86-
)
87-
logger: str | None = Field(
88-
None, description="name of the logger receiving this message"
89-
)
90-
91-
model_config = ConfigDict(
92-
json_schema_extra={
93-
"example": {
94-
"message": "Hi there, Mr user",
95-
"level": "INFO",
96-
"logger": "user-logger",
97-
}
98-
}
99-
)
100-
90+
ErrorT = TypeVar("ErrorT")
10191

102-
class ErrorItem(BaseModel):
103-
code: str = Field(
104-
...,
105-
description="Typically the name of the exception that produced it otherwise some known error code",
106-
)
107-
message: str = Field(..., description="Error message specific to this item")
108-
resource: str | None = Field(
109-
None, description="API resource affected by this error"
110-
)
111-
field: str | None = Field(None, description="Specific field within the resource")
11292

93+
class EnvelopeE(BaseModel, Generic[ErrorT]):
94+
"""Complementary to models_library.generics.Envelope just for the generators"""
11395

114-
class Error(BaseModel):
115-
logs: list[Log] | None = Field(None, description="log messages")
116-
errors: list[ErrorItem] | None = Field(None, description="errors metadata")
117-
status: int | None = Field(None, description="HTTP error code")
96+
error: ErrorT | None = None
97+
data: Any | None = None
11898

11999

120100
class ParamSpec(NamedTuple):

api/specs/web-server/_folders.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@
99

1010
from typing import Annotated
1111

12-
from _common import as_query
12+
from _common import EnvelopeE, as_query
1313
from fastapi import APIRouter, Depends, status
1414
from models_library.api_schemas_webserver.folders_v2 import (
1515
FolderCreateBodyParams,
1616
FolderGet,
1717
FolderReplaceBodyParams,
1818
)
1919
from models_library.generics import Envelope
20+
from models_library.rest_error import ErrorGet
2021
from simcore_service_webserver._meta import API_VTAG
22+
from simcore_service_webserver.folders._exceptions_handlers import _TO_HTTP_ERROR_MAP
2123
from simcore_service_webserver.folders._models import (
2224
FolderSearchQueryParams,
2325
FoldersListQueryParams,
@@ -29,6 +31,10 @@
2931
tags=[
3032
"folders",
3133
],
34+
responses={
35+
i.status_code: {"model": EnvelopeE[ErrorGet]}
36+
for i in _TO_HTTP_ERROR_MAP.values()
37+
},
3238
)
3339

3440

api/specs/web-server/_workspaces.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@
99
from enum import Enum
1010
from typing import Annotated
1111

12-
from _common import as_query
12+
from _common import EnvelopeE, as_query
1313
from fastapi import APIRouter, Depends, status
1414
from models_library.api_schemas_webserver.workspaces import (
1515
WorkspaceCreateBodyParams,
1616
WorkspaceGet,
1717
WorkspaceReplaceBodyParams,
1818
)
1919
from models_library.generics import Envelope
20+
from models_library.rest_error import ErrorGet
2021
from simcore_service_webserver._meta import API_VTAG
22+
from simcore_service_webserver.folders._exceptions_handlers import _TO_HTTP_ERROR_MAP
2123
from simcore_service_webserver.workspaces._groups_api import WorkspaceGroupGet
2224
from simcore_service_webserver.workspaces._models import (
2325
WorkspacesGroupsBodyParams,
@@ -31,6 +33,10 @@
3133
tags=[
3234
"workspaces",
3335
],
36+
responses={
37+
i.status_code: {"model": EnvelopeE[ErrorGet]}
38+
for i in _TO_HTTP_ERROR_MAP.values()
39+
},
3440
)
3541

3642

services/web/server/src/simcore_service_webserver/login/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
from aiohttp import web
55
from common_library.json_serialization import json_dumps
66
from models_library.products import ProductName
7+
from models_library.rest_error import LogMessageType
78
from models_library.users import UserID
89
from pydantic import PositiveInt
910
from servicelib.aiohttp import observer
10-
from servicelib.aiohttp.rest_models import LogMessageType
1111
from servicelib.aiohttp.status import HTTP_200_OK
1212
from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON
1313
from simcore_postgres_database.models.users import UserRole

0 commit comments

Comments
 (0)