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
7 changes: 6 additions & 1 deletion public/locales/de-DE/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,12 @@
"jobs": {
"overriding-job-priority": "Überschriebene Job-Priorität",
"suspend": "Anhalten",
"change-priority": "Job-Priorität ändern"
"change-priority": "Job-Priorität ändern",
"retries": "Retries",
"due-date": "Fälligkeitsdatum",
"due-time": "Fälligkeitszeit",
"set-retries": "Retries setzen",
"retries-success": "Job-Retries aktualisiert."
},
"sort": {
"name": "Name",
Expand Down
7 changes: 6 additions & 1 deletion public/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,12 @@
"jobs": {
"overriding-job-priority": "Overriding Job Priority",
"suspend": "Suspend",
"change-priority": "Change Overriding Job Priority"
"change-priority": "Change Overriding Job Priority",
"retries": "Retries",
"due-date": "Due Date",
"due-time": "Due Time",
"set-retries": "Set Retries",
"retries-success": "Job retries updated."
},
"sort": {
"name": "Name",
Expand Down
15 changes: 12 additions & 3 deletions src/api/resources/job_definition.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { GET } from '../helper.jsx'
import { GET, PUT } from '../helper.jsx'

const get_job_definitions = (state, definition_id) =>
GET(`/job-definition?processDefinitionId=${definition_id}`, state, state.api.job_definition.all.by_process_definition)

const set_retries = (state, id, retries, due_date = null) =>
PUT(
`/job-definition/${id}/retries`,
{ retries, ...(due_date ? { dueDate: due_date } : {}) },
state,
state.api.job_definition.retries
)

const job_definition = {
all: {
by_process_definition: get_job_definitions
}
},
set_retries
}

export default job_definition
export default job_definition
28 changes: 27 additions & 1 deletion src/api/resources/job_definition.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { describe, it, vi, beforeEach } from "vitest";

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

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

Expand All @@ -22,4 +23,29 @@ describe("api/resources/job_definition", () => {
signal: state.api.job_definition.all.by_process_definition,
});
});

it("set_retries() PUTs retries to the job definition", () => {
job_definition.set_retries(state, "job-def-1", 3);
expect_api_call(PUT, {
url: "/job-definition/job-def-1/retries",
body: { retries: 3 },
state,
signal: state.api.job_definition.retries,
});
});

