From bd6b34004ec041f1aeb0f4baf67eda1e4ca08178 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 22 Jul 2024 18:41:23 +0200 Subject: [PATCH 1/2] Improve typing for archived histories API models Drop Any and use partial model for custom keys. Consolidate archived histories serialization with regular histories. --- lib/galaxy/schema/schema.py | 24 ++++++++++++++----- lib/galaxy/webapps/galaxy/api/histories.py | 2 ++ .../webapps/galaxy/services/histories.py | 16 +++++++------ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/galaxy/schema/schema.py b/lib/galaxy/schema/schema.py index e83c76edfef2..570ee99b9558 100644 --- a/lib/galaxy/schema/schema.py +++ b/lib/galaxy/schema/schema.py @@ -1896,12 +1896,24 @@ class ArchivedHistoryDetailed(HistoryDetailed, ExportAssociationData): pass -AnyArchivedHistoryView = Union[ - ArchivedHistorySummary, - ArchivedHistoryDetailed, - # Any will cover those cases in which only specific `keys` are requested - # otherwise the validation will fail because the required fields are not returned - Any, +@partial_model() +class CustomArchivedHistoryView(CustomHistoryView, ExportAssociationData): + """Archived History Response with all optional fields. + + It is used for serializing only specific attributes using the "keys" + query parameter. + """ + + pass + + +AnyArchivedHistoryView = Annotated[ + Union[ + CustomArchivedHistoryView, + ArchivedHistoryDetailed, + ArchivedHistorySummary, + ], + Field(union_mode="left_to_right"), ] diff --git a/lib/galaxy/webapps/galaxy/api/histories.py b/lib/galaxy/webapps/galaxy/api/histories.py index 067dd425369a..3f9bdfd7b829 100644 --- a/lib/galaxy/webapps/galaxy/api/histories.py +++ b/lib/galaxy/webapps/galaxy/api/histories.py @@ -271,6 +271,7 @@ def shared_with_me( @router.get( "/api/histories/archived", summary="Get a list of all archived histories for the current user.", + response_model_exclude_unset=True, ) def get_archived_histories( self, @@ -613,6 +614,7 @@ def get_custom_builds_metadata( @router.post( "/api/histories/{history_id}/archive", summary="Archive a history.", + response_model_exclude_unset=True, ) def archive_history( self, diff --git a/lib/galaxy/webapps/galaxy/services/histories.py b/lib/galaxy/webapps/galaxy/services/histories.py index d5a6a75ffba7..32e8a9fa8a1c 100644 --- a/lib/galaxy/webapps/galaxy/services/histories.py +++ b/lib/galaxy/webapps/galaxy/services/histories.py @@ -686,7 +686,7 @@ def _serialize_history( history: model.History, serialization_params: SerializationParams, default_view: str = "detailed", - ) -> AnyHistoryView: + ): """ Returns a dictionary with the corresponding values depending on the serialization parameters provided. @@ -793,7 +793,10 @@ def get_archived_histories( filters=filters, order_by=order_by, limit=filter_query_params.limit, offset=filter_query_params.offset ) - histories = [self._serialize_archived_history(trans, history, serialization_params) for history in histories] + histories = [ + self._serialize_archived_history(trans, history, serialization_params, default_view="summary") + for history in histories + ] return histories, total_matches def _serialize_archived_history( @@ -801,14 +804,13 @@ def _serialize_archived_history( trans: ProvidesHistoryContext, history: model.History, serialization_params: Optional[SerializationParams] = None, + default_view: str = "detailed", ): if serialization_params is None: - serialization_params = SerializationParams(default_view="summary") - archived_history = self.serializer.serialize_to_view( - history, user=trans.user, trans=trans, **serialization_params.dict() - ) + serialization_params = SerializationParams() + archived_history = self._serialize_history(trans, history, serialization_params, default_view) export_record_data = self._get_export_record_data(history) - archived_history["export_record_data"] = export_record_data.dict() if export_record_data else None + archived_history["export_record_data"] = export_record_data.model_dump() if export_record_data else None return archived_history def _get_export_record_data(self, history: model.History) -> Optional[WriteStoreToPayload]: From 6f060d3ecb648337274214774d3a7d8d35f82ef7 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:30:39 +0200 Subject: [PATCH 2/2] Update client API schema --- client/src/api/schema/schema.ts | 163 +++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 2 deletions(-) diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts index 7e3cda1f61c4..af70c43ec230 100644 --- a/client/src/api/schema/schema.ts +++ b/client/src/api/schema/schema.ts @@ -3485,6 +3485,158 @@ export interface components { */ username: string; }; + /** CustomArchivedHistoryView */ + CustomArchivedHistoryView: { + /** + * Annotation + * @description An annotation to provide details or to help understand the purpose and usage of this item. + */ + annotation?: string | null; + /** + * Archived + * @description Whether this item has been archived and is no longer active. + */ + archived?: boolean | null; + /** + * Contents Active + * @description Contains the number of active, deleted or hidden items in a History. + */ + contents_active?: components["schemas"]["HistoryActiveContentCounts"] | null; + /** + * Contents States + * @description A dictionary keyed to possible dataset states and valued with the number of datasets in this history that have those states. + */ + contents_states?: { + [key: string]: number; + } | null; + /** + * Contents URL + * @description The relative URL to access the contents of this History. + */ + contents_url?: string | null; + /** + * Count + * @description The number of items in the history. + */ + count?: number | null; + /** + * Create Time + * @description The time and date this item was created. + */ + create_time?: string | null; + /** + * Deleted + * @description Whether this item is marked as deleted. + */ + deleted?: boolean | null; + /** + * Export Record Data + * @description The export record data associated with this archived history. Used to recover the history. + */ + export_record_data?: components["schemas"]["ExportRecordData"] | null; + /** + * Genome Build + * @description TODO + */ + genome_build?: string | null; + /** + * History ID + * @example 0123456789ABCDEF + */ + id?: string; + /** + * Importable + * @description Whether this History can be imported by other users with a shared link. + */ + importable?: boolean | null; + /** + * Model class + * @description The name of the database model class. + * @constant + */ + model_class?: "History"; + /** + * Name + * @description The name of the history. + */ + name?: string | null; + /** + * Nice Size + * @description The total size of the contents of this history in a human-readable format. + */ + nice_size?: string | null; + /** + * Preferred Object Store ID + * @description The ID of the object store that should be used to store new datasets in this history. + */ + preferred_object_store_id?: string | null; + /** + * Published + * @description Whether this resource is currently publicly available to all users. + */ + published?: boolean | null; + /** + * Purged + * @description Whether this item has been permanently removed. + */ + purged?: boolean | null; + /** + * Size + * @description The total size of the contents of this history in bytes. + */ + size?: number | null; + /** + * Slug + * @description Part of the URL to uniquely identify this History by link in a readable way. + */ + slug?: string | null; + /** + * State + * @description The current state of the History based on the states of the datasets it contains. + */ + state?: components["schemas"]["DatasetState"] | null; + /** + * State Counts + * @description A dictionary keyed to possible dataset states and valued with the number of datasets in this history that have those states. + */ + state_details?: { + [key: string]: number; + } | null; + /** + * State IDs + * @description A dictionary keyed to possible dataset states and valued with lists containing the ids of each HDA in that state. + */ + state_ids?: { + [key: string]: string[]; + } | null; + tags?: components["schemas"]["TagCollection"] | null; + /** + * Update Time + * @description The last time and date this item was updated. + */ + update_time?: string | null; + /** + * URL + * @deprecated + * @description The relative URL to access this item. + */ + url?: string | null; + /** + * User ID + * @description The encoded ID of the user that owns this History. + */ + user_id?: string | null; + /** + * Username + * @description Owner of the history + */ + username?: string | null; + /** + * Username and slug + * @description The relative URL in the form of /u/{username}/h/{slug} + */ + username_and_slug?: string | null; + }; /** CustomBuildCreationPayload */ CustomBuildCreationPayload: { /** @@ -17085,7 +17237,11 @@ export interface operations { /** @description Successful Response */ 200: { content: { - "application/json": unknown[]; + "application/json": ( + | components["schemas"]["CustomArchivedHistoryView"] + | components["schemas"]["ArchivedHistoryDetailed"] + | components["schemas"]["ArchivedHistorySummary"] + )[]; }; }; /** @description Request Error */ @@ -17700,7 +17856,10 @@ export interface operations { /** @description Successful Response */ 200: { content: { - "application/json": unknown; + "application/json": + | components["schemas"]["CustomArchivedHistoryView"] + | components["schemas"]["ArchivedHistoryDetailed"] + | components["schemas"]["ArchivedHistorySummary"]; }; }; /** @description Request Error */