"""Bundle, licensing and feature-toggle DTOs — Phase 11.8.""" from datetime import datetime from uuid import UUID from pydantic import BaseModel, ConfigDict, Field from app.models.types import ( BundleDefinitionStatus, BundleKey, FeatureToggleStatus, LicenseBindingStatus, TenantBundleStatus, ) from app.schemas.common import ORMBase _FORBID = ConfigDict(extra="forbid") class _Create(BaseModel): model_config = _FORBID metadata_json: dict | None = None class _Update(BaseModel): model_config = _FORBID metadata_json: dict | None = None version: int = Field(ge=1) class _Read(ORMBase): id: UUID tenant_id: UUID metadata_json: dict | None version: int is_deleted: bool created_by: str | None updated_by: str | None created_at: datetime updated_at: datetime class BundleDefinitionCreate(_Create): workspace_id: UUID | None = None code: str = Field(min_length=1, max_length=50) bundle_key: BundleKey name: str = Field(min_length=1, max_length=255) description: str | None = None status: BundleDefinitionStatus = BundleDefinitionStatus.DRAFT feature_keys: list[str] | None = None permission_prefixes: list[str] | None = None api_prefixes: list[str] | None = None class BundleDefinitionUpdate(_Update): name: str | None = Field(default=None, min_length=1, max_length=255) description: str | None = None status: BundleDefinitionStatus | None = None feature_keys: list[str] | None = None permission_prefixes: list[str] | None = None api_prefixes: list[str] | None = None class BundleDefinitionRead(_Read): workspace_id: UUID | None code: str bundle_key: BundleKey name: str description: str | None status: BundleDefinitionStatus feature_keys: list | None permission_prefixes: list | None api_prefixes: list | None class BundleDefinitionListResponse(BaseModel): items: list[BundleDefinitionRead] total: int page: int page_size: int class TenantBundleCreate(_Create): workspace_id: UUID site_id: UUID | None = None bundle_definition_id: UUID status: TenantBundleStatus = TenantBundleStatus.PENDING core_entitlement_ref: str | None = Field(default=None, max_length=255) expires_at: datetime | None = None class TenantBundleUpdate(_Update): status: TenantBundleStatus | None = None core_entitlement_ref: str | None = Field(default=None, max_length=255) expires_at: datetime | None = None class TenantBundleRead(_Read): workspace_id: UUID site_id: UUID | None bundle_definition_id: UUID bundle_key: BundleKey status: TenantBundleStatus core_entitlement_ref: str | None activated_at: datetime | None expires_at: datetime | None activated_by: str | None class TenantBundleListResponse(BaseModel): items: list[TenantBundleRead] total: int page: int page_size: int class LicenseBindingCreate(_Create): workspace_id: UUID tenant_bundle_id: UUID | None = None bundle_key: BundleKey | None = None core_entitlement_ref: str = Field(min_length=1, max_length=255) status: LicenseBindingStatus = LicenseBindingStatus.PENDING starts_at: datetime | None = None ends_at: datetime | None = None class LicenseBindingUpdate(_Update): status: LicenseBindingStatus | None = None core_entitlement_ref: str | None = Field(default=None, min_length=1, max_length=255) starts_at: datetime | None = None ends_at: datetime | None = None class LicenseBindingRead(_Read): workspace_id: UUID tenant_bundle_id: UUID | None bundle_key: BundleKey | None core_entitlement_ref: str status: LicenseBindingStatus starts_at: datetime | None ends_at: datetime | None class LicenseBindingListResponse(BaseModel): items: list[LicenseBindingRead] total: int page: int page_size: int class FeatureToggleCreate(_Create): workspace_id: UUID site_id: UUID | None = None feature_key: str = Field(min_length=1, max_length=100) enabled: bool = False status: FeatureToggleStatus = FeatureToggleStatus.ACTIVE bundle_key: BundleKey | None = None class FeatureToggleUpdate(_Update): enabled: bool | None = None status: FeatureToggleStatus | None = None bundle_key: BundleKey | None = None class FeatureToggleRead(_Read): workspace_id: UUID site_id: UUID | None feature_key: str enabled: bool status: FeatureToggleStatus bundle_key: BundleKey | None class FeatureToggleListResponse(BaseModel): items: list[FeatureToggleRead] total: int page: int page_size: int