it("set_retries() can include a due date", () => {
job_definition.set_retries(
state,
"job-def-1",
3,
"2017-04-06T13:57:45.000+0200",
);
expect_api_call(PUT, {
url: "/job-definition/job-def-1/retries",
body: { retries: 3, dueDate: "2017-04-06T13:57:45.000+0200" },
state,
signal: state.api.job_definition.retries,
});
});
});
119 changes: 90 additions & 29 deletions src/pages/Processes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ const DefinitionsEmpty = () => {
<a
href="https://docs.operaton.org/manual/latest/installation/full/tomcat/manual/"
target="_blank"
rel="noopener"
rel="noreferrer"
>
{t("processes.empty.how-to")}
</a>
Expand Down Expand Up @@ -928,8 +928,7 @@ const InstanceDetails = () => {
params: { selection_id, definition_id, panel },
query,
} = useRoute(),
history_mode = query.history === "true",
[t] = useTranslation();
history_mode = query.history === "true";

if (selection_id) {
if (
Expand Down Expand Up @@ -1044,19 +1043,17 @@ const InstanceVariables = () => {
? Object.entries(
state.api.process.instance.variables.value.data,
).map(
// eslint-disable-next-line react/jsx-key
([name, { type, value }]) => (
<tr>
<tr key={name}>
<td>{name}</td>
<td>{type}</td>
<td>{value}</td>
</tr>
),
)
: state.api.process.instance.variables.value.data.map(
// eslint-disable-next-line react/jsx-key
({ name, type, value }) => (
<tr>
<tr key={name}>
<td>{name}</td>
<td>{type}</td>
<td>{value}</td>
Expand Down Expand Up @@ -1426,6 +1423,14 @@ const JobDefinitions = () => {
{history_mode && (
<small class="history-na">{t("processes.history-mode-na")}</small>
)}
{state.api.job_definition.retries.value ? (
<RequestState
signal={state.api.job_definition.retries}
on_success={() => (
<p class="success">{t("processes.jobs.retries-success")}</p>
)}
/>
) : null}
<table>
<thead>
<tr>
Expand All @@ -1440,22 +1445,7 @@ const JobDefinitions = () => {
<tbody>
{state.api.job_definition.all.by_process_definition.value?.data?.map(
(definition) => (
<tr key={definition.id}>
<td>
{definition.suspended
? t("common.suspended")
: t("common.active")}
</td>
<td>?</td>
{/*<td>{definition.calledFromActivityIds.map(a => `${a}, `)}</td>*/}
<td>{definition.jobType}</td>
<td>{definition.jobConfiguration}</td>
<td>{definition.overridingJobPriority ?? "-"}</td>
<td>
<button>{t("processes.jobs.suspend")}</button>
<button>{t("processes.jobs.change-priority")}</button>
</td>
</tr>
<JobDefinitionRow key={definition.id} definition={definition} />
),
)}
</tbody>
Expand All @@ -1464,12 +1454,83 @@ const JobDefinitions = () => {
);
};

const BackToListBtn = ({ url, title, className }) => (
<a className={`tabs-back ${className || ""}`} href={url} title={title}>
<Icons.arrow_left />
<Icons.list />
</a>
);
const JobDefinitionRow = ({ definition }) => {
const state = useContext(AppState),
{ definition_id } = useRoute(),
[t] = useTranslation(),
retries = useSignal(""),
due_date = useSignal(""),
due_time = useSignal(""),
input_id = `job-retries-${definition.id}`;

const refresh = () =>
engine_rest.job_definition.all.by_process_definition(
state,
definition_id,
),
format_due_date = () =>
due_date.value
? `${due_date.value}T${due_time.value || "00:00"}:00.000+0000`
: null,
on_submit = (e) => {
e.preventDefault();
const raw = String(retries.value).trim();
if (raw === "") return;
const parsed = Number(raw);
if (!Number.isInteger(parsed) || parsed < 0) return;
void engine_rest.job_definition.set_retries(
state,
definition.id,
parsed,
format_due_date(),
).then(refresh);
};

return (
<tr>
<td>
{definition.suspended
? t("common.suspended")
: t("common.active")}
</td>
<td>{definition.activityId ?? "—"}</td>
<td>{definition.jobType}</td>
<td>{definition.jobConfiguration}</td>
<td>{definition.overridingJobPriority ?? "-"}</td>
<td>
<form onSubmit={on_submit}>
<label for={input_id}>{t("processes.jobs.retries")}</label>
<input
id={input_id}
type="number"
min="0"
step="1"
value={retries.value}
onInput={(e) => (retries.value = e.currentTarget.value)}
required
/>
<label for={`${input_id}-date`}>{t("processes.jobs.due-date")}</label>
<input
id={`${input_id}-date`}
type="date"
value={due_date.value}
onInput={(e) => (due_date.value = e.currentTarget.value)}
/>
<label for={`${input_id}-time`}>{t("processes.jobs.due-time")}</label>
<input
id={`${input_id}-time`}
type="time"
value={due_time.value}
onInput={(e) => (due_time.value = e.currentTarget.value)}
/>
<div class="button-group">
<button type="submit">{t("processes.jobs.set-retries")}</button>
</div>
</form>
</td>
</tr>
);
};

const DefinitionsManage = () => {
const state = useContext(AppState),
Expand Down
39 changes: 39 additions & 0 deletions src/pages/Processes.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ describe("ProcessesPage — definition tabs", () => {
{
id: "jd1",
suspended: true,
activityId: "timer_start",
jobType: "timer",
jobConfiguration: "R/PT5M",
overridingJobPriority: 10,
Expand All @@ -353,9 +354,47 @@ describe("ProcessesPage — definition tabs", () => {
expect(
engine_rest.job_definition.all.by_process_definition,
).toHaveBeenCalled();
expect(getByText("timer_start")).toBeTruthy();
expect(getByText("timer")).toBeTruthy();
expect(getByText("R/PT5M")).toBeTruthy();
});

it("sets failed job retries for a job definition with an optional due date", async () => {
mockParams = { definition_id: "proc:1", panel: "jobs" };
engine_rest.job_definition.set_retries.mockResolvedValue(undefined);
signal_response(state.api.job_definition.all.by_process_definition, [
{
id: "jd1",
suspended: false,
activityId: "timer_start",
jobType: "timer",
jobConfiguration: "R/PT5M",
overridingJobPriority: null,
},
]);
const { container, getByLabelText } = renderPage(state);

fireEvent.input(getByLabelText("processes.jobs.retries"), {
target: { value: "3" },
});
fireEvent.input(getByLabelText("processes.jobs.due-date"), {
target: { value: "2017-04-06" },
});
fireEvent.input(getByLabelText("processes.jobs.due-time"), {
target: { value: "13:57" },
});
fireEvent.submit(container.querySelector("form"));
await Promise.resolve();
await Promise.resolve();

expect(engine_rest.job_definition.set_retries).toHaveBeenCalled();
expect(engine_rest.job_definition.set_retries.mock.lastCall[0]).toBe(state);
expect(engine_rest.job_definition.set_retries.mock.lastCall[1]).toBe("jd1");
expect(engine_rest.job_definition.set_retries.mock.lastCall[2]).toBe(3);
expect(engine_rest.job_definition.set_retries.mock.lastCall[3]).toBe(
"2017-04-06T13:57:00.000+0000",
);
});
});

describe("ProcessesPage — instance details", () => {
Expand Down
1 change: 1 addition & 0 deletions src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ const createAppState = () => {
all: {
by_process_definition: signal(null),
},
retries: signal(null),
},
};

Expand Down