@cap-js/audit-logging 1.2.0 → 1.2.2

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/lib/utils.js CHANGED
@@ -1,278 +1,304 @@
1
- const cds = require('@sap/cds')
1
+ const cds = require("@sap/cds");
2
+ const LOG = cds.log("audit-log");
2
3
 
3
- const { Relation, exposeRelation, relationHandler } = require('./_relation')
4
+ const { Relation, exposeRelation, relationHandler } = require("./_relation");
4
5
 
5
- const WRITE = { CREATE: 1, UPDATE: 1, DELETE: 1 }
6
+ const WRITE = { CREATE: 1, UPDATE: 1, DELETE: 1 };
6
7
 
7
- const $hasPersonalData = Symbol('@cap-js/audit-logging:hasPersonalData')
8
- const $dataSubject = Symbol('@cap-js/audit-logging:dataSubject')
9
- const $parents = Symbol('@cap-js/audit-logging:parents')
10
- const $visitedUp = Symbol('@cap-js/audit-logging:visitedUp')
11
- const $visitedDown = Symbol('@cap-js/audit-logging:visitedDown')
8
+ const $hasPersonalData = Symbol("@cap-js/audit-logging:hasPersonalData");
9
+ const $dataSubject = Symbol("@cap-js/audit-logging:dataSubject");
10
+ const $parents = Symbol("@cap-js/audit-logging:parents");
11
+ const $visitedUp = Symbol("@cap-js/audit-logging:visitedUp");
12
+ const $visitedDown = Symbol("@cap-js/audit-logging:visitedDown");
12
13
 
13
- const hasPersonalData = entity => {
14
+ const hasPersonalData = (entity) => {
14
15
  if (entity.own($hasPersonalData) == null) {
15
- if (!entity['@PersonalData.EntitySemantics']) entity.set($hasPersonalData, false)
16
+ if (!entity["@PersonalData.EntitySemantics"]) entity.set($hasPersonalData, false);
16
17
  else {
17
18
  // default role to entity name
18
- if (entity['@PersonalData.EntitySemantics'] === 'DataSubject' && !entity['@PersonalData.DataSubjectRole'])
19
- entity['@PersonalData.DataSubjectRole'] = entity.name.match(/\w+/g).pop()
19
+ if (
20
+ entity["@PersonalData.EntitySemantics"] === "DataSubject" &&
21
+ !entity["@PersonalData.DataSubjectRole"]
22
+ )
23
+ entity["@PersonalData.DataSubjectRole"] = entity.name.match(/\w+/g).pop();
20
24
  // prettier-ignore
21
25
  const hasPersonalData = !!Object.values(entity.elements).some(element =>
22
26
  element['@PersonalData.IsPotentiallyPersonal'] ||
23
27
  element['@PersonalData.IsPotentiallySensitive'] ||
24
28
  (element['@PersonalData.FieldSemantics'] && element['@PersonalData.FieldSemantics'] === 'DataSubjectID'))
25
- entity.set($hasPersonalData, hasPersonalData)
29
+ entity.set($hasPersonalData, hasPersonalData);
26
30
  }
27
31
  }
28
- return entity.own($hasPersonalData)
29
- }
32
+ return entity.own($hasPersonalData);
33
+ };
30
34
 
31
- const getMapKeyForCurrentRequest = req => {
35
+ const getMapKeyForCurrentRequest = (req) => {
32
36
  // running in srv or db layer? -> srv's req.query used as key of diff and logs maps at req.context
33
37
  // REVISIT: req._tx should not be used like that!
34
- return req.tx.isDatabaseService ? req._.query : req.query
35
- }
38
+ return req.tx.isDatabaseService ? req._.query : req.query;
39
+ };
36
40
 
