Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More data access tests, some refactoring and cleanup #18312

Merged
merged 29 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1c90246
Improve model fixtures
jdavcs May 15, 2024
71b9e4b
Add first library data access tests
jdavcs Apr 15, 2024
0dd284a
Use session.get instead of custom method
jdavcs Apr 15, 2024
0c840d0
Add first user data access tests
jdavcs Apr 16, 2024
fd71792
Move+test get_users_by_ids
jdavcs Apr 16, 2024
b8e05af
security.get_npns_roles: test + factor out
jdavcs Apr 16, 2024
fabce5b
security.get_private_user_role: test + factor out
jdavcs Apr 16, 2024
4329a5e
webapps.galaxy.services.user.get_users_for_index: test + factor out
jdavcs Apr 16, 2024
f91bd11
Test User.username model definition (unique constraint)
jdavcs Jun 3, 2024
4aaed3e
Move+test get_roles_by_ids
jdavcs Apr 17, 2024
7ad6d43
Start replacing test_galaxy_mapping w/data access tests
jdavcs Feb 28, 2024
bc6fb89
Improve test: enable db triggers; do not verify HistoryAudit table
jdavcs Jun 3, 2024
558d836
More test-galaxy-mapping conversions
jdavcs Apr 16, 2024
51ffbfd
Convert test_default_disk_usage
jdavcs Apr 16, 2024
9572a93
Convert test_ratings
jdavcs Mar 4, 2024
7060791
Drop test_dataset_job_relationship
jdavcs Mar 7, 2024
1a4d6e6
Drop test_jobs
jdavcs Mar 7, 2024
da3c885
Convert test_history_contents
jdavcs Mar 7, 2024
2d4a65d
Convert test_current_galaxy_sesssion
jdavcs Mar 7, 2024
23ae962
Convert hid tests
jdavcs Mar 7, 2024
c8df2fd
Convert test_get_display_name
jdavcs Mar 7, 2024
47a8bf7
Drop test_tags
jdavcs Mar 7, 2024
83e124f
Drop incomplete test
jdavcs Mar 7, 2024
e7d4270
Drop test_basic
jdavcs Mar 7, 2024
5123c39
Convert test_metadata_spec
jdavcs Mar 7, 2024
65530c9
Drop unused fixture arg
jdavcs Mar 7, 2024
1d7d655
Convert + improve test_job/task_metrics
jdavcs Mar 7, 2024
fd29aab
Drop test_tasks
jdavcs Mar 7, 2024
0712fbf
Add test_history_update
jdavcs Apr 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/galaxy/managers/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
RequestParameterInvalidException,
)
from galaxy.managers.context import ProvidesAppContext
from galaxy.managers.roles import get_roles_by_ids
from galaxy.managers.users import get_users_by_ids
from galaxy.model import Group
from galaxy.model.base import transaction
from galaxy.model.db.role import get_roles_by_ids
from galaxy.model.db.user import get_users_by_ids
from galaxy.model.scoped_session import galaxy_scoped_session
from galaxy.schema.fields import Security
from galaxy.schema.groups import GroupCreatePayload
Expand Down
65 changes: 9 additions & 56 deletions lib/galaxy/managers/libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,6 @@
Tuple,
)

