feat(fc3b): migration 0012 + AppSetting model

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 18:33:14 -04:00
parent e623e97be2
commit 2a18016b69
4 changed files with 94 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
"""fc3b: app_setting key/value table
Revision ID: 0012
Revises: 0011
Create Date: 2026-05-20
A simple key/value table for small app settings that don't fit
ImportSettings. Initially seeds only `extension_api_key` (done in
create_app on first boot — not in the migration, to keep it
deterministic and independent of randomness).
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
revision: str = "0012"
down_revision: Union[str, None] = "0011"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"app_setting",
sa.Column("key", sa.String(length=64), primary_key=True),
sa.Column("value", sa.Text(), nullable=False),
sa.Column(
"updated_at", sa.DateTime(timezone=True),
nullable=False, server_default=sa.func.now(),
),
)
def downgrade() -> None:
op.drop_table("app_setting")
+2
View File
@@ -1,5 +1,6 @@
"""All ORM models. Import this module to make every model visible to Alembic.""" """All ORM models. Import this module to make every model visible to Alembic."""
from .app_setting import AppSetting
from .artist import Artist from .artist import Artist
from .base import Base from .base import Base
from .credential import Credential from .credential import Credential
@@ -22,6 +23,7 @@ from .tag_suggestion_rejection import TagSuggestionRejection
__all__ = [ __all__ = [
"Base", "Base",
"AppSetting",
"Artist", "Artist",
"Source", "Source",
"Credential", "Credential",
+24
View File
@@ -0,0 +1,24 @@
"""AppSetting — small key/value app config (extension API key, etc.).
For settings that need DB persistence + runtime mutation. Use
`ImportSettings` for the structured import-tuning settings; this
table is for one-off scalar values keyed by string.
"""
from datetime import datetime
from sqlalchemy import DateTime, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column
from .base import Base
class AppSetting(Base):
__tablename__ = "app_setting"
key: Mapped[str] = mapped_column(String(64), primary_key=True)
value: Mapped[str] = mapped_column(Text, nullable=False)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False,
server_default=func.now(), onupdate=func.now(),
)
+32
View File
@@ -0,0 +1,32 @@
import pytest
from sqlalchemy import select
from backend.app.models import AppSetting
pytestmark = pytest.mark.integration
@pytest.mark.asyncio
async def test_app_setting_table_round_trip(db):
db.add(AppSetting(key="extension_api_key", value="abc123"))
await db.flush()
row = (await db.execute(
select(AppSetting).where(AppSetting.key == "extension_api_key")
)).scalar_one()
assert row.value == "abc123"
assert row.updated_at is not None
@pytest.mark.asyncio
async def test_app_setting_upsert(db):
db.add(AppSetting(key="k", value="v1"))
await db.flush()
row = (await db.execute(
select(AppSetting).where(AppSetting.key == "k")
)).scalar_one()
row.value = "v2"
await db.flush()
again = (await db.execute(
select(AppSetting.value).where(AppSetting.key == "k")
)).scalar_one()
assert again == "v2"