Do not initialize a full repository registry in ToolShed 2.0.

It seems like it is only used by repository_grids - which I think are unused by the new tool shed. I don't recall writing any client side grid framework code and my PR description did claim it was mako free.
This commit is contained in:
John Chilton
2024-08-05 13:53:14 -04:00
parent efe76b53e0
commit fd2425fb37
11 changed files with 159 additions and 31 deletions
+1 -3
View File
@@ -59,9 +59,7 @@ class CategoryManager:
def to_dict(self, category: Category) -> Dict[str, Any]:
category_dict = category.to_dict(view="collection", value_mapper=get_value_mapper(self.app))
category_dict["repositories"] = self.app.repository_registry.viewable_repositories_and_suites_by_category.get(
category.name, 0
)
category_dict["repositories"] = category.active_repository_count()
category_dict["url"] = web.url_for(
controller="categories", action="show", id=self.app.security.encode_id(category.id)
)
+96 -8
View File
@@ -6,6 +6,7 @@ from sqlalchemy import (
or_,
select,
)
from typing_extensions import Protocol
import tool_shed.repository_types.util as rt_util
from tool_shed.util import (
@@ -18,11 +19,77 @@ from tool_shed.webapp.model import (
Repository,
RepositoryMetadata,
)
from .structured_app import ToolShedApp
log = logging.getLogger(__name__)
class Registry:
class RegistryInterface(Protocol):
# only used in legacy controllers - can drop when drop old tool shed webapp
def add_category_entry(self, category): ...
def add_entry(self, repository):
# used when creating a repository
# Used in legacy add_repository_registry_entry API endpoint - already deemed not worth pulling over to 2.0
# used in legacy undelete_repository admin controller
# used in legacy deprecate controller
...
def edit_category_entry(self, old_name, new_name):
# used in legacy admin controller
...
def is_valid(self, repository) -> bool:
# probably not used outside this class but also not hurting anything
...
def remove_category_entry(self, category): ...
def remove_entry(self, repository): ...
# stop gap to implement the same general interface as repository_registry but do nothing,
# I don't think this stuff is needed - outside potentially old test cases?
class NullRepositoryRegistry(RegistryInterface):
def __init__(self, app: ToolShedApp):
self.app = app
# all of these are only used by repository_grids - which I think the tool shed 2.0
# does not use at all - but they are part of the "public interface" consumed by
# the full registry.
self.certified_level_one_viewable_repositories_and_suites_by_category: dict = {}
self.certified_level_one_viewable_suites_by_category: dict = {}
self.viewable_repositories_and_suites_by_category: dict = {}
self.viewable_suites_by_category: dict = {}
self.viewable_valid_repositories_and_suites_by_category: dict = {}
self.viewable_valid_suites_by_category: dict = {}
def add_category_entry(self, category):
# only used in legacy controllers - can drop when drop old tool shed webapp
pass
def add_entry(self, repository):
# used when creating a repository, and maybe more?
pass
def edit_category_entry(self, old_name, new_name):
pass
def is_valid(self, repository) -> bool:
if repository and not repository.deleted and not repository.deprecated and repository.downloadable_revisions:
return True
return False
def remove_category_entry(self, category):
pass
def remove_entry(self, repository):
pass
class Registry(RegistryInterface):
def __init__(self, app):
log.debug("Loading the repository registry...")
self.app = app
@@ -32,17 +99,30 @@ class Registry:
self.certified_level_one_suite_tuples = []
# These category dictionaries contain entries where the key is the category and the value is the integer count
# of viewable repositories within that category.
# only used internally to class and by repository_grids
self.certified_level_one_viewable_repositories_and_suites_by_category = {}
# only used internally to class and by repository_grids
self.certified_level_one_viewable_suites_by_category = {}
self.certified_level_two_repository_and_suite_tuples = []
self.certified_level_two_suite_tuples = []
self.certified_level_two_viewable_repositories_and_suites_by_category = {}
self.certified_level_two_viewable_suites_by_category = {}
# not even used in legacy shed code...
# self.certified_level_two_repository_and_suite_tuples = []
# self.certified_level_two_suite_tuples = []
# self.certified_level_two_viewable_repositories_and_suites_by_category = {}
# self.certified_level_two_viewable_suites_by_category = {}
# only used internally to class
self.repository_and_suite_tuples = []
# only used internally to class
self.suite_tuples = []
# only used internally to class and by repository_grids
self.viewable_repositories_and_suites_by_category = {}
# only used internally to class and by repository_grids
self.viewable_suites_by_category = {}
# only used internally to class and by repository_grids
self.viewable_valid_repositories_and_suites_by_category = {}
# only used internally to class and by repository_grids
self.viewable_valid_suites_by_category = {}
self.load()
@@ -152,6 +232,7 @@ class Registry:
self.certified_level_one_viewable_suites_by_category[new_name] = 0
def get_certified_level_one_clause_list(self):
# only used internally to class
clause_list = []
for repository in get_repositories(self.sa_session):
certified_level_one_tuple = self.get_certified_level_one_tuple(repository)
@@ -169,6 +250,7 @@ class Registry:
"""
Return True if the latest installable changeset_revision of the received repository is level one certified.
"""
# only used internally to class
if repository is None:
return (None, False)
if repository.deleted or repository.deprecated:
@@ -190,6 +272,7 @@ class Registry:
return (None, False)
def is_level_one_certified(self, repository_metadata):
# only used internally to class
if repository_metadata:
repository = repository_metadata.repository
if repository:
@@ -206,12 +289,13 @@ class Registry:
return tuple in self.certified_level_one_repository_and_suite_tuples
return False
def is_valid(self, repository):
def is_valid(self, repository) -> bool:
if repository and not repository.deleted and not repository.deprecated and repository.downloadable_revisions:
return True
return False
def load_certified_level_one_repository_and_suite_tuple(self, repository):
# only used internally to class
# The received repository has been determined to be level one certified.
name = str(repository.name)
owner = str(repository.user.username)
@@ -226,6 +310,7 @@ class Registry:
self.certified_level_one_repository_and_suite_tuples.append(certified_level_one_tuple)
def load_repository_and_suite_tuple(self, repository):
# only used internally to class
name = str(repository.name)
owner = str(repository.user.username)
for repository_metadata in repository.metadata_revisions:
@@ -238,6 +323,7 @@ class Registry:
self.suite_tuples.append(tuple)
def load_repository_and_suite_tuples(self):
# only used internally to class
# Load self.certified_level_one_repository_and_suite_tuples and self.certified_level_one_suite_tuples.
clauses = self.get_certified_level_one_clause_list()
for repository in get_certified_repositories_with_user(self.sa_session, clauses, model.User):
@@ -247,11 +333,12 @@ class Registry:
self.load_repository_and_suite_tuple(repository)
def load_viewable_repositories_and_suites_by_category(self):
# only used internally to class
# Clear all dictionaries just in case they were previously loaded.
self.certified_level_one_viewable_repositories_and_suites_by_category = {}
self.certified_level_one_viewable_suites_by_category = {}
self.certified_level_two_viewable_repositories_and_suites_by_category = {}
self.certified_level_two_viewable_suites_by_category = {}
# self.certified_level_two_viewable_repositories_and_suites_by_category = {}
# self.certified_level_two_viewable_suites_by_category = {}
self.viewable_repositories_and_suites_by_category = {}
self.viewable_suites_by_category = {}
self.viewable_valid_repositories_and_suites_by_category = {}
@@ -363,6 +450,7 @@ class Registry:
@property
def sa_session(self):
# only used internally to class
return self.app.model.session
def unload_certified_level_one_repository_and_suite_tuple(self, repository):
+2 -2
View File
@@ -4,7 +4,7 @@ from galaxy.structured_app import BasicSharedApp
if TYPE_CHECKING:
from tool_shed.managers.model_cache import ModelCache
from tool_shed.repository_registry import Registry as RepositoryRegistry
from tool_shed.repository_registry import RegistryInterface
from tool_shed.repository_types.registry import Registry as RepositoryTypesRegistry
from tool_shed.util.hgweb_config import HgWebConfigManager
from tool_shed.webapp.model import mapping
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
class ToolShedApp(BasicSharedApp):
repository_types_registry: "RepositoryTypesRegistry"
model: "mapping.ToolShedModelMapping"
repository_registry: "RepositoryRegistry"
repository_registry: "RegistryInterface"
hgweb_config_manager: "HgWebConfigManager"
security_agent: "CommunityRBACAgent"
model_cache: "ModelCache"
+4 -1
View File
@@ -108,7 +108,10 @@ class UniverseApplication(ToolShedApp, SentryClientMixin, HaltableContainer):
self.hgweb_config_manager.hgweb_config_dir = self.config.hgweb_config_dir
self.hgweb_config_manager.hgweb_repo_prefix = self.config.hgweb_repo_prefix
# Initialize the repository registry.
self.repository_registry = tool_shed.repository_registry.Registry(self)
if config.SHED_API_VERSION != "v2":
self.repository_registry = tool_shed.repository_registry.Registry(self)
else:
self.repository_registry = tool_shed.repository_registry.NullRepositoryRegistry(self)
# Configure Sentry client if configured
self.configure_sentry_client()
# used for cachebusting -- refactor this into a *SINGLE* UniverseApplication base.
+1 -2
View File
@@ -26,8 +26,7 @@ from galaxy.webapps.base.webapp import (
GalaxyWebTransaction,
)
from galaxy.webapps.util import wrap_if_allowed
SHED_API_VERSION = os.environ.get("TOOL_SHED_API_VERSION", "v1")
from .config import SHED_API_VERSION
log = logging.getLogger(__name__)
+1
View File
@@ -27,6 +27,7 @@ from galaxy.version import (
log = logging.getLogger(__name__)
TOOLSHED_APP_NAME = "tool_shed"
SHED_API_VERSION = os.environ.get("TOOL_SHED_API_VERSION", "v1")
class ToolShedAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
+1 -1
View File
@@ -166,7 +166,7 @@ def initialize_fast_app(gx_webapp, tool_shed_app):
app = get_fastapi_instance()
add_exception_handler(app)
add_request_id_middleware(app)
from .buildapp import SHED_API_VERSION
from .config import SHED_API_VERSION
def mount_static(directory: Path):
name = directory.name
+18
View File
@@ -28,6 +28,7 @@ from sqlalchemy import (
not_,
String,
Table,
text,
TEXT,
true,
UniqueConstraint,
@@ -35,6 +36,7 @@ from sqlalchemy import (
from sqlalchemy.orm import (
Mapped,
mapped_column,
object_session,
registry,
relationship,
)
@@ -618,6 +620,22 @@ class Category(Base, Dictifiable):
dict_collection_visible_keys = ["id", "name", "description", "deleted"]
dict_element_visible_keys = ["id", "name", "description", "deleted"]
def active_repository_count(self, session=None) -> int:
statement = """
SELECT count(*) as count
FROM repository r
INNER JOIN repository_category_association rca on rca.repository_id = r.id
WHERE
rca.category_id = :category_id
AND r.private = false
AND r.deleted = false
and r.deprecated = false
"""
if session is None:
session = object_session(self)
params = {"category_id": self.id}
return session.execute(text(statement), params).scalar()
def __init__(self, deleted=False, **kwd):
super().__init__(**kwd)
self.deleted = deleted
+10
View File
@@ -33,6 +33,7 @@ from tool_shed.webapp.model import (
Category,
mapping,
Repository,
RepositoryCategoryAssociation,
User,
)
from tool_shed_client.schema import CreateCategoryRequest
@@ -195,3 +196,12 @@ def create_category(provides_repositories: ProvidesRepositoriesContext, create:
request = CreateCategoryRequest(**create)
return CategoryManager(provides_repositories.app).create(provides_repositories, request)
def attach_category(provides_repositories: ProvidesRepositoriesContext, repository: Repository, category: Category):
assoc = RepositoryCategoryAssociation(
repository=repository,
category=category,
)
provides_repositories.sa_session.add(assoc)
provides_repositories.sa_session.flush()
+2 -14
View File
@@ -12,12 +12,9 @@ from tool_shed.context import (
ProvidesUserContext,
)
from tool_shed.webapp.graphql.schema import schema
from tool_shed.webapp.model import (
Category,
Repository,
RepositoryCategoryAssociation,
)
from tool_shed.webapp.model import Repository
from ._util import (
attach_category,
create_category,
repository_fixture,
upload_directories_to_repository,
@@ -110,15 +107,6 @@ def test_simple_repositories(provides_repositories: ProvidesRepositoriesContext,
assert new_repository.name in repository_names
def attach_category(provides_repositories: ProvidesRepositoriesContext, repository: Repository, category: Category):
assoc = RepositoryCategoryAssociation(
repository=repository,
category=category,
)
provides_repositories.sa_session.add(assoc)
provides_repositories.sa_session.flush()
def test_relay_repos_by_category(provides_repositories: ProvidesRepositoriesContext, new_repository: Repository):
name = new_repository.name
category = create_category(provides_repositories, {"name": "test_graphql_relay_categories_1"})
@@ -5,6 +5,8 @@ from tool_shed.webapp.model import (
User,
)
from ._util import (
attach_category,
create_category,
repository_fixture,
TEST_DATA_FILES,
TestToolShedApp,
@@ -83,6 +85,27 @@ def test_upload_dry_run_ok(provides_repositories: ProvidesRepositoriesContext, n
assert old_tip == new_tip
def test_category_count(provides_repositories: ProvidesRepositoriesContext, new_repository: Repository):
category = create_category(provides_repositories, {"name": "test_category_count_1"})
attach_category(provides_repositories, new_repository, category)
category = new_repository.categories[0].category
assert category.active_repository_count() == 1
tar_resource = TEST_DATA_FILES.joinpath("column_maker/column_maker.tar")
upload_ok, *_ = upload_tar(
provides_repositories,
new_repository.user.username,
new_repository,
tar_resource,
commit_message="Commit Message",
)
assert upload_ok
category = new_repository.categories[0].category
assert category.active_repository_count() == 1
def test_upload_dry_run_failed(provides_repositories: ProvidesRepositoriesContext, new_repository: Repository):
tar_resource = TEST_DATA_FILES.joinpath("safetar_with_symlink.tar")
upload_ok, message, _, _, _, _ = upload_tar(