Skip to content

Commit d2908b5

Browse files
committed
oas
1 parent dd4f14f commit d2908b5

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
@@ -3,21 +3,31 @@
33
from enum import Enum
44
from typing import Annotated, Any, Literal, Self
55

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

1221
from ..basic_types import IDStr
1322
from ..emails import LowerCaseEmailStr
14-
from ..groups import AccessRightsDict, Group, GroupsByTypeTuple
23+
from ..groups import AccessRightsDict, Group, GroupID, GroupsByTypeTuple
1524
from ..products import ProductName
1625
from ..users import (
1726
FirstNameStr,
1827
LastNameStr,
1928
MyProfile,
2029
UserID,
30+
UserNameID,
2131
UserPermission,
2232
UserThirdPartyToken,
2333
)
@@ -185,6 +195,28 @@ def _validate_user_name(cls, value: str):
185195
#
186196

187197

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

198230

199231
class UserGet(OutputSchema):
232+
# ONLY for admins
200233
first_name: str | None
201234
last_name: str | None
202235
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:
@@ -10855,6 +10904,45 @@ components:
1085510904
- service
1085610905
- token_key
1085710906
title: MyTokenGet
10907+
MyUserGet:
10908+
properties:
10909+
userId:
10910+
type: integer
10911+
exclusiveMinimum: true
10912+
title: Userid
10913+
minimum: 0
10914+
groupId:
10915+
type: integer
10916+
exclusiveMinimum: true
10917+
title: Groupid
10918+
minimum: 0
10919+
userName:
10920+
type: string
10921+
maxLength: 100
10922+
minLength: 1
10923+
title: Username
10924+
firstName:
10925+
anyOf:
10926+
- type: string
10927+
- type: 'null'
10928+
title: Firstname
10929+
lastName:
10930+
anyOf:
10931+
- type: string
10932+
- type: 'null'
10933+
title: Lastname
10934+
email:
10935+
anyOf:
10936+
- type: string
10937+
format: email
10938+
- type: 'null'
10939+
title: Email
10940+
type: object
10941+
required:
10942+
- userId
10943+
- groupId
10944+
- userName
10945+
title: MyUserGet
1085810946
Node-Input:
1085910947
properties:
1086010948
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)