Coverage for src / idx_api / models / brokerage_content_source.py: 100%

19 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2025-12-28 11:09 -0700

1"""BrokerageContentSource model - git repository configuration for brokerage blog content.""" 

2 

3from datetime import datetime 

4from typing import TYPE_CHECKING, Optional 

5 

6from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text 

7from sqlalchemy.orm import Mapped, mapped_column, relationship 

8 

9from idx_api.models.base import Base, TimestampMixin 

10 

11if TYPE_CHECKING: 

12 from idx_api.models.brokerage import Brokerage 

13 

14 

15class BrokerageContentSource(Base, TimestampMixin): 

16 """ 

17 Git repository configuration for brokerage blog content. 

18 

19 Each brokerage can have one content repository containing MDX blog posts. 

20 The content is synced at build time and rendered via Astro content collections. 

21 """ 

22 

23 __tablename__ = "brokerage_content_sources" 

24 

25 id: Mapped[int] = mapped_column(primary_key=True) 

26 brokerage_id: Mapped[int] = mapped_column( 

27 ForeignKey("brokerages.id", ondelete="CASCADE"), 

28 nullable=False, 

29 unique=True, # One content source per brokerage 

30 index=True, 

31 ) 

32 

33 # Git repository URL (supports HTTPS and SSH) 

34 # e.g., "https://github.com/elevateidaho/blog-content.git" 

35 git_url: Mapped[str] = mapped_column(String(500), nullable=False) 

36 

37 # Branch to pull from (default: main) 

38 git_branch: Mapped[str] = mapped_column(String(100), nullable=False, default="main", server_default="main") 

39 

40 # Optional: path within repo where content lives (default: root) 

41 # e.g., "content" if posts are in content/posts/ 

42 content_path: Mapped[Optional[str]] = mapped_column(String(200)) 

43 

44 # Authentication method: "none", "token", "ssh_key", "deploy_key" 

45 auth_method: Mapped[str] = mapped_column(String(50), nullable=False, default="none", server_default="none") 

46 

47 # Encrypted credentials (stored securely, decrypted at build time) 

48 # For tokens: the token itself (should be encrypted in production) 

49 # For SSH: reference to secret in vault/env 

50 auth_credential: Mapped[Optional[str]] = mapped_column(String(500)) 

51 

52 # Whether content sync is enabled 

53 enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default="1") 

54 

55 # Last successful sync timestamp 

56 last_synced_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) 

57 

58 # Last sync commit SHA (40 character git hash) 

59 last_sync_commit: Mapped[Optional[str]] = mapped_column(String(40)) 

60 

61 # Last sync error message (if any) 

62 last_sync_error: Mapped[Optional[str]] = mapped_column(Text) 

63 

64 # Relationships 

65 brokerage: Mapped["Brokerage"] = relationship(back_populates="content_source") 

66 

67 def __repr__(self) -> str: 

68 return f"<BrokerageContentSource(id={self.id}, brokerage_id={self.brokerage_id}, git_url='{self.git_url}')>"