import { apiFetch, type ApiSettings } from "@govoplan/core-webui"; export type DatasourceMode = "live" | "cached" | "static"; export type DatasourceShape = "tabular" | "document" | "binary" | "directory" | "stream"; export type SourceAuthorityMode = | "native_authoritative" | "external_authoritative" | "external_mirror" | "governed_sync" | "governance_overlay" | "linked_reference"; export type DatasourceGovernance = { owner_ref?: string | null; steward_ref?: string | null; responsible_organization_ref?: string | null; responsible_function_ref?: string | null; authoritative_source_ref?: string | null; authority_mode: SourceAuthorityMode; legal_basis_refs: string[]; purposes: string[]; semantic_definition?: string | null; schema_owner_ref?: string | null; official_keys: string[]; classification: string; privacy_profile_ref?: string | null; retention_policy_ref?: string | null; hold_refs: string[]; publication_state: string; transfer_agreement_ref?: string | null; freshness_policy: Record; quality_policy: Record; known_limits: string[]; correction_procedure_ref?: string | null; affected_refs: string[]; dependency_refs: string[]; }; export type DatasourceField = { name: string; data_type: string; nullable: boolean; }; export type Datasource = { ref: string; source_name: string; name: string; description?: string | null; kind: string; mode: DatasourceMode; shape: DatasourceShape; status: string; provider?: string | null; provider_ref?: string | null; schema: DatasourceField[]; schema_version: string; fingerprint: string; current_materialization_ref?: string | null; row_count?: number | null; byte_count?: number | null; updated_at?: string | null; capabilities: string[]; provenance: Record; metadata: Record; governance: DatasourceGovernance; }; export type DatasourceMaterialization = { ref: string; datasource_ref: string; revision: number; state: string; fingerprint: string; schema: DatasourceField[]; row_count?: number | null; byte_count?: number | null; frozen_at?: string | null; frozen_label?: string | null; source_timestamp?: string | null; created_at?: string | null; provenance: Record; metadata: Record; governance: DatasourceGovernance; }; export type DatasourceValidationDiagnostic = { severity: "error" | "warning"; code: string; message: string; rule_id?: string; affected_rows?: number; row_numbers?: number[]; row_numbers_truncated?: boolean; details?: Record; }; export type DatasourceSchemaChange = { code: string; classification: "compatible" | "warning" | "breaking"; message: string; field?: string; before?: DatasourceField; after?: DatasourceField; }; export type DatasourceStageValidation = { version?: number; policy_version?: string; policy_hash?: string; valid?: boolean; errors?: DatasourceValidationDiagnostic[]; warnings?: DatasourceValidationDiagnostic[]; quality?: { rules_evaluated?: number; rules_passed?: number; rules_failed?: number; }; schema_change?: { classification?: "new" | "compatible" | "warning" | "breaking"; changes?: DatasourceSchemaChange[]; }; }; export type DatasourceStage = { ref: string; name: string; source_name: string; kind: string; mode: "static" | "cached"; shape: DatasourceShape; state: string; target_datasource_ref?: string | null; fingerprint: string; schema: DatasourceField[]; row_count?: number | null; byte_count?: number | null; validation: DatasourceStageValidation; created_at?: string | null; promoted_at?: string | null; promoted_materialization_ref?: string | null; provenance: Record; metadata: Record; governance: DatasourceGovernance; }; export type DatasourceOrigin = { ref: string; source_name: string; name: string; description?: string | null; kind: string; shape: DatasourceShape; supported_modes: DatasourceMode[]; provider: string; schema: DatasourceField[]; schema_version: string; fingerprint: string; row_count?: number | null; byte_count?: number | null; updated_at?: string | null; capabilities: string[]; metadata: Record; }; export type DatasourcePreview = { datasource: Datasource; rows: Record[]; total_rows: number; truncated: boolean; materialization?: DatasourceMaterialization | null; }; export async function listDatasources( settings: ApiSettings, query = "", filters: Partial> & { affected_ref?: string; dependency_ref?: string; } = {} ): Promise { const params = new URLSearchParams(); if (query.trim()) params.set("query", query.trim()); for (const [key, value] of Object.entries(filters)) { if (String(value ?? "").trim()) params.set(key, String(value).trim()); } const suffix = params.size ? `?${params.toString()}` : ""; const response = await apiFetch<{ datasources: Datasource[] }>( settings, `/api/v1/datasources${suffix}` ); return response.datasources; } export async function listDatasourceStages( settings: ApiSettings ): Promise { const response = await apiFetch<{ stages: DatasourceStage[] }>( settings, "/api/v1/datasources/stages" ); return response.stages; } export async function listDatasourceOrigins( settings: ApiSettings ): Promise<{ available: boolean; origins: DatasourceOrigin[] }> { return apiFetch(settings, "/api/v1/datasources/origins"); } export async function listDatasourceMaterializations( settings: ApiSettings, datasourceRef: string ): Promise { const response = await apiFetch<{ materializations: DatasourceMaterialization[] }>( settings, `/api/v1/datasources/${refId(datasourceRef)}/materializations` ); return response.materializations; } export function previewDatasource( settings: ApiSettings, datasourceRef: string, consistency: "current" | "live" | "frozen" = "current" ): Promise { return apiFetch( settings, `/api/v1/datasources/${refId(datasourceRef)}/preview?limit=100&consistency=${consistency}` ); } export function createDatasourceStage( settings: ApiSettings, payload: { name: string; source_name: string; description?: string | null; mode: "static" | "cached"; target_datasource_ref?: string | null; governance?: DatasourceGovernance | null; } & ( { format: "json"; rows: Record[] } | { format: "csv"; csv_text: string; delimiter: string } ) ): Promise { return apiFetch(settings, "/api/v1/datasources/stages", { method: "POST", body: JSON.stringify(payload) }); } export function promoteDatasourceStage( settings: ApiSettings, stageRef: string, payload: { freeze?: boolean; frozen_label?: string | null } = {} ): Promise<{ datasource: Datasource; materialization: DatasourceMaterialization }> { return apiFetch(settings, `/api/v1/datasources/stages/${refId(stageRef)}/promote`, { method: "POST", body: JSON.stringify(payload) }); } export function registerDatasourceOrigin( settings: ApiSettings, payload: { origin_ref: string; name: string; source_name: string; mode: "live" | "cached"; description?: string | null; governance?: DatasourceGovernance | null; } ): Promise { return apiFetch(settings, "/api/v1/datasources/origins/register", { method: "POST", body: JSON.stringify(payload) }); } export function refreshDatasource( settings: ApiSettings, datasourceRef: string ): Promise<{ datasource: Datasource; materialization: DatasourceMaterialization }> { return apiFetch(settings, `/api/v1/datasources/${refId(datasourceRef)}/refresh`, { method: "POST" }); } export function freezeDatasource( settings: ApiSettings, datasourceRef: string, label: string ): Promise { return apiFetch(settings, `/api/v1/datasources/${refId(datasourceRef)}/freeze`, { method: "POST", body: JSON.stringify({ label: label.trim() || null }) }); } export function retireDatasource( settings: ApiSettings, datasourceRef: string ): Promise<{ retired: boolean; datasource_ref: string }> { return apiFetch(settings, `/api/v1/datasources/${refId(datasourceRef)}`, { method: "DELETE" }); } export function updateDatasourceGovernance( settings: ApiSettings, datasourceRef: string, governance: DatasourceGovernance ): Promise { return apiFetch(settings, `/api/v1/datasources/${refId(datasourceRef)}/governance`, { method: "PATCH", body: JSON.stringify({ governance }) }); } function refId(ref: string): string { const separator = ref.indexOf(":"); return encodeURIComponent(separator >= 0 ? ref.slice(separator + 1) : ref); }