from datetime import datetime from pydantic import BaseModel, Field, field_validator from admin_app.core import AdminCredentialStrategy from shared.contracts import ( AdminPermission, OperationalConsistencyModel, OperationalDataDomain, OperationalDataSensitivity, OperationalFreshnessTarget, OperationalQuerySurface, OperationalReadGranularity, OperationalReadModel, OperationalStorageShape, OperationalSyncStrategy, ServiceName, StaffRole, ToolLifecycleStatus, ToolParameterType, ) class AdminRootResponse(BaseModel): service: str status: str message: str environment: str class AdminHealthResponse(BaseModel): service: str status: str version: str class AdminSystemInfoResponse(BaseModel): service: str app_name: str environment: str version: str api_prefix: str debug: bool class AdminAuthenticatedStaffResponse(BaseModel): id: int email: str display_name: str role: StaffRole is_active: bool class AdminCurrentAccessResponse(BaseModel): service: str staff_account: AdminAuthenticatedStaffResponse permissions: list[str] class AdminCapabilityResponse(BaseModel): service: str action: str allowed: bool role: StaffRole class AdminAuditEntryResponse(BaseModel): id: int actor_staff_account_id: int | None event_type: str resource_type: str resource_id: str | None outcome: str message: str | None payload_json: dict | None ip_address: str | None user_agent: str | None created_at: datetime class AdminAuditListResponse(BaseModel): service: str events: list[AdminAuditEntryResponse] class AdminRuntimeApplicationConfigurationResponse(BaseModel): app_name: str environment: str version: str api_prefix: str debug: bool class AdminRuntimeDatabaseConfigurationResponse(BaseModel): host: str port: int name: str cloud_sql_configured: bool class AdminPanelSessionConfigurationResponse(BaseModel): access_cookie_name: str refresh_cookie_name: str cookie_path: str same_site: str secure_cookies: bool class AdminSystemRuntimeConfigurationPayload(BaseModel): application: AdminRuntimeApplicationConfigurationResponse database: AdminRuntimeDatabaseConfigurationResponse panel_session: AdminPanelSessionConfigurationResponse class AdminConfigurationSourceResponse(BaseModel): key: str source: str mutable: bool description: str class AdminFunctionalConfigurationFieldResponse(BaseModel): name: str description: str writable: bool secret: bool class AdminFunctionalConfigurationContractResponse(BaseModel): config_key: str domain: str description: str source: str read_permission: str write_permission: str | None = None mutability: str propagation: str affects_product_runtime: bool direct_product_write_allowed: bool fields: list[AdminFunctionalConfigurationFieldResponse] class AdminModelRuntimeProfileResponse(BaseModel): runtime_target: str config_key: str catalog_runtime_target: str purpose: str consumed_by_service: str description: str read_permission: str write_permission: str published_independently: bool rollback_independently: bool cross_target_propagation_allowed: bool affects_customer_response: bool can_generate_code: bool class AdminSystemModelRuntimeSeparationPayload(BaseModel): runtime_profiles: list[AdminModelRuntimeProfileResponse] separation_rules: list[str] atendimento_runtime_configuration: AdminFunctionalConfigurationContractResponse tool_generation_runtime_configuration: AdminFunctionalConfigurationContractResponse bot_governed_parent_config_keys: list[str] class AdminSystemRuntimeConfigurationResponse(BaseModel): service: str runtime: AdminSystemRuntimeConfigurationPayload class AdminSystemSecurityConfigurationResponse(BaseModel): service: str security: AdminCredentialStrategy class AdminSystemWriteGovernancePayload(BaseModel): mode: str allowed_direct_write_tables: list[str] blocked_operational_dataset_keys: list[str] blocked_product_source_tables: list[str] governed_configuration_keys: list[str] enforcement_points: list[str] governance_rules: list[str] class AdminSystemConfigurationResponse(BaseModel): service: str runtime: AdminSystemRuntimeConfigurationPayload security: AdminCredentialStrategy model_runtimes: AdminSystemModelRuntimeSeparationPayload write_governance: AdminSystemWriteGovernancePayload sources: list[AdminConfigurationSourceResponse] class AdminSystemModelRuntimeSeparationResponse(BaseModel): service: str model_runtimes: AdminSystemModelRuntimeSeparationPayload class AdminSystemWriteGovernanceResponse(BaseModel): service: str write_governance: AdminSystemWriteGovernancePayload class AdminBotGovernedSettingResponse(BaseModel): setting_key: str parent_config_key: str field_name: str area: str description: str read_permission: str write_permission: str mutability: str versioned_publication_required: bool direct_product_write_allowed: bool class AdminSystemFunctionalConfigurationCatalogResponse(BaseModel): service: str mode: str configurations: list[AdminFunctionalConfigurationContractResponse] bot_governed_parent_config_keys: list[str] next_steps: list[str] class AdminSystemFunctionalConfigurationDetailResponse(BaseModel): service: str configuration: AdminFunctionalConfigurationContractResponse linked_bot_settings: list[AdminBotGovernedSettingResponse] related_runtime_profile: AdminModelRuntimeProfileResponse | None = None managed_by_bot_governance: bool class AdminSystemBotGovernedConfigurationResponse(BaseModel): service: str parent_config_keys: list[str] settings: list[AdminBotGovernedSettingResponse] class AdminReportMetricResponse(BaseModel): key: str label: str value: str description: str class AdminReportFamilyResponse(BaseModel): key: str label: str description: str dataset_keys: list[str] class AdminReportMaterializationResponse(BaseModel): report_read_model: OperationalReadModel consistency_model: OperationalConsistencyModel sync_strategy: OperationalSyncStrategy storage_shape: OperationalStorageShape query_surface: OperationalQuerySurface uses_product_replica: bool direct_product_query_allowed: bool refresh_behavior: str class AdminReportFieldResponse(BaseModel): name: str description: str sensitivity: OperationalDataSensitivity class AdminReportDatasetSummaryResponse(BaseModel): dataset_key: str domain: OperationalDataDomain description: str source_table: str freshness_target: OperationalFreshnessTarget allowed_granularities: list[OperationalReadGranularity] allowed_field_count: int blocked_field_count: int write_allowed: bool materialization_status: str last_consolidated_at: datetime | None = None source_watermark: str | None = None class AdminReportDatasetDetailResponse(BaseModel): dataset_key: str domain: OperationalDataDomain description: str source_table: str read_permission: AdminPermission report_read_model: OperationalReadModel consistency_model: OperationalConsistencyModel sync_strategy: OperationalSyncStrategy storage_shape: OperationalStorageShape query_surface: OperationalQuerySurface uses_product_replica: bool direct_product_query_allowed: bool freshness_target: OperationalFreshnessTarget allowed_granularities: list[OperationalReadGranularity] write_allowed: bool materialization_status: str last_consolidated_at: datetime | None = None source_watermark: str | None = None allowed_fields: list[AdminReportFieldResponse] blocked_fields: list[AdminReportFieldResponse] class AdminReportOverviewResponse(BaseModel): service: str mode: str metrics: list[AdminReportMetricResponse] materialization: AdminReportMaterializationResponse report_families: list[AdminReportFamilyResponse] next_steps: list[str] class AdminReportDatasetListResponse(BaseModel): service: str source: str materialization: AdminReportMaterializationResponse datasets: list[AdminReportDatasetSummaryResponse] class AdminReportDatasetResponse(BaseModel): service: str source: str materialization: AdminReportMaterializationResponse dataset: AdminReportDatasetDetailResponse class AdminSalesReportMetricDefinitionResponse(BaseModel): key: str label: str aggregation: str description: str class AdminSalesReportDimensionResponse(BaseModel): field_name: str label: str description: str default_group_by: bool = False class AdminSalesReportFilterResponse(BaseModel): field_name: str label: str filter_type: str description: str required: bool = False class AdminSalesReportDefinitionSummaryResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity supported_metric_keys: list[str] supported_dimension_fields: list[str] materialization_status: str last_consolidated_at: datetime | None = None source_watermark: str | None = None class AdminSalesReportDefinitionDetailResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity metrics: list[AdminSalesReportMetricDefinitionResponse] dimensions: list[AdminSalesReportDimensionResponse] filters: list[AdminSalesReportFilterResponse] dataset: AdminReportDatasetDetailResponse class AdminSalesReportOverviewResponse(BaseModel): service: str domain: OperationalDataDomain mode: str source_dataset_keys: list[str] metrics: list[AdminReportMetricResponse] materialization: AdminReportMaterializationResponse reports: list[AdminSalesReportDefinitionSummaryResponse] next_steps: list[str] class AdminSalesReportCatalogResponse(BaseModel): service: str domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse reports: list[AdminSalesReportDefinitionSummaryResponse] class AdminSalesReportResponse(BaseModel): service: str domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse report: AdminSalesReportDefinitionDetailResponse class AdminRevenueReportDefinitionSummaryResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity supported_metric_keys: list[str] supported_dimension_fields: list[str] materialization_status: str last_consolidated_at: datetime | None = None source_watermark: str | None = None class AdminRevenueReportDefinitionDetailResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity metrics: list[AdminSalesReportMetricDefinitionResponse] dimensions: list[AdminSalesReportDimensionResponse] filters: list[AdminSalesReportFilterResponse] dataset: AdminReportDatasetDetailResponse class AdminRevenueReportOverviewResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain mode: str source_dataset_keys: list[str] metrics: list[AdminReportMetricResponse] materialization: AdminReportMaterializationResponse reports: list[AdminRevenueReportDefinitionSummaryResponse] next_steps: list[str] class AdminRevenueReportCatalogResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse reports: list[AdminRevenueReportDefinitionSummaryResponse] class AdminRevenueReportResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse report: AdminRevenueReportDefinitionDetailResponse class AdminRentalReportDefinitionSummaryResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity supported_metric_keys: list[str] supported_dimension_fields: list[str] materialization_status: str last_consolidated_at: datetime | None = None source_watermark: str | None = None class AdminRentalReportDefinitionDetailResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity metrics: list[AdminSalesReportMetricDefinitionResponse] dimensions: list[AdminSalesReportDimensionResponse] filters: list[AdminSalesReportFilterResponse] dataset: AdminReportDatasetDetailResponse class AdminRentalReportOverviewResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain mode: str source_dataset_keys: list[str] metrics: list[AdminReportMetricResponse] materialization: AdminReportMaterializationResponse reports: list[AdminRentalReportDefinitionSummaryResponse] next_steps: list[str] class AdminRentalReportCatalogResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse reports: list[AdminRentalReportDefinitionSummaryResponse] class AdminRentalReportResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse report: AdminRentalReportDefinitionDetailResponse class AdminBotFlowReportDefinitionSummaryResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity supported_metric_keys: list[str] supported_dimension_fields: list[str] materialization_status: str last_consolidated_at: datetime | None = None source_watermark: str | None = None class AdminBotFlowReportDefinitionDetailResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity metrics: list[AdminSalesReportMetricDefinitionResponse] dimensions: list[AdminSalesReportDimensionResponse] filters: list[AdminSalesReportFilterResponse] dataset: AdminReportDatasetDetailResponse class AdminBotFlowReportOverviewResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain mode: str source_dataset_keys: list[str] metrics: list[AdminReportMetricResponse] materialization: AdminReportMaterializationResponse reports: list[AdminBotFlowReportDefinitionSummaryResponse] next_steps: list[str] class AdminBotFlowReportCatalogResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse reports: list[AdminBotFlowReportDefinitionSummaryResponse] class AdminBotFlowReportResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse report: AdminBotFlowReportDefinitionDetailResponse class AdminConversationTelemetryReportDefinitionSummaryResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity supported_metric_keys: list[str] supported_dimension_fields: list[str] materialization_status: str last_consolidated_at: datetime | None = None source_watermark: str | None = None class AdminConversationTelemetryReportDefinitionDetailResponse(BaseModel): report_key: str label: str description: str dataset_key: str default_time_field: str default_granularity: OperationalReadGranularity metrics: list[AdminSalesReportMetricDefinitionResponse] dimensions: list[AdminSalesReportDimensionResponse] filters: list[AdminSalesReportFilterResponse] dataset: AdminReportDatasetDetailResponse class AdminConversationTelemetryReportOverviewResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain mode: str source_dataset_keys: list[str] metrics: list[AdminReportMetricResponse] materialization: AdminReportMaterializationResponse reports: list[AdminConversationTelemetryReportDefinitionSummaryResponse] next_steps: list[str] class AdminConversationTelemetryReportCatalogResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse reports: list[AdminConversationTelemetryReportDefinitionSummaryResponse] class AdminConversationTelemetryReportResponse(BaseModel): service: str area: str source_domain: OperationalDataDomain source: str materialization: AdminReportMaterializationResponse report: AdminConversationTelemetryReportDefinitionDetailResponse class AdminLoginRequest(BaseModel): email: str password: str = Field(min_length=1) @field_validator("email") @classmethod def validate_email(cls, value: str) -> str: normalized = value.strip().lower() if "@" not in normalized or normalized.startswith("@") or normalized.endswith("@"): raise ValueError("email must be a valid administrative login") return normalized class AdminRefreshTokenRequest(BaseModel): refresh_token: str = Field(min_length=1) class AdminSessionResponse(BaseModel): session_id: int access_token: str refresh_token: str token_type: str expires_in_seconds: int staff_account: AdminAuthenticatedStaffResponse class AdminLogoutResponse(BaseModel): service: str status: str message: str session_id: int class AdminPanelWebSessionResponse(BaseModel): service: str status: str message: str session_id: int expires_in_seconds: int staff_account: AdminAuthenticatedStaffResponse redirect_to: str | None = None class AdminPanelLogoutResponse(BaseModel): service: str status: str message: str session_id: int | None redirect_to: str class AdminToolManagementMetricResponse(BaseModel): key: str label: str value: str description: str class AdminToolLifecycleStageResponse(BaseModel): code: ToolLifecycleStatus label: str description: str order: int = Field(ge=1) terminal: bool = False class AdminToolParameterTypeResponse(BaseModel): code: ToolParameterType label: str description: str class AdminToolManagementActionResponse(BaseModel): key: str label: str href: str required_permission: AdminPermission description: str class AdminToolOverviewResponse(BaseModel): service: str mode: str metrics: list[AdminToolManagementMetricResponse] workflow: list[AdminToolLifecycleStageResponse] actions: list[AdminToolManagementActionResponse] next_steps: list[str] class AdminToolContractsResponse(BaseModel): service: str publication_source_service: ServiceName publication_target_service: ServiceName lifecycle_statuses: list[AdminToolLifecycleStageResponse] parameter_types: list[AdminToolParameterTypeResponse] publication_fields: list[str] published_tool_fields: list[str] class AdminToolDraftSummaryResponse(BaseModel): draft_id: str tool_name: str display_name: str status: ToolLifecycleStatus summary: str current_version_number: int = Field(ge=1) version_count: int = Field(ge=1) owner_name: str | None = None updated_at: datetime | None = None class AdminToolDraftListResponse(BaseModel): service: str storage_status: str message: str drafts: list[AdminToolDraftSummaryResponse] supported_statuses: list[ToolLifecycleStatus] class AdminToolReviewQueueEntryResponse(BaseModel): entry_id: str tool_name: str display_name: str status: ToolLifecycleStatus gate: str summary: str owner_name: str | None = None queued_at: datetime | None = None class AdminToolReviewQueueResponse(BaseModel): service: str queue_mode: str message: str items: list[AdminToolReviewQueueEntryResponse] supported_statuses: list[ToolLifecycleStatus] class AdminToolPublicationParameterResponse(BaseModel): name: str parameter_type: ToolParameterType description: str required: bool class AdminToolPublicationSummaryResponse(BaseModel): publication_id: str tool_name: str display_name: str description: str domain: str version: int status: ToolLifecycleStatus parameter_count: int parameters: list[AdminToolPublicationParameterResponse] = Field(default_factory=list) author_name: str | None = None implementation_module: str implementation_callable: str published_by: str | None = None published_at: datetime | None = None class AdminToolPublicationListResponse(BaseModel): service: str source: str target_service: ServiceName publications: list[AdminToolPublicationSummaryResponse] class AdminToolDraftIntakeParameterRequest(BaseModel): name: str = Field(min_length=1, max_length=64) parameter_type: ToolParameterType description: str = Field(min_length=1, max_length=180) required: bool = True @field_validator("name") @classmethod def normalize_name(cls, value: str) -> str: return value.strip().lower() @field_validator("description") @classmethod def normalize_description(cls, value: str) -> str: return value.strip() class AdminToolDraftIntakeRequest(BaseModel): tool_name: str = Field(min_length=3, max_length=64) display_name: str = Field(min_length=4, max_length=120) domain: str = Field(min_length=3, max_length=40) description: str = Field(min_length=16, max_length=280) business_goal: str = Field(min_length=12, max_length=280) parameters: list[AdminToolDraftIntakeParameterRequest] = Field(default_factory=list, max_length=10) @field_validator("tool_name") @classmethod def normalize_tool_name(cls, value: str) -> str: return value.strip().lower() @field_validator("display_name", "description", "business_goal") @classmethod def strip_text_fields(cls, value: str) -> str: return value.strip() @field_validator("domain") @classmethod def normalize_domain(cls, value: str) -> str: return value.strip().lower() class AdminToolDraftIntakePreviewParameterResponse(BaseModel): name: str parameter_type: ToolParameterType description: str required: bool class AdminToolDraftIntakePreviewResponse(BaseModel): draft_id: str version_id: str tool_name: str display_name: str domain: str status: ToolLifecycleStatus summary: str business_goal: str version_number: int = Field(ge=1) version_count: int = Field(ge=1) parameter_count: int required_parameter_count: int requires_director_approval: bool owner_name: str | None = None parameters: list[AdminToolDraftIntakePreviewParameterResponse] class AdminToolDraftIntakeResponse(BaseModel): service: str storage_status: str message: str draft_preview: AdminToolDraftIntakePreviewResponse warnings: list[str] next_steps: list[str] class AdminCollaboratorSummaryResponse(BaseModel): id: int email: str display_name: str role: StaffRole is_active: bool last_login_at: datetime | None = None created_at: datetime | None = None updated_at: datetime | None = None class AdminCollaboratorListResponse(BaseModel): service: str total: int active_count: int inactive_count: int collaborators: list[AdminCollaboratorSummaryResponse] class AdminCollaboratorCreateRequest(BaseModel): email: str display_name: str = Field(min_length=3, max_length=150) password: str = Field(min_length=1) is_active: bool = True @field_validator("email") @classmethod def normalize_email(cls, value: str) -> str: normalized = value.strip().lower() if "@" not in normalized or normalized.startswith("@") or normalized.endswith("@"): raise ValueError("email must be a valid administrative login") return normalized @field_validator("display_name") @classmethod def normalize_display_name(cls, value: str) -> str: return value.strip() class AdminCollaboratorCreateResponse(BaseModel): service: str message: str collaborator: AdminCollaboratorSummaryResponse class AdminCollaboratorStatusUpdateRequest(BaseModel): is_active: bool class AdminCollaboratorStatusUpdateResponse(BaseModel): service: str message: str collaborator: AdminCollaboratorSummaryResponse