Replace policy status with pipeline enabled

This commit is contained in:
James Brunton
2026-09-02 15:52:42 +01:00
parent 03f9897669
commit 137883a159
18 changed files with 33 additions and 34 deletions
@@ -80,7 +80,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
classification: { classification: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-classification", backendId: "backend-classification",
runOn: "upload", runOn: "upload",
order: 0, order: 0,
@@ -90,7 +90,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
security: { security: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-security", backendId: "backend-security",
runOn: securityRunOn.value, runOn: securityRunOn.value,
order: 1, order: 1,
@@ -18,7 +18,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
security: { security: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-sec", backendId: "backend-sec",
runOn: "upload", runOn: "upload",
order: 0, order: 0,
@@ -26,7 +26,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
compliance: { compliance: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-comp", backendId: "backend-comp",
runOn: "upload", runOn: "upload",
order: 1, order: 1,
@@ -34,7 +34,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
classification: { classification: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-cls", backendId: "backend-cls",
runOn: "upload", runOn: "upload",
order: 2, order: 2,
@@ -38,7 +38,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
security: { security: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-1", backendId: "backend-1",
runOn: "upload", runOn: "upload",
outputMode: "new_version", outputMode: "new_version",
@@ -53,7 +53,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
classification: { classification: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-classification", backendId: "backend-classification",
runOn: "upload", runOn: "upload",
order: 0, order: 0,
@@ -32,7 +32,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
security: { security: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-security", backendId: "backend-security",
runOn: "upload", runOn: "upload",
order: 0, order: 0,
@@ -14,7 +14,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
security: { security: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-1", backendId: "backend-1",
runOn: "upload", runOn: "upload",
}, },
@@ -56,7 +56,7 @@ vi.mock("@app/hooks/usePolicies", () => ({
classification: { classification: {
configured: true, configured: true,
runsOnEditor: true, runsOnEditor: true,
status: "active", enabled: true,
backendId: "backend-classification", backendId: "backend-classification",
runOn: runOn.value, runOn: runOn.value,
sources: ["editor"], sources: ["editor"],
@@ -53,7 +53,7 @@ export function usePolicyLocalPasses(): void {
for (const [categoryId, s] of Object.entries(policies)) { for (const [categoryId, s] of Object.entries(policies)) {
const active = const active =
s.configured && s.configured &&
s.status === "active" && s.enabled &&
s.backendId && s.backendId &&
s.runsOnEditor && s.runsOnEditor &&
(s.runOn ?? "upload") === "upload"; (s.runOn ?? "upload") === "upload";
@@ -11,7 +11,7 @@ import type { PoliciesByCategory } from "@app/types/policies";
const rewriter = (order: number) => const rewriter = (order: number) =>
({ ({
configured: true, configured: true,
status: "active", enabled: true,
backendId: `backend-${order}`, backendId: `backend-${order}`,
runsOnEditor: true, runsOnEditor: true,
runOn: "upload", runOn: "upload",
@@ -57,7 +57,7 @@ describe("orderedRewritingCategories", () => {
it("excludes inactive, non-editor, export-triggered, and unconfigured policies", () => { it("excludes inactive, non-editor, export-triggered, and unconfigured policies", () => {
const mixed = { const mixed = {
security: rewriter(0), security: rewriter(0),
inactive: { ...rewriter(1), status: "paused" }, inactive: { ...rewriter(1), enabled: false },
notEditor: { ...rewriter(2), runsOnEditor: false }, notEditor: { ...rewriter(2), runsOnEditor: false },
onExport: { ...rewriter(3), runOn: "export" }, onExport: { ...rewriter(3), runOn: "export" },
unconfigured: { ...rewriter(4), configured: false }, unconfigured: { ...rewriter(4), configured: false },
@@ -57,7 +57,7 @@ export function orderedRewritingCategories(
.filter( .filter(
([id, s]) => ([id, s]) =>
s.configured && s.configured &&
s.status === "active" && s.enabled &&
Boolean(s.backendId) && Boolean(s.backendId) &&
s.runsOnEditor && s.runsOnEditor &&
(s.runOn ?? "upload") === "upload" && (s.runOn ?? "upload") === "upload" &&
@@ -96,7 +96,7 @@ describe("usePolicies", () => {
JSON.stringify({ JSON.stringify({
"be-deleted": { "be-deleted": {
configured: true, configured: true,
status: "active", enabled: true,
backendId: "be-deleted", backendId: "be-deleted",
sources: ["editor"], sources: ["editor"],
runsOnEditor: true, runsOnEditor: true,
@@ -64,7 +64,7 @@ export function usePolicies() {
: { : {
...local[cat.id], ...local[cat.id],
configured: false, configured: false,
status: "default", enabled: false,
backendId: undefined, backendId: undefined,
}; };
} }
@@ -48,7 +48,7 @@ export function decodedToState(
): PolicyState { ): PolicyState {
return { return {
configured: true, configured: true,
status: decoded.enabled ? "active" : "paused", enabled: decoded.enabled,
name: decoded.name, name: decoded.name,
sources: decoded.sources, sources: decoded.sources,
runsOnEditor: decoded.runsOnEditor, runsOnEditor: decoded.runsOnEditor,
@@ -42,7 +42,7 @@ const { enforceExportPolicies } = await import("@app/services/policyExport");
const exportPolicy = (over: Partial<PolicyState>): PolicyState => const exportPolicy = (over: Partial<PolicyState>): PolicyState =>
({ ({
configured: true, configured: true,
status: "active", enabled: true,
backendId: "backend-1", backendId: "backend-1",
sources: [], sources: [],
runsOnEditor: false, runsOnEditor: false,
@@ -67,7 +67,7 @@ function activeExportPolicies(): ExportPolicy[] {
.filter( .filter(
([, s]) => ([, s]) =>
s.configured && s.configured &&
s.status === "active" && s.enabled &&
s.backendId && s.backendId &&
s.runsOnEditor && s.runsOnEditor &&
s.runOn === "export", s.runOn === "export",
@@ -13,17 +13,17 @@ describe("policyStorage", () => {
it("defaults every category to unconfigured (backend is the source of truth)", () => { it("defaults every category to unconfigured (backend is the source of truth)", () => {
const p = loadPolicies(); const p = loadPolicies();
expect(p.ingestion.configured).toBe(false); expect(p.ingestion.configured).toBe(false);
expect(p.ingestion.status).toBe("default"); expect(p.ingestion.enabled).toBe(false);
expect(p.security.configured).toBe(false); expect(p.security.configured).toBe(false);
expect(p.security.status).toBe("default"); expect(p.security.enabled).toBe(false);
expect(p.retention.configured).toBe(false); expect(p.retention.configured).toBe(false);
}); });
it("persists an update and reflects it on reload", () => { it("persists an update and reflects it on reload", () => {
updatePolicy("security", { configured: true, status: "active" }); updatePolicy("security", { configured: true, enabled: true });
const reloaded = loadPolicies(); const reloaded = loadPolicies();
expect(reloaded.security.configured).toBe(true); expect(reloaded.security.configured).toBe(true);
expect(reloaded.security.status).toBe("active"); expect(reloaded.security.enabled).toBe(true);
// Other categories untouched. // Other categories untouched.
expect(reloaded.retention.configured).toBe(false); expect(reloaded.retention.configured).toBe(false);
}); });
@@ -39,7 +39,7 @@ describe("policyStorage", () => {
it("heals missing categories from corrupt/partial storage", () => { it("heals missing categories from corrupt/partial storage", () => {
localStorage.setItem( localStorage.setItem(
"stirling-policies-state", "stirling-policies-state",
JSON.stringify({ ingestion: { configured: true, status: "active" } }), JSON.stringify({ ingestion: { configured: true, enabled: true } }),
); );
const p = loadPolicies(); const p = loadPolicies();
// Missing category gets a default rather than being undefined. // Missing category gets a default rather than being undefined.
@@ -52,7 +52,7 @@ describe("policyStorage", () => {
localStorage.setItem( localStorage.setItem(
"stirling-policies-state", "stirling-policies-state",
JSON.stringify({ JSON.stringify({
security: { configured: true, status: "active", sources: ["s3"] }, security: { configured: true, enabled: true, sources: ["s3"] },
}), }),
); );
// Without the migration the default (true) would wrongly win. // Without the migration the default (true) would wrongly win.
@@ -63,7 +63,7 @@ describe("policyStorage", () => {
localStorage.setItem( localStorage.setItem(
"stirling-policies-state", "stirling-policies-state",
JSON.stringify({ JSON.stringify({
security: { configured: true, status: "active", sources: ["editor"] }, security: { configured: true, enabled: true, sources: ["editor"] },
}), }),
); );
expect(loadPolicies().security.runsOnEditor).toBe(true); expect(loadPolicies().security.runsOnEditor).toBe(true);
@@ -75,7 +75,7 @@ describe("policyStorage", () => {
JSON.stringify({ JSON.stringify({
security: { security: {
configured: true, configured: true,
status: "active", enabled: true,
sources: ["editor"], sources: ["editor"],
runsOnEditor: false, runsOnEditor: false,
}, },
@@ -87,10 +87,10 @@ describe("policyStorage", () => {
it("fires a change event on update", () => { it("fires a change event on update", () => {
const cb = vi.fn(); const cb = vi.fn();
const off = onPoliciesChange(cb); const off = onPoliciesChange(cb);
updatePolicy("routing", { status: "paused" }); updatePolicy("routing", { enabled: false });
expect(cb).toHaveBeenCalledTimes(1); expect(cb).toHaveBeenCalledTimes(1);
off(); off();
updatePolicy("routing", { status: "active" }); updatePolicy("routing", { enabled: true });
expect(cb).toHaveBeenCalledTimes(1); // not called after unsubscribe expect(cb).toHaveBeenCalledTimes(1); // not called after unsubscribe
}); });
}); });
@@ -17,7 +17,7 @@ function defaultState(categoryId: string): PolicyState {
// actually configured + active; this is just the empty local-cache shape. // actually configured + active; this is just the empty local-cache shape.
return { return {
configured: false, configured: false,
status: "default", enabled: false,
sources: ["editor"], sources: ["editor"],
runsOnEditor: true, runsOnEditor: true,
scopeTypes: [], scopeTypes: [],
@@ -11,9 +11,6 @@
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import type { AutomationOperation } from "@app/types/automation"; import type { AutomationOperation } from "@app/types/automation";
/** Lifecycle status of a policy category for the current user/org. */
export type PolicyStatus = "default" | "active" | "paused";
/** A configurable field within a policy's settings. */ /** A configurable field within a policy's settings. */
export type PolicyFieldType = "toggle" | "select" | "chips" | "text"; export type PolicyFieldType = "toggle" | "select" | "chips" | "text";
@@ -82,7 +79,9 @@ export interface PolicySource {
/** Per-category runtime state held in the local cache. */ /** Per-category runtime state held in the local cache. */
export interface PolicyState { export interface PolicyState {
configured: boolean; configured: boolean;
status: PolicyStatus; /** Whether the backend policy is enabled (fires on the editor). Meaningful only
* when `configured`; false otherwise. Mirrors the backend `Policy.enabled`. */
enabled: boolean;
/** Selected sources (ids from POLICY_SOURCES). */ /** Selected sources (ids from POLICY_SOURCES). */
sources: string[]; sources: string[];
/** The policy's own name. Set for builder pipelines, which have no built-in category label. */ /** The policy's own name. Set for builder pipelines, which have no built-in category label. */