@memberjunction/actions-base 5.51.0 → 6.1.0-edge.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/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/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
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MemberJunction
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseEngine, IMetadataProvider, UserInfo } from "@memberjunction/core";
|
|
2
|
-
import { MJActionCategoryEntity, MJActionEntity, MJActionExecutionLogEntity, MJActionFilterEntity, MJActionLibraryEntity, MJActionParamEntity, MJActionResultCodeEntity } from "@memberjunction/core-entities";
|
|
2
|
+
import { MJActionCategoryEntity, MJActionEntity, MJActionExecutionLogEntity, MJActionFilterEntity, MJActionLibraryEntity, MJActionParamEntity, MJActionResultCodeEntity, MJEntityActionEntity, MJEntityActionParamEntity } from "@memberjunction/core-entities";
|
|
3
3
|
import { MJActionEntityExtended } from "./MJActionEntityExtended.js";
|
|
4
4
|
export declare class ActionLibrary {
|
|
5
5
|
LibraryName: string;
|
|
@@ -152,6 +152,49 @@ export declare class ActionParam {
|
|
|
152
152
|
*/
|
|
153
153
|
Type: 'Input' | 'Output' | 'Both';
|
|
154
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Where an action run came from, when it was dispatched by an Entity Action binding rather than
|
|
157
|
+
* invoked directly. Carried on {@link RunActionParams.Provenance} and stamped onto
|
|
158
|
+
* `ActionExecutionLog` so a failed workflow is diagnosable: *which binding fired this, on which
|
|
159
|
+
* record, from which event.*
|
|
160
|
+
*
|
|
161
|
+
* Also supplies the two things the engine cannot work out for itself once the run is under way —
|
|
162
|
+
* the binding's `LoggingMode`, and the `EntityActionParam` rows the redaction rules need in order
|
|
163
|
+
* to see a parameter's `ValueType` and per-binding `LogValue` override.
|
|
164
|
+
*
|
|
165
|
+
* Absent for direct invocations (a resolver, a script, an agent step, a scheduled action), which
|
|
166
|
+
* is exactly what the log's NULL provenance columns mean.
|
|
167
|
+
*/
|
|
168
|
+
export declare class ActionInvocationProvenance {
|
|
169
|
+
/** The Entity Action binding that caused this run. */
|
|
170
|
+
EntityActionID?: string;
|
|
171
|
+
/**
|
|
172
|
+
* Which lifecycle event fired the binding — `AfterUpdate`, `Validate`, `List` and so on.
|
|
173
|
+
* Recorded separately from {@link EntityActionID} because one binding may be attached to
|
|
174
|
+
* several invocation types, and telling a `Validate` refusal apart from an `AfterUpdate` side
|
|
175
|
+
* effect is the first question anyone asks of the log.
|
|
176
|
+
*/
|
|
177
|
+
EntityActionInvocationTypeID?: string;
|
|
178
|
+
/**
|
|
179
|
+
* The entity of the record the run operated on. Denormalized rather than derived through
|
|
180
|
+
* {@link EntityActionID} so it survives the binding being deleted or retargeted, and so the log
|
|
181
|
+
* can be queried by record with no join. Kept generic — every invoker has a subject, not only
|
|
182
|
+
* Entity Actions.
|
|
183
|
+
*/
|
|
184
|
+
TargetEntityID?: string;
|
|
185
|
+
/**
|
|
186
|
+
* The primary key of the record the run operated on, as text. For multi-record invocation types
|
|
187
|
+
* (`List`, `View`) one log row is written per record, so this is always a single record.
|
|
188
|
+
*/
|
|
189
|
+
TargetRecordID?: string;
|
|
190
|
+
/** The binding's `LoggingMode` — `All` / `FailuresOnly` / `None`. Defaults to `All` when absent. */
|
|
191
|
+
LoggingMode?: MJEntityActionEntity['LoggingMode'];
|
|
192
|
+
/**
|
|
193
|
+
* The binding's parameter rows. Required by the redaction rules: `ValueType` drives the hard
|
|
194
|
+
* whole-record rule, and `LogValue` supplies the per-binding override.
|
|
195
|
+
*/
|
|
196
|
+
EntityActionParams?: MJEntityActionParamEntity[];
|
|
197
|
+
}
|
|
155
198
|
/**
|
|
156
199
|
* Class that holds the parameters for an action to be run. This is passed to the Run method of an action.
|
|
157
200
|
*
|
|
@@ -253,6 +296,12 @@ export declare class RunActionParams<TContext = any> {
|
|
|
253
296
|
* to honor the caller's provider when supplied while remaining backward compatible.
|
|
254
297
|
*/
|
|
255
298
|
Provider?: IMetadataProvider;
|
|
299
|
+
/**
|
|
300
|
+
* Optional. Set by the Entity Action invocation path to record which binding, which event and
|
|
301
|
+
* which record produced this run — see {@link ActionInvocationProvenance}. Left undefined for
|
|
302
|
+
* direct invocations, which is what the execution log's NULL provenance columns mean.
|
|
303
|
+
*/
|
|
304
|
+
Provenance?: ActionInvocationProvenance;
|
|
256
305
|
}
|
|
257
306
|
/**
|
|
258
307
|
* Base class for Action metadata.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionEngine-Base.d.ts","sourceRoot":"","sources":["../src/ActionEngine-Base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,QAAQ,EAAqC,MAAM,sBAAsB,CAAC;AAClH,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"ActionEngine-Base.d.ts","sourceRoot":"","sources":["../src/ActionEngine-Base.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,21 @@ export class ActionResult {
|
|
|
21
21
|
*/
|
|
22
22
|
export class ActionParam {
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Where an action run came from, when it was dispatched by an Entity Action binding rather than
|
|
26
|
+
* invoked directly. Carried on {@link RunActionParams.Provenance} and stamped onto
|
|
27
|
+
* `ActionExecutionLog` so a failed workflow is diagnosable: *which binding fired this, on which
|
|
28
|
+
* record, from which event.*
|
|
29
|
+
*
|
|
30
|
+
* Also supplies the two things the engine cannot work out for itself once the run is under way —
|
|
31
|
+
* the binding's `LoggingMode`, and the `EntityActionParam` rows the redaction rules need in order
|
|
32
|
+
* to see a parameter's `ValueType` and per-binding `LogValue` override.
|
|
33
|
+
*
|
|
34
|
+
* Absent for direct invocations (a resolver, a script, an agent step, a scheduled action), which
|
|
35
|
+
* is exactly what the log's NULL provenance columns mean.
|
|
36
|
+
*/
|
|
37
|
+
export class ActionInvocationProvenance {
|
|
38
|
+
}
|
|
24
39
|
/**
|
|
25
40
|
* Class that holds the parameters for an action to be run. This is passed to the Run method of an action.
|
|
26
41
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionEngine-Base.js","sourceRoot":"","sources":["../src/ActionEngine-Base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA+B,OAAO,EAA4B,MAAM,sBAAsB,CAAC;AAKlH,MAAM,OAAO,aAAa;CAGzB;AAED;;GAEG;AACH,MAAM,OAAO,aAAa;CAqBzB;AA8CD;;GAEG;AAEH,MAAM,OAAO,kBAAkB;CA4B9B;AAED;;GAEG;AACH,MAAM,OAAO,YAAY;CAoCxB;AAED;;GAEG;AACH,MAAM,OAAO,WAAW;CAavB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,eAAe;
|
|
1
|
+
{"version":3,"file":"ActionEngine-Base.js","sourceRoot":"","sources":["../src/ActionEngine-Base.ts"],"names":[],"mappings":"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"}
|
|
@@ -110,6 +110,26 @@ export declare class EntityActionEngineBase extends BaseEngine<EntityActionEngin
|
|
|
110
110
|
* @returns
|
|
111
111
|
*/
|
|
112
112
|
GetActionsByEntityName(entityName: string, status?: 'Active' | 'Pending' | 'Disabled'): MJEntityActionEntityExtended[];
|
|
113
|
+
/**
|
|
114
|
+
* Orders bindings the way they will execute: ascending `Sequence`, ties broken by `Name` so the
|
|
115
|
+
* order is total and stable rather than dependent on how the rows happened to load.
|
|
116
|
+
*
|
|
117
|
+
* The dispatch loop runs bindings in array order, so this ordering IS the execution contract —
|
|
118
|
+
* a `Validate` binding that normalizes a field must be able to run before one that checks it.
|
|
119
|
+
* `Sequence` defaults to 0, so unsequenced bindings keep their pre-existing name-ordered behaviour
|
|
120
|
+
* relative to each other and run before anything deliberately pushed later.
|
|
121
|
+
*/
|
|
122
|
+
protected SortBySequence(entityActions: MJEntityActionEntityExtended[]): MJEntityActionEntityExtended[];
|
|
123
|
+
/**
|
|
124
|
+
* Narrows a candidate set to the bindings that apply to `subject`, honouring `ScopeEntityID` /
|
|
125
|
+
* `ScopeRecordID`. Unscoped bindings (the overwhelming majority, and the pre-existing behaviour)
|
|
126
|
+
* always apply; scoped ones are asked of the `@RegisterClass`-resolved
|
|
127
|
+
* {@link EntityActionScopeResolver} for their scope entity.
|
|
128
|
+
*
|
|
129
|
+
* Call this from a dispatch path that has a record in hand. Ordering from
|
|
130
|
+
* {@link SortBySequence} is preserved.
|
|
131
|
+
*/
|
|
132
|
+
FilterByScope(entityActions: MJEntityActionEntityExtended[], subject: BaseEntity | undefined | null): Promise<MJEntityActionEntityExtended[]>;
|
|
113
133
|
/**
|
|
114
134
|
* Helper method to get the MJEntityActionEntityExtended object for a given entity ID
|
|
115
135
|
* @param entityID
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityActionEngine-Base.d.ts","sourceRoot":"","sources":["../src/EntityActionEngine-Base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,UAAU,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErH,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,8BAA8B,EAAE,kCAAkC,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAChO,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"EntityActionEngine-Base.d.ts","sourceRoot":"","sources":["../src/EntityActionEngine-Base.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseEngine } from "@memberjunction/core";
|
|
2
2
|
import { UUIDsEqual } from "@memberjunction/global";
|
|
3
|
+
import { IsEntityActionInScope, ResolveEntityActionScopeResolver } from "./EntityActionScopeResolver.js";
|
|
3
4
|
/**
|
|
4
5
|
* Parameters type for invoking an entity action
|
|
5
6
|
*/
|
|
@@ -99,7 +100,35 @@ export class EntityActionEngineBase extends BaseEngine {
|
|
|
99
100
|
* @returns
|
|
100
101
|
*/
|
|
101
102
|
GetActionsByEntityName(entityName, status) {
|
|
102
|
-
return this._EntityActions.filter(e => (!status || e.Status === status) && e.Entity.trim().toLowerCase() === entityName.trim().toLowerCase());
|
|
103
|
+
return this.SortBySequence(this._EntityActions.filter(e => (!status || e.Status === status) && e.Entity.trim().toLowerCase() === entityName.trim().toLowerCase()));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Orders bindings the way they will execute: ascending `Sequence`, ties broken by `Name` so the
|
|
107
|
+
* order is total and stable rather than dependent on how the rows happened to load.
|
|
108
|
+
*
|
|
109
|
+
* The dispatch loop runs bindings in array order, so this ordering IS the execution contract —
|
|
110
|
+
* a `Validate` binding that normalizes a field must be able to run before one that checks it.
|
|
111
|
+
* `Sequence` defaults to 0, so unsequenced bindings keep their pre-existing name-ordered behaviour
|
|
112
|
+
* relative to each other and run before anything deliberately pushed later.
|
|
113
|
+
*/
|
|
114
|
+
SortBySequence(entityActions) {
|
|
115
|
+
return [...entityActions].sort((a, b) => {
|
|
116
|
+
const bySequence = (a.Sequence ?? 0) - (b.Sequence ?? 0);
|
|
117
|
+
return bySequence !== 0 ? bySequence : (a.Action ?? '').localeCompare(b.Action ?? '');
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Narrows a candidate set to the bindings that apply to `subject`, honouring `ScopeEntityID` /
|
|
122
|
+
* `ScopeRecordID`. Unscoped bindings (the overwhelming majority, and the pre-existing behaviour)
|
|
123
|
+
* always apply; scoped ones are asked of the `@RegisterClass`-resolved
|
|
124
|
+
* {@link EntityActionScopeResolver} for their scope entity.
|
|
125
|
+
*
|
|
126
|
+
* Call this from a dispatch path that has a record in hand. Ordering from
|
|
127
|
+
* {@link SortBySequence} is preserved.
|
|
128
|
+
*/
|
|
129
|
+
async FilterByScope(entityActions, subject) {
|
|
130
|
+
const verdicts = await Promise.all(entityActions.map(ea => IsEntityActionInScope(ea, subject, name => ResolveEntityActionScopeResolver(name))));
|
|
131
|
+
return entityActions.filter((_ea, i) => verdicts[i]);
|
|
103
132
|
}
|
|
104
133
|
/**
|
|
105
134
|
* Helper method to get the MJEntityActionEntityExtended object for a given entity ID
|
|
@@ -107,7 +136,7 @@ export class EntityActionEngineBase extends BaseEngine {
|
|
|
107
136
|
* @returns
|
|
108
137
|
*/
|
|
109
138
|
GetActionsByEntityID(entityID) {
|
|
110
|
-
return this._EntityActions.filter(e => UUIDsEqual(e.EntityID, entityID));
|
|
139
|
+
return this.SortBySequence(this._EntityActions.filter(e => UUIDsEqual(e.EntityID, entityID)));
|
|
111
140
|
}
|
|
112
141
|
/**
|
|
113
142
|
* Helper method to get the MJEntityActionEntityExtended object for a given entity name and invocation type
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityActionEngine-Base.js","sourceRoot":"","sources":["../src/EntityActionEngine-Base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqE,MAAM,sBAAsB,CAAC;AACrH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"EntityActionEngine-Base.js","sourceRoot":"","sources":["../src/EntityActionEngine-Base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqE,MAAM,sBAAsB,CAAC;AACrH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpD,OAAO,EAAE,qBAAqB,EAAE,gCAAgC,EAAE,MAAM,6BAA6B,CAAC;AAEtG;;GAEG;AACH,MAAM,OAAO,4BAA4B;CA4BxC;AAGD;;GAEG;AACH,MAAM,OAAO,kBAAkB;CAqC7B;AAEF;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,UAAkC;IAA9E;;QAMI,8DAA8D;QACtD,mBAAc,GAAmC,EAAE,CAAC;QACpD,wBAAmB,GAAgC,EAAE,CAAC;QACtD,iCAA4B,GAAyC,EAAE,CAAC;QACxE,yBAAoB,GAAiC,EAAE,CAAC;QACxD,6BAAwB,GAAqC,EAAE,CAAC;IAkJ5E,CAAC;IA5JU,MAAM,KAAK,QAAQ;QACtB,OAAO,KAAK,CAAC,WAAW,CAAyB,wBAAwB,CAAC,CAAC;IAC/E,CAAC;IAUD;;;;;OAKG;IACI,KAAK,CAAC,MAAM,CAAC,eAAwB,KAAK,EAAE,WAAsB,EAAE,QAA4B;QACnG,MAAM,OAAO,GAAwC;YACjD;gBACI,UAAU,EAAE,oCAAoC;gBAChD,YAAY,EAAE,8BAA8B;gBAC5C,UAAU,EAAE,IAAI;aACnB;YACD;gBACI,UAAU,EAAE,2BAA2B;gBACvC,YAAY,EAAE,sBAAsB;gBACpC,UAAU,EAAE,IAAI;aACnB;YACD;gBACI,UAAU,EAAE,+BAA+B;gBAC3C,YAAY,EAAE,0BAA0B;gBACxC,UAAU,EAAE,IAAI;aACnB;YACD;gBACI,UAAU,EAAE,oBAAoB,EAAE,0IAA0I;gBAC5K,YAAY,EAAE,gBAAgB;gBAC9B,UAAU,EAAE,IAAI;aACnB;YACD;gBACI,UAAU,EAAE,0BAA0B;gBACtC,YAAY,EAAE,qBAAqB;gBACnC,UAAU,EAAE,IAAI;aACnB;SACJ,CAAC;QACF,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;IAClE,CAAC;IAGD;;OAEG;IACH,IAAW,eAAe;QACtB,OAAO,IAAI,CAAC,4BAA4B,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,IAAW,OAAO;QACd,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,IAAW,WAAW;QAClB,OAAO,IAAI,CAAC,wBAAwB,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,IAAW,aAAa;QACpB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,IAAW,MAAM;QACb,OAAO,IAAI,CAAC,mBAAmB,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACI,sBAAsB,CAAC,UAAkB,EAAE,MAA0C;QACxF,OAAO,IAAI,CAAC,cAAc,CACtB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CACzI,CAAC;IACN,CAAC;IAED;;;;;;;;OAQG;IACO,cAAc,CAAC,aAA6C;QAClE,OAAO,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACpC,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;YACzD,OAAO,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QAC1F,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,aAAa,CACtB,aAA6C,EAC7C,OAAsC;QAEtC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,qBAAqB,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC,CAC9G,CAAC;QACF,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACI,oBAAoB,CAAC,QAAgB;QACxC,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClG,CAAC;IAED;;;;;;OAMG;IACI,uCAAuC,CAAC,UAAkB,EAAE,cAAsB,EAAE,MAA0C;QACjI,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACtE,2DAA2D;QAC3D,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YAC5B,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,cAAc,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;YAC9J,OAAO,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QACtC,CAAC,CAAC,CAAC;IACP,CAAC;CACJ"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { BaseEntity, EntityInfo } from "@memberjunction/core";
|
|
2
|
+
import { MJEntityActionEntityExtended } from "./MJEntityActionEntityExtended.js";
|
|
3
|
+
/**
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* SCOPE RESOLUTION SEAM
|
|
6
|
+
* ============================================================================
|
|
7
|
+
*
|
|
8
|
+
* An `EntityAction` may be narrowed to ONE configuration record — *this Deal
|
|
9
|
+
* Type*, *this Contract Type*, *this Pipeline*, *this Company* — via the
|
|
10
|
+
* `ScopeEntityID` / `ScopeRecordID` pair. `NULL` means "applies to every record
|
|
11
|
+
* of the entity", which is the pre-existing behaviour and by far the common case.
|
|
12
|
+
*
|
|
13
|
+
* The framework **stores and filters on the pair; it does not interpret it.**
|
|
14
|
+
* *How* a scope record relates to a subject record is an application concern:
|
|
15
|
+
* Sales knows a Deal reaches its Deal Type through `Deal.DealTypeID`, and core
|
|
16
|
+
* must not. So the question *"does this subject record fall under this scope
|
|
17
|
+
* record?"* is answered by a `@RegisterClass`-resolved resolver, keyed by scope
|
|
18
|
+
* entity name, most-specific wins — the same shape as `BasePriceResolver` in
|
|
19
|
+
* BizApps Orders and `GLAccountResolver` in Accounting.
|
|
20
|
+
*
|
|
21
|
+
* The default implementation below walks the subject entity's foreign keys
|
|
22
|
+
* looking for one that points at the scope entity. That covers the direct case
|
|
23
|
+
* (Sales registers nothing for `Deal Types` because `Deal.DealTypeID` is found
|
|
24
|
+
* automatically). An app needing something indirect — scope by Company where the
|
|
25
|
+
* subject reaches Company through a Pipeline — registers its own resolver:
|
|
26
|
+
*
|
|
27
|
+
* ```typescript
|
|
28
|
+
* @RegisterClass(EntityActionScopeResolver, 'Companies')
|
|
29
|
+
* export class CompanyScopeResolver extends EntityActionScopeResolver {
|
|
30
|
+
* public override async IsInScope(subject: BaseEntity, scopeEntityID: string, scopeRecordID: string): Promise<boolean | null> {
|
|
31
|
+
* // return null to decline and fall back to the default FK walk
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* **Declining returns `null`, not `false`.** `false` means "I looked and this
|
|
37
|
+
* record is out of scope"; `null` means "not my call" and falls back to the
|
|
38
|
+
* default. Conflating them would silently disable every binding a partially
|
|
39
|
+
* applicable resolver didn't recognise.
|
|
40
|
+
*/
|
|
41
|
+
export declare class EntityActionScopeResolver {
|
|
42
|
+
/**
|
|
43
|
+
* Answers whether `subject` falls under the scope record identified by
|
|
44
|
+
* `scopeEntityID` / `scopeRecordID`.
|
|
45
|
+
*
|
|
46
|
+
* @returns `true` / `false` for a decision, or `null` to decline and let the
|
|
47
|
+
* caller fall back to the default foreign-key walk.
|
|
48
|
+
*/
|
|
49
|
+
IsInScope(subject: BaseEntity, scopeEntityID: string, scopeRecordID: string): Promise<boolean | null>;
|
|
50
|
+
/**
|
|
51
|
+
* The default answer: look for a foreign key on the subject entity that points at the scope
|
|
52
|
+
* entity, and compare the subject's value for that field against the scope record's ID.
|
|
53
|
+
*
|
|
54
|
+
* Returns `null` when the subject has no foreign key to the scope entity at all — the
|
|
55
|
+
* relationship is indirect and only the owning app knows how to traverse it, so declining is
|
|
56
|
+
* more honest than answering `false`. Returns `null` for ambiguity too: when several distinct
|
|
57
|
+
* foreign keys point at the scope entity, picking one arbitrarily would silently bind the
|
|
58
|
+
* workflow to the wrong relationship (a `Deal` with both `OwnerCompanyID` and `ClientCompanyID`
|
|
59
|
+
* has no single defensible "the" Company).
|
|
60
|
+
*/
|
|
61
|
+
protected DefaultForeignKeyWalk(subject: BaseEntity, scopeEntityID: string, scopeRecordID: string): boolean | null;
|
|
62
|
+
/** Names of the subject entity's fields that are foreign keys into the scope entity. */
|
|
63
|
+
protected FindForeignKeyFields(subjectEntity: EntityInfo, scopeEntityID: string): string[];
|
|
64
|
+
/**
|
|
65
|
+
* Compares a subject's foreign-key value against a scope record ID. Scope record IDs are stored
|
|
66
|
+
* as text and may be UUIDs (which differ in case between SQL Server and PostgreSQL) or numeric
|
|
67
|
+
* keys, so `UUIDsEqual` handles the former and a trimmed comparison the latter.
|
|
68
|
+
*/
|
|
69
|
+
protected ValuesMatch(subjectValue: string, scopeRecordID: string): boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Decides whether a candidate binding applies to a subject record.
|
|
73
|
+
*
|
|
74
|
+
* Unscoped bindings (`ScopeEntityID` NULL) always apply — that is the
|
|
75
|
+
* pre-existing behaviour and must never be narrowed. Scoped bindings ask the
|
|
76
|
+
* resolver registered for the scope entity, falling back to the default
|
|
77
|
+
* foreign-key walk when no app-specific resolver declines or exists.
|
|
78
|
+
*
|
|
79
|
+
* **Fails open on an unanswerable scope**: when the scope entity is unknown to
|
|
80
|
+
* metadata or no resolver can decide, the binding is treated as *not*
|
|
81
|
+
* applicable, because a binding an administrator deliberately narrowed should
|
|
82
|
+
* not silently fire on every record of the entity. That is the safe direction —
|
|
83
|
+
* a workflow that doesn't run is visible; one that runs on every record is a
|
|
84
|
+
* production incident.
|
|
85
|
+
*/
|
|
86
|
+
export declare function IsEntityActionInScope(entityAction: MJEntityActionEntityExtended, subject: BaseEntity | undefined | null, resolveResolver: (scopeEntityName: string) => EntityActionScopeResolver | null): Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Resolves the {@link EntityActionScopeResolver} registered for a scope entity, most-specific wins.
|
|
89
|
+
* Falls back to the base implementation (the foreign-key walk), which is what makes the direct case —
|
|
90
|
+
* `Deal.DealTypeID` reaching `Deal Types` — work with no app registration at all.
|
|
91
|
+
*/
|
|
92
|
+
export declare function ResolveEntityActionScopeResolver(scopeEntityName: string): EntityActionScopeResolver | null;
|
|
93
|
+
//# sourceMappingURL=EntityActionScopeResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityActionScopeResolver.d.ts","sourceRoot":"","sources":["../src/EntityActionScopeResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAY,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,yBAAyB;IAClC;;;;;;OAMG;IACU,SAAS,CAClB,OAAO,EAAE,UAAU,EACnB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAI1B;;;;;;;;;;OAUG;IACH,SAAS,CAAC,qBAAqB,CAC3B,OAAO,EAAE,UAAU,EACnB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,GACtB,OAAO,GAAG,IAAI;IAYjB,wFAAwF;IACxF,SAAS,CAAC,oBAAoB,CAAC,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE;IAM1F;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO;CAI9E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CACvC,YAAY,EAAE,4BAA4B,EAC1C,OAAO,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,EACtC,eAAe,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,yBAAyB,GAAG,IAAI,GAC/E,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,eAAe,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI,CAY1G"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Metadata } from "@memberjunction/core";
|
|
2
|
+
import { MJGlobal, UUIDsEqual } from "@memberjunction/global";
|
|
3
|
+
/**
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* SCOPE RESOLUTION SEAM
|
|
6
|
+
* ============================================================================
|
|
7
|
+
*
|
|
8
|
+
* An `EntityAction` may be narrowed to ONE configuration record — *this Deal
|
|
9
|
+
* Type*, *this Contract Type*, *this Pipeline*, *this Company* — via the
|
|
10
|
+
* `ScopeEntityID` / `ScopeRecordID` pair. `NULL` means "applies to every record
|
|
11
|
+
* of the entity", which is the pre-existing behaviour and by far the common case.
|
|
12
|
+
*
|
|
13
|
+
* The framework **stores and filters on the pair; it does not interpret it.**
|
|
14
|
+
* *How* a scope record relates to a subject record is an application concern:
|
|
15
|
+
* Sales knows a Deal reaches its Deal Type through `Deal.DealTypeID`, and core
|
|
16
|
+
* must not. So the question *"does this subject record fall under this scope
|
|
17
|
+
* record?"* is answered by a `@RegisterClass`-resolved resolver, keyed by scope
|
|
18
|
+
* entity name, most-specific wins — the same shape as `BasePriceResolver` in
|
|
19
|
+
* BizApps Orders and `GLAccountResolver` in Accounting.
|
|
20
|
+
*
|
|
21
|
+
* The default implementation below walks the subject entity's foreign keys
|
|
22
|
+
* looking for one that points at the scope entity. That covers the direct case
|
|
23
|
+
* (Sales registers nothing for `Deal Types` because `Deal.DealTypeID` is found
|
|
24
|
+
* automatically). An app needing something indirect — scope by Company where the
|
|
25
|
+
* subject reaches Company through a Pipeline — registers its own resolver:
|
|
26
|
+
*
|
|
27
|
+
* ```typescript
|
|
28
|
+
* @RegisterClass(EntityActionScopeResolver, 'Companies')
|
|
29
|
+
* export class CompanyScopeResolver extends EntityActionScopeResolver {
|
|
30
|
+
* public override async IsInScope(subject: BaseEntity, scopeEntityID: string, scopeRecordID: string): Promise<boolean | null> {
|
|
31
|
+
* // return null to decline and fall back to the default FK walk
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* **Declining returns `null`, not `false`.** `false` means "I looked and this
|
|
37
|
+
* record is out of scope"; `null` means "not my call" and falls back to the
|
|
38
|
+
* default. Conflating them would silently disable every binding a partially
|
|
39
|
+
* applicable resolver didn't recognise.
|
|
40
|
+
*/
|
|
41
|
+
export class EntityActionScopeResolver {
|
|
42
|
+
/**
|
|
43
|
+
* Answers whether `subject` falls under the scope record identified by
|
|
44
|
+
* `scopeEntityID` / `scopeRecordID`.
|
|
45
|
+
*
|
|
46
|
+
* @returns `true` / `false` for a decision, or `null` to decline and let the
|
|
47
|
+
* caller fall back to the default foreign-key walk.
|
|
48
|
+
*/
|
|
49
|
+
async IsInScope(subject, scopeEntityID, scopeRecordID) {
|
|
50
|
+
return this.DefaultForeignKeyWalk(subject, scopeEntityID, scopeRecordID);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The default answer: look for a foreign key on the subject entity that points at the scope
|
|
54
|
+
* entity, and compare the subject's value for that field against the scope record's ID.
|
|
55
|
+
*
|
|
56
|
+
* Returns `null` when the subject has no foreign key to the scope entity at all — the
|
|
57
|
+
* relationship is indirect and only the owning app knows how to traverse it, so declining is
|
|
58
|
+
* more honest than answering `false`. Returns `null` for ambiguity too: when several distinct
|
|
59
|
+
* foreign keys point at the scope entity, picking one arbitrarily would silently bind the
|
|
60
|
+
* workflow to the wrong relationship (a `Deal` with both `OwnerCompanyID` and `ClientCompanyID`
|
|
61
|
+
* has no single defensible "the" Company).
|
|
62
|
+
*/
|
|
63
|
+
DefaultForeignKeyWalk(subject, scopeEntityID, scopeRecordID) {
|
|
64
|
+
const candidates = this.FindForeignKeyFields(subject.EntityInfo, scopeEntityID);
|
|
65
|
+
if (candidates.length !== 1) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const value = subject.Get(candidates[0]);
|
|
69
|
+
if (value === null || value === undefined) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return this.ValuesMatch(String(value), scopeRecordID);
|
|
73
|
+
}
|
|
74
|
+
/** Names of the subject entity's fields that are foreign keys into the scope entity. */
|
|
75
|
+
FindForeignKeyFields(subjectEntity, scopeEntityID) {
|
|
76
|
+
return subjectEntity.Fields
|
|
77
|
+
.filter(f => f.RelatedEntityID && UUIDsEqual(f.RelatedEntityID, scopeEntityID))
|
|
78
|
+
.map(f => f.Name);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Compares a subject's foreign-key value against a scope record ID. Scope record IDs are stored
|
|
82
|
+
* as text and may be UUIDs (which differ in case between SQL Server and PostgreSQL) or numeric
|
|
83
|
+
* keys, so `UUIDsEqual` handles the former and a trimmed comparison the latter.
|
|
84
|
+
*/
|
|
85
|
+
ValuesMatch(subjectValue, scopeRecordID) {
|
|
86
|
+
return UUIDsEqual(subjectValue, scopeRecordID) ||
|
|
87
|
+
subjectValue.trim() === (scopeRecordID ?? '').trim();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Decides whether a candidate binding applies to a subject record.
|
|
92
|
+
*
|
|
93
|
+
* Unscoped bindings (`ScopeEntityID` NULL) always apply — that is the
|
|
94
|
+
* pre-existing behaviour and must never be narrowed. Scoped bindings ask the
|
|
95
|
+
* resolver registered for the scope entity, falling back to the default
|
|
96
|
+
* foreign-key walk when no app-specific resolver declines or exists.
|
|
97
|
+
*
|
|
98
|
+
* **Fails open on an unanswerable scope**: when the scope entity is unknown to
|
|
99
|
+
* metadata or no resolver can decide, the binding is treated as *not*
|
|
100
|
+
* applicable, because a binding an administrator deliberately narrowed should
|
|
101
|
+
* not silently fire on every record of the entity. That is the safe direction —
|
|
102
|
+
* a workflow that doesn't run is visible; one that runs on every record is a
|
|
103
|
+
* production incident.
|
|
104
|
+
*/
|
|
105
|
+
export async function IsEntityActionInScope(entityAction, subject, resolveResolver) {
|
|
106
|
+
const scopeEntityID = entityAction.ScopeEntityID;
|
|
107
|
+
const scopeRecordID = entityAction.ScopeRecordID;
|
|
108
|
+
// Unscoped — applies to all records. The overwhelmingly common case, and the
|
|
109
|
+
// pre-existing behaviour, so it short-circuits before any lookup.
|
|
110
|
+
if (!scopeEntityID || !scopeRecordID) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
// A scoped binding with no subject record to test (a View/List dispatch that
|
|
114
|
+
// hasn't resolved to a record yet) cannot be evaluated — decline rather than
|
|
115
|
+
// fire it against everything.
|
|
116
|
+
if (!subject) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
const scopeEntity = new Metadata().EntityByID(scopeEntityID); // global-provider-ok: entity-definition lookup (structural metadata)
|
|
120
|
+
if (!scopeEntity) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const resolver = resolveResolver(scopeEntity.Name);
|
|
124
|
+
if (!resolver) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const answer = await resolver.IsInScope(subject, scopeEntityID, scopeRecordID);
|
|
128
|
+
return answer === true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Resolves the {@link EntityActionScopeResolver} registered for a scope entity, most-specific wins.
|
|
132
|
+
* Falls back to the base implementation (the foreign-key walk), which is what makes the direct case —
|
|
133
|
+
* `Deal.DealTypeID` reaching `Deal Types` — work with no app registration at all.
|
|
134
|
+
*/
|
|
135
|
+
export function ResolveEntityActionScopeResolver(scopeEntityName) {
|
|
136
|
+
// GetRegistration first: having NO app-specific resolver is the normal, expected case (the default
|
|
137
|
+
// foreign-key walk is the whole point), and going straight to CreateInstance would log a
|
|
138
|
+
// "no registration found" warning on every scoped dispatch for something that isn't a problem.
|
|
139
|
+
const registration = MJGlobal.Instance.ClassFactory.GetRegistration(EntityActionScopeResolver, scopeEntityName);
|
|
140
|
+
if (!registration) {
|
|
141
|
+
return new EntityActionScopeResolver();
|
|
142
|
+
}
|
|
143
|
+
return MJGlobal.Instance.ClassFactory.CreateInstance(EntityActionScopeResolver, scopeEntityName) ?? new EntityActionScopeResolver();
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=EntityActionScopeResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityActionScopeResolver.js","sourceRoot":"","sources":["../src/EntityActionScopeResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAG9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,OAAO,yBAAyB;IAClC;;;;;;OAMG;IACI,KAAK,CAAC,SAAS,CAClB,OAAmB,EACnB,aAAqB,EACrB,aAAqB;QAErB,OAAO,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;IAC7E,CAAC;IAED;;;;;;;;;;OAUG;IACO,qBAAqB,CAC3B,OAAmB,EACnB,aAAqB,EACrB,aAAqB;QAErB,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QAChF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxC,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,CAAC;IAC1D,CAAC;IAED,wFAAwF;IAC9E,oBAAoB,CAAC,aAAyB,EAAE,aAAqB;QAC3E,OAAO,aAAa,CAAC,MAAM;aACtB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,IAAI,UAAU,CAAC,CAAC,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;aAC9E,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACO,WAAW,CAAC,YAAoB,EAAE,aAAqB;QAC7D,OAAO,UAAU,CAAC,YAAY,EAAE,aAAa,CAAC;YAC1C,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,CAAC;CACJ;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACvC,YAA0C,EAC1C,OAAsC,EACtC,eAA8E;IAE9E,MAAM,aAAa,GAAG,YAAY,CAAC,aAAa,CAAC;IACjD,MAAM,aAAa,GAAG,YAAY,CAAC,aAAa,CAAC;IAEjD,6EAA6E;IAC7E,kEAAkE;IAClE,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,8BAA8B;IAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,QAAQ,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,qEAAqE;IACnI,IAAI,CAAC,WAAW,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;IAC/E,OAAO,MAAM,KAAK,IAAI,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gCAAgC,CAAC,eAAuB;IACpE,mGAAmG;IACnG,yFAAyF;IACzF,+FAA+F;IAC/F,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,eAAe,CAAC,yBAAyB,EAAE,eAAe,CAAC,CAAC;IAChH,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,OAAO,IAAI,yBAAyB,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,cAAc,CAChD,yBAAyB,EACzB,eAAe,CAClB,IAAI,IAAI,yBAAyB,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -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"}
|