Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions public/locales/de-DE/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"deployments": "Deployments",
"batches": "Stapelverarbeitung",
"migrations": "Migrationen",
"cleanup": "Cleanup",
"admin": "Admin",
"help": "Hilfe",
"account": "Konto",
Expand Down Expand Up @@ -77,6 +78,7 @@
"decisions": "Entscheidungen",
"deployments": "Deployments",
"migrations": "Migrationen",
"cleanup": "Cleanup",
"admin": "Admin",
"admin-users": "Admin – Benutzer",
"admin-groups": "Admin – Gruppen",
Expand Down Expand Up @@ -559,6 +561,37 @@
"without-tenant-id": "Ohne Mandanten-ID"
}
},
"cleanup": {
"title": "Cleanup",
"subtitle": "History Cleanup überwachen, bereinigbare Daten prüfen und die History Time To Live anpassen.",
"refresh": "Aktualisieren",
"run-now": "Cleanup jetzt starten",
"configuration": "Konfiguration",
"enabled": "Aktiviert",
"strategy": "Strategie",
"batch-window": "Batch-Fenster",
"parallelism": "Parallelität",
"deleted-data": "Gelöschte Daten in den letzten {{days}} Tagen",
"jobs": "Cleanup-Jobs",
"no-jobs": "Es sind keine Cleanup-Jobs geplant.",
"due-date": "Fälligkeitsdatum",
"retries": "Retries",
"exception": "Exception",
"process-definitions": "Bereinigbare Prozessdefinitionen",
"decision-definitions": "Bereinigbare Entscheidungsdefinitionen",
"batches": "Bereinigbare Batches",
"no-cleanable-data": "Keine bereinigbaren Daten gemeldet.",
"version": "Version",
"finished": "Beendet",
"cleanable": "Bereinigbar",
"ttl": "History TTL",
"save-ttl": "TTL speichern",
"metrics": {
"process-instances": "Prozessinstanzen",
"decision-instances": "Entscheidungsinstanzen",
"batch-operations": "Batch-Operationen"
}
},
"admin": {
"users": "Benutzer",
"groups": "Gruppen",
Expand Down
33 changes: 33 additions & 0 deletions public/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"deployments": "Deployments",
"batches": "Batches",
"migrations": "Migrations",
"cleanup": "Cleanup",
"admin": "Admin",
"help": "Help",
"account": "Account",
Expand Down Expand Up @@ -77,6 +78,7 @@
"decisions": "Decisions",
"deployments": "Deployments",
"migrations": "Migrations",
"cleanup": "Cleanup",
"admin": "Admin",
"admin-users": "Admin – Users",
"admin-groups": "Admin – Groups",
Expand Down Expand Up @@ -559,6 +561,37 @@
"without-tenant-id": "Without Tenant ID"
}
},
"cleanup": {
"title": "Cleanup",
"subtitle": "Monitor history cleanup, review cleanable data, and adjust history time to live.",
"refresh": "Refresh",
"run-now": "Run cleanup now",
"configuration": "Configuration",
"enabled": "Enabled",
"strategy": "Strategy",
"batch-window": "Batch window",
"parallelism": "Parallelism",
"deleted-data": "Deleted data in the last {{days}} days",
"jobs": "Cleanup jobs",
"no-jobs": "No cleanup jobs are scheduled.",
"due-date": "Due date",
"retries": "Retries",
"exception": "Exception",
"process-definitions": "Cleanable process definitions",
"decision-definitions": "Cleanable decision definitions",
"batches": "Cleanable batches",
"no-cleanable-data": "No cleanable data reported.",
"version": "Version",
"finished": "Finished",
"cleanable": "Cleanable",
"ttl": "History TTL",
"save-ttl": "Save TTL",
"metrics": {
"process-instances": "Process instances",
"decision-instances": "Decision instances",
"batch-operations": "Batch operations"
}
},
"admin": {
"users": "Users",
"groups": "Groups",
Expand Down
2 changes: 2 additions & 0 deletions src/api/engine_rest.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from "./helper.jsx";
import auth from "./resources/auth.js";
import batch from "./resources/batch.js";
import cleanup from "./resources/cleanup.js";
import engine from "./resources/engine.js";
import user from "./resources/user.js";
import group from "./resources/group.js";
Expand All @@ -23,6 +24,7 @@ const engine_rest = {
auth,
authorization,
batch,
cleanup,
decision,
deployment,
engine,
Expand Down
98 changes: 98 additions & 0 deletions src/api/resources/cleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { GET, POST, PUT } from "../helper.jsx";

const metric_url = (name, params = {}) => {
const query = new URLSearchParams(params).toString();
return `/metrics/${name}/sum${query ? `?${query}` : ""}`;

Check warning on line 5 in src/api/resources/cleanup.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not use nested template literals.

See more on https://sonarcloud.io/project/issues?id=operaton_web-apps&issues=AZ7Rs7LSSJ3_fRBzwAWn&open=AZ7Rs7LSSJ3_fRBzwAWn&pullRequest=62
};

const get_configuration = (state) =>
GET("/history/cleanup/configuration", state, state.api.cleanup.configuration);

const get_jobs = (state) =>
GET("/history/cleanup/jobs", state, state.api.cleanup.jobs);

const run_cleanup = (state, immediately_due = false) =>
POST(
`/history/cleanup?immediatelyDue=${immediately_due}`,
{},
state,
state.api.cleanup.run,
);

const get_cleanable_process_definitions = (state) =>
GET(
"/history/process-definition/cleanable-process-instance-report",
state,
state.api.cleanup.cleanable.process_definitions,
);

const get_cleanable_decision_definitions = (state) =>
GET(
"/history/decision-definition/cleanable-decision-instance-report",
state,
state.api.cleanup.cleanable.decision_definitions,
);

const get_cleanable_batches = (state) =>
GET(
"/history/batch/cleanable-batch-report",
state,
state.api.cleanup.cleanable.batches,
);

const get_removed_process_instances = (state, params = {}) =>
GET(
metric_url("history-cleanup-removed-process-instances", params),
state,
state.api.cleanup.metrics.process_instances,
);

const get_removed_decision_instances = (state, params = {}) =>
GET(
metric_url("history-cleanup-removed-decision-instances", params),
state,
state.api.cleanup.metrics.decision_instances,
);

const get_removed_batch_operations = (state, params = {}) =>
GET(
metric_url("history-cleanup-removed-batch-operations", params),
state,
state.api.cleanup.metrics.batch_operations,
);

const set_process_definition_ttl = (state, id, historyTimeToLive) =>
PUT(
`/process-definition/${id}/history-time-to-live`,
{ historyTimeToLive },
state,
state.api.cleanup.update_ttl,
);

const set_decision_definition_ttl = (state, id, historyTimeToLive) =>
PUT(
`/decision-definition/${id}/history-time-to-live`,
{ historyTimeToLive },
state,
state.api.cleanup.update_ttl,
);

const cleanup = {
configuration: get_configuration,
jobs: get_jobs,
run: run_cleanup,
cleanable: {
process_definitions: get_cleanable_process_definitions,
decision_definitions: get_cleanable_decision_definitions,
batches: get_cleanable_batches,
},
metrics: {
process_instances: get_removed_process_instances,
decision_instances: get_removed_decision_instances,
batch_operations: get_removed_batch_operations,
},
set_process_definition_ttl,
set_decision_definition_ttl,
};

export default cleanup;
124 changes: 124 additions & 0 deletions src/api/resources/cleanup.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { describe, it, vi, beforeEach } from "vitest";

vi.mock("../helper.jsx", () => ({
GET: vi.fn(),
POST: vi.fn(),
PUT: vi.fn(),
}));

import { GET, POST, PUT } from "../helper.jsx";
import { create_mock_state, expect_api_call } from "../../test/helpers.js";
import cleanup from "./cleanup.js";

describe("api/resources/cleanup", () => {
let state;

beforeEach(() => {
state = create_mock_state();
});

it("configuration() GETs history cleanup configuration", () => {
cleanup.configuration(state);
expect_api_call(GET, {
url: "/history/cleanup/configuration",
state,
signal: state.api.cleanup.configuration,
});
});

it("jobs() GETs history cleanup jobs", () => {
cleanup.jobs(state);
expect_api_call(GET, {
url: "/history/cleanup/jobs",
state,
signal: state.api.cleanup.jobs,
});
});

it("run() POSTs the cleanup trigger", () => {
cleanup.run(state, true);
expect_api_call(POST, {
url: "/history/cleanup?immediatelyDue=true",
body: {},
state,
signal: state.api.cleanup.run,
});
});

it("cleanable.process_definitions() GETs the cleanable process report", () => {
cleanup.cleanable.process_definitions(state);
expect_api_call(GET, {
url: "/history/process-definition/cleanable-process-instance-report",
state,
signal: state.api.cleanup.cleanable.process_definitions,
});
});

it("cleanable.decision_definitions() GETs the cleanable decision report", () => {
cleanup.cleanable.decision_definitions(state);
expect_api_call(GET, {
url: "/history/decision-definition/cleanable-decision-instance-report",
state,
signal: state.api.cleanup.cleanable.decision_definitions,
});
});

it("cleanable.batches() GETs the cleanable batch report", () => {
cleanup.cleanable.batches(state);
expect_api_call(GET, {
url: "/history/batch/cleanable-batch-report",
state,
signal: state.api.cleanup.cleanable.batches,
});
});

it("metrics.process_instances() GETs the metric sum", () => {
cleanup.metrics.process_instances(state, {
startDate: "2026-05-17T00:00:00.000+0000",
endDate: "2026-06-16T00:00:00.000+0000",
});
expect_api_call(GET, {
url: "/metrics/history-cleanup-removed-process-instances/sum?startDate=2026-05-17T00%3A00%3A00.000%2B0000&endDate=2026-06-16T00%3A00%3A00.000%2B0000",
state,
signal: state.api.cleanup.metrics.process_instances,
});
});

it("metrics.decision_instances() GETs the metric sum", () => {
cleanup.metrics.decision_instances(state);
expect_api_call(GET, {
url: "/metrics/history-cleanup-removed-decision-instances/sum",
state,
signal: state.api.cleanup.metrics.decision_instances,
});
});

it("metrics.batch_operations() GETs the metric sum", () => {
cleanup.metrics.batch_operations(state);
expect_api_call(GET, {
url: "/metrics/history-cleanup-removed-batch-operations/sum",
state,
signal: state.api.cleanup.metrics.batch_operations,
});
});

it("set_process_definition_ttl() PUTs historyTimeToLive", () => {
cleanup.set_process_definition_ttl(state, "process:1", 30);
expect_api_call(PUT, {
url: "/process-definition/process:1/history-time-to-live",
body: { historyTimeToLive: 30 },
state,
signal: state.api.cleanup.update_ttl,
});
});

it("set_decision_definition_ttl() PUTs nullable historyTimeToLive", () => {
cleanup.set_decision_definition_ttl(state, "decision:1", null);
expect_api_call(PUT, {
url: "/decision-definition/decision:1/history-time-to-live",
body: { historyTimeToLive: null },
state,
signal: state.api.cleanup.update_ttl,
});
});
});
1 change: 1 addition & 0 deletions src/components/GoTo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const PAGES = [
{ nameKey: "goto.pages.decisions", href: "/decisions" },
{ nameKey: "goto.pages.deployments", href: "/deployments" },
{ nameKey: "goto.pages.migrations", href: "/migrations" },
{ nameKey: "goto.pages.cleanup", href: "/cleanup" },
{ nameKey: "goto.pages.admin", href: "/admin" },
{ nameKey: "goto.pages.admin-users", href: "/admin/users" },
{ nameKey: "goto.pages.admin-groups", href: "/admin/groups" },
Expand Down
7 changes: 7 additions & 0 deletions src/components/GoTo.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ describe("GoTo", () => {
a.getAttribute("href"),
),
).not.toContain("/tasks");

type(input, "cleanup");
expect(
Array.from(container.querySelectorAll(".goto-item")).map((a) =>
a.getAttribute("href"),
),
).toContain("/cleanup");
});

it("navigates and closes when a result is clicked", () => {
Expand Down
6 changes: 6 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export function Header() {
<nav id="secondary-navigation">
<menu>
<li><a href="/help"> {t("nav.help")}</a></li>
<li><a href="/cleanup">{t("nav.cleanup")}</a></li>
<li><a href="/account">{t("nav.account")}</a></li>
</menu>
</nav>
Expand Down Expand Up @@ -146,6 +147,11 @@ export function Header() {
{t("nav.migrations")}
</a>
</li>
<li>
<a href="/cleanup" class={url.startsWith("/cleanup") && "active"}>
{t("nav.cleanup")}
</a>
</li>
<li>
<a href="/admin" class={url.startsWith("/admin") && "active"}>
{t("nav.admin")}
Expand Down
Loading