37
- const getRootEntity = element => {
38
- let entity = element.parent
39
- while (entity.kind !== 'entity') entity = entity.parent
40
- return entity
41
- }
41
+ const getRootEntity = (element) => {
42
+ let entity = element.parent;
43
+ while (entity.kind !== "entity") entity = entity.parent;
44
+ return entity;
45
+ };
42
46
 
43
47
  const _isDataSubject = (element, target) => {
44
48
  return (
45
49
  !element.isAssociation &&
46
- element['@PersonalData.FieldSemantics'] === 'DataSubjectID' &&
47
- target['@PersonalData.EntitySemantics'] === 'DataSubject'
48
- )
49
- }
50
+ element["@PersonalData.FieldSemantics"] === "DataSubjectID" &&
51
+ target["@PersonalData.EntitySemantics"] === "DataSubject"
52
+ );
53
+ };
50
54
 
51
- const getPick = event => {
55
+ const getPick = (event) => {
52
56
  return (element, target) => {
53
- if (!hasPersonalData(target)) return
54
- const categories = []
55
- if (!element.isAssociation && element.key) categories.push('ObjectID')
56
- if (_isDataSubject(element, target)) categories.push('DataSubjectID')
57
- if (event in WRITE && element['@PersonalData.IsPotentiallyPersonal']) categories.push('IsPotentiallyPersonal')
58
- if (element['@PersonalData.IsPotentiallySensitive']) categories.push('IsPotentiallySensitive')
59
- if (categories.length) return { categories }
60
- }
61
- }
57
+ if (!hasPersonalData(target)) return;
58
+ const categories = [];
59
+ if (!element.isAssociation && element.key) categories.push("ObjectID");
60
+ if (_isDataSubject(element, target)) categories.push("DataSubjectID");
61
+ if (event in WRITE && element["@PersonalData.IsPotentiallyPersonal"])
62
+ categories.push("IsPotentiallyPersonal");
63
+ if (element["@PersonalData.IsPotentiallySensitive"]) categories.push("IsPotentiallySensitive");
64
+ if (categories.length) return { categories };
65
+ };
66
+ };
62
67
 
