refactor(SKY-62): consolidate repository pattern follow-up PRs (#5283)
Some checks are pending
Run tests and pre-commit / Run tests and pre-commit hooks (push) Waiting to run
Run tests and pre-commit / Frontend Lint and Build (push) Waiting to run
Publish Fern Docs / run (push) Waiting to run

This commit is contained in:
Aaron Perez 2026-03-28 16:27:43 -05:00 committed by GitHub
parent 22d05d5091
commit d3bba42792
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 65 additions and 18 deletions

View file

@ -0,0 +1,4 @@
"""Sentinel values for the database layer."""
# Sentinel for distinguishing "not passed" from "passed as None" in update methods.
_UNSET = object()

View file

@ -36,7 +36,16 @@ LOG = structlog.get_logger()
class BrowserSessionsMixin:
"""Database operations for browser profiles and persistent browser sessions."""
"""Database operations for browser profiles and persistent browser sessions.
.. deprecated::
This mixin is part of the legacy database layer. New code should use the
repository classes in ``skyvern.forge.sdk.db.repositories`` instead.
Cross-mixin migrations already completed:
- ``get_last_workflow_run_for_browser_session`` ``WorkflowRunsRepository``
(queries workflow runs as the primary entity, browser session is just a filter).
"""
Session: _SessionFactory
@ -46,6 +55,9 @@ class BrowserSessionsMixin:
browser_session_id: str,
organization_id: str | None = None,
) -> WorkflowRun | None:
# Deprecated: moved to WorkflowRunsRepository.get_last_workflow_run_for_browser_session
# (skyvern/forge/sdk/db/repositories/workflow_runs.py). The primary entity is the
# workflow run, not the browser session. This copy remains for legacy mixin compatibility.
async with self.Session() as session:
# check if there's a queued run
query = select(WorkflowRunModel).filter_by(browser_session_id=browser_session_id)

View file

@ -14,7 +14,7 @@ from skyvern.forge.sdk.schemas.organization_bitwarden_collections import Organiz
if TYPE_CHECKING:
from skyvern.forge.sdk.db.base_alchemy_db import _SessionFactory
_UNSET = object()
from skyvern.forge.sdk.db._sentinels import _UNSET
class CredentialsMixin:

View file

