@memberjunction/actions-base 5.51.0 → 6.1.0-edge.1
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/LICENSE +7 -0
- package/dist/ActionEngine-Base.d.ts +50 -1
- package/dist/ActionEngine-Base.d.ts.map +1 -1
- package/dist/ActionEngine-Base.js +15 -0
- package/dist/ActionEngine-Base.js.map +1 -1
- package/dist/EntityActionEngine-Base.d.ts +20 -0
- package/dist/EntityActionEngine-Base.d.ts.map +1 -1
- package/dist/EntityActionEngine-Base.js +31 -2
- package/dist/EntityActionEngine-Base.js.map +1 -1
- package/dist/EntityActionScopeResolver.d.ts +93 -0
- package/dist/EntityActionScopeResolver.d.ts.map +1 -0
- package/dist/EntityActionScopeResolver.js +145 -0
- package/dist/EntityActionScopeResolver.js.map +1 -0
- package/dist/MJActionEntityExtended.d.ts +1 -10
- package/dist/MJActionEntityExtended.d.ts.map +1 -1
- package/dist/MJActionEntityExtended.js +1 -31
- package/dist/MJActionEntityExtended.js.map +1 -1
- package/dist/ParamRedaction.d.ts +97 -0
- package/dist/ParamRedaction.d.ts.map +1 -0
- package/dist/ParamRedaction.js +142 -0
- package/dist/ParamRedaction.js.map +1 -0
- package/dist/__tests__/EntityActionEngine-Base.test.d.ts +2 -0
- package/dist/__tests__/EntityActionEngine-Base.test.d.ts.map +1 -0
- package/dist/__tests__/EntityActionEngine-Base.test.js +160 -0
- package/dist/__tests__/EntityActionEngine-Base.test.js.map +1 -0
- package/dist/__tests__/EntityActionScopeResolver.test.d.ts +2 -0
- package/dist/__tests__/EntityActionScopeResolver.test.d.ts.map +1 -0
- package/dist/__tests__/EntityActionScopeResolver.test.js +149 -0
- package/dist/__tests__/EntityActionScopeResolver.test.js.map +1 -0
- package/dist/__tests__/ParamRedaction.test.d.ts +2 -0
- package/dist/__tests__/ParamRedaction.test.d.ts.map +1 -0
- package/dist/__tests__/ParamRedaction.test.js +190 -0
- package/dist/__tests__/ParamRedaction.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { MJActionParamEntity, MJEntityActionParamEntity } from "@memberjunction/core-entities";
|
|
2
|
+
import { ActionParam } from "./ActionEngine-Base.js";
|
|
3
|
+
/**
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* PARAMETER REDACTION — the single place action parameter values are made safe
|
|
6
|
+
* to persist.
|
|
7
|
+
* ============================================================================
|
|
8
|
+
*
|
|
9
|
+
* **THE INVARIANT: no code path may write a raw `ActionParam[]` to persistent
|
|
10
|
+
* storage.** Every persister — `ActionExecutionLog.Params` / `.ResultParams`,
|
|
11
|
+
* `QueueTask.Data`, and anything added later — routes through
|
|
12
|
+
* {@link RedactParams} first. Redaction lives here, with the parameters, rather
|
|
13
|
+
* than inside any one persister, because a persister-local implementation
|
|
14
|
+
* covers exactly one door: routing entity-action `After*` invocations through
|
|
15
|
+
* the queue would re-open the hole one layer down via `QueueTask.Data`.
|
|
16
|
+
*
|
|
17
|
+
* The posture is **fail-closed** for whole records and **opt-out** for
|
|
18
|
+
* everything else, applied in this order:
|
|
19
|
+
*
|
|
20
|
+
* | # | Rule | Beats |
|
|
21
|
+
* |---|---|---|
|
|
22
|
+
* | 1 | An Entity Action binding whose `ValueType` is `'Entity Object'` or `'Entity Object Data'` is **never** logged. Whole records by definition. **No configuration can re-enable it.** | everything |
|
|
23
|
+
* | 2 | `EntityActionParam.LogValue = 0` — per-binding suppression. `NULL` inherits rule 3. | rule 3 |
|
|
24
|
+
* | 3 | `ActionParam.LogValue = 0` — the definition declares the parameter unloggable. Default `1`. | — |
|
|
25
|
+
*
|
|
26
|
+
* When a value is suppressed the log still records **the shape** — name, type,
|
|
27
|
+
* value type, byte length, key count and truncated top-level keys — so
|
|
28
|
+
* *"it was called with the 41-column deal row, not the 3-field summary"* stays
|
|
29
|
+
* answerable without the payload being readable. **Shape, never values.** Only
|
|
30
|
+
* top-level keys are walked: for a record those are column names, i.e. schema.
|
|
31
|
+
* Deeper structure can encode content (a `Notes` object keyed by author), so it
|
|
32
|
+
* is deliberately not descended into.
|
|
33
|
+
*/
|
|
34
|
+
/** Why a parameter's value was withheld from persistent storage. */
|
|
35
|
+
export type ParamRedactionReason =
|
|
36
|
+
/** Rule 1 — the binding's `ValueType` passes a whole record. Not configurable. */
|
|
37
|
+
'WholeRecordValueType'
|
|
38
|
+
/** Rule 2 — `EntityActionParam.LogValue = 0` on this specific binding. */
|
|
39
|
+
| 'BindingLogValueFalse'
|
|
40
|
+
/** Rule 3 — `ActionParam.LogValue = 0` on the parameter definition. */
|
|
41
|
+
| 'ParamLogValueFalse';
|
|
42
|
+
/**
|
|
43
|
+
* The record written in place of a parameter whose value is suppressed. Carries
|
|
44
|
+
* enough shape to diagnose a run and no content.
|
|
45
|
+
*/
|
|
46
|
+
export interface RedactedParam {
|
|
47
|
+
/** The parameter name — always recorded, never redacted. */
|
|
48
|
+
Name: string;
|
|
49
|
+
/** Input / Output / Both — always recorded. */
|
|
50
|
+
Type: 'Input' | 'Output' | 'Both';
|
|
51
|
+
/** The Entity Action binding's `ValueType`, when the run came from a binding. */
|
|
52
|
+
ValueType?: string;
|
|
53
|
+
/** Always `false` — the marker that distinguishes a redaction record from a logged param. */
|
|
54
|
+
Logged: false;
|
|
55
|
+
/** Which rule suppressed the value. */
|
|
56
|
+
Reason: ParamRedactionReason;
|
|
57
|
+
/** UTF-8 byte length of the value's JSON form. `-1` when the value could not be serialized. */
|
|
58
|
+
ByteLength: number;
|
|
59
|
+
/** Number of top-level keys, for object values. */
|
|
60
|
+
KeyCount?: number;
|
|
61
|
+
/** Top-level key names, truncated at {@link MAX_REDACTED_KEYS}. Schema, not data. */
|
|
62
|
+
Keys?: string[];
|
|
63
|
+
/** True when {@link Keys} was truncated — so a very wide row can't reintroduce the size problem. */
|
|
64
|
+
KeysElided?: boolean;
|
|
65
|
+
/** Number of elements, for array values. */
|
|
66
|
+
ItemCount?: number;
|
|
67
|
+
}
|
|
68
|
+
/** A parameter as persisted: either the parameter itself, or its redaction record. */
|
|
69
|
+
export type LoggedParam = ActionParam | RedactedParam;
|
|
70
|
+
/**
|
|
71
|
+
* Upper bound on how many top-level key names a redaction record carries. A wide
|
|
72
|
+
* record would otherwise reintroduce the size problem the redaction exists to solve.
|
|
73
|
+
*/
|
|
74
|
+
export declare const MAX_REDACTED_KEYS = 25;
|
|
75
|
+
/** True when a redaction record (rather than a live parameter) is what's in hand. */
|
|
76
|
+
export declare function IsRedactedParam(param: LoggedParam): param is RedactedParam;
|
|
77
|
+
/**
|
|
78
|
+
* Produces the persistable form of an action's parameters, applying the three
|
|
79
|
+
* redaction rules documented on this module.
|
|
80
|
+
*
|
|
81
|
+
* @param params The runtime parameters, as passed to (or returned by) the action.
|
|
82
|
+
* @param actionParams The `ActionParam` definition rows for the action. When a
|
|
83
|
+
* runtime parameter has no matching definition its value is logged —
|
|
84
|
+
* `ActionParam.LogValue` defaults to `1`, and an undeclared parameter has
|
|
85
|
+
* no declaration to opt out with. Rule 1 does not depend on this lookup.
|
|
86
|
+
* @param entityActionParams The `EntityActionParam` binding rows, when the run
|
|
87
|
+
* originated from an Entity Action. Omitted for direct invocations, in
|
|
88
|
+
* which case rules 1 and 2 cannot apply.
|
|
89
|
+
*/
|
|
90
|
+
export declare function RedactParams(params: ActionParam[] | undefined | null, actionParams?: MJActionParamEntity[] | null, entityActionParams?: MJEntityActionParamEntity[] | null): LoggedParam[];
|
|
91
|
+
/**
|
|
92
|
+
* Convenience wrapper producing the JSON string persisters actually write. Kept
|
|
93
|
+
* beside {@link RedactParams} so no caller is tempted to `JSON.stringify` a raw
|
|
94
|
+
* `ActionParam[]` itself.
|
|
95
|
+
*/
|
|
96
|
+
export declare function RedactParamsToJSON(params: ActionParam[] | undefined | null, actionParams?: MJActionParamEntity[] | null, entityActionParams?: MJEntityActionParamEntity[] | null): string;
|
|
97
|
+
//# sourceMappingURL=ParamRedaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamRedaction.d.ts","sourceRoot":"","sources":["../src/ParamRedaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAE/F,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,oEAAoE;AACpE,MAAM,MAAM,oBAAoB;AAC5B,kFAAkF;AAChF,sBAAsB;AACxB,0EAA0E;GACxE,sBAAsB;AACxB,uEAAuE;GACrE,oBAAoB,CAAC;AAE3B;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,MAAM,EAAE,KAAK,CAAC;IACd,uCAAuC;IACvC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,+FAA+F;IAC/F,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,oGAAoG;IACpG,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,sFAAsF;AACtF,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,aAAa,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAKpC,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CACxB,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,GAAG,IAAI,EACxC,YAAY,CAAC,EAAE,mBAAmB,EAAE,GAAG,IAAI,EAC3C,kBAAkB,CAAC,EAAE,yBAAyB,EAAE,GAAG,IAAI,GACxD,WAAW,EAAE,CAcf;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,GAAG,IAAI,EACxC,YAAY,CAAC,EAAE,mBAAmB,EAAE,GAAG,IAAI,EAC3C,kBAAkB,CAAC,EAAE,yBAAyB,EAAE,GAAG,IAAI,GACxD,MAAM,CAER"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { BaseEntity } from "@memberjunction/core";
|
|
2
|
+
import { UUIDsEqual } from "@memberjunction/global";
|
|
3
|
+
/**
|
|
4
|
+
* Upper bound on how many top-level key names a redaction record carries. A wide
|
|
5
|
+
* record would otherwise reintroduce the size problem the redaction exists to solve.
|
|
6
|
+
*/
|
|
7
|
+
export const MAX_REDACTED_KEYS = 25;
|
|
8
|
+
/** The Entity Action `ValueType` values that pass a whole record and are therefore never logged. */
|
|
9
|
+
const WHOLE_RECORD_VALUE_TYPES = new Set(['entity object', 'entity object data']);
|
|
10
|
+
/** True when a redaction record (rather than a live parameter) is what's in hand. */
|
|
11
|
+
export function IsRedactedParam(param) {
|
|
12
|
+
return param.Logged === false;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Produces the persistable form of an action's parameters, applying the three
|
|
16
|
+
* redaction rules documented on this module.
|
|
17
|
+
*
|
|
18
|
+
* @param params The runtime parameters, as passed to (or returned by) the action.
|
|
19
|
+
* @param actionParams The `ActionParam` definition rows for the action. When a
|
|
20
|
+
* runtime parameter has no matching definition its value is logged —
|
|
21
|
+
* `ActionParam.LogValue` defaults to `1`, and an undeclared parameter has
|
|
22
|
+
* no declaration to opt out with. Rule 1 does not depend on this lookup.
|
|
23
|
+
* @param entityActionParams The `EntityActionParam` binding rows, when the run
|
|
24
|
+
* originated from an Entity Action. Omitted for direct invocations, in
|
|
25
|
+
* which case rules 1 and 2 cannot apply.
|
|
26
|
+
*/
|
|
27
|
+
export function RedactParams(params, actionParams, entityActionParams) {
|
|
28
|
+
if (!params) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return params.map(p => {
|
|
32
|
+
const definition = findDefinition(p.Name, actionParams);
|
|
33
|
+
const binding = findBinding(definition, entityActionParams);
|
|
34
|
+
const reason = resolveRedactionReason(definition, binding);
|
|
35
|
+
if (!reason) {
|
|
36
|
+
return p;
|
|
37
|
+
}
|
|
38
|
+
return buildRedactionRecord(p, binding, reason);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Convenience wrapper producing the JSON string persisters actually write. Kept
|
|
43
|
+
* beside {@link RedactParams} so no caller is tempted to `JSON.stringify` a raw
|
|
44
|
+
* `ActionParam[]` itself.
|
|
45
|
+
*/
|
|
46
|
+
export function RedactParamsToJSON(params, actionParams, entityActionParams) {
|
|
47
|
+
return JSON.stringify(RedactParams(params, actionParams, entityActionParams));
|
|
48
|
+
}
|
|
49
|
+
/** Case-insensitive lookup of a runtime parameter's definition row. */
|
|
50
|
+
function findDefinition(name, actionParams) {
|
|
51
|
+
if (!actionParams || !name) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const target = name.trim().toLowerCase();
|
|
55
|
+
return actionParams.find(ap => (ap.Name ?? '').trim().toLowerCase() === target);
|
|
56
|
+
}
|
|
57
|
+
/** Finds the Entity Action binding row that supplies this parameter, if the run came from a binding. */
|
|
58
|
+
function findBinding(definition, entityActionParams) {
|
|
59
|
+
if (!definition || !entityActionParams) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return entityActionParams.find(eap => UUIDsEqual(eap.ActionParamID, definition.ID));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Applies the three rules in precedence order and returns the reason a value is
|
|
66
|
+
* withheld, or `undefined` when it may be logged.
|
|
67
|
+
*/
|
|
68
|
+
function resolveRedactionReason(definition, binding) {
|
|
69
|
+
// Rule 1 — whole-record value types. Unconditional; nothing below can re-enable it.
|
|
70
|
+
if (binding && WHOLE_RECORD_VALUE_TYPES.has((binding.ValueType ?? '').trim().toLowerCase())) {
|
|
71
|
+
return 'WholeRecordValueType';
|
|
72
|
+
}
|
|
73
|
+
// Rule 2 — the binding's explicit override. NULL falls through to the definition.
|
|
74
|
+
if (binding && binding.LogValue != null) {
|
|
75
|
+
return binding.LogValue ? undefined : 'BindingLogValueFalse';
|
|
76
|
+
}
|
|
77
|
+
// Rule 3 — the definition. An absent definition means an undeclared parameter,
|
|
78
|
+
// which has nothing to opt out with, so it is logged (LogValue's default is 1).
|
|
79
|
+
if (definition && definition.LogValue === false) {
|
|
80
|
+
return 'ParamLogValueFalse';
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
/** Builds the shape-only record persisted in place of a suppressed value. */
|
|
85
|
+
function buildRedactionRecord(param, binding, reason) {
|
|
86
|
+
const record = {
|
|
87
|
+
Name: param.Name,
|
|
88
|
+
Type: param.Type,
|
|
89
|
+
Logged: false,
|
|
90
|
+
Reason: reason,
|
|
91
|
+
ByteLength: 0
|
|
92
|
+
};
|
|
93
|
+
if (binding?.ValueType) {
|
|
94
|
+
record.ValueType = binding.ValueType;
|
|
95
|
+
}
|
|
96
|
+
describeShape(param.Value, record);
|
|
97
|
+
return record;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Fills the shape fields of a redaction record. A `BaseEntity` is described
|
|
101
|
+
* through `GetAll()` — its fields are getters, not enumerable own properties, so
|
|
102
|
+
* describing the instance directly would report a misleading empty shape (the
|
|
103
|
+
* same trap `'Entity Object Data'` exists to avoid).
|
|
104
|
+
*/
|
|
105
|
+
function describeShape(value, record) {
|
|
106
|
+
if (value === null || value === undefined) {
|
|
107
|
+
record.ByteLength = 0;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const described = value instanceof BaseEntity ? value.GetAll() : value;
|
|
111
|
+
record.ByteLength = utf8ByteLength(described);
|
|
112
|
+
if (Array.isArray(described)) {
|
|
113
|
+
record.ItemCount = described.length;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (typeof described === 'object') {
|
|
117
|
+
const keys = Object.keys(described);
|
|
118
|
+
record.KeyCount = keys.length;
|
|
119
|
+
record.Keys = keys.slice(0, MAX_REDACTED_KEYS);
|
|
120
|
+
if (keys.length > MAX_REDACTED_KEYS) {
|
|
121
|
+
record.KeysElided = true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* UTF-8 byte length of a value's JSON form. Returns `-1` when the value cannot be
|
|
127
|
+
* serialized (a circular reference, say) — a distinguishable signal rather than a
|
|
128
|
+
* misleading zero, and never a reason to throw out of a logging path.
|
|
129
|
+
*/
|
|
130
|
+
function utf8ByteLength(value) {
|
|
131
|
+
try {
|
|
132
|
+
const json = JSON.stringify(value);
|
|
133
|
+
if (json === undefined) {
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
return new TextEncoder().encode(json).length;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return -1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=ParamRedaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamRedaction.js","sourceRoot":"","sources":["../src/ParamRedaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityActionEngine-Base.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/EntityActionEngine-Base.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
const mockEntityByID = vi.fn();
|
|
3
|
+
vi.mock('@memberjunction/core', () => ({
|
|
4
|
+
BaseEngine: class BaseEngine {
|
|
5
|
+
static getInstance(_key) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
async Load() { }
|
|
9
|
+
},
|
|
10
|
+
BaseEnginePropertyConfig: class BaseEnginePropertyConfig {
|
|
11
|
+
},
|
|
12
|
+
BaseEntity: class BaseEntity {
|
|
13
|
+
},
|
|
14
|
+
EntityInfo: class EntityInfo {
|
|
15
|
+
},
|
|
16
|
+
IMetadataProvider: class IMetadataProvider {
|
|
17
|
+
},
|
|
18
|
+
UserInfo: class UserInfo {
|
|
19
|
+
},
|
|
20
|
+
Metadata: class Metadata {
|
|
21
|
+
EntityByID(id) {
|
|
22
|
+
return mockEntityByID(id);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
RunView: class RunView {
|
|
26
|
+
}
|
|
27
|
+
}));
|
|
28
|
+
vi.mock('@memberjunction/core-entities', () => ({}));
|
|
29
|
+
import { EntityActionEngineBase } from '../EntityActionEngine-Base.js';
|
|
30
|
+
const DEAL_TYPES_ID = 'AAAAAAAA-0000-0000-0000-000000000001';
|
|
31
|
+
const SCOPE_RECORD_ID = 'CCCCCCCC-0000-0000-0000-000000000003';
|
|
32
|
+
const DEALS_ENTITY_ID = 'EEEEEEEE-0000-0000-0000-000000000005';
|
|
33
|
+
function bindingRow(spec) {
|
|
34
|
+
return {
|
|
35
|
+
Entity: spec.Entity ?? 'Deals',
|
|
36
|
+
EntityID: spec.EntityID ?? DEALS_ENTITY_ID,
|
|
37
|
+
Status: spec.Status ?? 'Active',
|
|
38
|
+
ScopeEntityID: spec.ScopeEntityID ?? null,
|
|
39
|
+
ScopeRecordID: spec.ScopeRecordID ?? null,
|
|
40
|
+
...spec
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A live engine instance with its cache pre-seeded. The engine's cached arrays are private and
|
|
45
|
+
* normally filled by `Config()`; seeding them directly is what lets these tests exercise the pure
|
|
46
|
+
* ordering/filtering logic without a database.
|
|
47
|
+
*/
|
|
48
|
+
function engineWith(bindings) {
|
|
49
|
+
const engine = new EntityActionEngineBase();
|
|
50
|
+
engine._EntityActions = bindings;
|
|
51
|
+
return engine;
|
|
52
|
+
}
|
|
53
|
+
function dealRecord(dealTypeID) {
|
|
54
|
+
return {
|
|
55
|
+
EntityInfo: { Fields: [{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }] },
|
|
56
|
+
Get: (name) => (name === 'DealTypeID' ? dealTypeID : undefined)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
describe('EntityActionEngineBase — Sequence ordering', () => {
|
|
60
|
+
it('returns bindings in ascending Sequence order', () => {
|
|
61
|
+
const engine = engineWith([
|
|
62
|
+
bindingRow({ Action: 'Notify', Sequence: 30 }),
|
|
63
|
+
bindingRow({ Action: 'Normalize', Sequence: 10 }),
|
|
64
|
+
bindingRow({ Action: 'Validate', Sequence: 20 })
|
|
65
|
+
]);
|
|
66
|
+
expect(engine.GetActionsByEntityName('Deals').map(e => e.Action)).toEqual(['Normalize', 'Validate', 'Notify']);
|
|
67
|
+
});
|
|
68
|
+
it('breaks Sequence ties by Action name so the order is total and stable', () => {
|
|
69
|
+
const engine = engineWith([
|
|
70
|
+
bindingRow({ Action: 'Zulu', Sequence: 5 }),
|
|
71
|
+
bindingRow({ Action: 'Alpha', Sequence: 5 }),
|
|
72
|
+
bindingRow({ Action: 'Mike', Sequence: 5 })
|
|
73
|
+
]);
|
|
74
|
+
expect(engine.GetActionsByEntityName('Deals').map(e => e.Action)).toEqual(['Alpha', 'Mike', 'Zulu']);
|
|
75
|
+
});
|
|
76
|
+
it('treats a null Sequence as 0, so unsequenced bindings run before deliberately-later ones', () => {
|
|
77
|
+
const engine = engineWith([
|
|
78
|
+
bindingRow({ Action: 'Later', Sequence: 100 }),
|
|
79
|
+
bindingRow({ Action: 'Unsequenced', Sequence: null }),
|
|
80
|
+
bindingRow({ Action: 'Explicit Zero', Sequence: 0 })
|
|
81
|
+
]);
|
|
82
|
+
expect(engine.GetActionsByEntityName('Deals').map(e => e.Action)).toEqual([
|
|
83
|
+
'Explicit Zero',
|
|
84
|
+
'Unsequenced',
|
|
85
|
+
'Later'
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
it('supports negative Sequence for a binding that must run first', () => {
|
|
89
|
+
const engine = engineWith([
|
|
90
|
+
bindingRow({ Action: 'Default', Sequence: 0 }),
|
|
91
|
+
bindingRow({ Action: 'Preflight', Sequence: -10 })
|
|
92
|
+
]);
|
|
93
|
+
expect(engine.GetActionsByEntityName('Deals').map(e => e.Action)).toEqual(['Preflight', 'Default']);
|
|
94
|
+
});
|
|
95
|
+
it('does not mutate the cached array — ordering must not reorder the engine cache', () => {
|
|
96
|
+
const cached = [bindingRow({ Action: 'B', Sequence: 2 }), bindingRow({ Action: 'A', Sequence: 1 })];
|
|
97
|
+
const engine = engineWith(cached);
|
|
98
|
+
engine.GetActionsByEntityName('Deals');
|
|
99
|
+
expect(cached.map(e => e.Action)).toEqual(['B', 'A']);
|
|
100
|
+
});
|
|
101
|
+
it('orders GetActionsByEntityID the same way', () => {
|
|
102
|
+
const engine = engineWith([
|
|
103
|
+
bindingRow({ Action: 'Second', Sequence: 2 }),
|
|
104
|
+
bindingRow({ Action: 'First', Sequence: 1 })
|
|
105
|
+
]);
|
|
106
|
+
expect(engine.GetActionsByEntityID(DEALS_ENTITY_ID).map(e => e.Action)).toEqual(['First', 'Second']);
|
|
107
|
+
});
|
|
108
|
+
it('still honours the entity-name and status filters', () => {
|
|
109
|
+
const engine = engineWith([
|
|
110
|
+
bindingRow({ Action: 'OtherEntity', Sequence: 1, Entity: 'Contacts' }),
|
|
111
|
+
bindingRow({ Action: 'Disabled', Sequence: 2, Status: 'Disabled' }),
|
|
112
|
+
bindingRow({ Action: 'Active', Sequence: 3 })
|
|
113
|
+
]);
|
|
114
|
+
expect(engine.GetActionsByEntityName('Deals', 'Active').map(e => e.Action)).toEqual(['Active']);
|
|
115
|
+
// Entity name matching is case/whitespace-insensitive, as before.
|
|
116
|
+
expect(engine.GetActionsByEntityName(' deals ').map(e => e.Action)).toEqual([
|
|
117
|
+
'Disabled',
|
|
118
|
+
'Active'
|
|
119
|
+
]);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('EntityActionEngineBase — FilterByScope', () => {
|
|
123
|
+
beforeEach(() => {
|
|
124
|
+
mockEntityByID.mockReset();
|
|
125
|
+
mockEntityByID.mockImplementation((id) => (id === DEAL_TYPES_ID ? { Name: 'Deal Types' } : undefined));
|
|
126
|
+
});
|
|
127
|
+
it('keeps unscoped bindings and drops scoped ones the record falls outside of', async () => {
|
|
128
|
+
const engine = engineWith([]);
|
|
129
|
+
const bindings = [
|
|
130
|
+
bindingRow({ Action: 'Applies To All', Sequence: 1 }),
|
|
131
|
+
bindingRow({ Action: 'Scoped Match', Sequence: 2, ScopeEntityID: DEAL_TYPES_ID, ScopeRecordID: SCOPE_RECORD_ID }),
|
|
132
|
+
bindingRow({ Action: 'Scoped Other', Sequence: 3, ScopeEntityID: DEAL_TYPES_ID, ScopeRecordID: 'a-different-type' })
|
|
133
|
+
];
|
|
134
|
+
const kept = await engine.FilterByScope(bindings, dealRecord(SCOPE_RECORD_ID));
|
|
135
|
+
expect(kept.map(e => e.Action)).toEqual(['Applies To All', 'Scoped Match']);
|
|
136
|
+
});
|
|
137
|
+
it('preserves the incoming Sequence ordering', async () => {
|
|
138
|
+
const engine = engineWith([]);
|
|
139
|
+
const bindings = engine.GetActionsByEntityName.call(engineWith([
|
|
140
|
+
bindingRow({ Action: 'Third', Sequence: 3 }),
|
|
141
|
+
bindingRow({ Action: 'First', Sequence: 1 }),
|
|
142
|
+
bindingRow({ Action: 'Second', Sequence: 2 })
|
|
143
|
+
]), 'Deals');
|
|
144
|
+
const kept = await engine.FilterByScope(bindings, dealRecord(SCOPE_RECORD_ID));
|
|
145
|
+
expect(kept.map(e => e.Action)).toEqual(['First', 'Second', 'Third']);
|
|
146
|
+
});
|
|
147
|
+
it('drops every scoped binding when there is no subject record to evaluate', async () => {
|
|
148
|
+
const engine = engineWith([]);
|
|
149
|
+
const bindings = [
|
|
150
|
+
bindingRow({ Action: 'Unscoped' }),
|
|
151
|
+
bindingRow({ Action: 'Scoped', ScopeEntityID: DEAL_TYPES_ID, ScopeRecordID: SCOPE_RECORD_ID })
|
|
152
|
+
];
|
|
153
|
+
const kept = await engine.FilterByScope(bindings, undefined);
|
|
154
|
+
expect(kept.map(e => e.Action)).toEqual(['Unscoped']);
|
|
155
|
+
});
|
|
156
|
+
it('returns an empty array for an empty candidate set', async () => {
|
|
157
|
+
await expect(engineWith([]).FilterByScope([], dealRecord(SCOPE_RECORD_ID))).resolves.toEqual([]);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
//# sourceMappingURL=EntityActionEngine-Base.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityActionEngine-Base.test.js","sourceRoot":"","sources":["../../src/__tests__/EntityActionEngine-Base.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityActionScopeResolver.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/EntityActionScopeResolver.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* The scope resolver reads two things: an entity's field metadata (to walk foreign keys) and the
|
|
4
|
+
* `Metadata` catalog (to turn `ScopeEntityID` into an entity name for resolver lookup). Both are
|
|
5
|
+
* mocked so the tests are pure and deterministic.
|
|
6
|
+
*/
|
|
7
|
+
const mockEntityByID = vi.fn();
|
|
8
|
+
vi.mock('@memberjunction/core', () => ({
|
|
9
|
+
BaseEntity: class BaseEntity {
|
|
10
|
+
},
|
|
11
|
+
EntityInfo: class EntityInfo {
|
|
12
|
+
},
|
|
13
|
+
Metadata: class Metadata {
|
|
14
|
+
EntityByID(id) {
|
|
15
|
+
return mockEntityByID(id);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}));
|
|
19
|
+
import { EntityActionScopeResolver, IsEntityActionInScope } from '../EntityActionScopeResolver.js';
|
|
20
|
+
// ── Fixtures ─────────────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
const DEAL_TYPES_ID = 'AAAAAAAA-0000-0000-0000-000000000001';
|
|
22
|
+
const COMPANIES_ID = 'BBBBBBBB-0000-0000-0000-000000000002';
|
|
23
|
+
const SCOPE_RECORD_ID = 'CCCCCCCC-0000-0000-0000-000000000003';
|
|
24
|
+
/** A subject record with the given FK fields and values. */
|
|
25
|
+
function subject(fields, values) {
|
|
26
|
+
return {
|
|
27
|
+
EntityInfo: { Fields: fields },
|
|
28
|
+
Get: (name) => values[name]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function entityAction(scopeEntityID, scopeRecordID) {
|
|
32
|
+
return { ScopeEntityID: scopeEntityID, ScopeRecordID: scopeRecordID };
|
|
33
|
+
}
|
|
34
|
+
/** The default resolver, used wherever a test isn't exercising a custom registration. */
|
|
35
|
+
const defaultResolverFactory = () => new EntityActionScopeResolver();
|
|
36
|
+
describe('EntityActionScopeResolver — default foreign-key walk', () => {
|
|
37
|
+
const resolver = new EntityActionScopeResolver();
|
|
38
|
+
it('matches when the single FK to the scope entity holds the scope record ID', async () => {
|
|
39
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: SCOPE_RECORD_ID });
|
|
40
|
+
await expect(resolver.IsInScope(deal, DEAL_TYPES_ID, SCOPE_RECORD_ID)).resolves.toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it('rejects when the FK points at a different scope record', async () => {
|
|
43
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], {
|
|
44
|
+
DealTypeID: 'DDDDDDDD-0000-0000-0000-00000000000D'
|
|
45
|
+
});
|
|
46
|
+
await expect(resolver.IsInScope(deal, DEAL_TYPES_ID, SCOPE_RECORD_ID)).resolves.toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it('compares UUIDs case-insensitively — SQL Server uppercases, PostgreSQL lowercases', async () => {
|
|
49
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], {
|
|
50
|
+
DealTypeID: SCOPE_RECORD_ID.toLowerCase()
|
|
51
|
+
});
|
|
52
|
+
await expect(resolver.IsInScope(deal, DEAL_TYPES_ID, SCOPE_RECORD_ID.toUpperCase())).resolves.toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it('matches non-UUID (numeric/text) scope record keys', async () => {
|
|
55
|
+
const deal = subject([{ Name: 'RegionID', RelatedEntityID: DEAL_TYPES_ID }], { RegionID: 42 });
|
|
56
|
+
await expect(resolver.IsInScope(deal, DEAL_TYPES_ID, '42')).resolves.toBe(true);
|
|
57
|
+
await expect(resolver.IsInScope(deal, DEAL_TYPES_ID, '43')).resolves.toBe(false);
|
|
58
|
+
});
|
|
59
|
+
it('rejects when the FK is null on this record', async () => {
|
|
60
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: null });
|
|
61
|
+
await expect(resolver.IsInScope(deal, DEAL_TYPES_ID, SCOPE_RECORD_ID)).resolves.toBe(false);
|
|
62
|
+
});
|
|
63
|
+
it('DECLINES (null) when the subject has no FK to the scope entity — the relationship is indirect', async () => {
|
|
64
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: SCOPE_RECORD_ID });
|
|
65
|
+
await expect(resolver.IsInScope(deal, COMPANIES_ID, SCOPE_RECORD_ID)).resolves.toBeNull();
|
|
66
|
+
});
|
|
67
|
+
it('DECLINES (null) on ambiguity — two FKs to the same entity have no defensible "the" one', async () => {
|
|
68
|
+
const deal = subject([
|
|
69
|
+
{ Name: 'OwnerCompanyID', RelatedEntityID: COMPANIES_ID },
|
|
70
|
+
{ Name: 'ClientCompanyID', RelatedEntityID: COMPANIES_ID }
|
|
71
|
+
], { OwnerCompanyID: SCOPE_RECORD_ID, ClientCompanyID: 'other' });
|
|
72
|
+
await expect(resolver.IsInScope(deal, COMPANIES_ID, SCOPE_RECORD_ID)).resolves.toBeNull();
|
|
73
|
+
});
|
|
74
|
+
it('ignores non-FK fields when looking for the scope relationship', async () => {
|
|
75
|
+
const deal = subject([{ Name: 'Amount' }, { Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { Amount: 100, DealTypeID: SCOPE_RECORD_ID });
|
|
76
|
+
await expect(resolver.IsInScope(deal, DEAL_TYPES_ID, SCOPE_RECORD_ID)).resolves.toBe(true);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('IsEntityActionInScope', () => {
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
mockEntityByID.mockReset();
|
|
82
|
+
mockEntityByID.mockImplementation((id) => (id === DEAL_TYPES_ID ? { Name: 'Deal Types' } : undefined));
|
|
83
|
+
});
|
|
84
|
+
it('an unscoped binding always applies — the pre-existing behaviour', async () => {
|
|
85
|
+
const deal = subject([], {});
|
|
86
|
+
await expect(IsEntityActionInScope(entityAction(null, null), deal, defaultResolverFactory)).resolves.toBe(true);
|
|
87
|
+
});
|
|
88
|
+
it('a half-configured scope (entity but no record) is treated as unscoped', async () => {
|
|
89
|
+
const deal = subject([], {});
|
|
90
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, null), deal, defaultResolverFactory)).resolves.toBe(true);
|
|
91
|
+
await expect(IsEntityActionInScope(entityAction(null, SCOPE_RECORD_ID), deal, defaultResolverFactory)).resolves.toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it('an unscoped binding applies even with no subject record in hand', async () => {
|
|
94
|
+
await expect(IsEntityActionInScope(entityAction(null, null), undefined, defaultResolverFactory)).resolves.toBe(true);
|
|
95
|
+
});
|
|
96
|
+
it('a scoped binding applies when the default FK walk matches', async () => {
|
|
97
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: SCOPE_RECORD_ID });
|
|
98
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), deal, defaultResolverFactory)).resolves.toBe(true);
|
|
99
|
+
});
|
|
100
|
+
it('a scoped binding does NOT apply to a record under a different scope record', async () => {
|
|
101
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: 'someone-else' });
|
|
102
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), deal, defaultResolverFactory)).resolves.toBe(false);
|
|
103
|
+
});
|
|
104
|
+
it('a scoped binding does NOT fire when there is no subject record to evaluate', async () => {
|
|
105
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), null, defaultResolverFactory)).resolves.toBe(false);
|
|
106
|
+
});
|
|
107
|
+
it('a scoped binding does NOT fire when the scope entity is unknown to metadata', async () => {
|
|
108
|
+
const deal = subject([{ Name: 'CompanyID', RelatedEntityID: COMPANIES_ID }], { CompanyID: SCOPE_RECORD_ID });
|
|
109
|
+
await expect(IsEntityActionInScope(entityAction(COMPANIES_ID, SCOPE_RECORD_ID), deal, defaultResolverFactory)).resolves.toBe(false);
|
|
110
|
+
});
|
|
111
|
+
it('a scoped binding does NOT fire when no resolver can be produced', async () => {
|
|
112
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: SCOPE_RECORD_ID });
|
|
113
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), deal, () => null)).resolves.toBe(false);
|
|
114
|
+
});
|
|
115
|
+
it('a resolver that DECLINES (null) leaves the binding not-applicable — declining is not approval', async () => {
|
|
116
|
+
class DecliningResolver extends EntityActionScopeResolver {
|
|
117
|
+
async IsInScope() {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: SCOPE_RECORD_ID });
|
|
122
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), deal, () => new DecliningResolver())).resolves.toBe(false);
|
|
123
|
+
});
|
|
124
|
+
it('an app-specific resolver overrides the default FK walk in both directions', async () => {
|
|
125
|
+
class AlwaysInScope extends EntityActionScopeResolver {
|
|
126
|
+
async IsInScope() {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
class NeverInScope extends EntityActionScopeResolver {
|
|
131
|
+
async IsInScope() {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// No FK at all — the default walk would decline, but the app resolver says yes.
|
|
136
|
+
const orphan = subject([], {});
|
|
137
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), orphan, () => new AlwaysInScope())).resolves.toBe(true);
|
|
138
|
+
// FK matches — the default walk would say yes, but the app resolver says no.
|
|
139
|
+
const matching = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: SCOPE_RECORD_ID });
|
|
140
|
+
await expect(IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), matching, () => new NeverInScope())).resolves.toBe(false);
|
|
141
|
+
});
|
|
142
|
+
it('looks the resolver up by the scope entity NAME', async () => {
|
|
143
|
+
const lookup = vi.fn().mockReturnValue(new EntityActionScopeResolver());
|
|
144
|
+
const deal = subject([{ Name: 'DealTypeID', RelatedEntityID: DEAL_TYPES_ID }], { DealTypeID: SCOPE_RECORD_ID });
|
|
145
|
+
await IsEntityActionInScope(entityAction(DEAL_TYPES_ID, SCOPE_RECORD_ID), deal, lookup);
|
|
146
|
+
expect(lookup).toHaveBeenCalledWith('Deal Types');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=EntityActionScopeResolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityActionScopeResolver.test.js","sourceRoot":"","sources":["../../src/__tests__/EntityActionScopeResolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamRedaction.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ParamRedaction.test.ts"],"names":[],"mappings":""}
|