//! Inquiry data transfer objects use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use utoipa::{IntoParams, ToSchema}; use validator::Validate; use attune_common::models::{enums::InquiryStatus, inquiry::Inquiry, Id, JsonDict, JsonSchema}; use serde_json::Value as JsonValue; /// Full inquiry response with all details #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct InquiryResponse { /// Inquiry ID #[schema(example = 1)] pub id: Id, /// Execution ID this inquiry belongs to #[schema(example = 1)] pub execution: Id, /// Prompt text displayed to the user #[schema(example = "Approve deployment to production?")] pub prompt: String, /// JSON schema for expected response #[schema(value_type = Object, nullable = true)] pub response_schema: Option, /// Identity ID this inquiry is assigned to #[schema(example = 1)] pub assigned_to: Option, /// Current status of the inquiry #[schema(example = "pending")] pub status: InquiryStatus, /// Response data provided by the user #[schema(value_type = Object, nullable = true)] pub response: Option, /// When the inquiry expires #[schema(example = "2024-01-13T11:30:00Z")] pub timeout_at: Option>, /// When the inquiry was responded to #[schema(example = "2024-01-13T10:45:00Z")] pub responded_at: Option>, /// Creation timestamp #[schema(example = "2024-01-13T10:30:00Z")] pub created: DateTime, /// Last update timestamp #[schema(example = "2024-01-13T10:45:00Z")] pub updated: DateTime, } impl From for InquiryResponse { fn from(inquiry: Inquiry) -> Self { Self { id: inquiry.id, execution: inquiry.execution, prompt: inquiry.prompt, response_schema: inquiry.response_schema, assigned_to: inquiry.assigned_to, status: inquiry.status, response: inquiry.response, timeout_at: inquiry.timeout_at, responded_at: inquiry.responded_at, created: inquiry.created, updated: inquiry.updated, } } } /// Summary inquiry response for list views #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct InquirySummary { /// Inquiry ID #[schema(example = 1)] pub id: Id, /// Execution ID #[schema(example = 1)] pub execution: Id, /// Prompt text #[schema(example = "Approve deployment to production?")] pub prompt: String, /// Assigned identity ID #[schema(example = 1)] pub assigned_to: Option, /// Inquiry status #[schema(example = "pending")] pub status: InquiryStatus, /// Whether a response has been provided #[schema(example = false)] pub has_response: bool, /// Timeout timestamp #[schema(example = "2024-01-13T11:30:00Z")] pub timeout_at: Option>, /// Creation timestamp #[schema(example = "2024-01-13T10:30:00Z")] pub created: DateTime, } impl From for InquirySummary { fn from(inquiry: Inquiry) -> Self { Self { id: inquiry.id, execution: inquiry.execution, prompt: inquiry.prompt, assigned_to: inquiry.assigned_to, status: inquiry.status, has_response: inquiry.response.is_some(), timeout_at: inquiry.timeout_at, created: inquiry.created, } } } /// Request to create a new inquiry #[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)] pub struct CreateInquiryRequest { /// Execution ID this inquiry belongs to #[schema(example = 1)] pub execution: Id, /// Prompt text to display to the user #[validate(length(min = 1, max = 10000))] #[schema(example = "Approve deployment to production?")] pub prompt: String, /// Optional schema for the expected response format (flat format with inline required/secret) #[schema(value_type = Object, example = json!({"approved": {"type": "boolean", "description": "Whether the deployment is approved", "required": true}}))] pub response_schema: Option, /// Optional identity ID to assign this inquiry to #[schema(example = 1)] pub assigned_to: Option, /// Optional timeout timestamp (when inquiry expires) #[schema(example = "2024-01-13T11:30:00Z")] pub timeout_at: Option>, } /// Request to update an inquiry #[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)] pub struct UpdateInquiryRequest { /// Update the inquiry status #[schema(example = "responded")] pub status: Option, /// Update the response data #[schema(value_type = Object, nullable = true)] pub response: Option, /// Update the assigned_to identity #[schema(example = 2)] pub assigned_to: Option, } /// Request to respond to an inquiry (user-facing endpoint) #[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)] pub struct InquiryRespondRequest { /// Response data conforming to the inquiry's response_schema #[schema(value_type = Object)] pub response: JsonValue, } /// Query parameters for filtering inquiries #[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] pub struct InquiryQueryParams { /// Filter by status #[param(example = "pending")] pub status: Option, /// Filter by execution ID #[param(example = 1)] pub execution: Option, /// Filter by assigned identity #[param(example = 1)] pub assigned_to: Option, /// Pagination offset #[param(example = 0)] pub offset: Option, /// Pagination limit #[param(example = 50)] pub limit: Option, } /// Paginated list response #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ListResponse { /// List of items pub data: Vec, /// Total count of items (before pagination) pub total: usize, /// Offset used for this page pub offset: usize, /// Limit used for this page pub limit: usize, }