core/steps/quality.step.ts

core/steps/quality.step.ts is a file in GovLab Quality. 119 lines of code and 23 definitions.

import type {
    CatalogRule,
    ConcernControl,
    QualityConcernRecord,
    QualityToolRecord,
    RuleKnob,
} from "#types/catalog.types";
import { concernControls, knobLookup } from "#core/aggregators/concern.aggregator";
import { readdirSync, rmSync } from "node:fs";
import { stringArrayField, stringField } from "#core/selectors/record.selector";
import { absolutePath } from "@ssot/paths";
import { defineStep } from "#core/registries/step.registry";
import { loadKnobConcepts } from "#core/loaders/knob.loader";
import path from "node:path";
import { slugify } from "@govlab/context";

const CONCERNS_FILE = "concerns.generated.json";
const TOOLS_FILE = "tools.generated.json";
const GENERATED_JSON = ".generated.json";
const UNKNOWN = "unknown";

const concernRecordOf = function concernRecordOf([name, control]: [string, ConcernControl]): QualityConcernRecord {
    return {
        name,
        numeric: typeof control.value === "number",
        ruleCount: control.rules,
        severity: control.severity,
        toolCount: control.tools,
        value: control.value,
        ...(control.valueDerivation === undefined || control.valueDerivation === ""
            ? {}
            : { valueDerivation: control.valueDerivation }),
        ...(control.knobPerTool ? { knobPerTool: control.knobPerTool } : {}),
        ...(control.configOptions ? { configOptions: control.configOptions } : {}),
    };
};

const ruleRecordOf = function ruleRecordOf(rule: CatalogRule, knobs: readonly RuleKnob[]): Record<string, unknown> {
    const tool = stringField(rule, "tool");
    const { concern, ecosystem } = rule;
    const canonical = stringArrayField(rule, "canonical");
    return {
        category: stringField(rule, "category"),
        name: `${tool}:${rule.ruleId}`,
        ruleId: rule.ruleId,
        ruleName: stringField(rule, "name") || rule.ruleId,
        tool,
        url: stringField(rule, "url"),
        ...(typeof concern === "string" ? { concern } : {}),
        ...(typeof ecosystem === "string" ? { ecosystem } : {}),
        ...(canonical.length > 0 ? { canonical } : {}),
        ...(knobs.length > 0 ? { knobs } : {}),
    };
};

const toolRecordsOf = function toolRecordsOf(
    rules: readonly CatalogRule[],
    knobCount: (rule: CatalogRule) => number,
): Partial<QualityToolRecord>[] {
    const tallies = new Map<string, { ecosystem?: string; knobCount: number; ruleCount: number }>();
    for (const rule of rules) {
        const tool = stringField(rule, "tool");
        const { ecosystem } = rule;
        const tally = tallies.get(tool) ?? {
            knobCount: 0,
            ruleCount: 0,
            ...(typeof ecosystem === "string" ? { ecosystem } : {}),
        };
        tally.ruleCount += 1;
        tally.knobCount += knobCount(rule);
        tallies.set(tool, tally);
    }
    return [...tallies]
        .toSorted((a, b) => a[0].localeCompare(b[0]))
        .map(([name, tally]) => ({
            detectionOnly: tally.knobCount === 0,
            ...(tally.ecosystem === undefined ? {} : { ecosystem: tally.ecosystem }),
            knobCount: tally.knobCount,
            name,
            ruleCount: tally.ruleCount,
        }));
};

const clearEcosystemRules = function clearEcosystemRules(dir: string): void {
    for (const name of readdirSync(dir).filter((file) => file.endsWith(GENERATED_JSON))) {
        rmSync(path.join(dir, name));
    }
};

defineStep({
    gives: [],
    name: "quality",
    needs: ["catalog", "ruleKnobs"],
    run: async (state, writer) => {
        const catalog = state.catalog ?? [];
        const ruleKnobs = state.ruleKnobs ?? {};
        const knobsOf = (rule: CatalogRule): RuleKnob[] =>
            ruleKnobs[`${stringField(rule, "tool")}:${rule.ruleId}`] ?? [];
        const controls = concernControls(catalog, knobLookup(catalog, ruleKnobs, loadKnobConcepts()));
        const byEcosystem = new Map<string, Record<string, unknown>[]>();
        for (const rule of catalog) {
            const ecosystem = typeof rule["ecosystem"] === "string" ? rule["ecosystem"] : UNKNOWN;
            const group = byEcosystem.get(ecosystem) ?? [];
            group.push(ruleRecordOf(rule, knobsOf(rule)));
            byEcosystem.set(ecosystem, group);
        }
        const dir = absolutePath("govlab.quality.ecosystemRules");
        clearEcosystemRules(dir);
        await writer.json(absolutePath("govlab.quality.generated", CONCERNS_FILE), {
            category: "quality-concerns",
            records: controls.map(concernRecordOf),
        });
        await writer.json(absolutePath("govlab.quality.generated", TOOLS_FILE), {
            category: "quality-tools",
            records: toolRecordsOf(catalog, (rule) => knobsOf(rule).length),
        });
        await Promise.all(
            [...byEcosystem].map(async ([ecosystem, records]) => {
                const file = path.join(dir, `${slugify(ecosystem) || UNKNOWN}${GENERATED_JSON}`);
                await writer.json(file, { category: "quality-rules", ecosystem, records });
            }),
        );
        return {};
    },
});