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
|
@@ -27,13 +27,13 @@ from phoenix.auth import (
|
|
|
27
27
|
)
|
|
28
28
|
from phoenix.config import get_env_disable_basic_auth
|
|
29
29
|
from phoenix.db import models
|
|
30
|
-
from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly
|
|
30
|
+
from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly, IsNotViewer
|
|
31
31
|
from phoenix.server.api.context import Context
|
|
32
32
|
from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound, Unauthorized
|
|
33
33
|
from phoenix.server.api.input_types.UserRoleInput import UserRoleInput
|
|
34
34
|
from phoenix.server.api.types.AuthMethod import AuthMethod
|
|
35
35
|
from phoenix.server.api.types.node import from_global_id_with_expected_type
|
|
36
|
-
from phoenix.server.api.types.User import User
|
|
36
|
+
from phoenix.server.api.types.User import User
|
|
37
37
|
from phoenix.server.bearer_auth import PhoenixUser
|
|
38
38
|
from phoenix.server.types import AccessTokenId, ApiKeyId, PasswordResetTokenId, RefreshTokenId
|
|
39
39
|
|
|
@@ -50,7 +50,10 @@ class CreateUserInput:
|
|
|
50
50
|
auth_method: Optional[AuthMethod] = AuthMethod.LOCAL
|
|
51
51
|
|
|
52
52
|
def __post_init__(self) -> None:
|
|
53
|
-
if self.auth_method is AuthMethod.
|
|
53
|
+
if self.auth_method is AuthMethod.LDAP:
|
|
54
|
+
if self.password:
|
|
55
|
+
raise BadRequest("Password is not allowed for LDAP authentication")
|
|
56
|
+
elif self.auth_method is AuthMethod.OAUTH2:
|
|
54
57
|
if self.password:
|
|
55
58
|
raise BadRequest("Password is not allowed for OAuth2 authentication")
|
|
56
59
|
elif get_env_disable_basic_auth():
|
|
@@ -103,6 +106,11 @@ class DeleteUsersInput:
|
|
|
103
106
|
user_ids: list[GlobalID]
|
|
104
107
|
|
|
105
108
|
|
|
109
|
+
@strawberry.type
|
|
110
|
+
class DeleteUsersPayload:
|
|
111
|
+
user_ids: list[GlobalID]
|
|
112
|
+
|
|
113
|
+
|
|
106
114
|
@strawberry.type
|
|
107
115
|
class UserMutationPayload:
|
|
108
116
|
user: User
|
|
@@ -110,7 +118,7 @@ class UserMutationPayload:
|
|
|
110
118
|
|
|
111
119
|
@strawberry.type
|
|
112
120
|
class UserMutationMixin:
|
|
113
|
-
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin, IsLocked]) # type: ignore
|
|
121
|
+
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsAdmin, IsLocked]) # type: ignore
|
|
114
122
|
async def create_user(
|
|
115
123
|
self,
|
|
116
124
|
info: Info[Context, None],
|
|
@@ -120,7 +128,12 @@ class UserMutationMixin:
|
|
|
120
128
|
email = sanitize_email(input.email)
|
|
121
129
|
|
|
122
130
|
user: models.User
|
|
123
|
-
if input.auth_method is AuthMethod.
|
|
131
|
+
if input.auth_method is AuthMethod.LDAP:
|
|
132
|
+
user = models.LDAPUser(
|
|
133
|
+
email=email,
|
|
134
|
+
username=input.username,
|
|
135
|
+
)
|
|
136
|
+
elif input.auth_method is AuthMethod.OAUTH2:
|
|
124
137
|
user = models.OAuth2User(
|
|
125
138
|
email=email,
|
|
126
139
|
username=input.username,
|
|
@@ -155,9 +168,9 @@ class UserMutationMixin:
|
|
|
155
168
|
except Exception as error:
|
|
156
169
|
# Log the error but do not raise it
|
|
157
170
|
logger.error(f"Failed to send welcome email: {error}")
|
|
158
|
-
return UserMutationPayload(user=
|
|
171
|
+
return UserMutationPayload(user=User(id=user.id, db_record=user))
|
|
159
172
|
|
|
160
|
-
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin]) # type: ignore
|
|
173
|
+
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsAdmin]) # type: ignore
|
|
161
174
|
async def patch_user(
|
|
162
175
|
self,
|
|
163
176
|
info: Info[Context, None],
|
|
@@ -174,6 +187,7 @@ class UserMutationMixin:
|
|
|
174
187
|
if not (user := await session.scalar(_select_user_by_id(user_id))):
|
|
175
188
|
raise NotFound("User not found")
|
|
176
189
|
stack.enter_context(session.no_autoflush)
|
|
190
|
+
should_log_out = False
|
|
177
191
|
if input.new_role:
|
|
178
192
|
if user.email == DEFAULT_ADMIN_EMAIL:
|
|
179
193
|
raise Unauthorized("Cannot modify role for the default admin user")
|
|
@@ -183,6 +197,7 @@ class UserMutationMixin:
|
|
|
183
197
|
if user_role_id is None:
|
|
184
198
|
raise NotFound(f"Role {input.new_role.value} not found")
|
|
185
199
|
user.user_role_id = user_role_id
|
|
200
|
+
should_log_out = True
|
|
186
201
|
if password := input.new_password:
|
|
187
202
|
if user.auth_method != "LOCAL":
|
|
188
203
|
raise Conflict("Cannot modify password for non-local user")
|
|
@@ -191,6 +206,7 @@ class UserMutationMixin:
|
|
|
191
206
|
user.password_salt = salt
|
|
192
207
|
user.password_hash = await info.context.hash_password(Secret(password), salt)
|
|
193
208
|
user.reset_password = True
|
|
209
|
+
should_log_out = True
|
|
194
210
|
if username := input.new_username:
|
|
195
211
|
user.username = username
|
|
196
212
|
assert user in session.dirty
|
|
@@ -199,9 +215,9 @@ class UserMutationMixin:
|
|
|
199
215
|
except (PostgreSQLIntegrityError, SQLiteIntegrityError) as error:
|
|
200
216
|
raise Conflict(_user_operation_error_message(error, "modify"))
|
|
201
217
|
assert user
|
|
202
|
-
if
|
|
218
|
+
if should_log_out:
|
|
203
219
|
await info.context.log_out(user.id)
|
|
204
|
-
return UserMutationPayload(user=
|
|
220
|
+
return UserMutationPayload(user=User(id=user.id, db_record=user))
|
|
205
221
|
|
|
206
222
|
@strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
|
|
207
223
|
async def patch_viewer(
|
|
@@ -243,23 +259,26 @@ class UserMutationMixin:
|
|
|
243
259
|
response = info.context.get_response()
|
|
244
260
|
response.delete_cookie(PHOENIX_REFRESH_TOKEN_COOKIE_NAME)
|
|
245
261
|
response.delete_cookie(PHOENIX_ACCESS_TOKEN_COOKIE_NAME)
|
|
246
|
-
return UserMutationPayload(user=
|
|
262
|
+
return UserMutationPayload(user=User(id=user.id, db_record=user))
|
|
247
263
|
|
|
248
|
-
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin, IsLocked]) # type: ignore
|
|
264
|
+
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsAdmin, IsLocked]) # type: ignore
|
|
249
265
|
async def delete_users(
|
|
250
266
|
self,
|
|
251
267
|
info: Info[Context, None],
|
|
252
268
|
input: DeleteUsersInput,
|
|
253
|
-
) ->
|
|
269
|
+
) -> DeleteUsersPayload:
|
|
254
270
|
assert (token_store := info.context.token_store) is not None
|
|
255
271
|
if not input.user_ids:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
272
|
+
raise BadRequest("At least one user ID is required")
|
|
273
|
+
user_rowid_to_gid: dict[int, GlobalID] = {}
|
|
274
|
+
for user_gid in input.user_ids:
|
|
275
|
+
try:
|
|
276
|
+
user_rowid = from_global_id_with_expected_type(user_gid, User.__name__)
|
|
277
|
+
except ValueError:
|
|
278
|
+
raise BadRequest(f"Invalid user ID: '{user_gid}'")
|
|
279
|
+
user_rowid_to_gid[user_rowid] = user_gid
|
|
280
|
+
|
|
281
|
+
user_rowids = list(user_rowid_to_gid.keys())
|
|
263
282
|
system_user_role_id = select(models.UserRole.id).filter_by(name="SYSTEM").scalar_subquery()
|
|
264
283
|
admin_user_role_id = select(models.UserRole.id).filter_by(name="ADMIN").scalar_subquery()
|
|
265
284
|
default_admin_user_id = (
|
|
@@ -298,7 +317,7 @@ class UserMutationMixin:
|
|
|
298
317
|
.select_from(models.User)
|
|
299
318
|
.where(
|
|
300
319
|
and_(
|
|
301
|
-
models.User.id.in_(
|
|
320
|
+
models.User.id.in_(user_rowids),
|
|
302
321
|
models.User.user_role_id != system_user_role_id,
|
|
303
322
|
)
|
|
304
323
|
)
|
|
@@ -306,41 +325,51 @@ class UserMutationMixin:
|
|
|
306
325
|
).all()
|
|
307
326
|
if deletes_default_admin:
|
|
308
327
|
raise Conflict("Cannot delete the default admin user")
|
|
309
|
-
if num_resolved_user_ids < len(
|
|
328
|
+
if num_resolved_user_ids < len(user_rowids):
|
|
310
329
|
raise NotFound("Some user IDs could not be found")
|
|
311
330
|
password_reset_token_ids = [
|
|
312
331
|
PasswordResetTokenId(id_)
|
|
313
332
|
async for id_ in await session.stream_scalars(
|
|
314
333
|
select(models.PasswordResetToken.id).where(
|
|
315
|
-
models.PasswordResetToken.user_id.in_(
|
|
334
|
+
models.PasswordResetToken.user_id.in_(user_rowids)
|
|
316
335
|
)
|
|
317
336
|
)
|
|
318
337
|
]
|
|
319
338
|
access_token_ids = [
|
|
320
339
|
AccessTokenId(id_)
|
|
321
340
|
async for id_ in await session.stream_scalars(
|
|
322
|
-
select(models.AccessToken.id).where(models.AccessToken.user_id.in_(
|
|
341
|
+
select(models.AccessToken.id).where(models.AccessToken.user_id.in_(user_rowids))
|
|
323
342
|
)
|
|
324
343
|
]
|
|
325
344
|
refresh_token_ids = [
|
|
326
345
|
RefreshTokenId(id_)
|
|
327
346
|
async for id_ in await session.stream_scalars(
|
|
328
|
-
select(models.RefreshToken.id).where(
|
|
347
|
+
select(models.RefreshToken.id).where(
|
|
348
|
+
models.RefreshToken.user_id.in_(user_rowids)
|
|
349
|
+
)
|
|
329
350
|
)
|
|
330
351
|
]
|
|
331
352
|
api_key_ids = [
|
|
332
353
|
ApiKeyId(id_)
|
|
333
354
|
async for id_ in await session.stream_scalars(
|
|
334
|
-
select(models.ApiKey.id).where(models.ApiKey.user_id.in_(
|
|
355
|
+
select(models.ApiKey.id).where(models.ApiKey.user_id.in_(user_rowids))
|
|
335
356
|
)
|
|
336
357
|
]
|
|
337
|
-
await session.
|
|
358
|
+
deleted_user_ids = await session.scalars(
|
|
359
|
+
delete(models.User).where(models.User.id.in_(user_rowids)).returning(models.User.id)
|
|
360
|
+
)
|
|
338
361
|
await token_store.revoke(
|
|
339
362
|
*password_reset_token_ids,
|
|
340
363
|
*access_token_ids,
|
|
341
364
|
*refresh_token_ids,
|
|
342
365
|
*api_key_ids,
|
|
343
366
|
)
|
|
367
|
+
unique_deleted_user_ids = set(deleted_user_ids)
|
|
368
|
+
deleted_user_gids: list[GlobalID] = []
|
|
369
|
+
for user_rowid, user_gid in user_rowid_to_gid.items():
|
|
370
|
+
if user_rowid in unique_deleted_user_ids:
|
|
371
|
+
deleted_user_gids.append(user_gid)
|
|
372
|
+
return DeleteUsersPayload(user_ids=deleted_user_gids)
|
|
344
373
|
|
|
345
374
|
|
|
346
375
|
def _select_role_id_by_name(role_name: str) -> Select[tuple[int]]:
|