Add datasource quality and schema gates
This commit is contained in:
@@ -56,7 +56,9 @@ import {
|
||||
type DatasourceMaterialization,
|
||||
type DatasourceOrigin,
|
||||
type DatasourcePreview,
|
||||
type DatasourceStage
|
||||
type DatasourceSchemaChange,
|
||||
type DatasourceStage,
|
||||
type DatasourceValidationDiagnostic
|
||||
} from "../../api/datasources";
|
||||
import {
|
||||
DATASOURCE_FIELDS_DOCUMENTATION,
|
||||
@@ -717,6 +719,10 @@ function DatasourceDetail({
|
||||
}
|
||||
|
||||
function StageDetail({ stage }: { stage: DatasourceStage }) {
|
||||
const errors = stage.validation.errors ?? [];
|
||||
const warnings = stage.validation.warnings ?? [];
|
||||
const schemaChanges = stage.validation.schema_change?.changes ?? [];
|
||||
const schemaClassification = stage.validation.schema_change?.classification ?? "new";
|
||||
return (
|
||||
<>
|
||||
<div className="datasources-metrics">
|
||||
@@ -738,8 +744,44 @@ function StageDetail({ stage }: { stage: DatasourceStage }) {
|
||||
<span><small>Fingerprint</small><strong>{shortFingerprint(stage.fingerprint)}</strong></span>
|
||||
<span><small>Target</small><strong>{stage.target_datasource_ref || "New datasource"}</strong></span>
|
||||
<span><small>Promoted revision</small><strong>{stage.promoted_materialization_ref || "Not promoted"}</strong></span>
|
||||
<span><small>Quality policy</small><strong>{stage.validation.policy_version || "Local default"}</strong></span>
|
||||
<span><small>Policy hash</small><strong>{shortFingerprint(stage.validation.policy_hash || "")}</strong></span>
|
||||
<span><small>Schema change</small><strong>{readableToken(schemaClassification)}</strong></span>
|
||||
</div>
|
||||
{errors.length || warnings.length ? (
|
||||
<div className="datasources-validation-alerts">
|
||||
{errors.length ? (
|
||||
<DismissibleAlert tone="danger" compact dismissible={false}>
|
||||
<strong>Promotion blockers</strong>
|
||||
<ValidationDiagnosticList diagnostics={errors} />
|
||||
</DismissibleAlert>
|
||||
) : null}
|
||||
{warnings.length ? (
|
||||
<DismissibleAlert tone="warning" compact dismissible={false}>
|
||||
<strong>Review warnings</strong>
|
||||
<ValidationDiagnosticList diagnostics={warnings} />
|
||||
</DismissibleAlert>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="datasources-validation-ok">
|
||||
All configured quality rules passed and no blocking schema change was detected.
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
{schemaChanges.length ? (
|
||||
<section className="datasources-detail-section">
|
||||
<div className="datasources-section-heading">
|
||||
<span>Schema comparison</span>
|
||||
<StatusBadge status={schemaClassification} label={readableToken(schemaClassification)} />
|
||||
</div>
|
||||
<ul className="datasources-schema-changes">
|
||||
{schemaChanges.map((change, index) => (
|
||||
<SchemaChangeItem key={`${change.code}-${change.field ?? index}`} change={change} />
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
) : null}
|
||||
<section className="datasources-detail-section">
|
||||
<div className="datasources-section-heading">
|
||||
<span>Detected schema</span>
|
||||
@@ -751,6 +793,42 @@ function StageDetail({ stage }: { stage: DatasourceStage }) {
|
||||
);
|
||||
}
|
||||
|
||||
function ValidationDiagnosticList({
|
||||
diagnostics
|
||||
}: {
|
||||
diagnostics: DatasourceValidationDiagnostic[];
|
||||
}) {
|
||||
return (
|
||||
<ul className="datasources-diagnostic-list">
|
||||
{diagnostics.map((diagnostic, index) => {
|
||||
const details = [
|
||||
diagnostic.rule_id ? `Rule ${diagnostic.rule_id}` : "",
|
||||
diagnostic.affected_rows !== undefined ? `${diagnostic.affected_rows} affected row${diagnostic.affected_rows === 1 ? "" : "s"}` : "",
|
||||
diagnostic.row_numbers?.length ? `Rows ${diagnostic.row_numbers.join(", ")}${diagnostic.row_numbers_truncated ? ", …" : ""}` : ""
|
||||
].filter(Boolean);
|
||||
return (
|
||||
<li key={`${diagnostic.code}-${diagnostic.rule_id ?? index}`}>
|
||||
<span>{diagnostic.message}</span>
|
||||
{details.length ? <small>{details.join(" · ")}</small> : null}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
function SchemaChangeItem({ change }: { change: DatasourceSchemaChange }) {
|
||||
return (
|
||||
<li>
|
||||
<StatusBadge status={change.classification} label={readableToken(change.classification)} />
|
||||
<span>
|
||||
<strong>{change.message}</strong>
|
||||
{change.field ? <small>{change.field}</small> : null}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
function OriginDetail({ origin }: { origin: DatasourceOrigin }) {
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user