63
68
  const _getHash = (entity, row) => {
64
69
  return `${entity.name}(${Object.keys(entity.keys)
65
- .map(k => `${k}=${row[k]}`)
66
- .join(',')})`
67
- }
70
+ .map((k) => `${k}=${row[k]}`)
71
+ .join(",")})`;
72
+ };
68
73
 
69
74
  const createLogEntry = (logs, entity, row) => {
70
- const hash = _getHash(entity, row)
71
- let log = logs[hash]
75
+ const hash = _getHash(entity, row);
76
+ let log = logs[hash];
72
77
  if (!log) {
73
78
  logs[hash] = {
74
- data_subject: { id: {}, role: entity['@PersonalData.DataSubjectRole'] },
79
+ data_subject: { id: {}, role: entity["@PersonalData.DataSubjectRole"] },
75
80
  object: { type: entity.name, id: {} },
76
81
  attributes: []
77
- }
78
- log = logs[hash]
82
+ };
83
+ log = logs[hash];
79
84
  }
80
- return log
81
- }
85
+ return log;
86
+ };
82
87
 
83
88
  const addObjectID = (log, row, key) => {
84
- if (!(key in log.object.id) && key !== 'IsActiveEntity') log.object.id[key] = row[key] || row._old?.[key]
85
- }
89
+ if (!(key in log.object.id) && key !== "IsActiveEntity")
90
+ log.object.id[key] = row[key] || row._old?.[key];
91
+ };
86
92
 
87
93
  const addDataSubject = (log, row, key, entity) => {
88
- if (!log.data_subject.type) log.data_subject.type = entity.name
94
+ if (!log.data_subject.type) log.data_subject.type = entity.name;
89
95
  if (!(key in log.data_subject.id)) {
90
- const value = row[key] || row._old?.[key]
91
- log.data_subject.id[key] = value
96
+ const value = row[key] || row._old?.[key];
97
+ log.data_subject.id[key] = value;
92
98
  }
93
- }
99
+ };
94
100
 
95
101
  const _addKeysToWhere = (keys, row, alias) => {
96
102
  return keys
97
- .filter(key => !key.isAssociation && key.name !== 'IsActiveEntity')
103
+ .filter((key) => !key.isAssociation && key.name !== "IsActiveEntity")
98
104
  .reduce((keys, key) => {
99
- if (keys.length) keys.push('and')
100
- keys.push({ ref: [alias, key.name] }, '=', { val: row[key.name] || row._old?.[key.name] })
101
- return keys
102
- }, [])
103
- }
105
+ if (keys.length) keys.push("and");
106
+ keys.push({ ref: [alias, key.name] }, "=", { val: row[key.name] || row._old?.[key.name] });
107
+ return keys;
108
+ }, []);
109
+ };
104
110
 
105
111
  const _keyColumns = (keys, alias) => {
106
112
  return keys
107
- .filter(key => !key.isAssociation && key.name !== 'IsActiveEntity')
108
- .map(key => ({ ref: [alias, key.name] }))
109
- }
113
+ .filter((key) => !key.isAssociation && key.name !== "IsActiveEntity")
114
+ .map((key) => ({ ref: [alias, key.name] }));
115
+ };
110
116
 
111
- const _alias = entity => {
117
+ const _alias = (entity) => {
112
118
  // REVISIT: we should not rely on entity._service (but I don't want to break existing behavior right now)
113
- if (!entity._service) return `${entity}`
114
- return entity.name.replace(`${entity._service.name}.`, '').replace(/\./g, '_')
115
- }
119
+ if (!entity._service) return `${entity}`;
120
+ return entity.name.replace(`${entity._service.name}.`, "").replace(/\./g, "_");
121
+ };
116
122
 
117
123
  const _buildSubSelect = (model, { entity, relative, element, next }, row, previousCqn) => {
118
124
  // relative is a parent or an entity itself
119
125
 
120
- const keys = Object.values(entity.keys)
126
+ const keys = Object.values(entity.keys);
121
127
 
122
- const entityName = entity.name
123
- const as = _alias(entity)
128
+ const entityName = entity.name;
129
+ const as = _alias(entity);
124
130
 
125
- const childCqn = SELECT.from({ ref: [entityName], as }).columns(_keyColumns(keys, as))
131
+ const childCqn = SELECT.from({ ref: [entityName], as }).columns(_keyColumns(keys, as));
126
132
 
127
- const targetAlias = _alias(element._target)
128
- const relativeAlias = _alias(relative)
133
+ const targetAlias = _alias(element._target);
134
+ const relativeAlias = _alias(relative);
129
135
 
130
136
  // REVISIT: there seems to be a caching issue in cds^9 when elemets are renamed
131
- if (!('_relations' in relative) || !relative._relations[element.name]) {
132
- const newRelation = Relation.to(relative)
133
- relative._relations = new Proxy(exposeRelation(newRelation), relationHandler(newRelation))
137
+ if (!("_relations" in relative) || !relative._relations[element.name]) {
138
+ const newRelation = Relation.to(relative);
139
+ relative._relations = new Proxy(exposeRelation(newRelation), relationHandler(newRelation));
134
140
  }
135
141
 
136
- let w = relative._relations[element.name].join(targetAlias, relativeAlias)
142
+ let w = relative._relations[element.name].join(targetAlias, relativeAlias);
137
143
 
138
144
  // REVISIT: rewrite to path expression, if alias for relative is already used in subselect to avoid sql error
139
145
  if (previousCqn?._aliases.has(relativeAlias)) {
140
- let t
141
- for (const a in entity.associations) if (entity.associations[a].target === relative.name) t = entity.associations[a]
142
- if (t && w[0]?.xpr) for (const ele of w[0].xpr) if (ele.ref?.[0] === relativeAlias) ele.ref.splice(0, 1, as, t.name)
146
+ let t;
147
+ for (const a in entity.associations)
148
+ if (entity.associations[a].target === relative.name) t = entity.associations[a];
149
+ if (t && w[0]?.xpr)
150
+ for (const ele of w[0].xpr)
151
+ if (ele.ref?.[0] === relativeAlias) ele.ref.splice(0, 1, as, t.name);
143
152
  }
144
- childCqn._aliases = new Set(previousCqn ? [...previousCqn._aliases.values(), as] : [as])
153
+ childCqn._aliases = new Set(previousCqn ? [...previousCqn._aliases.values(), as] : [as]);
145
154
 
146
- childCqn.where(w)
155
+ childCqn.where(w);
147
156
 
148
- if (previousCqn) childCqn.where('exists', previousCqn)
149
- else childCqn.where(_addKeysToWhere(keys, row, as))
157
+ if (previousCqn) childCqn.where("exists", previousCqn);
158
+ else childCqn.where(_addKeysToWhere(keys, row, as));
150
159
 
151
- if (next) return _buildSubSelect(model, next, {}, childCqn)
160
+ if (next) return _buildSubSelect(model, next, {}, childCqn);
152
161
 
153
- return childCqn
154
- }
162
+ return childCqn;
163
+ };
155
164
 
156
165
  const _getDataSubjectIdQuery = ({ dataSubjectEntity, subs }, row, model) => {
157
- const keys = Object.values(dataSubjectEntity.keys)
158
- const as = _alias(dataSubjectEntity)
166
+ const keys = Object.values(dataSubjectEntity.keys);
167
+ const as = _alias(dataSubjectEntity);
159
168
 
160
169
  const cqn = SELECT.one
161
170
  .from({ ref: [dataSubjectEntity.name], as })
162
171
  .columns(_keyColumns(keys, as))
163
- .where(['exists', _buildSubSelect(model, subs[0], row)])
172
+ .where(["exists", _buildSubSelect(model, subs[0], row)]);
164
173
 
165
174
  // entity reused in different branches => must check all
166
- for (let i = 1; i < subs.length; i++) cqn.or(['exists', _buildSubSelect(model, subs[i], row)])
175
+ for (let i = 1; i < subs.length; i++) cqn.or(["exists", _buildSubSelect(model, subs[i], row)]);
167
176
 
168
- return cqn
169
- }
177
+ return cqn;
178
+ };
170
179
 
171
180
  const _getUps = (entity, model) => {
172
181
  if (entity.own($parents) == null) {
173
- const ups = []
182
+ const ups = [];
174
183
  for (const def of Object.values(model.definitions)) {
175
- if (def.kind !== 'entity' || !def.associations) continue
184
+ if (def.kind !== "entity" || !def.associations) continue;
176
185
  for (const element of Object.values(def.associations)) {
177
- if (element.target !== entity.name || element._isBacklink || element.name === 'SiblingEntity') continue
178
- ups.push(element)
186
+ if (
187
+ element.target !== entity.name ||
188
+ element._isBacklink ||
189
+ element.name === "SiblingEntity"
190
+ )
191
+ continue;
192
+ ups.push(element);
179
193
  }
180
194
  }
181
- entity.set($parents, ups)
195
+ entity.set($parents, ups);
182
196
  }
183
- return entity.own($parents)
184
- }
197
+ return entity.own($parents);
198
+ };
185
199
 
186
200
  const _getDataSubjectUp = (root, model, entity, prev, next, result) => {
187
201
  for (const element of _getUps(entity, model)) {
188
202
  // cycle detection
189
- if (element.own($visitedUp) == null) element.set($visitedUp, new Set())
190
- if (element.own($visitedUp).has(root)) continue
191
- element.own($visitedUp).add(root)
192
-
193
- const me = { entity, relative: element.parent, element }
194
- if (prev) prev.next = me
195
- if (element.parent['@PersonalData.EntitySemantics'] === 'DataSubject') {
196
- if (!result) result = { dataSubjectEntity: element.parent, subs: [] }
197
- result.subs.push(next || me)
198
- return result
203
+ if (element.own($visitedUp) == null) element.set($visitedUp, new Set());
204
+ if (element.own($visitedUp).has(root)) continue;
205
+ element.own($visitedUp).add(root);
206
+
207
+ const me = { entity, relative: element.parent, element };
208
+ if (prev) prev.next = me;
209
+ if (element.parent["@PersonalData.EntitySemantics"] === "DataSubject") {
210
+ if (!result) result = { dataSubjectEntity: element.parent, subs: [] };
211
+ result.subs.push(next || me);
212
+ return result;
199
213
  } else {
200
214
  // dfs is a must here
201
- result = _getDataSubjectUp(root, model, element.parent, me, next || me, result)
215
+ result = _getDataSubjectUp(root, model, element.parent, me, next || me, result);
216
+ // If upward traversal dead-ends, try finding the DataSubject downward from the parent entity.
217
+ // This handles composition targets whose parent reaches a DataSubject via a sibling association
218
+ // (e.g., Attachments → up to Things → down via owner to Users).
219
+ if (!result) result = _getDataSubjectDown(root, element.parent, me, next || me);
202
220
  }
203
221
  }
204
- return result
205
- }
222
+ return result;
223
+ };
206
224
 
207
225
  const _getDataSubjectDown = (root, entity, prev, next) => {
208
- const associations = Object.values(entity.associations || {}).filter(e => !e._isBacklink)
226
+ const associations = Object.values(entity.associations || {}).filter((e) => !e._isBacklink);
209
227
  // bfs makes more sense here -> check all own assocs first before going deeper
210
228
  for (const element of associations) {
211
- const me = { entity, relative: entity, element }
212
- if (element._target['@PersonalData.EntitySemantics'] === 'DataSubject') {
213
- if (prev) prev.next = me
214
- return { dataSubjectEntity: element._target, subs: [next || me] }
229
+ const me = { entity, relative: entity, element };
230
+ if (element._target["@PersonalData.EntitySemantics"] === "DataSubject") {
231
+ if (prev) prev.next = me;
232
+ return { dataSubjectEntity: element._target, subs: [next || me] };
215
233
  }
216
234
  }
217
235
  for (const element of associations) {
218
236
  // cycle detection
219
- if (element.own($visitedDown) == null) element.set($visitedDown, new Set())
220
- if (element.own($visitedDown).has(root)) continue
221
- element.own($visitedDown).add(root)
222
-
223
- const me = { entity, relative: entity, element }
224
- if (prev) prev.next = me
225
- const dataSubject = _getDataSubjectDown(root, element._target, me, next || me)
226
- if (dataSubject) return dataSubject
237
+ if (element.own($visitedDown) == null) element.set($visitedDown, new Set());
238
+ if (element.own($visitedDown).has(root)) continue;
239
+ element.own($visitedDown).add(root);
240
+
241
+ const me = { entity, relative: entity, element };
242
+ if (prev) prev.next = me;
243
+ const dataSubject = _getDataSubjectDown(root, element._target, me, next || me);
244
+ if (dataSubject) return dataSubject;
227
245
  }
228
- }
246
+ };
229
247
 
230
248
  const getDataSubject = (entity, model) => {
231
249
  if (entity.own($dataSubject) == null) {
232
250
  // entities with EntitySemantics 'DataSubjectDetails' or 'Other' must not necessarily
233
251
  // be always below or always above 'DataSubject' entity in CSN tree
234
- let dataSubjectInfo = _getDataSubjectUp(entity.name, model, entity)
235
- if (!dataSubjectInfo) dataSubjectInfo = _getDataSubjectDown(entity.name, entity)
236
- entity.set($dataSubject, dataSubjectInfo)
252
+ let dataSubjectInfo = _getDataSubjectUp(entity.name, model, entity);
253
+ if (!dataSubjectInfo) dataSubjectInfo = _getDataSubjectDown(entity.name, entity);
254
+ entity.set($dataSubject, dataSubjectInfo || false);
237
255
  }
238
- return entity.own($dataSubject)
239
- }
256
+ return entity.own($dataSubject) || undefined;
257
+ };
240
258
 
241
- const _getDataSubjectsMap = req => {
242
- const mapKey = getMapKeyForCurrentRequest(req)
243
- const _audit = (req.context._audit ??= {})
244
- if (!_audit.dataSubjects) _audit.dataSubjects = new Map()
245
- if (!_audit.dataSubjects.has(mapKey)) _audit.dataSubjects.set(mapKey, new Map())
246
- return _audit.dataSubjects.get(mapKey)
247
- }
259
+ const _getDataSubjectsMap = (req) => {
260
+ const mapKey = getMapKeyForCurrentRequest(req);
261
+ const _audit = (req.context._audit ??= {});
262
+ if (!_audit.dataSubjects) _audit.dataSubjects = new Map();
263
+ if (!_audit.dataSubjects.has(mapKey)) _audit.dataSubjects.set(mapKey, new Map());
264
+ return _audit.dataSubjects.get(mapKey);
265
+ };
248
266
 
249
267
  const addDataSubjectForDetailsEntity = (row, log, req, entity, model) => {
250
- const dataSubjectInfo = getDataSubject(entity, model)
251
- const role = dataSubjectInfo.dataSubjectEntity['@PersonalData.DataSubjectRole']
252
- log.data_subject.role ??= role
253
- log.data_subject.type = dataSubjectInfo.dataSubjectEntity.name
268
+ const dataSubjectInfo = getDataSubject(entity, model);
269
+ if (!dataSubjectInfo) {
270
+ LOG.warn(
271
+ `No DataSubject entity reachable from '${entity.name}' annotated with @PersonalData.EntitySemantics: '${entity["@PersonalData.EntitySemantics"]}'. ` +
272
+ `Skipping data-subject enrichment for this entity. ` +
273
+ `Ensure a path to an entity annotated with @PersonalData.EntitySemantics: 'DataSubject' exists via associations.`
274
+ );
275
+ return;
276
+ }
277
+ const role = dataSubjectInfo.dataSubjectEntity["@PersonalData.DataSubjectRole"];
278
+ log.data_subject.role ??= role;
279
+ log.data_subject.type = dataSubjectInfo.dataSubjectEntity.name;
254
280
  /*
255
281
  * for each req (cf. $batch with atomicity) and data subject role (e.g., customer vs supplier),
256
282
  * store (in audit data structure at context) and reuse a single promise to look up the respective data subject
257
283
  */
258
- const map = _getDataSubjectsMap(req)
259
- if (map.has(role)) log.data_subject.id = map.get(role)
284
+ const map = _getDataSubjectsMap(req);
285
+ if (map.has(role)) log.data_subject.id = map.get(role);
260
286
  // REVISIT by downward lookups row might already contain ID - some potential to optimize
261
- else map.set(role, _getDataSubjectIdQuery(dataSubjectInfo, row, model))
262
- }
287
+ else map.set(role, _getDataSubjectIdQuery(dataSubjectInfo, row, model));
288
+ };
263
289
 
264
290
  const resolveDataSubjects = (logs, req) => {
265
- const ps = []
291
+ const ps = [];
266
292
 
267
- const map = _getDataSubjectsMap(req)
293
+ const map = _getDataSubjectsMap(req);
268
294
 
269
295
  for (const each of Object.values(logs)) {
270
296
  if (each.data_subject.id instanceof cds.ql.Query) {
271
- const q = each.data_subject.id
297
+ const q = each.data_subject.id;
272
298
  if (!map.has(q)) {
273
- const p = cds.run(q).then(res => map.set(q, res))
274
- map.set(q, p)
275
- ps.push(p)
299
+ const p = cds.run(q).then((res) => map.set(q, res));
300
+ map.set(q, p);
301
+ ps.push(p);
276
302
  }
277
303
  }
278
304
  }
@@ -280,11 +306,11 @@ const resolveDataSubjects = (logs, req) => {
280
306
  return Promise.all(ps).then(() => {
281
307
  for (const each of Object.values(logs)) {
282
308
  if (each.data_subject.id instanceof cds.ql.Query) {
283
- each.data_subject.id = map.get(each.data_subject.id)
309
+ each.data_subject.id = map.get(each.data_subject.id);
284
310
  }
285
311
  }
286
- })
287
- }
312
+ });
313
+ };
288
314
 
289
315
  const getAppMetadata = () => {
290
316
  const appMetadata = cds.env.app;
@@ -295,13 +321,12 @@ const getAppMetadata = () => {
295
321
  appName: appMetadata.name,
296
322
  appURL: appMetadata.url,
297
323
  organization_name: appMetadata.organization_name,
298
- space_name: appMetadata.space_name,
324
+ space_name: appMetadata.space_name
299
325
  };
300
326
  }
301
327
 
302
328
  // fallback: if the app metadata is undefined, then extract the metadata from the underlying environment (CF/Kyma/...)
303
- const vcapApplication =
304
- process.env.VCAP_APPLICATION && JSON.parse(process.env.VCAP_APPLICATION);
329
+ const vcapApplication = process.env.VCAP_APPLICATION && JSON.parse(process.env.VCAP_APPLICATION);
305
330
 
306
331
  return {
307
332
  appID: vcapApplication && vcapApplication.application_id,
@@ -312,7 +337,7 @@ const getAppMetadata = () => {
312
337
  vcapApplication &&
313
338
  vcapApplication.application_uris &&
314
339
  vcapApplication.application_uris[0] &&
315
- `https://${vcapApplication.application_uris[0].replace(/^https?:\/\//, "")}`,
340
+ `https://${vcapApplication.application_uris[0].replace(/^https?:\/\//, "")}`
316
341
  };
