-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcds-plugin.ts
More file actions
126 lines (110 loc) · 4.19 KB
/
cds-plugin.ts
File metadata and controls
126 lines (110 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import cds, { Results } from '@sap/cds';
import { EntityEventCache } from './types/cds-plugin';
import {
addDeletedEntityToRequest,
handleProcessCancel,
handleProcessResume,
handleProcessStart,
handleProcessSuspend,
ProcessValidationPlugin,
registerProcessServiceHandlers,
PROCESS_START_ID,
PROCESS_START_ON,
PROCESS_CANCEL_ON,
PROCESS_SUSPEND_ON,
PROCESS_RESUME_ON,
PROCESS_PREFIX,
CUD_EVENTS,
} from './lib/index';
import { importProcess } from './lib/processImport';
// Register build plugin for annotation validation during cds build
cds.build?.register?.('process-validation', ProcessValidationPlugin);
// Register import handler for: cds import --from process
// @ts-expect-error: import does not exist on cds type
cds.import ??= {};
//@ts-expect-error: cds type does not exist
cds.import.options ??= {};
//@ts-expect-error: cds type does not exist
cds.import.options.process = { no_copy: true, as: 'cds', config: 'kind=rest' };
// until above is not released for cds-dk, use:
// cds-tsx import --from process ./workflows/eu12.bpm-horizon-walkme.sdshipmentprocessor.shipmentHandler.json --force --config kind=rest --no-copy --as cds
// @ts-expect-error: process does not exist on cds.import type
cds.import.from ??= {};
// @ts-expect-error: from does not exist on cds.import type
cds.import.from.process = importProcess;
cds.on('serving', async (service: cds.Service) => {
if (service instanceof cds.ApplicationService == false) return;
const annotationCache = buildAnnotationCache(service);
service.before('DELETE', async (req: cds.Request) => {
const cacheKey = `${req.target.name}:${req.event}`;
const cached = annotationCache.get(cacheKey);
if (!cached) return; // Fast exit - no annotations
if (cached.hasCancel || cached.hasSuspend || cached.hasStart || cached.hasResume) {
await addDeletedEntityToRequest(req, cached.hasStart);
}
});
service.after('*', async (each: Results, req: cds.Request) => {
if (!req.target) return;
const cacheKey = `${req.target.name}:${req.event}`;
const cached = annotationCache.get(cacheKey);
if (!cached) return; // Fast exit - no annotations
if (cached.hasStart) {
await handleProcessStart(req);
}
if (cached.hasCancel) {
await handleProcessCancel(req);
}
if (cached.hasSuspend) {
await handleProcessSuspend(req);
}
if (cached.hasResume) {
await handleProcessResume(req);
}
});
});
function expandEvent(event: string | undefined, entity: cds.entity): string[] {
if (!event) return [];
if (event === '*') {
const boundActions = entity.actions ? Object.keys(entity.actions) : [];
return [...CUD_EVENTS, ...boundActions];
}
return [event];
}
function buildAnnotationCache(service: cds.Service) {
const cache = new Map<string, EntityEventCache>();
for (const entity of Object.values(service.entities)) {
const startEvent = entity[PROCESS_START_ON];
const cancelEvent = entity[PROCESS_CANCEL_ON];
const suspendEvent = entity[PROCESS_SUSPEND_ON];
const resumeEvent = entity[PROCESS_RESUME_ON];
const events = new Set<string>();
for (const ev of expandEvent(startEvent, entity)) events.add(ev);
for (const ev of expandEvent(cancelEvent, entity)) events.add(ev);
for (const ev of expandEvent(suspendEvent, entity)) events.add(ev);
for (const ev of expandEvent(resumeEvent, entity)) events.add(ev);
for (const event of events) {
const matchesEvent = (annotationEvent: string | undefined) =>
annotationEvent === event || annotationEvent === '*';
const hasStart = !!(matchesEvent(startEvent) && entity[PROCESS_START_ID]);
const hasCancel = !!matchesEvent(cancelEvent);
const hasSuspend = !!matchesEvent(suspendEvent);
const hasResume = !!matchesEvent(resumeEvent);
const cacheKey = `${entity.name}:${event}`;
cache.set(cacheKey, {
hasStart,
hasCancel,
hasSuspend,
hasResume,
});
}
}
return cache;
}
cds.on('served', async (services) => {
const processServices = Object.values(services).filter(
(service) => service.definition?.[PROCESS_PREFIX],
);
for (const service of processServices) {
registerProcessServiceHandlers(service);
}
});