281 lines
7.7 KiB
Swift
281 lines
7.7 KiB
Swift
import Foundation
|
|
|
|
struct ProjectItem: Codable, Identifiable, Sendable {
|
|
let id: String
|
|
let name: String
|
|
let created_at: String
|
|
}
|
|
|
|
struct RequirementItem: Codable, Identifiable, Sendable {
|
|
let id: String
|
|
let project_id: String
|
|
let name: String
|
|
let status: String
|
|
let current_version: Int
|
|
let created_at: String
|
|
let source_path: String
|
|
let extraction: RequirementExtraction?
|
|
let metadata_ready: Bool
|
|
let metadata_complete: Bool
|
|
let metadata_missing_tables: [String]
|
|
}
|
|
|
|
struct RequirementExtraction: Codable, Sendable {
|
|
let requirement_name: String
|
|
let tasks: [ExtractedTask]
|
|
let open_questions: [String]
|
|
}
|
|
|
|
struct ExtractedTask: Codable, Identifiable, Sendable {
|
|
var id: String { name + targets.joined(separator: "|") }
|
|
let name: String
|
|
let sources: [String]
|
|
let targets: [String]
|
|
let field_mappings: [JSONValue]
|
|
let rules: [String]
|
|
}
|
|
|
|
struct CaseCount: Codable, Identifiable, Sendable {
|
|
var id: String { status }
|
|
let status: String
|
|
let count: Int
|
|
}
|
|
|
|
struct TestCaseItem: Codable, Identifiable, Sendable {
|
|
let id: String
|
|
let requirement_id: String
|
|
let requirement_name: String
|
|
let name: String
|
|
let table_name: String
|
|
let category: String
|
|
let status: String
|
|
let version: Int
|
|
let fields: [String]
|
|
let sql: String
|
|
let assertions: [CaseAssertion]
|
|
let validation_errors: [String]
|
|
let created_at: String
|
|
}
|
|
|
|
struct CaseAssertion: Codable, Sendable {
|
|
let type: String
|
|
let actual: String?
|
|
let expected: JSONValue?
|
|
let minimum: Double?
|
|
let maximum: Double?
|
|
}
|
|
|
|
enum JSONValue: Codable, Sendable, CustomStringConvertible {
|
|
case string(String)
|
|
case number(Double)
|
|
case boolean(Bool)
|
|
case object([String: JSONValue])
|
|
case array([JSONValue])
|
|
case null
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.singleValueContainer()
|
|
if container.decodeNil() { self = .null }
|
|
else if let value = try? container.decode(Bool.self) { self = .boolean(value) }
|
|
else if let value = try? container.decode(Double.self) { self = .number(value) }
|
|
else if let value = try? container.decode(String.self) { self = .string(value) }
|
|
else if let value = try? container.decode([String: JSONValue].self) { self = .object(value) }
|
|
else { self = .array(try container.decode([JSONValue].self)) }
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
switch self {
|
|
case .string(let value): try container.encode(value)
|
|
case .number(let value): try container.encode(value)
|
|
case .boolean(let value): try container.encode(value)
|
|
case .object(let value): try container.encode(value)
|
|
case .array(let value): try container.encode(value)
|
|
case .null: try container.encodeNil()
|
|
}
|
|
}
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .string(let value): value
|
|
case .number(let value): value.rounded() == value ? String(Int(value)) : String(value)
|
|
case .boolean(let value): String(value)
|
|
case .object(let value): value.map { "\($0.key)=\($0.value)" }.sorted().joined(separator: ", ")
|
|
case .array(let value): value.map(\.description).joined(separator: ", ")
|
|
case .null: "null"
|
|
}
|
|
}
|
|
}
|
|
|
|
struct MetadataColumn: Codable, Identifiable, Sendable {
|
|
var id: Int { cid }
|
|
let cid: Int
|
|
let name: String
|
|
let type: String
|
|
let notNull: Int
|
|
let defaultValue: String?
|
|
let pk: Int
|
|
|
|
var ordinalText: String { String(cid + 1) }
|
|
var nullableText: String { notNull == 0 ? "是" : "否" }
|
|
var defaultText: String { defaultValue ?? "—" }
|
|
var primaryKeyText: String { pk > 0 ? "是" : "—" }
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case cid, name, type, pk
|
|
case notNull = "notnull"
|
|
case defaultValue = "dflt_value"
|
|
}
|
|
}
|
|
|
|
struct MetadataTableItem: Codable, Identifiable, Sendable {
|
|
let id: String
|
|
let requirement_id: String
|
|
let requirement_name: String
|
|
let database_name: String
|
|
let name: String
|
|
let type: String
|
|
let row_count: Int
|
|
let columns: [MetadataColumn]
|
|
}
|
|
|
|
struct RunItem: Codable, Identifiable, Sendable {
|
|
let id: String
|
|
let requirement_id: String
|
|
let requirement_version: Int
|
|
let status: String
|
|
let batch_id: String
|
|
let biz_date: String?
|
|
let started_at: String
|
|
let finished_at: String?
|
|
}
|
|
|
|
struct MetricItem: Codable, Identifiable, Sendable {
|
|
let id: Int
|
|
let requirement_id: String
|
|
let table_name: String
|
|
let field_name: String?
|
|
let batch_id: String
|
|
let biz_date: String?
|
|
let metric_type: String
|
|
let metric_value: Double?
|
|
let metric_json: String?
|
|
let definition_version: Int
|
|
let collected_at: String
|
|
}
|
|
|
|
struct ReportItem: Codable, Identifiable, Sendable {
|
|
let id: String
|
|
let run_id: String
|
|
let requirement_id: String
|
|
let requirement_version: Int
|
|
let status: String
|
|
let format: String
|
|
let file_path: String
|
|
let content: String
|
|
let created_at: String
|
|
}
|
|
|
|
struct CaseResultItem: Codable, Identifiable, Sendable {
|
|
let id: Int
|
|
let run_id: String
|
|
let case_id: String
|
|
let requirement_id: String
|
|
let case_name: String
|
|
let status: String
|
|
let actual_json: String?
|
|
let assertion_json: String
|
|
let sample_json: String?
|
|
let error_message: String?
|
|
let duration_ms: Int
|
|
let created_at: String
|
|
}
|
|
|
|
struct FailureAnalysis: Codable, Sendable {
|
|
let summary: String
|
|
let suspected_layer: String
|
|
let root_cause: String
|
|
let evidence: [String]
|
|
let recommendations: [String]
|
|
let validation_sql: [String]
|
|
let confidence: String
|
|
}
|
|
|
|
struct FailureAnalysisItem: Codable, Identifiable, Sendable {
|
|
let id: Int
|
|
let result_id: Int
|
|
let run_id: String
|
|
let case_id: String
|
|
let requirement_id: String
|
|
let status: String
|
|
let created_at: String
|
|
let analysis: FailureAnalysis
|
|
}
|
|
|
|
struct CaseAgentMessageItem: Codable, Identifiable, Sendable {
|
|
let id: Int
|
|
let requirement_id: String
|
|
let role: String
|
|
let content: String
|
|
let operation_json: String?
|
|
let created_at: String
|
|
}
|
|
|
|
struct CaseChatProgressEvent: Codable, Identifiable, Sendable {
|
|
var id: Int { sequence }
|
|
let type: String
|
|
let sequence: Int
|
|
let phase: String
|
|
let status: String
|
|
let title: String
|
|
let detail: String
|
|
let timestamp: String
|
|
}
|
|
|
|
struct TestRunProgressEvent: Codable, Identifiable, Sendable {
|
|
var id: Int { sequence }
|
|
let type: String
|
|
let sequence: Int
|
|
let event: String
|
|
let run_id: String?
|
|
let case_id: String?
|
|
let case_name: String?
|
|
let status: String
|
|
let index: Int?
|
|
let total: Int?
|
|
let duration_ms: Int?
|
|
let error: String?
|
|
let detail: String?
|
|
let timestamp: String
|
|
}
|
|
|
|
struct CaseReviewEventItem: Codable, Identifiable, Sendable {
|
|
let id: Int
|
|
let case_id: String
|
|
let requirement_id: String
|
|
let case_version: Int
|
|
let decision: String
|
|
let comment: String?
|
|
let created_at: String
|
|
}
|
|
|
|
struct Dashboard: Codable, Sendable {
|
|
let projects: [ProjectItem]
|
|
let requirements: [RequirementItem]
|
|
let cases: [CaseCount]
|
|
let case_items: [TestCaseItem]
|
|
let metadata: [MetadataTableItem]
|
|
let runs: [RunItem]
|
|
let metrics: [MetricItem]
|
|
let reports: [ReportItem]
|
|
let result_items: [CaseResultItem]
|
|
let failure_analyses: [FailureAnalysisItem]
|
|
let case_agent_messages: [CaseAgentMessageItem]
|
|
let case_review_events: [CaseReviewEventItem]
|
|
|
|
static let empty = Dashboard(
|
|
projects: [], requirements: [], cases: [], case_items: [], metadata: [], runs: [], metrics: [], reports: [],
|
|
result_items: [], failure_analyses: [], case_agent_messages: [], case_review_events: []
|
|
)
|
|
}
|