Unverified Commit d23186b6 authored by mvdbeek's avatar mvdbeek
Browse files

Use pydantic UUID4 type to validate UUID strings

This works fine for prasing strings:
```
In [9]: UserConcreteObjectStoreModel(uuid=uuid.uuid4().hex, private=False, quota={"enabled": False}, badges=[])
Out[9]: UserConcreteObjectStoreModel(object_store_id=None, private=False, name=None, description=None, quota=QuotaModel(source=None, enabled=False), badges=[], device=None, uuid=UUID('c7a6751b-4d78-4d7f-b3b1-b2baf0a50d90'))
```

Fixes https://github.com/galaxyproject/galaxy/issues/18684#event-13846279733
parent 2d149f6e
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -12848,7 +12848,10 @@ export interface components {
             * @enum {string}
             */
            type: "aws_s3" | "azure_blob" | "boto3" | "disk" | "generic_s3";
            /** Uuid */
            /**
             * Uuid
             * Format: uuid4
             */
            uuid: string;
            /** Variables */
            variables: {
@@ -12922,7 +12925,10 @@ export interface components {
            type: "ftp" | "posix" | "s3fs" | "azure";
            /** Uri Root */
            uri_root: string;
            /** Uuid */
            /**
             * Uuid
             * Format: uuid4
             */
            uuid: string;
            /** Variables */
            variables: {
+10 −9
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ from uuid import uuid4

from pydantic import (
    BaseModel,
    UUID4,
    ValidationError,
)

@@ -87,7 +88,7 @@ USER_FILE_SOURCES_SCHEME = "gxuserfiles"


class UserFileSourceModel(BaseModel):
    uuid: str
    uuid: UUID4
    uri_root: str
    name: str
    description: Optional[str]
@@ -142,16 +143,16 @@ class FileSourceInstancesManager:
        stores = self._sa_session.query(UserFileSource).filter(UserFileSource.user_id == trans.user.id).all()
        return [self._to_model(trans, s) for s in stores]

    def show(self, trans: ProvidesUserContext, uuid: str) -> UserFileSourceModel:
    def show(self, trans: ProvidesUserContext, uuid: UUID4) -> UserFileSourceModel:
        user_file_source = self._get(trans, uuid)
        return self._to_model(trans, user_file_source)

    def purge_instance(self, trans: ProvidesUserContext, uuid: str) -> None:
    def purge_instance(self, trans: ProvidesUserContext, uuid: UUID4) -> None:
        persisted_file_source = self._get(trans, uuid)
        purge_template_instance(trans, persisted_file_source, self._app_config)

    def modify_instance(
        self, trans: ProvidesUserContext, id: str, payload: ModifyInstancePayload
        self, trans: ProvidesUserContext, id: UUID4, payload: ModifyInstancePayload
    ) -> UserFileSourceModel:
        if isinstance(payload, UpgradeInstancePayload):
            return self._upgrade_instance(trans, id, payload)
@@ -162,7 +163,7 @@ class FileSourceInstancesManager:
            return self._update_instance(trans, id, payload)

    def _upgrade_instance(
        self, trans: ProvidesUserContext, id: str, payload: UpgradeInstancePayload
        self, trans: ProvidesUserContext, id: UUID4, payload: UpgradeInstancePayload
    ) -> UserFileSourceModel:
        persisted_file_source = self._get(trans, id)
        template = self._get_template(persisted_file_source, payload.template_version)
@@ -181,7 +182,7 @@ class FileSourceInstancesManager:
        return self._to_model(trans, persisted_file_source)

    def _update_instance(
        self, trans: ProvidesUserContext, id: str, payload: UpdateInstancePayload
        self, trans: ProvidesUserContext, id: UUID4, payload: UpdateInstancePayload
    ) -> UserFileSourceModel:
        persisted_file_source = self._get(trans, id)
        template = self._get_template(persisted_file_source)
@@ -189,7 +190,7 @@ class FileSourceInstancesManager:
        return self._to_model(trans, persisted_file_source)

    def _update_instance_secret(
        self, trans: ProvidesUserContext, id: str, payload: UpdateInstanceSecretPayload
        self, trans: ProvidesUserContext, id: UUID4, payload: UpdateInstanceSecretPayload
    ) -> UserFileSourceModel:
        persisted_file_source = self._get(trans, id)
        template = self._get_template(persisted_file_source)
@@ -300,10 +301,10 @@ class FileSourceInstancesManager:
            exception = e
        return file_source, connection_exception_to_status("file source", exception)

    def _index_filter(self, uuid: str):
    def _index_filter(self, uuid: UUID4):
        return UserFileSource.__table__.c.uuid == uuid

    def _get(self, trans: ProvidesUserContext, uuid: str) -> UserFileSource:
    def _get(self, trans: ProvidesUserContext, uuid: UUID4) -> UserFileSource:
        filter = self._index_filter(uuid)
        user_file_source = self._sa_session.query(UserFileSource).filter(filter).one_or_none()
        if user_file_source is None:
+11 −9
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@ from typing import (
)
from uuid import uuid4

from pydantic import UUID4

from galaxy.exceptions import (
    ItemOwnershipException,
    RequestParameterInvalidException,
@@ -73,7 +75,7 @@ log = logging.getLogger(__name__)


class UserConcreteObjectStoreModel(ConcreteObjectStoreModel):
    uuid: str
    uuid: UUID4
    type: ObjectStoreTemplateType
    template_id: str
    template_version: int
@@ -106,7 +108,7 @@ class ObjectStoreInstancesManager:
        return self._catalog.summaries

    def modify_instance(
        self, trans: ProvidesUserContext, id: str, payload: ModifyInstancePayload
        self, trans: ProvidesUserContext, id: UUID4, payload: ModifyInstancePayload
    ) -> UserConcreteObjectStoreModel:
        if isinstance(payload, UpgradeInstancePayload):
            return self._upgrade_instance(trans, id, payload)
@@ -116,12 +118,12 @@ class ObjectStoreInstancesManager:
            assert isinstance(payload, UpdateInstancePayload)
            return self._update_instance(trans, id, payload)

    def purge_instance(self, trans: ProvidesUserContext, id: str) -> None:
    def purge_instance(self, trans: ProvidesUserContext, id: UUID4) -> None:
        persisted_object_store = self._get(trans, id)
        purge_template_instance(trans, persisted_object_store, self._app_config)

    def _upgrade_instance(
        self, trans: ProvidesUserContext, id: str, payload: UpgradeInstancePayload
        self, trans: ProvidesUserContext, id: UUID4, payload: UpgradeInstancePayload
    ) -> UserConcreteObjectStoreModel:
        persisted_object_store = self._get(trans, id)
        template = self._get_template(persisted_object_store, payload.template_version)
@@ -140,7 +142,7 @@ class ObjectStoreInstancesManager:
        return self._to_model(trans, persisted_object_store)

    def _update_instance(
        self, trans: ProvidesUserContext, id: str, payload: UpdateInstancePayload
        self, trans: ProvidesUserContext, id: UUID4, payload: UpdateInstancePayload
    ) -> UserConcreteObjectStoreModel:
        persisted_object_store = self._get(trans, id)
        template = self._get_template(persisted_object_store)
@@ -148,7 +150,7 @@ class ObjectStoreInstancesManager:
        return self._to_model(trans, persisted_object_store)

    def _update_instance_secret(
        self, trans: ProvidesUserContext, id: str, payload: UpdateInstanceSecretPayload
        self, trans: ProvidesUserContext, id: UUID4, payload: UpdateInstanceSecretPayload
    ) -> UserConcreteObjectStoreModel:
        persisted_object_store = self._get(trans, id)
        template = self._get_template(persisted_object_store)
@@ -200,14 +202,14 @@ class ObjectStoreInstancesManager:
        stores = self._sa_session.query(UserObjectStore).filter(UserObjectStore.user_id == trans.user.id).all()
        return [self._to_model(trans, s) for s in stores]

    def show(self, trans: ProvidesUserContext, id: str) -> UserConcreteObjectStoreModel:
    def show(self, trans: ProvidesUserContext, id: UUID4) -> UserConcreteObjectStoreModel:
        user_object_store = self._get(trans, id)
        return self._to_model(trans, user_object_store)

    def _save(self, persisted_object_store: UserObjectStore) -> None:
        save_template_instance(self._sa_session, persisted_object_store)

    def _get(self, trans: ProvidesUserContext, id: str) -> UserObjectStore:
    def _get(self, trans: ProvidesUserContext, id: UUID4) -> UserObjectStore:
        filter = self._index_filter(id)
        user_object_store = self._sa_session.query(UserObjectStore).filter(filter).one_or_none()
        if user_object_store is None:
@@ -274,7 +276,7 @@ class ObjectStoreInstancesManager:
            exception = e
        return object_store, connection_exception_to_status("storage location", exception)

    def _index_filter(self, uuid: str):
    def _index_filter(self, uuid: UUID4):
        return UserObjectStore.__table__.c.uuid == uuid

    def _get_template(
+5 −4
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from fastapi import (
    Response,
    status,
)
from pydantic import UUID4

from galaxy.files.templates import FileSourceTemplateSummaries
from galaxy.managers.context import ProvidesUserContext
@@ -30,7 +31,7 @@ log = logging.getLogger(__name__)
router = Router(tags=["file_sources"])


UserFileSourceIdPathParam: str = Path(
UserFileSourceIdPathParam: UUID4 = Path(
    ..., title="User File Source UUID", description="The UUID index for a persisted UserFileSourceStore object."
)

@@ -95,7 +96,7 @@ class FastAPIFileSources:
    def instances_show(
        self,
        trans: ProvidesUserContext = DependsOnTrans,
        user_file_source_id: str = UserFileSourceIdPathParam,
        user_file_source_id: UUID4 = UserFileSourceIdPathParam,
    ) -> UserFileSourceModel:
        return self.file_source_instances_manager.show(trans, user_file_source_id)

@@ -107,7 +108,7 @@ class FastAPIFileSources:
    def update_instance(
        self,
        trans: ProvidesUserContext = DependsOnTrans,
        user_file_source_id: str = UserFileSourceIdPathParam,
        user_file_source_id: UUID4 = UserFileSourceIdPathParam,
        payload: ModifyInstancePayload = Body(...),
    ) -> UserFileSourceModel:
        return self.file_source_instances_manager.modify_instance(trans, user_file_source_id, payload)
@@ -121,7 +122,7 @@ class FastAPIFileSources:
    def purge_instance(
        self,
        trans: ProvidesUserContext = DependsOnTrans,
        user_file_source_id: str = UserFileSourceIdPathParam,
        user_file_source_id: UUID4 = UserFileSourceIdPathParam,
    ):
        self.file_source_instances_manager.purge_instance(trans, user_file_source_id)
        return Response(status_code=status.HTTP_204_NO_CONTENT)
+5 −4
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ from fastapi import (
    Response,
    status,
)
from pydantic import UUID4

from galaxy.exceptions import (
    ObjectNotFound,
@@ -49,7 +50,7 @@ ConcreteObjectStoreIdPathParam: str = Path(
    ..., title="Concrete Object Store ID", description="The concrete object store ID."
)

UserObjectStoreIdPathParam: str = Path(
UserObjectStoreIdPathParam: UUID4 = Path(
    ...,
    title="User Object Store UUID",
    description="The UUID used to identify a persisted UserObjectStore object.",
@@ -136,7 +137,7 @@ class FastAPIObjectStore:
        self,
        trans: ProvidesUserContext = DependsOnTrans,
        user: User = DependsOnUser,
        user_object_store_id: str = UserObjectStoreIdPathParam,
        user_object_store_id: UUID4 = UserObjectStoreIdPathParam,
    ) -> UserConcreteObjectStoreModel:
        return self.object_store_instance_manager.show(trans, user_object_store_id)

@@ -160,7 +161,7 @@ class FastAPIObjectStore:
        self,
        trans: ProvidesUserContext = DependsOnTrans,
        user: User = DependsOnUser,
        user_object_store_id: str = UserObjectStoreIdPathParam,
        user_object_store_id: UUID4 = UserObjectStoreIdPathParam,
        payload: ModifyInstancePayload = Body(...),
    ) -> UserConcreteObjectStoreModel:
        return self.object_store_instance_manager.modify_instance(trans, user_object_store_id, payload)
@@ -175,7 +176,7 @@ class FastAPIObjectStore:
        self,
        trans: ProvidesUserContext = DependsOnTrans,
        user: User = DependsOnUser,
        user_object_store_id: str = UserObjectStoreIdPathParam,
        user_object_store_id: UUID4 = UserObjectStoreIdPathParam,
    ):
        self.object_store_instance_manager.purge_instance(trans, user_object_store_id)
        return Response(status_code=status.HTTP_204_NO_CONTENT)