Fix SA2.0 usage in managers.folders

This commit is contained in:
John Davis
2023-11-09 15:33:46 -05:00
parent a1b548556d
commit 311fe028da
+75 -69
View File
@@ -12,10 +12,12 @@ from typing import (
from sqlalchemy import (
and_,
exists,
false,
func,
not_,
or_,
select,
)
from sqlalchemy.orm import aliased
from sqlalchemy.orm.exc import (
@@ -36,6 +38,15 @@ from galaxy.exceptions import (
MalformedId,
RequestParameterInvalidException,
)
from galaxy.model import (
Dataset,
DatasetPermissions,
LibraryDataset,
LibraryDatasetDatasetAssociation,
LibraryDatasetPermissions,
LibraryFolder,
LibraryFolderPermissions,
)
from galaxy.model.base import transaction
from galaxy.model.scoped_session import galaxy_scoped_session
from galaxy.schema.fields import LibraryFolderDatabaseIdField
@@ -89,11 +100,7 @@ class FolderManager:
:raises: InconsistentDatabase, RequestParameterInvalidException, InternalServerError
"""
try:
folder = (
trans.sa_session.query(trans.app.model.LibraryFolder)
.filter(trans.app.model.LibraryFolder.table.c.id == decoded_folder_id)
.one()
)
folder = get_folder(trans.sa_session, decoded_folder_id)
except MultipleResultsFound:
raise InconsistentDatabase("Multiple folders found with the same id.")
except NoResultFound:
@@ -210,7 +217,7 @@ class FolderManager:
raise InsufficientPermissionsException(
"You do not have proper permission to create folders under given folder."
)
new_folder = trans.app.model.LibraryFolder(name=new_folder_name, description=new_folder_description)
new_folder = LibraryFolder(name=new_folder_name, description=new_folder_description)
# We are associating the last used genome build with folders, so we will always
# initialize a new folder with the first dbkey in genome builds list which is currently
# ? unspecified (?)
@@ -386,9 +393,9 @@ class FolderManager:
def get_contents(
self,
trans,
folder: model.LibraryFolder,
folder: LibraryFolder,
payload: LibraryFolderContentsIndexQueryPayload,
) -> Tuple[List[Union[model.LibraryFolder, model.LibraryDataset]], int]:
) -> Tuple[List[Union[LibraryFolder, LibraryDataset]], int]:
"""Retrieves the contents of the given folder that match the provided filters and pagination parameters.
Returns a tuple with the list of paginated contents and the total number of items contained in the folder."""
limit = payload.limit
@@ -400,19 +407,19 @@ class FolderManager:
is_admin=trans.user_is_admin,
)
content_items: List[Union[model.LibraryFolder, model.LibraryDataset]] = []
sub_folders_query = self._get_sub_folders_query(sa_session, folder, security_params, payload)
total_sub_folders: int = sub_folders_query.count()
content_items: List[Union[LibraryFolder, LibraryDataset]] = []
sub_folders_stmt = self._get_sub_folders_statement(sa_session, folder, security_params, payload)
total_sub_folders = get_count(sa_session, sub_folders_stmt)
if payload.order_by in FOLDER_SORT_COLUMN_MAP:
sort_column = FOLDER_SORT_COLUMN_MAP[payload.order_by](model.LibraryFolder)
sub_folders_query = sub_folders_query.order_by(sort_column.desc() if payload.sort_desc else sort_column)
sort_column = FOLDER_SORT_COLUMN_MAP[payload.order_by](LibraryFolder)
sub_folders_stmt = sub_folders_stmt.order_by(sort_column.desc() if payload.sort_desc else sort_column)
else: # Sort by name alphabetically by default
sub_folders_query = sub_folders_query.order_by(model.LibraryFolder.name)
sub_folders_stmt = sub_folders_stmt.order_by(LibraryFolder.name)
if limit is not None and limit > 0:
sub_folders_query = sub_folders_query.limit(limit)
sub_folders_stmt = sub_folders_stmt.limit(limit)
if offset is not None:
sub_folders_query = sub_folders_query.offset(offset)
folders = sub_folders_query.all()
sub_folders_stmt = sub_folders_stmt.offset(offset)
folders = sa_session.scalars(sub_folders_stmt).all()
content_items.extend(folders)
# Update pagination
@@ -424,77 +431,69 @@ class FolderManager:
offset -= num_folders_skipped
offset = max(0, offset)
datasets_query = self._get_contained_datasets_query(sa_session, folder, security_params, payload)
total_datasets = datasets_query.count()
datasets_stmt = self._get_contained_datasets_statement(sa_session, folder, security_params, payload)
total_datasets = get_count(sa_session, datasets_stmt)
if limit is not None and limit > 0:
datasets_query = datasets_query.limit(limit)
datasets_stmt = datasets_stmt.limit(limit)
if offset is not None:
datasets_query = datasets_query.offset(offset)
datasets = datasets_query.all()
datasets_stmt = datasets_stmt.offset(offset)
datasets = sa_session.scalars(datasets_stmt).all()
content_items.extend(datasets)
return (content_items, total_sub_folders + total_datasets)
def _get_sub_folders_query(
def _get_sub_folders_statement(
self,
sa_session: galaxy_scoped_session,
folder: model.LibraryFolder,
folder: LibraryFolder,
security: SecurityParams,
payload: LibraryFolderContentsIndexQueryPayload,
):
"""Builds a query to retrieve all the sub-folders contained in the given folder applying filters."""
item_model = model.LibraryFolder
item_permission_model = model.LibraryFolderPermissions
search_text = payload.search_text
query = sa_session.query(item_model)
query = query.filter(item_model.parent_id == folder.id)
query = self._filter_by_include_deleted(
query, item_model, item_permission_model, payload.include_deleted, security
stmt = select(LibraryFolder).where(LibraryFolder.parent_id == folder.id)
stmt = self._filter_by_include_deleted(
stmt, LibraryFolder, LibraryFolderPermissions, payload.include_deleted, security
)
if search_text:
search_text = search_text.lower()
query = query.filter(
stmt = stmt.where(
or_(
func.lower(item_model.name).contains(search_text, autoescape=True),
func.lower(item_model.description).contains(search_text, autoescape=True),
func.lower(LibraryFolder.name).contains(search_text, autoescape=True),
func.lower(LibraryFolder.description).contains(search_text, autoescape=True),
)
)
query = query.group_by(item_model.id)
return query
stmt = stmt.group_by(LibraryFolder.id)
return stmt
def _get_contained_datasets_query(
def _get_contained_datasets_statement(
self,
sa_session: galaxy_scoped_session,
folder: model.LibraryFolder,
folder: LibraryFolder,
security: SecurityParams,
payload: LibraryFolderContentsIndexQueryPayload,
):
"""Builds a query to retrieve all the datasets contained in the given folder applying filters."""
search_text = payload.search_text
item_model = model.LibraryDataset
item_permission_model = model.LibraryDatasetPermissions
access_action = security.security_agent.permitted_actions.DATASET_ACCESS.action
query = sa_session.query(item_model)
query = query.filter(item_model.folder_id == folder.id)
query = self._filter_by_include_deleted(
query, item_model, item_permission_model, payload.include_deleted, security
stmt = select(LibraryDataset).where(LibraryDataset.folder_id == folder.id)
stmt = self._filter_by_include_deleted(
stmt, LibraryDataset, LibraryDatasetPermissions, payload.include_deleted, security
)
ldda = aliased(model.LibraryDatasetDatasetAssociation)
associated_dataset = aliased(model.Dataset)
query = query.outerjoin(item_model.library_dataset_dataset_association.of_type(ldda))
ldda = aliased(LibraryDatasetDatasetAssociation)
associated_dataset = aliased(Dataset)
stmt = stmt.outerjoin(LibraryDataset.library_dataset_dataset_association.of_type(ldda))
if not security.is_admin: # Non-admin users require ACCESS permission
# We check against the actual dataset and not the ldda (for now?)
dataset_permission = aliased(model.DatasetPermissions)
dataset_permission = aliased(DatasetPermissions)
is_public_dataset = not_(
sa_session.query(model.DatasetPermissions)
.filter(
model.DatasetPermissions.dataset_id == associated_dataset.id,
model.DatasetPermissions.action == access_action,
)
.exists()
exists()
.where(DatasetPermissions.dataset_id == associated_dataset.id)
.where(DatasetPermissions.action == access_action)
)
query = query.outerjoin(ldda.dataset.of_type(associated_dataset))
query = query.outerjoin(associated_dataset.actions.of_type(dataset_permission))
query = query.filter(
stmt = stmt.outerjoin(ldda.dataset.of_type(associated_dataset))
stmt = stmt.outerjoin(associated_dataset.actions.of_type(dataset_permission))
stmt = stmt.where(
or_(
# The dataset is public
is_public_dataset,
@@ -505,30 +504,27 @@ class FolderManager:
),
)
)
if search_text:
search_text = search_text.lower()
query = query.filter(
stmt = stmt.where(
or_(
func.lower(ldda.name).contains(search_text, autoescape=True),
func.lower(ldda.message).contains(search_text, autoescape=True),
)
)
sort_column = LDDA_SORT_COLUMN_MAP[payload.order_by](ldda, associated_dataset)
query = query.order_by(sort_column.desc() if payload.sort_desc else sort_column)
query = query.group_by(item_model.id, sort_column)
return query
stmt = stmt.order_by(sort_column.desc() if payload.sort_desc else sort_column)
stmt = stmt.group_by(LibraryDataset.id, sort_column)
return stmt
def _filter_by_include_deleted(
self, query, item_model, item_permissions_model, include_deleted: Optional[bool], security: SecurityParams
self, stmt, item_model, item_permissions_model, include_deleted: Optional[bool], security: SecurityParams
):
if include_deleted: # Admins or users with MODIFY permissions can see deleted contents
if not security.is_admin:
item_permission = aliased(item_permissions_model)
query = query.outerjoin(item_model.actions.of_type(item_permission))
query = query.filter(
stmt = stmt.outerjoin(item_model.actions.of_type(item_permission))
stmt = stmt.where(
or_(
item_model.deleted == false(), # Is not deleted
# User has MODIFY permission
@@ -539,8 +535,8 @@ class FolderManager:
)
)
else:
query = query.filter(item_model.deleted == false())
return query
stmt = stmt.where(item_model.deleted == false())
return stmt
def build_folder_path(
self, sa_session: galaxy_scoped_session, folder: model.LibraryFolder
@@ -553,7 +549,17 @@ class FolderManager:
current_folder = folder
path_to_root = [(LibraryFolderDatabaseIdField.encode(current_folder.id), current_folder.name)]
while current_folder.parent_id is not None:
parent_folder = sa_session.query(model.LibraryFolder).get(current_folder.parent_id)
parent_folder = sa_session.get(LibraryFolder, current_folder.parent_id)
current_folder = parent_folder
path_to_root.insert(0, (LibraryFolderDatabaseIdField.encode(current_folder.id), current_folder.name))
return path_to_root
def get_folder(session, folder_id):
stmt = select(LibraryFolder).where(LibraryFolder.id == folder_id)
return session.execute(stmt).scalar_one()
def get_count(session, statement):
stmt = select(func.count()).select_from(statement)
return session.scalar(stmt)