328 lines
8.9 KiB
TypeScript
328 lines
8.9 KiB
TypeScript
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<string, unknown>;
|
|
quality_policy: Record<string, unknown>;
|
|
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<string, unknown>;
|
|
metadata: Record<string, unknown>;
|
|
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<string, unknown>;
|
|
metadata: Record<string, unknown>;
|
|
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<string, unknown>;
|
|
};
|
|
|
|
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<string, unknown>;
|
|
metadata: Record<string, unknown>;
|
|
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<string, unknown>;
|
|
};
|
|
|
|
export type DatasourcePreview = {
|
|
datasource: Datasource;
|
|
rows: Record<string, unknown>[];
|
|
total_rows: number;
|
|
truncated: boolean;
|
|
materialization?: DatasourceMaterialization | null;
|
|
};
|
|
|
|
export async function listDatasources(
|
|
settings: ApiSettings,
|
|
query = "",
|
|
filters: Partial<Pick<DatasourceGovernance, "authority_mode" | "classification" | "publication_state" | "owner_ref" | "responsible_organization_ref">> & {
|
|
affected_ref?: string;
|
|
dependency_ref?: string;
|
|
} = {}
|
|
): Promise<Datasource[]> {
|
|
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<DatasourceStage[]> {
|
|
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<DatasourceMaterialization[]> {
|
|
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<DatasourcePreview> {
|
|
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<string, unknown>[] }
|
|
| { format: "csv"; csv_text: string; delimiter: string }
|
|
)
|
|
): Promise<DatasourceStage> {
|
|
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<Datasource> {
|
|
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<DatasourceMaterialization> {
|
|
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<Datasource> {
|
|
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);
|
|
}
|