@cosmicdrift/kumiko-bundled-features 0.383.0 → 0.384.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/event-classification.d.ts +8 -0
- package/dist/audit/event-classification.js +69 -0
- package/dist/audit/feature.js +3 -3
- package/dist/audit/handlers/audit-events.d.ts +25 -3
- package/dist/audit/handlers/audit-events.js +41 -7
- package/dist/audit/handlers/details-all-tenants.query.d.ts +3 -0
- package/dist/audit/handlers/details-all-tenants.query.js +2 -5
- package/dist/audit/handlers/details.query.d.ts +3 -0
- package/dist/audit/handlers/details.query.js +5 -3
- package/dist/audit/handlers/list-all-tenants.query.d.ts +9 -2
- package/dist/audit/handlers/list-all-tenants.query.js +10 -8
- package/dist/audit/handlers/list.query.d.ts +8 -0
- package/dist/audit/handlers/list.query.js +12 -4
- package/dist/audit/screens.d.ts +37 -0
- package/dist/audit/screens.js +26 -4
- package/dist/audit/web/audit-event-cell.d.ts +4 -0
- package/dist/audit/web/audit-event-cell.js +20 -0
- package/dist/audit/web/audit-event-labels.d.ts +7 -0
- package/dist/audit/web/audit-event-labels.js +23 -0
- package/dist/audit/web/client-plugin.js +6 -1
- package/dist/audit/web/constants.d.ts +8 -0
- package/dist/audit/web/constants.js +7 -0
- package/dist/audit/web/i18n.js +48 -1
- package/dist/auth-email-password/feature.js +3 -2
- package/dist/auth-email-password/handlers/login-options.query.d.ts +5 -0
- package/dist/auth-email-password/handlers/login-options.query.js +17 -0
- package/dist/auth-email-password/index.d.ts +1 -1
- package/dist/auth-email-password/index.js +1 -1
- package/dist/auth-email-password/{auth-paths.d.ts → web/auth-paths.d.ts} +5 -3
- package/dist/auth-email-password/{auth-paths.js → web/auth-paths.js} +5 -3
- package/dist/auth-email-password/web/client-plugin.d.ts +5 -0
- package/dist/auth-email-password/web/client-plugin.js +2 -0
- package/dist/auth-email-password/web/constants.d.ts +1 -0
- package/dist/auth-email-password/web/constants.js +1 -0
- package/dist/auth-email-password/web/default-auth-pages-gate.d.ts +4 -0
- package/dist/auth-email-password/web/default-auth-pages-gate.js +28 -0
- package/dist/auth-email-password/web/login-screen.d.ts +13 -18
- package/dist/auth-email-password/web/login-screen.js +31 -19
- package/dist/auth-mfa/schema/user-mfa.d.ts +6 -126
- package/dist/billing-foundation/entities.d.ts +8 -168
- package/dist/billing-foundation/projection.d.ts +8 -168
- package/dist/cap-counter/entity.d.ts +1 -21
- package/dist/compliance-profiles/schema/profile-selection.d.ts +2 -28
- package/dist/config/table.d.ts +6 -126
- package/dist/custom-fields/entity.d.ts +4 -84
- package/dist/data-retention/schema/tenant-retention-override.d.ts +6 -112
- package/dist/delivery/tables.d.ts +6 -126
- package/dist/document-ingest-foundation/entity.d.ts +6 -112
- package/dist/folders/entity.d.ts +8 -168
- package/dist/form-draft/entity.d.ts +2 -42
- package/dist/inbound-mail-foundation/entities.d.ts +25 -525
- package/dist/inbound-mail-foundation/projection.d.ts +20 -420
- package/dist/jobs/feature.js +22 -117
- package/dist/jobs/handlers/consumer.write.d.ts +37 -0
- package/dist/jobs/handlers/consumer.write.js +59 -0
- package/dist/jobs/handlers/consumers.query.d.ts +52 -0
- package/dist/jobs/handlers/consumers.query.js +114 -0
- package/dist/jobs/job-run-table.d.ts +6 -126
- package/dist/jobs/screens.d.ts +6 -0
- package/dist/jobs/screens.js +241 -0
- package/dist/jobs/web/constants.d.ts +20 -0
- package/dist/jobs/web/constants.js +21 -0
- package/dist/jobs/web/i18n.js +41 -0
- package/dist/ledger/entity.d.ts +13 -273
- package/dist/managed-pages/table.d.ts +12 -252
- package/dist/notes-history/entity.d.ts +14 -280
- package/dist/personal-access-tokens/schema/api-token.d.ts +6 -126
- package/dist/secrets/i18n.d.ts +0 -3
- package/dist/secrets/i18n.js +0 -3
- package/dist/secrets/table.d.ts +1 -21
- package/dist/sessions/constants.d.ts +2 -1
- package/dist/sessions/constants.js +2 -1
- package/dist/sessions/handlers/detail.query.d.ts +5 -0
- package/dist/sessions/handlers/detail.query.js +7 -3
- package/dist/sessions/handlers/list.query.d.ts +5 -0
- package/dist/sessions/handlers/list.query.js +16 -10
- package/dist/sessions/handlers/mine.query.d.ts +5 -0
- package/dist/sessions/handlers/mine.query.js +15 -9
- package/dist/sessions/schema/user-session.d.ts +4 -84
- package/dist/sessions/screens.js +22 -8
- package/dist/sessions/web/client-plugin.d.ts +6 -0
- package/dist/sessions/web/client-plugin.js +11 -0
- package/dist/sessions/web/constants.d.ts +3 -0
- package/dist/sessions/web/constants.js +3 -0
- package/dist/sessions/web/default-translations.d.ts +2 -0
- package/dist/sessions/web/default-translations.js +3 -0
- package/dist/sessions/web/i18n.js +5 -1
- package/dist/sessions/web/index.d.ts +1 -0
- package/dist/sessions/web/index.js +1 -0
- package/dist/sessions/web/session-device-cell.d.ts +3 -0
- package/dist/sessions/web/session-device-cell.js +16 -0
- package/dist/sessions/web/user-agent.d.ts +26 -0
- package/dist/sessions/web/user-agent.js +58 -0
- package/dist/tags/entity.d.ts +9 -189
- package/dist/template-resolver/feature.d.ts +1 -0
- package/dist/template-resolver/feature.js +2 -0
- package/dist/template-resolver/handlers/overview.query.d.ts +5 -0
- package/dist/template-resolver/handlers/overview.query.js +19 -2
- package/dist/template-resolver/handlers/tenant-override-counts.query.d.ts +17 -0
- package/dist/template-resolver/handlers/tenant-override-counts.query.js +35 -0
- package/dist/template-resolver/handlers/tenant-override-selection.d.ts +12 -0
- package/dist/template-resolver/handlers/tenant-override-selection.js +34 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.js +3 -29
- package/dist/template-resolver/table.d.ts +20 -350
- package/dist/template-resolver/user-content-table.d.ts +12 -238
- package/dist/template-resolver/web/qualified-names.d.ts +1 -0
- package/dist/template-resolver/web/qualified-names.js +1 -0
- package/dist/tenant/invitation-table.d.ts +6 -126
- package/dist/tenant/membership-table.d.ts +4 -84
- package/dist/tenant/schema/tenant.d.ts +6 -126
- package/dist/tier-engine/entity.d.ts +1 -21
- package/dist/tier-engine/tier-resolver.d.ts +1 -21
- package/dist/user/schema/user.d.ts +12 -252
- package/dist/user-data-rights/feature.js +33 -33
- package/dist/user-data-rights/schema/download-attempt.d.ts +14 -294
- package/dist/user-data-rights/schema/download-token.d.ts +8 -168
- package/dist/user-data-rights/schema/export-job.d.ts +8 -154
- package/dist/user-data-rights/web/i18n.js +4 -1
- package/dist/waitlist/entity.d.ts +16 -322
- package/package.json +17 -9
- package/src/audit/changes.json +6 -0
- package/src/auth-email-password/changes.json +6 -0
- package/src/jobs/changes.json +6 -0
- package/src/sessions/changes.json +6 -0
- package/src/template-resolver/changes.json +6 -0
- package/src/user-data-rights/changes.json +18 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
export declare const consumerRestartWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<string, z.ZodObject<{
|
|
3
|
+
name: z.ZodString;
|
|
4
|
+
instanceId: z.ZodString;
|
|
5
|
+
}, z.core.$strip>, {
|
|
6
|
+
name: string;
|
|
7
|
+
instanceId: string;
|
|
8
|
+
status: string;
|
|
9
|
+
skippedEventId: string | null;
|
|
10
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
11
|
+
export declare const consumerSkipWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<string, z.ZodObject<{
|
|
12
|
+
name: z.ZodString;
|
|
13
|
+
instanceId: z.ZodString;
|
|
14
|
+
}, z.core.$strip>, {
|
|
15
|
+
name: string;
|
|
16
|
+
instanceId: string;
|
|
17
|
+
status: string;
|
|
18
|
+
skippedEventId: string | null;
|
|
19
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
20
|
+
export declare const consumerDisableWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<string, z.ZodObject<{
|
|
21
|
+
name: z.ZodString;
|
|
22
|
+
instanceId: z.ZodString;
|
|
23
|
+
}, z.core.$strip>, {
|
|
24
|
+
name: string;
|
|
25
|
+
instanceId: string;
|
|
26
|
+
status: string;
|
|
27
|
+
skippedEventId: string | null;
|
|
28
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
29
|
+
export declare const consumerEnableWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<string, z.ZodObject<{
|
|
30
|
+
name: z.ZodString;
|
|
31
|
+
instanceId: z.ZodString;
|
|
32
|
+
}, z.core.$strip>, {
|
|
33
|
+
name: string;
|
|
34
|
+
instanceId: string;
|
|
35
|
+
status: string;
|
|
36
|
+
skippedEventId: string | null;
|
|
37
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { InternalError, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
+
import { ConsumerStatuses, ConsumerTransitionError, disableConsumer, enableConsumer, restartConsumer, skipPoisonEvent, } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { JobErrors } from "../web/constants.js";
|
|
6
|
+
const consumerTargetSchema = z.object({ name: z.string().min(1), instanceId: z.string().min(1) });
|
|
7
|
+
const CONSUMER_STATE_REASON = "event consumer state is global dispatcher state, not tenant data";
|
|
8
|
+
function rejected(code, i18nKey) {
|
|
9
|
+
return writeFailure(new UnprocessableError(code, { i18nKey }));
|
|
10
|
+
}
|
|
11
|
+
function defineConsumerWrite(name, description, transition) {
|
|
12
|
+
return defineWriteHandler({
|
|
13
|
+
name,
|
|
14
|
+
description,
|
|
15
|
+
schema: consumerTargetSchema,
|
|
16
|
+
access: { roles: ["SystemAdmin"] },
|
|
17
|
+
// Operator recovery for people only; skip drops an event for good.
|
|
18
|
+
agent: { expose: false },
|
|
19
|
+
handler: async (event, ctx) => {
|
|
20
|
+
if (!ctx.systemDb) {
|
|
21
|
+
throw new InternalError({
|
|
22
|
+
message: `jobs:write:${name} requires ctx.systemDb (feature must declare r.systemScope())`,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const db = ctx.systemDb.unsafeRaw(CONSUMER_STATE_REASON);
|
|
26
|
+
try {
|
|
27
|
+
const next = await transition(db, event.payload.name, event.payload.instanceId);
|
|
28
|
+
if (next.skippedEventId === null) {
|
|
29
|
+
return rejected(JobErrors.consumerNothingToSkip, "jobs.errors.consumerNothingToSkip");
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
isSuccess: true,
|
|
33
|
+
data: {
|
|
34
|
+
name: next.name,
|
|
35
|
+
instanceId: next.instanceId,
|
|
36
|
+
status: next.status,
|
|
37
|
+
skippedEventId: next.skippedEventId?.toString() ?? null,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
if (e instanceof ConsumerTransitionError) {
|
|
43
|
+
return e.rejection === "unknown-consumer"
|
|
44
|
+
? rejected(JobErrors.consumerUnknown, "jobs.errors.consumerUnknown")
|
|
45
|
+
: rejected(JobErrors.consumerWrongStatus, "jobs.errors.consumerWrongStatus");
|
|
46
|
+
}
|
|
47
|
+
ctx.log?.error(`jobs:write:${name} failed: ${e instanceof Error ? e.message : String(e)}`, {
|
|
48
|
+
consumer: event.payload.name,
|
|
49
|
+
instanceId: event.payload.instanceId,
|
|
50
|
+
});
|
|
51
|
+
return rejected(JobErrors.consumerTransitionFailed, "jobs.errors.consumerTransitionFailed");
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export const consumerRestartWrite = defineConsumerWrite("consumer-restart", "Sets a dead event consumer back to idle so it retries the event that stopped it; use it after a transient failure is fixed.", restartConsumer);
|
|
57
|
+
export const consumerSkipWrite = defineConsumerWrite("consumer-skip", "Moves a stuck event consumer past the event that keeps failing; that event is never delivered to this consumer, so use it only for events that can never succeed.", (db, consumerName, instanceId) => skipPoisonEvent(db, consumerName, instanceId, { requireStatus: ConsumerStatuses.dead }));
|
|
58
|
+
export const consumerDisableWrite = defineConsumerWrite("consumer-disable", "Pauses an event consumer; the dispatcher skips it until it is enabled again.", disableConsumer);
|
|
59
|
+
export const consumerEnableWrite = defineConsumerWrite("consumer-enable", "Resumes a paused event consumer from where it stopped.", enableConsumer);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
export declare function consumerRowId(name: string, instanceId: string): string;
|
|
3
|
+
export declare function splitConsumerRowId(id: string): {
|
|
4
|
+
name: string;
|
|
5
|
+
instanceId: string;
|
|
6
|
+
} | null;
|
|
7
|
+
export declare const consumersQuery: import("@cosmicdrift/kumiko-types/define-handler").QueryHandlerDefinition<"consumers", z.ZodObject<{
|
|
8
|
+
filters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
9
|
+
field: z.ZodLiteral<"status">;
|
|
10
|
+
op: z.ZodLiteral<"in">;
|
|
11
|
+
value: z.ZodArray<z.ZodEnum<{
|
|
12
|
+
dead: "dead";
|
|
13
|
+
disabled: "disabled";
|
|
14
|
+
idle: "idle";
|
|
15
|
+
"never-run": "never-run";
|
|
16
|
+
processing: "processing";
|
|
17
|
+
}>>;
|
|
18
|
+
}, z.core.$strict>>>;
|
|
19
|
+
sort: z.ZodOptional<z.ZodEnum<{
|
|
20
|
+
attempts: "attempts";
|
|
21
|
+
lag: "lag";
|
|
22
|
+
name: "name";
|
|
23
|
+
status: "status";
|
|
24
|
+
}>>;
|
|
25
|
+
sortDirection: z.ZodOptional<z.ZodEnum<{
|
|
26
|
+
asc: "asc";
|
|
27
|
+
desc: "desc";
|
|
28
|
+
}>>;
|
|
29
|
+
}, z.core.$strip>, {
|
|
30
|
+
rows: {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
instanceId: string;
|
|
34
|
+
status: string;
|
|
35
|
+
lag: number;
|
|
36
|
+
attempts: number;
|
|
37
|
+
lastError: string | null;
|
|
38
|
+
}[];
|
|
39
|
+
nextCursor: null;
|
|
40
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
41
|
+
export declare const consumerDetailQuery: import("@cosmicdrift/kumiko-types/define-handler").QueryHandlerDefinition<"consumer-details", z.ZodObject<{
|
|
42
|
+
consumerId: z.ZodString;
|
|
43
|
+
}, z.core.$strip>, {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
instanceId: string;
|
|
47
|
+
status: string;
|
|
48
|
+
lag: number;
|
|
49
|
+
attempts: number;
|
|
50
|
+
lastError: string | null;
|
|
51
|
+
updatedAt: string | null;
|
|
52
|
+
} | null, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
+
import { getAllConsumerProgress, getConsumerState, SEARCH_CONSUMER_NAME, SSE_BROADCAST_CONSUMER_NAME, } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { CONSUMER_ID_SEPARATOR, CONSUMER_STATUSES, isConsumerStatus, } from "../web/constants.js";
|
|
6
|
+
const LAST_ERROR_PREVIEW_LENGTH = 90;
|
|
7
|
+
// Dead consumers are what support looks for, so they lead the default order.
|
|
8
|
+
const STATUS_RANK = {
|
|
9
|
+
dead: 0,
|
|
10
|
+
disabled: 1,
|
|
11
|
+
processing: 2,
|
|
12
|
+
"never-run": 3,
|
|
13
|
+
idle: 4,
|
|
14
|
+
};
|
|
15
|
+
export function consumerRowId(name, instanceId) {
|
|
16
|
+
return `${name}${CONSUMER_ID_SEPARATOR}${instanceId}`;
|
|
17
|
+
}
|
|
18
|
+
export function splitConsumerRowId(id) {
|
|
19
|
+
const at = id.lastIndexOf(CONSUMER_ID_SEPARATOR);
|
|
20
|
+
if (at <= 0)
|
|
21
|
+
return null;
|
|
22
|
+
return { name: id.slice(0, at), instanceId: id.slice(at + 1) };
|
|
23
|
+
}
|
|
24
|
+
function registeredConsumerNames(registry) {
|
|
25
|
+
return [
|
|
26
|
+
SSE_BROADCAST_CONSUMER_NAME,
|
|
27
|
+
SEARCH_CONSUMER_NAME,
|
|
28
|
+
...registry.getAllMultiStreamProjections().keys(),
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
function previewOf(error) {
|
|
32
|
+
if (error === null || error.length <= LAST_ERROR_PREVIEW_LENGTH)
|
|
33
|
+
return error;
|
|
34
|
+
return `${error.slice(0, LAST_ERROR_PREVIEW_LENGTH).trimEnd()}…`;
|
|
35
|
+
}
|
|
36
|
+
function toRow(c) {
|
|
37
|
+
return {
|
|
38
|
+
id: consumerRowId(c.name, c.instanceId),
|
|
39
|
+
name: c.name,
|
|
40
|
+
instanceId: c.instanceId,
|
|
41
|
+
status: c.status,
|
|
42
|
+
lag: Number(c.lag),
|
|
43
|
+
attempts: c.attempts,
|
|
44
|
+
lastError: previewOf(c.lastError),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const statusFilterSchema = z.strictObject({
|
|
48
|
+
field: z.literal("status"),
|
|
49
|
+
op: z.literal("in"),
|
|
50
|
+
value: z.array(z.enum(CONSUMER_STATUSES)),
|
|
51
|
+
});
|
|
52
|
+
export const consumersQuery = defineQueryHandler({
|
|
53
|
+
name: "consumers",
|
|
54
|
+
description: "Lists every event consumer (projections, search indexer, SSE broadcaster) with status, lag behind the event store and last error, dead consumers first; use it to find a stuck consumer.",
|
|
55
|
+
schema: z.object({
|
|
56
|
+
filters: z.array(statusFilterSchema).max(1).optional(),
|
|
57
|
+
sort: z.enum(["name", "status", "lag", "attempts"]).optional(),
|
|
58
|
+
sortDirection: z.enum(["asc", "desc"]).optional(),
|
|
59
|
+
}),
|
|
60
|
+
access: { roles: ["SystemAdmin"] },
|
|
61
|
+
handler: async (query, ctx) => {
|
|
62
|
+
if (!ctx.systemDb) {
|
|
63
|
+
throw new InternalError({
|
|
64
|
+
message: "jobs:query:consumers requires ctx.systemDb (feature must declare r.systemScope())",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const db = ctx.systemDb.unsafeRaw("event consumer state is global dispatcher state, not tenant data");
|
|
68
|
+
const progress = await getAllConsumerProgress(db, registeredConsumerNames(ctx.registry));
|
|
69
|
+
const statuses = query.payload.filters?.[0]?.value;
|
|
70
|
+
const visible = statuses
|
|
71
|
+
? progress.filter((c) => statuses.some((s) => s === c.status))
|
|
72
|
+
: progress;
|
|
73
|
+
const direction = query.payload.sortDirection === "desc" ? -1 : 1;
|
|
74
|
+
const sort = query.payload.sort;
|
|
75
|
+
const compare = (a, b) => {
|
|
76
|
+
if (sort === "name")
|
|
77
|
+
return a.name.localeCompare(b.name) * direction;
|
|
78
|
+
if (sort === "lag")
|
|
79
|
+
return Number(a.lag - b.lag) * direction;
|
|
80
|
+
if (sort === "attempts")
|
|
81
|
+
return (a.attempts - b.attempts) * direction;
|
|
82
|
+
const rank = (c) => isConsumerStatus(c.status) ? STATUS_RANK[c.status] : 5;
|
|
83
|
+
return (rank(a) - rank(b)) * direction || a.name.localeCompare(b.name);
|
|
84
|
+
};
|
|
85
|
+
return { rows: [...visible].sort(compare).map(toRow), nextCursor: null };
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
export const consumerDetailQuery = defineQueryHandler({
|
|
89
|
+
name: "consumer-details",
|
|
90
|
+
description: "Returns one event consumer with its status, lag, attempts, the full last error and the time of its last state change; use it to decide between restart, skip and pause.",
|
|
91
|
+
schema: z.object({ consumerId: z.string().min(1) }),
|
|
92
|
+
access: { roles: ["SystemAdmin"] },
|
|
93
|
+
handler: async (query, ctx) => {
|
|
94
|
+
if (!ctx.systemDb) {
|
|
95
|
+
throw new InternalError({
|
|
96
|
+
message: "jobs:query:consumer-details requires ctx.systemDb (feature must declare r.systemScope())",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const target = splitConsumerRowId(query.payload.consumerId);
|
|
100
|
+
if (!target)
|
|
101
|
+
return null;
|
|
102
|
+
const db = ctx.systemDb.unsafeRaw("event consumer state is global dispatcher state, not tenant data");
|
|
103
|
+
const all = await getAllConsumerProgress(db, registeredConsumerNames(ctx.registry));
|
|
104
|
+
const progress = all.find((c) => c.name === target.name && c.instanceId === target.instanceId);
|
|
105
|
+
if (!progress)
|
|
106
|
+
return null;
|
|
107
|
+
const state = await getConsumerState(db, target.name, target.instanceId);
|
|
108
|
+
return {
|
|
109
|
+
...toRow(progress),
|
|
110
|
+
lastError: progress.lastError,
|
|
111
|
+
updatedAt: state ? state.updatedAt.toString() : null,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
});
|
|
@@ -2,119 +2,19 @@ import { type EntityTableMeta, type WritableTable } from "@cosmicdrift/kumiko-fr
|
|
|
2
2
|
export type JobRunStatus = "queued" | "running" | "completed" | "failed";
|
|
3
3
|
export type JobLogLevel = "info" | "warn" | "error";
|
|
4
4
|
export declare const jobRunEntity: import("@cosmicdrift/kumiko-types/fields").EntityDefinition<{
|
|
5
|
-
readonly jobName: {
|
|
6
|
-
readonly type: "text";
|
|
7
|
-
readonly description?: string;
|
|
8
|
-
readonly maxLength?: number;
|
|
9
|
-
readonly minLength?: number;
|
|
10
|
-
readonly autoComplete?: string;
|
|
11
|
-
readonly required?: boolean;
|
|
12
|
-
readonly searchable?: boolean;
|
|
13
|
-
readonly sortable?: boolean;
|
|
14
|
-
readonly filterable?: boolean;
|
|
15
|
-
readonly encrypted?: boolean;
|
|
16
|
-
readonly sensitive?: boolean;
|
|
17
|
-
readonly writeOnly?: boolean;
|
|
18
|
-
readonly readAsInstruction?: boolean;
|
|
19
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
20
|
-
readonly default?: string;
|
|
21
|
-
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
22
|
-
readonly multiline?: boolean | {
|
|
23
|
-
readonly rows?: number;
|
|
24
|
-
};
|
|
25
|
-
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
5
|
+
readonly jobName: import("@cosmicdrift/kumiko-types/fields").EntityTextFieldDef & {
|
|
26
6
|
required: true;
|
|
27
7
|
};
|
|
28
|
-
readonly bullJobId: {
|
|
29
|
-
readonly type: "text";
|
|
30
|
-
readonly description?: string;
|
|
31
|
-
readonly maxLength?: number;
|
|
32
|
-
readonly minLength?: number;
|
|
33
|
-
readonly autoComplete?: string;
|
|
34
|
-
readonly required?: boolean;
|
|
35
|
-
readonly searchable?: boolean;
|
|
36
|
-
readonly sortable?: boolean;
|
|
37
|
-
readonly filterable?: boolean;
|
|
38
|
-
readonly encrypted?: boolean;
|
|
39
|
-
readonly sensitive?: boolean;
|
|
40
|
-
readonly writeOnly?: boolean;
|
|
41
|
-
readonly readAsInstruction?: boolean;
|
|
42
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
43
|
-
readonly default?: string;
|
|
44
|
-
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
45
|
-
readonly multiline?: boolean | {
|
|
46
|
-
readonly rows?: number;
|
|
47
|
-
};
|
|
48
|
-
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
8
|
+
readonly bullJobId: import("@cosmicdrift/kumiko-types/fields").EntityTextFieldDef & {
|
|
49
9
|
required: true;
|
|
50
10
|
};
|
|
51
|
-
readonly status: {
|
|
52
|
-
readonly type: "text";
|
|
53
|
-
readonly description?: string;
|
|
54
|
-
readonly maxLength?: number;
|
|
55
|
-
readonly minLength?: number;
|
|
56
|
-
readonly autoComplete?: string;
|
|
57
|
-
readonly required?: boolean;
|
|
58
|
-
readonly searchable?: boolean;
|
|
59
|
-
readonly sortable?: boolean;
|
|
60
|
-
readonly filterable?: boolean;
|
|
61
|
-
readonly encrypted?: boolean;
|
|
62
|
-
readonly sensitive?: boolean;
|
|
63
|
-
readonly writeOnly?: boolean;
|
|
64
|
-
readonly readAsInstruction?: boolean;
|
|
65
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
66
|
-
readonly default?: string;
|
|
67
|
-
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
68
|
-
readonly multiline?: boolean | {
|
|
69
|
-
readonly rows?: number;
|
|
70
|
-
};
|
|
71
|
-
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
11
|
+
readonly status: import("@cosmicdrift/kumiko-types/fields").EntityTextFieldDef & {
|
|
72
12
|
required: true;
|
|
73
13
|
};
|
|
74
|
-
readonly payload: {
|
|
75
|
-
readonly type: "text";
|
|
76
|
-
readonly description?: string;
|
|
77
|
-
readonly maxLength?: number;
|
|
78
|
-
readonly minLength?: number;
|
|
79
|
-
readonly autoComplete?: string;
|
|
80
|
-
readonly required?: boolean;
|
|
81
|
-
readonly searchable?: boolean;
|
|
82
|
-
readonly sortable?: boolean;
|
|
83
|
-
readonly filterable?: boolean;
|
|
84
|
-
readonly encrypted?: boolean;
|
|
85
|
-
readonly sensitive?: boolean;
|
|
86
|
-
readonly writeOnly?: boolean;
|
|
87
|
-
readonly readAsInstruction?: boolean;
|
|
88
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
89
|
-
readonly default?: string;
|
|
90
|
-
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
91
|
-
readonly multiline?: boolean | {
|
|
92
|
-
readonly rows?: number;
|
|
93
|
-
};
|
|
94
|
-
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
14
|
+
readonly payload: import("@cosmicdrift/kumiko-types/fields").EntityTextFieldDef & {
|
|
95
15
|
required: false;
|
|
96
16
|
};
|
|
97
|
-
readonly error: {
|
|
98
|
-
readonly type: "text";
|
|
99
|
-
readonly description?: string;
|
|
100
|
-
readonly maxLength?: number;
|
|
101
|
-
readonly minLength?: number;
|
|
102
|
-
readonly autoComplete?: string;
|
|
103
|
-
readonly required?: boolean;
|
|
104
|
-
readonly searchable?: boolean;
|
|
105
|
-
readonly sortable?: boolean;
|
|
106
|
-
readonly filterable?: boolean;
|
|
107
|
-
readonly encrypted?: boolean;
|
|
108
|
-
readonly sensitive?: boolean;
|
|
109
|
-
readonly writeOnly?: boolean;
|
|
110
|
-
readonly readAsInstruction?: boolean;
|
|
111
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
112
|
-
readonly default?: string;
|
|
113
|
-
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
114
|
-
readonly multiline?: boolean | {
|
|
115
|
-
readonly rows?: number;
|
|
116
|
-
};
|
|
117
|
-
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
17
|
+
readonly error: import("@cosmicdrift/kumiko-types/fields").EntityTextFieldDef & {
|
|
118
18
|
required: false;
|
|
119
19
|
};
|
|
120
20
|
readonly attempt: {
|
|
@@ -185,27 +85,7 @@ export declare const jobRunEntity: import("@cosmicdrift/kumiko-types/fields").En
|
|
|
185
85
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
186
86
|
required: false;
|
|
187
87
|
};
|
|
188
|
-
readonly triggeredById: {
|
|
189
|
-
readonly type: "text";
|
|
190
|
-
readonly description?: string;
|
|
191
|
-
readonly maxLength?: number;
|
|
192
|
-
readonly minLength?: number;
|
|
193
|
-
readonly autoComplete?: string;
|
|
194
|
-
readonly required?: boolean;
|
|
195
|
-
readonly searchable?: boolean;
|
|
196
|
-
readonly sortable?: boolean;
|
|
197
|
-
readonly filterable?: boolean;
|
|
198
|
-
readonly encrypted?: boolean;
|
|
199
|
-
readonly sensitive?: boolean;
|
|
200
|
-
readonly writeOnly?: boolean;
|
|
201
|
-
readonly readAsInstruction?: boolean;
|
|
202
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
203
|
-
readonly default?: string;
|
|
204
|
-
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
205
|
-
readonly multiline?: boolean | {
|
|
206
|
-
readonly rows?: number;
|
|
207
|
-
};
|
|
208
|
-
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
88
|
+
readonly triggeredById: import("@cosmicdrift/kumiko-types/fields").EntityTextFieldDef & {
|
|
209
89
|
required: false;
|
|
210
90
|
};
|
|
211
91
|
}, "tenant">;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ActionFormScreenDefinition, type ProjectionDetailScreenDefinition, type ProjectionListScreenDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
export declare const jobRunsScreen: ProjectionListScreenDefinition;
|
|
3
|
+
export declare const jobTriggerScreen: ActionFormScreenDefinition;
|
|
4
|
+
export declare const jobRunDetailScreen: ProjectionDetailScreenDefinition;
|
|
5
|
+
export declare const eventConsumersScreen: ProjectionListScreenDefinition;
|
|
6
|
+
export declare const eventConsumerDetailScreen: ProjectionDetailScreenDefinition;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { i18nKey, } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { CONSUMER_INSTANCE_OPTION_KEY_PREFIX, CONSUMER_STATUS_OPTION_KEY_PREFIX, CONSUMER_STATUSES, EVENT_CONSUMER_DETAIL_SCREEN_ID, EVENT_CONSUMERS_SCREEN_ID, JOB_RUN_DETAIL_SCREEN_ID, JOB_RUNS_SCREEN_ID, JobHandlers, JobQueries, } from "./web/constants.js";
|
|
3
|
+
const systemAdminAccess = { roles: ["SystemAdmin"] };
|
|
4
|
+
export const jobRunsScreen = {
|
|
5
|
+
id: JOB_RUNS_SCREEN_ID,
|
|
6
|
+
type: "projectionList",
|
|
7
|
+
query: JobQueries.list,
|
|
8
|
+
columns: [
|
|
9
|
+
{ field: "jobName", label: i18nKey("jobs.runs.col.job") },
|
|
10
|
+
{ field: "status", label: i18nKey("jobs.runs.col.status") },
|
|
11
|
+
{
|
|
12
|
+
field: "startedAt",
|
|
13
|
+
label: i18nKey("jobs.runs.col.started"),
|
|
14
|
+
renderer: { format: "timestamp" },
|
|
15
|
+
},
|
|
16
|
+
{ field: "duration", label: i18nKey("jobs.runs.col.duration") },
|
|
17
|
+
],
|
|
18
|
+
defaultSort: { field: "startedAt", dir: "desc" },
|
|
19
|
+
facets: [
|
|
20
|
+
{
|
|
21
|
+
field: "status",
|
|
22
|
+
type: "select",
|
|
23
|
+
label: i18nKey("jobs.runs.filter.status"),
|
|
24
|
+
options: [
|
|
25
|
+
{ value: "queued", label: i18nKey("jobs.runs.filter.queued") },
|
|
26
|
+
{ value: "running", label: i18nKey("jobs.runs.filter.running") },
|
|
27
|
+
{ value: "completed", label: i18nKey("jobs.runs.filter.completed") },
|
|
28
|
+
{ value: "failed", label: i18nKey("jobs.runs.filter.failed") },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
rowActions: [
|
|
33
|
+
{
|
|
34
|
+
kind: "navigate",
|
|
35
|
+
id: "open",
|
|
36
|
+
label: i18nKey("jobs.runs.open"),
|
|
37
|
+
screen: JOB_RUN_DETAIL_SCREEN_ID,
|
|
38
|
+
entityId: "id",
|
|
39
|
+
rowClick: true,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
toolbarActions: [
|
|
43
|
+
{
|
|
44
|
+
kind: "drawer",
|
|
45
|
+
id: "trigger",
|
|
46
|
+
label: i18nKey("jobs.trigger.title"),
|
|
47
|
+
screen: "job-trigger",
|
|
48
|
+
style: "primary",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
pagination: "infinite",
|
|
52
|
+
access: systemAdminAccess,
|
|
53
|
+
};
|
|
54
|
+
export const jobTriggerScreen = {
|
|
55
|
+
id: "job-trigger",
|
|
56
|
+
type: "actionForm",
|
|
57
|
+
handler: JobHandlers.trigger,
|
|
58
|
+
fields: {
|
|
59
|
+
jobName: { type: "text", required: true },
|
|
60
|
+
payload: { type: "longText", multiline: true, default: "{}" },
|
|
61
|
+
},
|
|
62
|
+
layout: { sections: [{ fields: ["jobName", "payload"] }] },
|
|
63
|
+
submitLabel: i18nKey("jobs.trigger.submit"),
|
|
64
|
+
access: systemAdminAccess,
|
|
65
|
+
description: "jobs.screen.job-trigger.subtitle",
|
|
66
|
+
};
|
|
67
|
+
export const jobRunDetailScreen = {
|
|
68
|
+
id: JOB_RUN_DETAIL_SCREEN_ID,
|
|
69
|
+
type: "projectionDetail",
|
|
70
|
+
query: JobQueries.details,
|
|
71
|
+
idParam: "runId",
|
|
72
|
+
fieldLabels: {
|
|
73
|
+
jobName: i18nKey("jobs.detail.field.job"),
|
|
74
|
+
status: i18nKey("jobs.detail.field.status"),
|
|
75
|
+
id: i18nKey("jobs.detail.field.id"),
|
|
76
|
+
startedAt: i18nKey("jobs.detail.field.started"),
|
|
77
|
+
finishedAt: i18nKey("jobs.detail.field.finished"),
|
|
78
|
+
duration: i18nKey("jobs.detail.field.duration"),
|
|
79
|
+
error: i18nKey("jobs.detail.field.error"),
|
|
80
|
+
logs: i18nKey("jobs.detail.logs"),
|
|
81
|
+
},
|
|
82
|
+
layout: {
|
|
83
|
+
sections: [
|
|
84
|
+
{
|
|
85
|
+
fields: [
|
|
86
|
+
"jobName",
|
|
87
|
+
"status",
|
|
88
|
+
"id",
|
|
89
|
+
{ field: "startedAt", renderer: { format: "timestamp" } },
|
|
90
|
+
{ field: "finishedAt", renderer: { format: "timestamp" } },
|
|
91
|
+
"duration",
|
|
92
|
+
"error",
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
// No section `title` here (fw#2312 label-dedup fix): the section
|
|
96
|
+
// holds exactly one field, so a title would repeat the field's own
|
|
97
|
+
// label (rendered by RenderField as the Field's heading) verbatim.
|
|
98
|
+
{
|
|
99
|
+
fields: [{ field: "logs", renderer: { format: "json" } }],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
actions: [
|
|
104
|
+
{
|
|
105
|
+
kind: "writeHandler",
|
|
106
|
+
id: "retry",
|
|
107
|
+
label: i18nKey("jobs.detail.retry"),
|
|
108
|
+
handler: JobHandlers.retry,
|
|
109
|
+
payload: { map: { runId: "id" } },
|
|
110
|
+
visible: { field: "status", eq: "failed" },
|
|
111
|
+
style: "primary",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
description: "jobs.screen.job-run-detail.subtitle",
|
|
115
|
+
listScreenId: JOB_RUNS_SCREEN_ID,
|
|
116
|
+
access: systemAdminAccess,
|
|
117
|
+
};
|
|
118
|
+
export const eventConsumersScreen = {
|
|
119
|
+
id: EVENT_CONSUMERS_SCREEN_ID,
|
|
120
|
+
type: "projectionList",
|
|
121
|
+
query: JobQueries.consumers,
|
|
122
|
+
columns: [
|
|
123
|
+
{ field: "name", label: i18nKey("jobs.consumers.col.name") },
|
|
124
|
+
{
|
|
125
|
+
field: "status",
|
|
126
|
+
label: i18nKey("jobs.consumers.col.status"),
|
|
127
|
+
renderer: { format: "enumOption", keyPrefix: CONSUMER_STATUS_OPTION_KEY_PREFIX },
|
|
128
|
+
},
|
|
129
|
+
{ field: "lag", label: i18nKey("jobs.consumers.col.lag"), hideOnNarrow: true },
|
|
130
|
+
{ field: "attempts", label: i18nKey("jobs.consumers.col.attempts"), hideOnNarrow: true },
|
|
131
|
+
{ field: "lastError", label: i18nKey("jobs.consumers.col.lastError") },
|
|
132
|
+
],
|
|
133
|
+
defaultSort: { field: "status", dir: "asc" },
|
|
134
|
+
facets: [
|
|
135
|
+
{
|
|
136
|
+
field: "status",
|
|
137
|
+
type: "select",
|
|
138
|
+
label: i18nKey("jobs.consumers.col.status"),
|
|
139
|
+
options: CONSUMER_STATUSES.map((status) => ({
|
|
140
|
+
value: status,
|
|
141
|
+
label: i18nKey(`${CONSUMER_STATUS_OPTION_KEY_PREFIX}${status}`),
|
|
142
|
+
})),
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
rowActions: [
|
|
146
|
+
{
|
|
147
|
+
kind: "navigate",
|
|
148
|
+
id: "open",
|
|
149
|
+
label: i18nKey("jobs.runs.open"),
|
|
150
|
+
screen: EVENT_CONSUMER_DETAIL_SCREEN_ID,
|
|
151
|
+
entityId: "id",
|
|
152
|
+
rowClick: true,
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
pagination: "infinite",
|
|
156
|
+
access: systemAdminAccess,
|
|
157
|
+
description: "jobs.screen.event-consumers.subtitle",
|
|
158
|
+
};
|
|
159
|
+
export const eventConsumerDetailScreen = {
|
|
160
|
+
id: EVENT_CONSUMER_DETAIL_SCREEN_ID,
|
|
161
|
+
type: "projectionDetail",
|
|
162
|
+
query: JobQueries.consumerDetails,
|
|
163
|
+
idParam: "consumerId",
|
|
164
|
+
fieldLabels: {
|
|
165
|
+
name: i18nKey("jobs.consumers.col.name"),
|
|
166
|
+
instanceId: i18nKey("jobs.consumers.field.instanceId"),
|
|
167
|
+
status: i18nKey("jobs.consumers.col.status"),
|
|
168
|
+
lag: i18nKey("jobs.consumers.col.lag"),
|
|
169
|
+
attempts: i18nKey("jobs.consumers.col.attempts"),
|
|
170
|
+
lastError: i18nKey("jobs.consumers.col.lastError"),
|
|
171
|
+
updatedAt: i18nKey("jobs.consumers.field.updatedAt"),
|
|
172
|
+
},
|
|
173
|
+
layout: {
|
|
174
|
+
sections: [
|
|
175
|
+
{
|
|
176
|
+
fields: [
|
|
177
|
+
"name",
|
|
178
|
+
{
|
|
179
|
+
field: "instanceId",
|
|
180
|
+
renderer: { format: "enumOption", keyPrefix: CONSUMER_INSTANCE_OPTION_KEY_PREFIX },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
field: "status",
|
|
184
|
+
renderer: { format: "enumOption", keyPrefix: CONSUMER_STATUS_OPTION_KEY_PREFIX },
|
|
185
|
+
},
|
|
186
|
+
"lag",
|
|
187
|
+
"attempts",
|
|
188
|
+
{ field: "updatedAt", renderer: { format: "timestamp" } },
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
{ fields: ["lastError"] },
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
actions: [
|
|
195
|
+
{
|
|
196
|
+
kind: "writeHandler",
|
|
197
|
+
id: "restart",
|
|
198
|
+
icon: "refresh",
|
|
199
|
+
label: i18nKey("jobs.consumers.restart"),
|
|
200
|
+
handler: JobHandlers.consumerRestart,
|
|
201
|
+
payload: { map: { name: "name", instanceId: "instanceId" } },
|
|
202
|
+
visible: { field: "status", eq: "dead" },
|
|
203
|
+
style: "primary",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
kind: "writeHandler",
|
|
207
|
+
id: "skip",
|
|
208
|
+
icon: "arrow-right",
|
|
209
|
+
label: i18nKey("jobs.consumers.skip"),
|
|
210
|
+
handler: JobHandlers.consumerSkip,
|
|
211
|
+
payload: { map: { name: "name", instanceId: "instanceId" } },
|
|
212
|
+
visible: { field: "status", eq: "dead" },
|
|
213
|
+
confirm: i18nKey("jobs.consumers.skip.confirm"),
|
|
214
|
+
style: "danger",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
kind: "writeHandler",
|
|
218
|
+
id: "disable",
|
|
219
|
+
icon: "x",
|
|
220
|
+
label: i18nKey("jobs.consumers.disable"),
|
|
221
|
+
handler: JobHandlers.consumerDisable,
|
|
222
|
+
payload: { map: { name: "name", instanceId: "instanceId" } },
|
|
223
|
+
visible: { field: "status", ne: "disabled" },
|
|
224
|
+
style: "secondary",
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
kind: "writeHandler",
|
|
228
|
+
id: "enable",
|
|
229
|
+
icon: "check",
|
|
230
|
+
label: i18nKey("jobs.consumers.enable"),
|
|
231
|
+
handler: JobHandlers.consumerEnable,
|
|
232
|
+
payload: { map: { name: "name", instanceId: "instanceId" } },
|
|
233
|
+
visible: { field: "status", eq: "disabled" },
|
|
234
|
+
style: "primary",
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
primaryAction: "restart",
|
|
238
|
+
description: "jobs.screen.event-consumer-detail.subtitle",
|
|
239
|
+
listScreenId: EVENT_CONSUMERS_SCREEN_ID,
|
|
240
|
+
access: systemAdminAccess,
|
|
241
|
+
};
|