@ -23,12 +23,22 @@ if TYPE_CHECKING:
from skyvern.forge.sdk.db.base_alchemy_db import _SessionFactory
from skyvern.forge.sdk.db._sentinels import _UNSET
LOG = structlog.get_logger()
_UNSET = object()
class SchedulesMixin:
"""Database operations for workflow schedules."""
"""Database operations for workflow schedules.
.. deprecated::
This mixin is part of the legacy database layer. New code should use the
repository classes in ``skyvern.forge.sdk.db.repositories`` instead.
Cross-mixin migrations already completed:
- ``soft_delete_workflow_and_schedules_by_permanent_id`` ``WorkflowsRepository``
(operates on workflows as the primary entity, schedules are a side-effect).
"""
Session: _SessionFactory
engine: AsyncEngine
@ -563,7 +573,14 @@ class SchedulesMixin:
workflow_permanent_id: str,
organization_id: str | None = None,
) -> list[str]:
"""Soft-delete a workflow and its active schedules in a single DB transaction."""
"""Soft-delete a workflow and its active schedules in a single DB transaction.
.. deprecated::
Moved to ``WorkflowsRepository.soft_delete_workflow_and_schedules_by_permanent_id``
(skyvern/forge/sdk/db/repositories/workflows.py). The primary entity is the
workflow, not the schedule, so it belongs in the workflows repository.
This copy remains for backward compatibility with the legacy mixin layer.
"""
async with self.Session() as session:
select_query = (
select(WorkflowScheduleModel.workflow_schedule_id)

View file

@ -65,8 +65,9 @@ from skyvern.webeye.actions.actions import Action
if TYPE_CHECKING:
from skyvern.forge.sdk.db.base_alchemy_db import _SessionFactory
from skyvern.forge.sdk.db._sentinels import _UNSET
LOG = structlog.get_logger()
_UNSET = object()
class WorkflowParametersMixin:

View file

@ -44,8 +44,9 @@ if TYPE_CHECKING:
from skyvern.forge.sdk.db.base_alchemy_db import _SessionFactory
from skyvern.forge.sdk.db._sentinels import _UNSET
LOG = structlog.get_logger()
_UNSET = object()
class WorkflowRunsMixin:

View file

@ -1,4 +1 @@
"""Domain-specific repository classes extracted from AgentDB mixins."""
# Sentinel for distinguishing "not passed" from "passed as None" in update methods.
_UNSET = object()

View file

@ -5,14 +5,13 @@ from datetime import datetime, timezone
from sqlalchemy import select
from skyvern.forge.sdk.db._error_handling import db_operation
from skyvern.forge.sdk.db._sentinels import _UNSET
from skyvern.forge.sdk.db.base_repository import BaseRepository
from skyvern.forge.sdk.db.exceptions import NotFoundError
from skyvern.forge.sdk.db.models import CredentialModel, OrganizationBitwardenCollectionModel
from skyvern.forge.sdk.schemas.credentials import Credential, CredentialType, CredentialVaultType
from skyvern.forge.sdk.schemas.organization_bitwarden_collections import OrganizationBitwardenCollection
from . import _UNSET
class CredentialRepository(BaseRepository):
"""Database operations for credential and Bitwarden collection management."""

View file

@ -21,7 +21,7 @@ from skyvern.forge.sdk.workflow.schedules import compute_next_run
if TYPE_CHECKING:
from skyvern.forge.sdk.db.base_alchemy_db import _SessionFactory
from . import _UNSET
from skyvern.forge.sdk.db._sentinels import _UNSET
LOG = structlog.get_logger()

View file

@ -9,6 +9,7 @@ from sqlalchemy import select
from skyvern.config import settings
from skyvern.forge.sdk.db._error_handling import db_operation
from skyvern.forge.sdk.db._sentinels import _UNSET
from skyvern.forge.sdk.db.base_repository import BaseRepository
from skyvern.forge.sdk.db.exceptions import NotFoundError
from skyvern.forge.sdk.db.models import (
@ -63,8 +64,6 @@ from skyvern.forge.sdk.workflow.models.parameter import (
from skyvern.schemas.runs import RunType
from skyvern.webeye.actions.actions import Action
from . import _UNSET
LOG = structlog.get_logger()

View file

@ -18,6 +18,7 @@ from skyvern.forge.sdk.db.exceptions import NotFoundError
if TYPE_CHECKING:
from skyvern.forge.sdk.db.base_alchemy_db import _SessionFactory
from skyvern.forge.sdk.db._sentinels import _UNSET
from skyvern.forge.sdk.db.models import (
TaskModel,
WorkflowModel,
@ -46,8 +47,6 @@ from skyvern.forge.sdk.workflow.models.workflow import (
)
from skyvern.schemas.runs import ProxyLocationInput
from . import _UNSET
LOG = structlog.get_logger()

View file

@ -1633,7 +1633,7 @@ async def update_bitwarden_credential(
"""
try:
# Atomically invalidate old + create new in a single transaction
auth_token = await app.DATABASE.replace_org_auth_token(
auth_token = await app.DATABASE.organizations.replace_org_auth_token(
organization_id=current_org.organization_id,
token_type=OrganizationAuthTokenType.bitwarden_credential,
token=request.credential,

View file

@ -0,0 +1,18 @@
"""Tests for the _UNSET sentinel value."""
from skyvern.forge.sdk.db._sentinels import _UNSET
def test_unset_is_unique_sentinel() -> None:
"""_UNSET should be distinguishable from None and other values."""
assert _UNSET is not None
assert _UNSET is not False
assert _UNSET != 0
assert _UNSET != ""
def test_unset_identity() -> None:
"""_UNSET should be the same object across imports."""
from skyvern.forge.sdk.db._sentinels import _UNSET as second_import
assert _UNSET is second_import