arize-phoenix 11.23.1__py3-none-any.whl → 12.28.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/METADATA +61 -36
- {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/RECORD +212 -162
- {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/WHEEL +1 -1
- {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/IP_NOTICE +1 -1
- phoenix/__generated__/__init__.py +0 -0
- phoenix/__generated__/classification_evaluator_configs/__init__.py +20 -0
- phoenix/__generated__/classification_evaluator_configs/_document_relevance_classification_evaluator_config.py +17 -0
- phoenix/__generated__/classification_evaluator_configs/_hallucination_classification_evaluator_config.py +17 -0
- phoenix/__generated__/classification_evaluator_configs/_models.py +18 -0
- phoenix/__generated__/classification_evaluator_configs/_tool_selection_classification_evaluator_config.py +17 -0
- phoenix/__init__.py +2 -1
- phoenix/auth.py +27 -2
- phoenix/config.py +1594 -81
- phoenix/db/README.md +546 -28
- phoenix/db/bulk_inserter.py +119 -116
- phoenix/db/engines.py +140 -33
- phoenix/db/facilitator.py +22 -1
- phoenix/db/helpers.py +818 -65
- phoenix/db/iam_auth.py +64 -0
- phoenix/db/insertion/dataset.py +133 -1
- phoenix/db/insertion/document_annotation.py +9 -6
- phoenix/db/insertion/evaluation.py +2 -3
- phoenix/db/insertion/helpers.py +2 -2
- phoenix/db/insertion/session_annotation.py +176 -0
- phoenix/db/insertion/span_annotation.py +3 -4
- phoenix/db/insertion/trace_annotation.py +3 -4
- phoenix/db/insertion/types.py +41 -18
- phoenix/db/migrations/versions/01a8342c9cdf_add_user_id_on_datasets.py +40 -0
- phoenix/db/migrations/versions/0df286449799_add_session_annotations_table.py +105 -0
- phoenix/db/migrations/versions/272b66ff50f8_drop_single_indices.py +119 -0
- phoenix/db/migrations/versions/58228d933c91_dataset_labels.py +67 -0
- phoenix/db/migrations/versions/699f655af132_experiment_tags.py +57 -0
- phoenix/db/migrations/versions/735d3d93c33e_add_composite_indices.py +41 -0
- phoenix/db/migrations/versions/ab513d89518b_add_user_id_on_dataset_versions.py +40 -0
- phoenix/db/migrations/versions/d0690a79ea51_users_on_experiments.py +40 -0
- phoenix/db/migrations/versions/deb2c81c0bb2_dataset_splits.py +139 -0
- phoenix/db/migrations/versions/e76cbd66ffc3_add_experiments_dataset_examples.py +87 -0
- phoenix/db/models.py +364 -56
- phoenix/db/pg_config.py +10 -0
- phoenix/db/types/trace_retention.py +7 -6
- phoenix/experiments/functions.py +69 -19
- phoenix/inferences/inferences.py +1 -2
- phoenix/server/api/auth.py +9 -0
- phoenix/server/api/auth_messages.py +46 -0
- phoenix/server/api/context.py +60 -0
- phoenix/server/api/dataloaders/__init__.py +36 -0
- phoenix/server/api/dataloaders/annotation_summaries.py +60 -8
- phoenix/server/api/dataloaders/average_experiment_repeated_run_group_latency.py +50 -0
- phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -24
- phoenix/server/api/dataloaders/cache/two_tier_cache.py +1 -2
- phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
- phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -1
- phoenix/server/api/dataloaders/dataset_example_splits.py +40 -0
- phoenix/server/api/dataloaders/dataset_examples_and_versions_by_experiment_run.py +47 -0
- phoenix/server/api/dataloaders/dataset_labels.py +36 -0
- phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -2
- phoenix/server/api/dataloaders/document_evaluations.py +6 -9
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
- phoenix/server/api/dataloaders/experiment_dataset_splits.py +43 -0
- phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
- phoenix/server/api/dataloaders/experiment_repeated_run_group_annotation_summaries.py +77 -0
- phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +57 -0
- phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
- phoenix/server/api/dataloaders/latency_ms_quantile.py +40 -8
- phoenix/server/api/dataloaders/record_counts.py +37 -10
- phoenix/server/api/dataloaders/session_annotations_by_session.py +29 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment_repeated_run_group.py +64 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_project.py +28 -14
- phoenix/server/api/dataloaders/span_costs.py +3 -9
- phoenix/server/api/dataloaders/table_fields.py +2 -2
- phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
- phoenix/server/api/dataloaders/trace_annotations_by_trace.py +27 -0
- phoenix/server/api/exceptions.py +5 -1
- phoenix/server/api/helpers/playground_clients.py +263 -83
- phoenix/server/api/helpers/playground_spans.py +2 -1
- phoenix/server/api/helpers/playground_users.py +26 -0
- phoenix/server/api/helpers/prompts/conversions/google.py +103 -0
- phoenix/server/api/helpers/prompts/models.py +61 -19
- phoenix/server/api/input_types/{SpanAnnotationFilter.py → AnnotationFilter.py} +22 -14
- phoenix/server/api/input_types/ChatCompletionInput.py +3 -0
- phoenix/server/api/input_types/CreateProjectSessionAnnotationInput.py +37 -0
- phoenix/server/api/input_types/DatasetFilter.py +5 -2
- phoenix/server/api/input_types/ExperimentRunSort.py +237 -0
- phoenix/server/api/input_types/GenerativeModelInput.py +3 -0
- phoenix/server/api/input_types/ProjectSessionSort.py +158 -1
- phoenix/server/api/input_types/PromptVersionInput.py +47 -1
- phoenix/server/api/input_types/SpanSort.py +3 -2
- phoenix/server/api/input_types/UpdateAnnotationInput.py +34 -0
- phoenix/server/api/input_types/UserRoleInput.py +1 -0
- phoenix/server/api/mutations/__init__.py +8 -0
- phoenix/server/api/mutations/annotation_config_mutations.py +8 -8
- phoenix/server/api/mutations/api_key_mutations.py +15 -20
- phoenix/server/api/mutations/chat_mutations.py +106 -37
- phoenix/server/api/mutations/dataset_label_mutations.py +243 -0
- phoenix/server/api/mutations/dataset_mutations.py +21 -16
- phoenix/server/api/mutations/dataset_split_mutations.py +351 -0
- phoenix/server/api/mutations/experiment_mutations.py +2 -2
- phoenix/server/api/mutations/export_events_mutations.py +3 -3
- phoenix/server/api/mutations/model_mutations.py +11 -9
- phoenix/server/api/mutations/project_mutations.py +4 -4
- phoenix/server/api/mutations/project_session_annotations_mutations.py +158 -0
- phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
- phoenix/server/api/mutations/prompt_label_mutations.py +74 -65
- phoenix/server/api/mutations/prompt_mutations.py +65 -129
- phoenix/server/api/mutations/prompt_version_tag_mutations.py +11 -8
- phoenix/server/api/mutations/span_annotations_mutations.py +15 -10
- phoenix/server/api/mutations/trace_annotations_mutations.py +13 -8
- phoenix/server/api/mutations/trace_mutations.py +3 -3
- phoenix/server/api/mutations/user_mutations.py +55 -26
- phoenix/server/api/queries.py +501 -617
- phoenix/server/api/routers/__init__.py +2 -2
- phoenix/server/api/routers/auth.py +141 -87
- phoenix/server/api/routers/ldap.py +229 -0
- phoenix/server/api/routers/oauth2.py +349 -101
- phoenix/server/api/routers/v1/__init__.py +22 -4
- phoenix/server/api/routers/v1/annotation_configs.py +19 -30
- phoenix/server/api/routers/v1/annotations.py +455 -13
- phoenix/server/api/routers/v1/datasets.py +355 -68
- phoenix/server/api/routers/v1/documents.py +142 -0
- phoenix/server/api/routers/v1/evaluations.py +20 -28
- phoenix/server/api/routers/v1/experiment_evaluations.py +16 -6
- phoenix/server/api/routers/v1/experiment_runs.py +335 -59
- phoenix/server/api/routers/v1/experiments.py +475 -47
- phoenix/server/api/routers/v1/projects.py +16 -50
- phoenix/server/api/routers/v1/prompts.py +50 -39
- phoenix/server/api/routers/v1/sessions.py +108 -0
- phoenix/server/api/routers/v1/spans.py +156 -96
- phoenix/server/api/routers/v1/traces.py +51 -77
- phoenix/server/api/routers/v1/users.py +64 -24
- phoenix/server/api/routers/v1/utils.py +3 -7
- phoenix/server/api/subscriptions.py +257 -93
- phoenix/server/api/types/Annotation.py +90 -23
- phoenix/server/api/types/ApiKey.py +13 -17
- phoenix/server/api/types/AuthMethod.py +1 -0
- phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +1 -0
- phoenix/server/api/types/Dataset.py +199 -72
- phoenix/server/api/types/DatasetExample.py +88 -18
- phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
- phoenix/server/api/types/DatasetLabel.py +57 -0
- phoenix/server/api/types/DatasetSplit.py +98 -0
- phoenix/server/api/types/DatasetVersion.py +49 -4
- phoenix/server/api/types/DocumentAnnotation.py +212 -0
- phoenix/server/api/types/Experiment.py +215 -68
- phoenix/server/api/types/ExperimentComparison.py +3 -9
- phoenix/server/api/types/ExperimentRepeatedRunGroup.py +155 -0
- phoenix/server/api/types/ExperimentRepeatedRunGroupAnnotationSummary.py +9 -0
- phoenix/server/api/types/ExperimentRun.py +120 -70
- phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
- phoenix/server/api/types/GenerativeModel.py +95 -42
- phoenix/server/api/types/GenerativeProvider.py +1 -1
- phoenix/server/api/types/ModelInterface.py +7 -2
- phoenix/server/api/types/PlaygroundModel.py +12 -2
- phoenix/server/api/types/Project.py +218 -185
- phoenix/server/api/types/ProjectSession.py +146 -29
- phoenix/server/api/types/ProjectSessionAnnotation.py +187 -0
- phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
- phoenix/server/api/types/Prompt.py +119 -39
- phoenix/server/api/types/PromptLabel.py +42 -25
- phoenix/server/api/types/PromptVersion.py +11 -8
- phoenix/server/api/types/PromptVersionTag.py +65 -25
- phoenix/server/api/types/Span.py +130 -123
- phoenix/server/api/types/SpanAnnotation.py +189 -42
- phoenix/server/api/types/SystemApiKey.py +65 -1
- phoenix/server/api/types/Trace.py +184 -53
- phoenix/server/api/types/TraceAnnotation.py +149 -50
- phoenix/server/api/types/User.py +128 -33
- phoenix/server/api/types/UserApiKey.py +73 -26
- phoenix/server/api/types/node.py +10 -0
- phoenix/server/api/types/pagination.py +11 -2
- phoenix/server/app.py +154 -36
- phoenix/server/authorization.py +5 -4
- phoenix/server/bearer_auth.py +13 -5
- phoenix/server/cost_tracking/cost_model_lookup.py +42 -14
- phoenix/server/cost_tracking/model_cost_manifest.json +1085 -194
- phoenix/server/daemons/generative_model_store.py +61 -9
- phoenix/server/daemons/span_cost_calculator.py +10 -8
- phoenix/server/dml_event.py +13 -0
- phoenix/server/email/sender.py +29 -2
- phoenix/server/grpc_server.py +9 -9
- phoenix/server/jwt_store.py +8 -6
- phoenix/server/ldap.py +1449 -0
- phoenix/server/main.py +9 -3
- phoenix/server/oauth2.py +330 -12
- phoenix/server/prometheus.py +43 -6
- phoenix/server/rate_limiters.py +4 -9
- phoenix/server/retention.py +33 -20
- phoenix/server/session_filters.py +49 -0
- phoenix/server/static/.vite/manifest.json +51 -53
- phoenix/server/static/assets/components-BreFUQQa.js +6702 -0
- phoenix/server/static/assets/{index-BPCwGQr8.js → index-CTQoemZv.js} +42 -35
- phoenix/server/static/assets/pages-DBE5iYM3.js +9524 -0
- phoenix/server/static/assets/vendor-BGzfc4EU.css +1 -0
- phoenix/server/static/assets/vendor-DCE4v-Ot.js +920 -0
- phoenix/server/static/assets/vendor-codemirror-D5f205eT.js +25 -0
- phoenix/server/static/assets/{vendor-recharts-Bw30oz1A.js → vendor-recharts-V9cwpXsm.js} +7 -7
- phoenix/server/static/assets/{vendor-shiki-DZajAPeq.js → vendor-shiki-Do--csgv.js} +1 -1
- phoenix/server/static/assets/vendor-three-CmB8bl_y.js +3840 -0
- phoenix/server/templates/index.html +7 -1
- phoenix/server/thread_server.py +1 -2
- phoenix/server/utils.py +74 -0
- phoenix/session/client.py +55 -1
- phoenix/session/data_extractor.py +5 -0
- phoenix/session/evaluation.py +8 -4
- phoenix/session/session.py +44 -8
- phoenix/settings.py +2 -0
- phoenix/trace/attributes.py +80 -13
- phoenix/trace/dsl/query.py +2 -0
- phoenix/trace/projects.py +5 -0
- phoenix/utilities/template_formatters.py +1 -1
- phoenix/version.py +1 -1
- phoenix/server/api/types/Evaluation.py +0 -39
- phoenix/server/static/assets/components-D0DWAf0l.js +0 -5650
- phoenix/server/static/assets/pages-Creyamao.js +0 -8612
- phoenix/server/static/assets/vendor-CU36oj8y.js +0 -905
- phoenix/server/static/assets/vendor-CqDb5u4o.css +0 -1
- phoenix/server/static/assets/vendor-arizeai-Ctgw0e1G.js +0 -168
- phoenix/server/static/assets/vendor-codemirror-Cojjzqb9.js +0 -25
- phoenix/server/static/assets/vendor-three-BLWp5bic.js +0 -2998
- phoenix/utilities/deprecation.py +0 -31
- {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,15 +12,6 @@ from sqlalchemy.exc import IntegrityError as PostgreSQLIntegrityError
|
|
|
12
12
|
from sqlalchemy.orm import joinedload
|
|
13
13
|
from sqlean.dbapi2 import IntegrityError as SQLiteIntegrityError # type: ignore[import-untyped]
|
|
14
14
|
from starlette.datastructures import Secret
|
|
15
|
-
from starlette.status import (
|
|
16
|
-
HTTP_201_CREATED,
|
|
17
|
-
HTTP_204_NO_CONTENT,
|
|
18
|
-
HTTP_400_BAD_REQUEST,
|
|
19
|
-
HTTP_403_FORBIDDEN,
|
|
20
|
-
HTTP_404_NOT_FOUND,
|
|
21
|
-
HTTP_409_CONFLICT,
|
|
22
|
-
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
23
|
-
)
|
|
24
15
|
from strawberry.relay import GlobalID
|
|
25
16
|
from typing_extensions import TypeAlias, assert_never
|
|
26
17
|
|
|
@@ -45,6 +36,7 @@ from phoenix.server.api.routers.v1.utils import (
|
|
|
45
36
|
)
|
|
46
37
|
from phoenix.server.api.types.node import from_global_id_with_expected_type
|
|
47
38
|
from phoenix.server.authorization import is_not_locked, require_admin
|
|
39
|
+
from phoenix.server.ldap import is_ldap_user, is_null_email_marker
|
|
48
40
|
|
|
49
41
|
logger = logging.getLogger(__name__)
|
|
50
42
|
|
|
@@ -68,6 +60,10 @@ class OAuth2UserData(UserData):
|
|
|
68
60
|
oauth2_user_id: str = UNDEFINED
|
|
69
61
|
|
|
70
62
|
|
|
63
|
+
class LDAPUserData(UserData):
|
|
64
|
+
auth_method: Literal["LDAP"]
|
|
65
|
+
|
|
66
|
+
|
|
71
67
|
class DbUser(V1RoutesBaseModel):
|
|
72
68
|
id: str
|
|
73
69
|
created_at: datetime
|
|
@@ -82,7 +78,13 @@ class OAuth2User(OAuth2UserData, DbUser):
|
|
|
82
78
|
profile_picture_url: str = UNDEFINED
|
|
83
79
|
|
|
84
80
|
|
|
85
|
-
|
|
81
|
+
class LDAPUser(LDAPUserData, DbUser):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
User: TypeAlias = Annotated[
|
|
86
|
+
Union[LocalUser, OAuth2User, LDAPUser], Field(..., discriminator="auth_method")
|
|
87
|
+
]
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
class GetUsersResponseBody(PaginatedResponseBody[User]):
|
|
@@ -94,7 +96,9 @@ class GetUserResponseBody(ResponseBody[User]):
|
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
class CreateUserRequestBody(V1RoutesBaseModel):
|
|
97
|
-
user: Annotated[
|
|
99
|
+
user: Annotated[
|
|
100
|
+
Union[LocalUserData, OAuth2UserData, LDAPUserData], Field(..., discriminator="auth_method")
|
|
101
|
+
]
|
|
98
102
|
send_welcome_email: bool = True
|
|
99
103
|
|
|
100
104
|
|
|
@@ -113,7 +117,7 @@ DEFAULT_PAGINATION_PAGE_LIMIT = 100
|
|
|
113
117
|
response_description="A list of users.",
|
|
114
118
|
responses=add_errors_to_responses(
|
|
115
119
|
[
|
|
116
|
-
|
|
120
|
+
422,
|
|
117
121
|
],
|
|
118
122
|
),
|
|
119
123
|
dependencies=[Depends(require_admin)],
|
|
@@ -161,6 +165,19 @@ async def list_users(
|
|
|
161
165
|
password_needs_reset=user.reset_password,
|
|
162
166
|
)
|
|
163
167
|
)
|
|
168
|
+
elif isinstance(user, models.OAuth2User) and is_ldap_user(user.oauth2_client_id):
|
|
169
|
+
# Check if this is an LDAP user (identified by special marker)
|
|
170
|
+
data.append(
|
|
171
|
+
LDAPUser(
|
|
172
|
+
id=str(GlobalID("User", str(user.id))),
|
|
173
|
+
username=user.username,
|
|
174
|
+
email="" if is_null_email_marker(user.email) else user.email,
|
|
175
|
+
role=user.role.name,
|
|
176
|
+
created_at=user.created_at,
|
|
177
|
+
updated_at=user.updated_at,
|
|
178
|
+
auth_method="LDAP",
|
|
179
|
+
)
|
|
180
|
+
)
|
|
164
181
|
elif isinstance(user, models.OAuth2User):
|
|
165
182
|
oauth2_user = OAuth2User(
|
|
166
183
|
id=str(GlobalID("User", str(user.id))),
|
|
@@ -187,12 +204,12 @@ async def list_users(
|
|
|
187
204
|
summary="Create a new user",
|
|
188
205
|
description="Create a new user with the specified configuration.",
|
|
189
206
|
response_description="The newly created user.",
|
|
190
|
-
status_code=
|
|
207
|
+
status_code=201,
|
|
191
208
|
responses=add_errors_to_responses(
|
|
192
209
|
[
|
|
193
|
-
{"status_code":
|
|
194
|
-
{"status_code":
|
|
195
|
-
|
|
210
|
+
{"status_code": 400, "description": "Role not found."},
|
|
211
|
+
{"status_code": 409, "description": "Username or email already exists."},
|
|
212
|
+
422,
|
|
196
213
|
]
|
|
197
214
|
),
|
|
198
215
|
dependencies=[Depends(require_admin), Depends(is_not_locked)],
|
|
@@ -213,10 +230,18 @@ async def create_user(
|
|
|
213
230
|
# Prevent creation of SYSTEM users
|
|
214
231
|
if role == "SYSTEM":
|
|
215
232
|
raise HTTPException(
|
|
216
|
-
status_code=
|
|
233
|
+
status_code=400,
|
|
217
234
|
detail="Cannot create users with SYSTEM role",
|
|
218
235
|
)
|
|
219
236
|
|
|
237
|
+
# Prevent OAuth2 users from using the LDAP marker or any variation
|
|
238
|
+
if isinstance(user_data, OAuth2UserData):
|
|
239
|
+
if is_ldap_user(user_data.oauth2_client_id):
|
|
240
|
+
raise HTTPException(
|
|
241
|
+
status_code=400,
|
|
242
|
+
detail="Cannot create OAuth2 users with reserved LDAP identifier",
|
|
243
|
+
)
|
|
244
|
+
|
|
220
245
|
user: models.User
|
|
221
246
|
if isinstance(user_data, LocalUserData):
|
|
222
247
|
password = (user_data.password or secrets.token_hex()).strip()
|
|
@@ -241,6 +266,11 @@ async def create_user(
|
|
|
241
266
|
oauth2_client_id=user_data.oauth2_client_id or None,
|
|
242
267
|
oauth2_user_id=user_data.oauth2_user_id or None,
|
|
243
268
|
)
|
|
269
|
+
elif isinstance(user_data, LDAPUserData):
|
|
270
|
+
user = models.LDAPUser(
|
|
271
|
+
email=email,
|
|
272
|
+
username=username,
|
|
273
|
+
)
|
|
244
274
|
else:
|
|
245
275
|
assert_never(user_data)
|
|
246
276
|
try:
|
|
@@ -252,12 +282,12 @@ async def create_user(
|
|
|
252
282
|
session.add(user)
|
|
253
283
|
except (PostgreSQLIntegrityError, SQLiteIntegrityError) as e:
|
|
254
284
|
if "users.username" in str(e):
|
|
255
|
-
raise HTTPException(status_code=
|
|
285
|
+
raise HTTPException(status_code=409, detail="Username already exists")
|
|
256
286
|
elif "users.email" in str(e):
|
|
257
|
-
raise HTTPException(status_code=
|
|
287
|
+
raise HTTPException(status_code=409, detail="Email already exists")
|
|
258
288
|
else:
|
|
259
289
|
raise HTTPException(
|
|
260
|
-
status_code=
|
|
290
|
+
status_code=409,
|
|
261
291
|
detail="Failed to create user due to a conflict with existing data",
|
|
262
292
|
)
|
|
263
293
|
id_ = str(GlobalID("User", str(user.id)))
|
|
@@ -289,6 +319,16 @@ async def create_user(
|
|
|
289
319
|
data.oauth2_user_id = user.oauth2_user_id
|
|
290
320
|
if user.profile_picture_url:
|
|
291
321
|
data.profile_picture_url = user.profile_picture_url
|
|
322
|
+
elif isinstance(user_data, LDAPUserData):
|
|
323
|
+
data = LDAPUser(
|
|
324
|
+
id=id_,
|
|
325
|
+
email=email,
|
|
326
|
+
username=username,
|
|
327
|
+
auth_method="LDAP",
|
|
328
|
+
role=user_data.role,
|
|
329
|
+
created_at=user.created_at,
|
|
330
|
+
updated_at=user.updated_at,
|
|
331
|
+
)
|
|
292
332
|
else:
|
|
293
333
|
assert_never(user_data)
|
|
294
334
|
# Send welcome email if requested
|
|
@@ -307,13 +347,13 @@ async def create_user(
|
|
|
307
347
|
summary="Delete a user by ID",
|
|
308
348
|
description="Delete an existing user by their unique GlobalID.",
|
|
309
349
|
response_description="No content returned on successful deletion.",
|
|
310
|
-
status_code=
|
|
350
|
+
status_code=204,
|
|
311
351
|
responses=add_errors_to_responses(
|
|
312
352
|
[
|
|
313
|
-
{"status_code":
|
|
314
|
-
|
|
353
|
+
{"status_code": 404, "description": "User not found."},
|
|
354
|
+
422,
|
|
315
355
|
{
|
|
316
|
-
"status_code":
|
|
356
|
+
"status_code": 403,
|
|
317
357
|
"description": "Cannot delete the default admin or system user",
|
|
318
358
|
},
|
|
319
359
|
]
|
|
@@ -3,10 +3,6 @@ from typing import Any, Generic, Optional, TypedDict, TypeVar, Union
|
|
|
3
3
|
from fastapi import HTTPException
|
|
4
4
|
from sqlalchemy import select
|
|
5
5
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
|
-
from starlette.status import (
|
|
7
|
-
HTTP_404_NOT_FOUND,
|
|
8
|
-
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
9
|
-
)
|
|
10
6
|
from strawberry.relay import GlobalID
|
|
11
7
|
from typing_extensions import TypeAlias, assert_never
|
|
12
8
|
|
|
@@ -135,21 +131,21 @@ async def _get_project_by_identifier(
|
|
|
135
131
|
name = project_identifier
|
|
136
132
|
except HTTPException:
|
|
137
133
|
raise HTTPException(
|
|
138
|
-
status_code=
|
|
134
|
+
status_code=422,
|
|
139
135
|
detail=f"Invalid project identifier format: {project_identifier}",
|
|
140
136
|
)
|
|
141
137
|
stmt = select(models.Project).filter_by(name=name)
|
|
142
138
|
project = await session.scalar(stmt)
|
|
143
139
|
if project is None:
|
|
144
140
|
raise HTTPException(
|
|
145
|
-
status_code=
|
|
141
|
+
status_code=404,
|
|
146
142
|
detail=f"Project with name {name} not found",
|
|
147
143
|
)
|
|
148
144
|
else:
|
|
149
145
|
project = await session.get(models.Project, id_)
|
|
150
146
|
if project is None:
|
|
151
147
|
raise HTTPException(
|
|
152
|
-
status_code=
|
|
148
|
+
status_code=404,
|
|
153
149
|
detail=f"Project with ID {project_identifier} not found",
|
|
154
150
|
)
|
|
155
151
|
return project
|