@lucaapp/service-utils 5.34.1 → 5.35.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.
@@ -1,11 +1,25 @@
1
1
  import type { Model, ModelStatic, Sequelize } from 'sequelize';
2
2
  import { DataTypes } from 'sequelize';
3
+ type AuditOperation = 'create' | 'update' | 'destroy';
3
4
  export interface PartitionTable {
4
5
  tableName: string;
5
6
  timestamp: Date;
6
7
  }
8
+ interface DiffEntry {
9
+ key: string;
10
+ values: {
11
+ old?: unknown;
12
+ new?: unknown;
13
+ };
14
+ }
7
15
  interface AddHistoryOptions {
8
16
  exclude?: string[];
17
+ /**
18
+ * Attributes whose change is recorded, but whose values are not. The diff
19
+ * keeps the key with a placeholder, so the log shows THAT the attribute
20
+ * changed without storing large or sensitive payloads.
21
+ */
22
+ redact?: string[];
9
23
  }
10
24
  interface AtomicAuditLogOptions {
11
25
  attributeRevisionModelTableName?: string;
@@ -18,6 +32,8 @@ export interface PartitionResult {
18
32
  archiveTableName: string;
19
33
  timestamp: Date;
20
34
  }
35
+ export declare const REDACTED_VALUE = "[redacted]";
36
+ export declare const computeDiff: <T extends Model>(instance: T, operation: AuditOperation, exclude: string[], redact: string[]) => DiffEntry[];
21
37
  export declare class AtomicAuditLog {
22
38
  private sequelize;
23
39
  private tableName;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AtomicAuditLog = void 0;
3
+ exports.AtomicAuditLog = exports.computeDiff = exports.REDACTED_VALUE = void 0;
4
4
  const sequelize_1 = require("sequelize");
5
5
  const DEFAULT_EXCLUDE = [
6
6
  'id',
@@ -11,25 +11,32 @@ const DEFAULT_EXCLUDE = [
11
11
  'revision',
12
12
  ];
13
13
  const isValidValue = (value) => value !== undefined && value !== null;
14
- const computeCreateDiff = (currentValues, excludeSet) => {
14
+ exports.REDACTED_VALUE = '[redacted]';
15
+ const computeCreateDiff = (currentValues, excludeSet, redactSet) => {
15
16
  const diff = [];
16
17
  for (const [key, value] of Object.entries(currentValues)) {
17
18
  if (!excludeSet.has(key) && isValidValue(value)) {
18
- diff.push({ key, values: { new: value } });
19
+ diff.push({
20
+ key,
21
+ values: { new: redactSet.has(key) ? exports.REDACTED_VALUE : value },
22
+ });
19
23
  }
20
24
  }
21
25
  return diff;
22
26
  };
23
- const computeDestroyDiff = (previousValues, excludeSet) => {
27
+ const computeDestroyDiff = (previousValues, excludeSet, redactSet) => {
24
28
  const diff = [];
25
29
  for (const [key, value] of Object.entries(previousValues)) {
26
30
  if (!excludeSet.has(key) && isValidValue(value)) {
27
- diff.push({ key, values: { old: value } });
31
+ diff.push({
32
+ key,
33
+ values: { old: redactSet.has(key) ? exports.REDACTED_VALUE : value },
34
+ });
28
35
  }
29
36
  }
30
37
  return diff;
31
38
  };
32
- const computeUpdateDiff = (instance, previousValues, currentValues, excludeSet) => {
39
+ const computeUpdateDiff = (instance, previousValues, currentValues, excludeSet, redactSet) => {
33
40
  const diff = [];
34
41
  const changedKeys = instance.changed();
35
42
  if (!changedKeys || !Array.isArray(changedKeys)) {
@@ -39,24 +46,29 @@ const computeUpdateDiff = (instance, previousValues, currentValues, excludeSet)
39
46
  if (!excludeSet.has(key)) {
40
47
  diff.push({
41
48
  key,
42
- values: { old: previousValues[key], new: currentValues[key] },
49
+ values: redactSet.has(key)
50
+ ? { old: exports.REDACTED_VALUE, new: exports.REDACTED_VALUE }
51
+ : { old: previousValues[key], new: currentValues[key] },
43
52
  });
44
53
  }
45
54
  }
46
55
  return diff;
47
56
  };
48
- const computeDiff = (instance, operation, exclude) => {
57
+ // exported for tests
58
+ const computeDiff = (instance, operation, exclude, redact) => {
49
59
  const previousValues = instance.previous();
50
60
  const currentValues = instance.dataValues;
51
61
  const excludeSet = new Set([...DEFAULT_EXCLUDE, ...exclude]);
62
+ const redactSet = new Set(redact);
52
63
  if (operation === 'create') {
53
- return computeCreateDiff(currentValues, excludeSet);
64
+ return computeCreateDiff(currentValues, excludeSet, redactSet);
54
65
  }
55
66
  if (operation === 'destroy') {
56
- return computeDestroyDiff(previousValues, excludeSet);
67
+ return computeDestroyDiff(previousValues, excludeSet, redactSet);
57
68
  }
58
- return computeUpdateDiff(instance, previousValues, currentValues, excludeSet);
69
+ return computeUpdateDiff(instance, previousValues, currentValues, excludeSet, redactSet);
59
70
  };
71
+ exports.computeDiff = computeDiff;
60
72
  class AtomicAuditLog {
61
73
  constructor(sequelize, options = {}) {
62
74
  this.sequelize = sequelize;
@@ -86,12 +98,12 @@ class AtomicAuditLog {
86
98
  return `${this.tableName}_View`;
87
99
  }
88
100
  addHistory(model, options = {}) {
89
- const { exclude = [] } = options;
101
+ const { exclude = [], redact = [] } = options;
90
102
  const modelName = model.name;
91
103
  const primaryKeyAttribute = model.primaryKeyAttribute || 'uuid';
92
- const createHook = this.createAuditHook(modelName, primaryKeyAttribute, exclude, 'create');
93
- const updateHook = this.createAuditHook(modelName, primaryKeyAttribute, exclude, 'update');
94
- const destroyHook = this.createAuditHook(modelName, primaryKeyAttribute, exclude, 'destroy');
104
+ const createHook = this.createAuditHook(modelName, primaryKeyAttribute, exclude, redact, 'create');
105
+ const updateHook = this.createAuditHook(modelName, primaryKeyAttribute, exclude, redact, 'update');
106
+ const destroyHook = this.createAuditHook(modelName, primaryKeyAttribute, exclude, redact, 'destroy');
95
107
  model.addHook('afterCreate', createHook);
96
108
  model.addHook('afterUpdate', updateHook);
97
109
  model.addHook('afterDestroy', destroyHook);
@@ -144,10 +156,10 @@ class AtomicAuditLog {
144
156
  transaction,
145
157
  });
146
158
  }
147
- createAuditHook(modelName, primaryKeyAttribute, exclude, operation) {
159
+ createAuditHook(modelName, primaryKeyAttribute, exclude, redact, operation) {
148
160
  return async (instance, options) => {
149
161
  try {
150
- const diff = computeDiff(instance, operation, exclude);
162
+ const diff = (0, exports.computeDiff)(instance, operation, exclude, redact);
151
163
  if (diff.length === 0) {
152
164
  return;
153
165
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaapp/service-utils",
3
- "version": "5.34.1",
3
+ "version": "5.35.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [