Skip to content

Commit 4c074d7

Browse files
committed
oas
1 parent 4dffe7c commit 4c074d7

File tree

4 files changed

+147
-15
lines changed

4 files changed

+147
-15
lines changed

api/specs/web-server/_users.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
MyProfilePatch,
1515
MyTokenCreate,
1616
MyTokenGet,
17+
MyUserGet,
18+
MyUsersSearchQueryParams,
1719
UserGet,
1820
UsersSearchQueryParams,
1921
)
@@ -138,23 +140,32 @@ async def list_user_permissions():
138140
...
139141

140142

143+
@router.get(
144+
"/me/users:search",
145+
response_model=Envelope[list[MyUserGet]],
146+
description="Search among users who are publicly visible to the caller (i.e., me) based on their privacy settings.",
147+
)
148+
async def search_users(_params: Annotated[MyUsersSearchQueryParams, Depends()]):
149+
...
150+
151+
141152
_extra_tags: list[str | Enum] = ["admin"]
142153

143154

144155
@router.get(
145-
"/admin/users:search",
156+
"/users:search",
146157
response_model=Envelope[list[UserGet]],
147158
tags=_extra_tags,
148159
)
149-
async def search_users(_params: Annotated[UsersSearchQueryParams, Depends()]):
160+
async def search_users_as_admin(_params: Annotated[UsersSearchQueryParams, Depends()]):
150161
# NOTE: see `Search` in `Common Custom Methods` in https://cloud.google.com/apis/design/custom_methods
151162
...
152163

153164

154165
@router.post(
155-
"/admin/users:pre-register",
166+
"/users:pre-register",
156167
response_model=Envelope[UserGet],
157168
tags=_extra_tags,
158169
)
159-
async def pre_register_user(_body: PreRegisteredUserGet):
170+
async def pre_register_user_as_admin(_body: PreRegisteredUserGet):
160171
...

packages/models-library/src/models_library/api_schemas_webserver/users.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,31 @@
44
from typing import Annotated, Any, Literal, Self
55
from uuid import UUID
66

7+
import annotated_types
78
from common_library.basic_types import DEFAULT_FACTORY
89
from common_library.dict_tools import remap_keys
910
from common_library.users_enums import UserStatus
1011
from models_library.groups import AccessRightsDict
11-
from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
12+
from pydantic import (
13+
BaseModel,
14+
ConfigDict,
15+
EmailStr,
16+
Field,
17+
StringConstraints,
18+
ValidationInfo,
19+
field_validator,
20+
)
1221

1322
from ..basic_types import IDStr
1423
from ..emails import LowerCaseEmailStr
15-
from ..groups import AccessRightsDict, Group, GroupsByTypeTuple
24+
from ..groups import AccessRightsDict, Group, GroupID, GroupsByTypeTuple
1625
from ..products import ProductName
1726
from ..users import (
1827
FirstNameStr,
1928
LastNameStr,
2029
MyProfile,
2130
UserID,
31+
UserNameID,
2232
UserPermission,
2333
UserThirdPartyToken,
2434
)
@@ -186,6 +196,28 @@ def _validate_user_name(cls, value: str):
186196
#
187197

188198

199+
class MyUsersSearchQueryParams(BaseModel):
200+
match_: Annotated[
201+
str,
202+
StringConstraints(strip_whitespace=True, min_length=1, max_length=50),
203+
Field(
204+
description="Search string to match with public usernames and emails",
205+
alias="match",
206+
),
207+
]
208+
limit: Annotated[int, annotated_types.Interval(ge=1, le=50)] = 10
209+
210+
211+
class MyUserGet(OutputSchema):
212+
# Public profile of a user subject to its privacy settings
213+
user_id: UserID
214+
group_id: GroupID
215+
user_name: UserNameID
216+
first_name: str | None = None
217+
last_name: str | None = None
218+
email: EmailStr | None = None
219+
220+
189221
class UsersSearchQueryParams(BaseModel):
190222
email: Annotated[
191223
str,
@@ -198,6 +230,7 @@ class UsersSearchQueryParams(BaseModel):
198230

199231

200232
class UserGet(OutputSchema):
233+
# ONLY for admins
201234
first_name: str | None
202235
last_name: str | None
203236
email: LowerCaseEmailStr

services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,14 +1323,47 @@ paths:
13231323
application/json:
13241324
schema:
13251325
$ref: '#/components/schemas/Envelope_list_MyPermissionGet__'
1326-
/v0/users:search:
1326+
/v0/me/users:search:
13271327
get:
13281328
tags:
13291329
- user
1330-
- po
13311330
summary: Search Users
1331+
description: Search among users who are publicly visible to the caller (i.e.,
1332+
me) based on their privacy settings.
13321333
operationId: search_users
13331334
parameters:
1335+
- name: match
1336+
in: query
1337+
required: true
1338+
schema:
1339+
type: string
1340+
minLength: 1
1341+
maxLength: 50
1342+
title: Match
1343+
- name: limit
1344+
in: query
1345+
required: false
1346+
schema:
1347+
type: integer
1348+
maximum: 50
1349+
minimum: 1
1350+
default: 10
1351+
title: Limit
1352+
responses:
1353+
'200':
1354+
description: Successful Response
1355+
content:
1356+
application/json:
1357+
schema:
1358+
$ref: '#/components/schemas/Envelope_list_MyUserGet__'
1359+
/v0/users:search:
1360+
get:
1361+
tags:
1362+
- user
1363+
- admin
1364+
summary: Search Users As Admin
1365+
operationId: search_users_as_admin
1366+
parameters:
13341367
- name: email
13351368
in: query
13361369
required: true
@@ -1350,9 +1383,9 @@ paths:
13501383
post:
13511384
tags:
13521385
- user
1353-
- po
1354-
summary: Pre Register User
1355-
operationId: pre_register_user
1386+
- admin
1387+
summary: Pre Register User As Admin
1388+
operationId: pre_register_user_as_admin
13561389
requestBody:
13571390
content:
13581391
application/json:
@@ -8962,6 +8995,22 @@ components:
89628995
title: Error
89638996
type: object
89648997
title: Envelope[list[MyTokenGet]]
8998+
Envelope_list_MyUserGet__:
8999+
properties:
9000+
data:
9001+
anyOf:
9002+
- items:
9003+
$ref: '#/components/schemas/MyUserGet'
9004+
type: array
9005+
- type: 'null'
9006+
title: Data
9007+
error:
9008+
anyOf:
9009+
- {}
9010+
- type: 'null'
9011+
title: Error
9012+
type: object
9013+
title: Envelope[list[MyUserGet]]
89659014
Envelope_list_OsparcCreditsAggregatedByServiceGet__:
89669015
properties:
89679016
data:
@@ -10847,6 +10896,45 @@ components:
1084710896
- service
1084810897
- token_key
1084910898
title: MyTokenGet
10899+
MyUserGet:
10900+
properties:
10901+
userId:
10902+
type: integer
10903+
exclusiveMinimum: true
10904+
title: Userid
10905+
minimum: 0
10906+
groupId:
10907+
type: integer
10908+
exclusiveMinimum: true
10909+
title: Groupid
10910+
minimum: 0
10911+
userName:
10912+
type: string
10913+
maxLength: 100
10914+
minLength: 1
10915+
title: Username
10916+
firstName:
10917+
anyOf:
10918+
- type: string
10919+
- type: 'null'
10920+
title: Firstname
10921+
lastName:
10922+
anyOf:
10923+
- type: string
10924+
- type: 'null'
10925+
title: Lastname
10926+
email:
10927+
anyOf:
10928+
- type: string
10929+
format: email
10930+
- type: 'null'
10931+
title: Email
10932+
type: object
10933+
required:
10934+
- userId
10935+
- groupId
10936+
- userName
10937+
title: MyUserGet
1085010938
Node-Input:
1085110939
properties:
1085210940
key:

services/web/server/src/simcore_service_webserver/users/_users_rest.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,11 @@ async def update_my_profile(request: web.Request) -> web.Response:
136136
_RESPONSE_MODEL_MINIMAL_POLICY["exclude_none"] = True
137137

138138

139-
@routes.get(f"/{API_VTAG}/users:search", name="search_users")
139+
@routes.get(f"/{API_VTAG}/admin/users:search", name="search_users_as_admin")
140140
@login_required
141141
@permission_required("user.users.*")
142142
@_handle_users_exceptions
143-
async def search_users(request: web.Request) -> web.Response:
143+
async def search_users_as_admin(request: web.Request) -> web.Response:
144144
req_ctx = UsersRequestContext.model_validate(request)
145145
assert req_ctx.product_name # nosec
146146

@@ -157,11 +157,11 @@ async def search_users(request: web.Request) -> web.Response:
157157
)
158158

159159

160-
@routes.post(f"/{API_VTAG}/users:pre-register", name="pre_register_user")
160+
@routes.post(f"/{API_VTAG}/admin/users:pre-register", name="pre_register_user_as_admin")
161161
@login_required
162162
@permission_required("user.users.*")
163163
@_handle_users_exceptions
164-
async def pre_register_user(request: web.Request) -> web.Response:
164+
async def pre_register_user_as_admin(request: web.Request) -> web.Response:
165165
req_ctx = UsersRequestContext.model_validate(request)
166166
pre_user_profile = await parse_request_body_as(PreRegisteredUserGet, request)
167167

0 commit comments

Comments
 (0)