317
342
  };
318
343
 
@@ -326,5 +351,5 @@ module.exports = {
326
351
  addDataSubject,
327
352
  addDataSubjectForDetailsEntity,
328
353
  resolveDataSubjects,
329
- appMetadata: getAppMetadata(),
354
+ appMetadata: getAppMetadata()
330
355
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/audit-logging",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "CDS plugin providing integration to the SAP Audit Log service as well as out-of-the-box personal data-related audit logging based on annotations.",
5
5
  "repository": "cap-js/audit-logging",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -13,7 +13,9 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "lint": "npx eslint . --max-warnings=0",
16
- "test": "npx jest --silent"
16
+ "format": "npx prettier . --write && format-cds -f",
17
+ "format:check": "npx prettier . --check && format-cds --check",
18
+ "test": "npx jest --silent --testPathIgnorePatterns=integration"
17
19
  },
18
20
  "peerDependencies": {
19
21
  "@sap/cds": ">=8"
@@ -22,13 +24,10 @@
22
24
  "@cap-js/audit-logging": "file:.",
23
25
  "@cap-js/cds-test": ">=0",
24
26
  "@cap-js/sqlite": ">=1",
27
+ "@sap/cds-lsp": "^9.9.0",
25
28
  "@sap/cds-mtxs": "^3.7.1",
26
- "axios": "^1",
27
- "eslint": "^9",
28
- "express": "^4",
29
- "jest": "^30",
30
- "pretty-quick": "^4.2.2",
31
- "simple-git-hooks": "^2.13.1"
29
+ "express": "^4 || ^5",
30
+ "jest": "^30"
32
31
  },
33
32
  "cds": {
34
33
  "requires": {