from sqlalchemy import (
asc,
false,
func,
not_,
or_,
select,
true,
)
from sqlalchemy.exc import (
MultipleResultsFound,
NoResultFound,
Expand All @@ -29,10 +20,16 @@
from galaxy.managers.folders import FolderManager
from galaxy.model import (
Library,
LibraryPermissions,
Role,
)
from galaxy.model.base import transaction
from galaxy.model.db.library import (
get_libraries_by_name,
get_libraries_for_admins,
get_libraries_for_nonadmins,
get_library_ids,
get_library_permissions_by_role,
)
from galaxy.util import (
pretty_print_time_interval,
unicodify,
Expand Down Expand Up @@ -60,7 +57,7 @@ def get(self, trans, decoded_library_id: int, check_accessible: bool = True) ->
:rtype: galaxy.model.Library
"""
try:
library = get_library(trans.sa_session, decoded_library_id)
library = trans.sa_session.get(Library, decoded_library_id)
except MultipleResultsFound:
raise exceptions.InconsistentDatabase("Multiple libraries found with the same id.")
except NoResultFound:
Expand Down Expand Up @@ -358,52 +355,8 @@ def get_containing_library_from_library_dataset(trans, library_dataset) -> Optio
while folder.parent:
folder = folder.parent
# We have folder set to the library's root folder, which has the same name as the library
stmt = select(Library).where(Library.deleted == false()).where(Library.name == folder.name)
for library in trans.sa_session.scalars(stmt):
for library in get_libraries_by_name(trans.sa_session, folder.name):
# Just to double-check
if library.root_folder == folder:
return library
return None


def get_library(session, library_id):
stmt = select(Library).where(Library.id == library_id)
return session.execute(stmt).scalar_one()


def get_library_ids(session, library_access_action):
stmt = select(LibraryPermissions.library_id).where(LibraryPermissions.action == library_access_action).distinct()
return session.scalars(stmt)


def get_library_permissions_by_role(session, role_ids):
stmt = select(LibraryPermissions).where(LibraryPermissions.role_id.in_(role_ids))
return session.scalars(stmt)


def get_libraries_for_admins(session, deleted):
stmt = select(Library)
if deleted is None:
# Flag is not specified, do not filter on it.
pass
elif deleted:
stmt = stmt.where(Library.deleted == true())
else:
stmt = stmt.where(Library.deleted == false())
stmt = stmt.order_by(asc(func.lower(Library.name)))
return session.scalars(stmt)


def get_libraries_for_nonadmins(session, restricted_library_ids, accessible_restricted_library_ids):
stmt = (
select(Library)
.where(Library.deleted == false())
.where(
or_(
not_(Library.id.in_(restricted_library_ids)),
Library.id.in_(accessible_restricted_library_ids),
)
)
)
stmt = stmt.order_by(asc(func.lower(Library.name)))
return session.scalars(stmt)
6 changes: 0 additions & 6 deletions lib/galaxy/managers/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
from galaxy.managers.context import ProvidesUserContext
from galaxy.model import Role
from galaxy.model.base import transaction
from galaxy.model.scoped_session import galaxy_scoped_session
from galaxy.schema.schema import RoleDefinitionModel
from galaxy.util import unicodify

Expand Down Expand Up @@ -162,8 +161,3 @@ def undelete(self, trans: ProvidesUserContext, role: model.Role) -> model.Role:
with transaction(trans.sa_session):
trans.sa_session.commit()
return role


def get_roles_by_ids(session: galaxy_scoped_session, role_ids):
stmt = select(Role).where(Role.id.in_(role_ids))
return session.scalars(stmt).all()
27 changes: 4 additions & 23 deletions lib/galaxy/managers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
UserQuotaUsage,
)
from galaxy.model.base import transaction
from galaxy.model.scoped_session import galaxy_scoped_session
from galaxy.model.db.user import (
get_user_by_email,
get_user_by_username,
)
from galaxy.security.validate_user_input import (
VALID_EMAIL_RE,
validate_email,
Expand Down Expand Up @@ -872,25 +875,3 @@ def _add_parsers(self):
)

self.fn_filter_parsers.update({})


def get_users_by_ids(session: galaxy_scoped_session, user_ids):
stmt = select(User).where(User.id.in_(user_ids))
return session.scalars(stmt).all()


# The get_user_by_email and get_user_by_username functions may be called from
# the tool_shed app, which has its own User model, which is different from
# galaxy.model.User. In that case, the tool_shed user model should be passed as
# the model_class argument.
def get_user_by_email(session, email: str, model_class=User, case_sensitive=True):
filter_clause = model_class.email == email
if not case_sensitive:
filter_clause = func.lower(model_class.email) == func.lower(email)
stmt = select(model_class).where(filter_clause).limit(1)
return session.scalars(stmt).first()


def get_user_by_username(session, username: str, model_class=User):
stmt = select(model_class).filter(model_class.username == username).limit(1)
return session.scalars(stmt).first()
Empty file added lib/galaxy/model/db/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions lib/galaxy/model/db/library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from sqlalchemy import (
asc,
false,
func,
not_,
or_,
select,
true,
)

from galaxy.model import (
Library,
LibraryPermissions,
)


def get_library_ids(session, library_access_action):
stmt = select(LibraryPermissions.library_id).where(LibraryPermissions.action == library_access_action).distinct()
return session.scalars(stmt)


def get_library_permissions_by_role(session, role_ids):
stmt = select(LibraryPermissions).where(LibraryPermissions.role_id.in_(role_ids))
return session.scalars(stmt)


def get_libraries_for_admins(session, deleted):
stmt = select(Library)
if deleted is None:
# Flag is not specified, do not filter on it.
pass
elif deleted:
stmt = stmt.where(Library.deleted == true())
else:
stmt = stmt.where(Library.deleted == false())
stmt = stmt.order_by(asc(func.lower(Library.name)))
return session.scalars(stmt)


def get_libraries_for_nonadmins(session, restricted_library_ids, accessible_restricted_library_ids):
stmt = (
select(Library)
.where(Library.deleted == false())
.where(
or_(
not_(Library.id.in_(restricted_library_ids)),
Library.id.in_(accessible_restricted_library_ids),
)
)
)
stmt = stmt.order_by(asc(func.lower(Library.name)))
return session.scalars(stmt)


def get_libraries_by_name(session, name):
stmt = select(Library).where(Library.deleted == false()).where(Library.name == name)
return session.scalars(stmt)
43 changes: 43 additions & 0 deletions lib/galaxy/model/db/role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from sqlalchemy import (
and_,
false,
select,
)

from galaxy.model import (
Role,
UserRoleAssociation,
)
from galaxy.model.scoped_session import galaxy_scoped_session


def get_npns_roles(session):
"""
non-private, non-sharing roles
"""
stmt = (
select(Role)
.where(and_(Role.deleted == false(), Role.type != Role.types.PRIVATE, Role.type != Role.types.SHARING))
.order_by(Role.name)
)
return session.scalars(stmt)


def get_private_user_role(user, session):
stmt = (
select(Role)
.where(
and_(
UserRoleAssociation.user_id == user.id,
Role.id == UserRoleAssociation.role_id,
Role.type == Role.types.PRIVATE,
)
)
.distinct()
)
return session.execute(stmt).scalar_one_or_none()


def get_roles_by_ids(session: galaxy_scoped_session, role_ids):
stmt = select(Role).where(Role.id.in_(role_ids))
return session.scalars(stmt).all()
66 changes: 66 additions & 0 deletions lib/galaxy/model/db/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from typing import Optional

from sqlalchemy import (
false,
func,
or_,
select,
true,
)

from galaxy.model import User
from galaxy.model.scoped_session import galaxy_scoped_session


def get_users_by_ids(session: galaxy_scoped_session, user_ids):
stmt = select(User).where(User.id.in_(user_ids))
return session.scalars(stmt).all()


# The get_user_by_email and get_user_by_username functions may be called from
# the tool_shed app, which has its own User model, which is different from
# galaxy.model.User. In that case, the tool_shed user model should be passed as
# the model_class argument.
def get_user_by_email(session, email: str, model_class=User, case_sensitive=True):
filter_clause = model_class.email == email
if not case_sensitive:
filter_clause = func.lower(model_class.email) == func.lower(email)
stmt = select(model_class).where(filter_clause).limit(1)
return session.scalars(stmt).first()


def get_user_by_username(session, username: str, model_class=User):
stmt = select(model_class).filter(model_class.username == username).limit(1)
return session.scalars(stmt).first()


def get_users_for_index(
session,
deleted: bool,
f_email: Optional[str] = None,
f_name: Optional[str] = None,
f_any: Optional[str] = None,
is_admin: bool = False,
expose_user_email: bool = False,
expose_user_name: bool = False,
):
stmt = select(User)
if f_email and (is_admin or expose_user_email):
stmt = stmt.where(User.email.like(f"%{f_email}%"))
if f_name and (is_admin or expose_user_name):
stmt = stmt.where(User.username.like(f"%{f_name}%"))
if f_any:
if is_admin:
stmt = stmt.where(or_(User.email.like(f"%{f_any}%"), User.username.like(f"%{f_any}%")))
else:
if expose_user_email and expose_user_name:
stmt = stmt.where(or_(User.email.like(f"%{f_any}%"), User.username.like(f"%{f_any}%")))
elif expose_user_email:
stmt = stmt.where(User.email.like(f"%{f_any}%"))
elif expose_user_name:
stmt = stmt.where(User.username.like(f"%{f_any}%"))
if deleted:
stmt = stmt.where(User.deleted == true())
else:
stmt = stmt.where(User.deleted == false())
return session.scalars(stmt).all()
Loading
Loading