@memberjunction/core-entities-server 6.2.0-edge.6 → 6.2.0-edge.7
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/README.md +155 -100
- package/dist/custom/MJAIAgentExampleEntityServer.server.d.ts +2 -0
- package/dist/custom/MJAIAgentExampleEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIAgentExampleEntityServer.server.js +8 -5
- package/dist/custom/MJAIAgentExampleEntityServer.server.js.map +1 -1
- package/dist/custom/MJAIAgentNoteEntityServer.server.d.ts +2 -0
- package/dist/custom/MJAIAgentNoteEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIAgentNoteEntityServer.server.js +8 -5
- package/dist/custom/MJAIAgentNoteEntityServer.server.js.map +1 -1
- package/dist/custom/MJAIAgentRelationshipEntityServer.server.d.ts +17 -0
- package/dist/custom/MJAIAgentRelationshipEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIAgentRelationshipEntityServer.server.js +71 -0
- package/dist/custom/MJAIAgentRelationshipEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIAgentSessionEntityServer.server.d.ts +8 -22
- package/dist/custom/MJAIAgentSessionEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIAgentSessionEntityServer.server.js +24 -1
- package/dist/custom/MJAIAgentSessionEntityServer.server.js.map +1 -1
- package/dist/custom/MJAIAgentUserPolicyEntityServer.server.d.ts +23 -0
- package/dist/custom/MJAIAgentUserPolicyEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIAgentUserPolicyEntityServer.server.js +53 -0
- package/dist/custom/MJAIAgentUserPolicyEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIPromptEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIPromptEntityServer.server.js +2 -1
- package/dist/custom/MJAIPromptEntityServer.server.js.map +1 -1
- package/dist/custom/MJBrandKitEntityServer.server.d.ts +14 -0
- package/dist/custom/MJBrandKitEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJBrandKitEntityServer.server.js +75 -0
- package/dist/custom/MJBrandKitEntityServer.server.js.map +1 -0
- package/dist/custom/MJCompanyIntegrationEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJCompanyIntegrationEntityServer.server.js +1 -0
- package/dist/custom/MJCompanyIntegrationEntityServer.server.js.map +1 -1
- package/dist/custom/MJComponentEntityServer.server.d.ts +2 -0
- package/dist/custom/MJComponentEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJComponentEntityServer.server.js +5 -1
- package/dist/custom/MJComponentEntityServer.server.js.map +1 -1
- package/dist/custom/MJEntityDocumentEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJEntityDocumentEntityServer.server.js +1 -0
- package/dist/custom/MJEntityDocumentEntityServer.server.js.map +1 -1
- package/dist/custom/MJListDetailEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJListDetailEntityServer.server.js +1 -0
- package/dist/custom/MJListDetailEntityServer.server.js.map +1 -1
- package/dist/custom/MJQueryEntityServer.server.d.ts +177 -47
- package/dist/custom/MJQueryEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJQueryEntityServer.server.js +465 -162
- package/dist/custom/MJQueryEntityServer.server.js.map +1 -1
- package/dist/custom/MJQuerySQLEntityServer.server.d.ts +15 -18
- package/dist/custom/MJQuerySQLEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJQuerySQLEntityServer.server.js +34 -42
- package/dist/custom/MJQuerySQLEntityServer.server.js.map +1 -1
- package/dist/custom/MJTagEntityServer.server.d.ts +2 -0
- package/dist/custom/MJTagEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJTagEntityServer.server.js +13 -7
- package/dist/custom/MJTagEntityServer.server.js.map +1 -1
- package/dist/custom/MJUserRoutineEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJUserRoutineEntityServer.server.js +8 -1
- package/dist/custom/MJUserRoutineEntityServer.server.js.map +1 -1
- package/dist/custom/query-extraction/childRowWriter.d.ts +77 -0
- package/dist/custom/query-extraction/childRowWriter.d.ts.map +1 -0
- package/dist/custom/query-extraction/childRowWriter.js +277 -0
- package/dist/custom/query-extraction/childRowWriter.js.map +1 -0
- package/dist/custom/query-extraction/compositionTargets.d.ts +40 -0
- package/dist/custom/query-extraction/compositionTargets.d.ts.map +1 -0
- package/dist/custom/query-extraction/compositionTargets.js +99 -0
- package/dist/custom/query-extraction/compositionTargets.js.map +1 -0
- package/dist/custom/query-extraction/declared.d.ts +79 -0
- package/dist/custom/query-extraction/declared.d.ts.map +1 -0
- package/dist/custom/query-extraction/declared.js +91 -0
- package/dist/custom/query-extraction/declared.js.map +1 -0
- package/dist/custom/query-extraction/describeFacts.d.ts +40 -0
- package/dist/custom/query-extraction/describeFacts.d.ts.map +1 -0
- package/dist/custom/query-extraction/describeFacts.js +225 -0
- package/dist/custom/query-extraction/describeFacts.js.map +1 -0
- package/dist/custom/query-extraction/descriptionPass.d.ts +23 -0
- package/dist/custom/query-extraction/descriptionPass.d.ts.map +1 -0
- package/dist/custom/query-extraction/descriptionPass.js +95 -0
- package/dist/custom/query-extraction/descriptionPass.js.map +1 -0
- package/dist/custom/query-extraction/extractQuery.d.ts +25 -0
- package/dist/custom/query-extraction/extractQuery.d.ts.map +1 -0
- package/dist/custom/query-extraction/extractQuery.js +146 -0
- package/dist/custom/query-extraction/extractQuery.js.map +1 -0
- package/dist/custom/query-extraction/extractionTypes.d.ts +133 -0
- package/dist/custom/query-extraction/extractionTypes.d.ts.map +1 -0
- package/dist/custom/query-extraction/extractionTypes.js +2 -0
- package/dist/custom/query-extraction/extractionTypes.js.map +1 -0
- package/dist/custom/query-extraction/index.d.ts +16 -0
- package/dist/custom/query-extraction/index.d.ts.map +1 -1
- package/dist/custom/query-extraction/index.js +11 -1
- package/dist/custom/query-extraction/index.js.map +1 -1
- package/dist/custom/query-extraction/pipeline.d.ts +6 -2
- package/dist/custom/query-extraction/pipeline.d.ts.map +1 -1
- package/dist/custom/query-extraction/pipeline.js +21 -14
- package/dist/custom/query-extraction/pipeline.js.map +1 -1
- package/dist/custom/query-extraction/resolve.d.ts +7 -2
- package/dist/custom/query-extraction/resolve.d.ts.map +1 -1
- package/dist/custom/query-extraction/resolve.js +17 -14
- package/dist/custom/query-extraction/resolve.js.map +1 -1
- package/dist/custom/query-extraction/sourceBinding.d.ts +57 -0
- package/dist/custom/query-extraction/sourceBinding.d.ts.map +1 -0
- package/dist/custom/query-extraction/sourceBinding.js +104 -0
- package/dist/custom/query-extraction/sourceBinding.js.map +1 -0
- package/dist/custom/query-extraction/sync.d.ts +27 -3
- package/dist/custom/query-extraction/sync.d.ts.map +1 -1
- package/dist/custom/query-extraction/sync.js +69 -44
- package/dist/custom/query-extraction/sync.js.map +1 -1
- package/dist/custom/query-extraction/templateFacts.d.ts +62 -0
- package/dist/custom/query-extraction/templateFacts.d.ts.map +1 -0
- package/dist/custom/query-extraction/templateFacts.js +229 -0
- package/dist/custom/query-extraction/templateFacts.js.map +1 -0
- package/dist/custom/query-extraction/types.d.ts +4 -0
- package/dist/custom/query-extraction/types.d.ts.map +1 -1
- package/dist/custom/query-refresh/QueryExtractionService.d.ts +54 -0
- package/dist/custom/query-refresh/QueryExtractionService.d.ts.map +1 -0
- package/dist/custom/query-refresh/QueryExtractionService.js +207 -0
- package/dist/custom/query-refresh/QueryExtractionService.js.map +1 -0
- package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.d.ts +28 -0
- package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.d.ts.map +1 -0
- package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.js +106 -0
- package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.js.map +1 -0
- package/dist/custom/query-refresh/index.d.ts +14 -0
- package/dist/custom/query-refresh/index.d.ts.map +1 -0
- package/dist/custom/query-refresh/index.js +10 -0
- package/dist/custom/query-refresh/index.js.map +1 -0
- package/dist/custom/query-refresh/queryRebind.d.ts +33 -0
- package/dist/custom/query-refresh/queryRebind.d.ts.map +1 -0
- package/dist/custom/query-refresh/queryRebind.js +85 -0
- package/dist/custom/query-refresh/queryRebind.js.map +1 -0
- package/dist/custom/query-refresh/refreshAuthorization.d.ts +29 -0
- package/dist/custom/query-refresh/refreshAuthorization.d.ts.map +1 -0
- package/dist/custom/query-refresh/refreshAuthorization.js +66 -0
- package/dist/custom/query-refresh/refreshAuthorization.js.map +1 -0
- package/dist/custom/query-refresh/refreshFormat.d.ts +16 -0
- package/dist/custom/query-refresh/refreshFormat.d.ts.map +1 -0
- package/dist/custom/query-refresh/refreshFormat.js +67 -0
- package/dist/custom/query-refresh/refreshFormat.js.map +1 -0
- package/dist/custom/query-refresh/refreshOrder.d.ts +25 -0
- package/dist/custom/query-refresh/refreshOrder.d.ts.map +1 -0
- package/dist/custom/query-refresh/refreshOrder.js +95 -0
- package/dist/custom/query-refresh/refreshOrder.js.map +1 -0
- package/dist/custom/query-refresh/refreshSelection.d.ts +15 -0
- package/dist/custom/query-refresh/refreshSelection.d.ts.map +1 -0
- package/dist/custom/query-refresh/refreshSelection.js +99 -0
- package/dist/custom/query-refresh/refreshSelection.js.map +1 -0
- package/dist/custom/query-refresh/refreshTypes.d.ts +81 -0
- package/dist/custom/query-refresh/refreshTypes.d.ts.map +1 -0
- package/dist/custom/query-refresh/refreshTypes.js +5 -0
- package/dist/custom/query-refresh/refreshTypes.js.map +1 -0
- package/dist/custom/util.d.ts +8 -0
- package/dist/custom/util.d.ts.map +1 -1
- package/dist/custom/util.js +18 -1
- package/dist/custom/util.js.map +1 -1
- package/dist/engines/QueryEngineServer.d.ts +4 -1
- package/dist/engines/QueryEngineServer.d.ts.map +1 -1
- package/dist/engines/QueryEngineServer.js +10 -7
- package/dist/engines/QueryEngineServer.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +30 -30
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BaseRemotableOperation, Metadata } from "@memberjunction/core";
|
|
8
|
+
import { QueryRefreshExtractionOperation } from "@memberjunction/core-entities";
|
|
9
|
+
import { RegisterClass } from "@memberjunction/global";
|
|
10
|
+
import { QueryExtractionService } from "./QueryExtractionService.js";
|
|
11
|
+
import { UserCanRefreshQueries } from "./refreshAuthorization.js";
|
|
12
|
+
import { FormatQueryRefreshSummary } from "./refreshFormat.js";
|
|
13
|
+
import { IsQueryRefreshProvider } from "./refreshTypes.js";
|
|
14
|
+
/**
|
|
15
|
+
* Server implementation of the `Query.RefreshExtraction` remote operation: refreshes one saved
|
|
16
|
+
* query, or a selection, through {@link QueryExtractionService}, reporting each query as a
|
|
17
|
+
* progress event as it finishes.
|
|
18
|
+
*
|
|
19
|
+
* `Authorize` refuses a caller without Update on `MJ: Queries` and Create, Update and Delete on
|
|
20
|
+
* its child entities; the service applies the same check and the per-query non-disclosure guard
|
|
21
|
+
* for every entry point. An API-key caller also needs the operation's `query:refresh` scope.
|
|
22
|
+
* A request for exactly one query ID, and nothing else, reports a query that does not exist and
|
|
23
|
+
* one the caller may not refresh with the same message.
|
|
24
|
+
*/
|
|
25
|
+
let QueryRefreshExtractionServerOperation = class QueryRefreshExtractionServerOperation extends QueryRefreshExtractionOperation {
|
|
26
|
+
async Authorize(_input, user) {
|
|
27
|
+
return UserCanRefreshQueries(user, Metadata.Provider.Entities); // global-provider-ok: Authorize receives no provider; entity permissions are process-global metadata
|
|
28
|
+
}
|
|
29
|
+
async InternalExecute(input, provider, user, context) {
|
|
30
|
+
if (!IsQueryRefreshProvider(provider))
|
|
31
|
+
throw new Error('Query.RefreshExtraction needs a database provider');
|
|
32
|
+
const filter = RefreshFilterOf(input);
|
|
33
|
+
if (!HasRefreshSelector(filter))
|
|
34
|
+
throw new Error('Give at least one selector: queryIDs, queryNames, categoryPath, entityNames or all.');
|
|
35
|
+
const service = new QueryExtractionService({ Provider: provider, User: user });
|
|
36
|
+
const options = {
|
|
37
|
+
DryRun: input.dryRun === true,
|
|
38
|
+
WithAI: input.withAI === true,
|
|
39
|
+
OnProgress: p => context.emitProgress({
|
|
40
|
+
OperationKey: this.OperationKey, Processed: p.Processed, Total: p.Total, Status: 'Running',
|
|
41
|
+
Message: `${p.Report.Outcome}: ${p.Report.QueryName || p.Report.QueryID}`,
|
|
42
|
+
Payload: { queryID: p.Report.QueryID, outcome: p.Report.Outcome },
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
const single = filter.QueryIDs?.length === 1 && Object.keys(filter).length === 1 ? filter.QueryIDs[0] : null;
|
|
46
|
+
const run = single
|
|
47
|
+
? { DryRun: options.DryRun === true, Reports: [await service.RefreshQuery(single, options)], Cycles: [] }
|
|
48
|
+
: await service.RefreshQueries(filter, options);
|
|
49
|
+
return RefreshOutputOf(run);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
QueryRefreshExtractionServerOperation = __decorate([
|
|
53
|
+
RegisterClass(BaseRemotableOperation, 'Query.RefreshExtraction')
|
|
54
|
+
], QueryRefreshExtractionServerOperation);
|
|
55
|
+
export { QueryRefreshExtractionServerOperation };
|
|
56
|
+
/** The service filter for an operation input; only the selectors the input gives. */
|
|
57
|
+
export function RefreshFilterOf(input) {
|
|
58
|
+
const filter = {};
|
|
59
|
+
if (input.queryIDs?.length)
|
|
60
|
+
filter.QueryIDs = input.queryIDs;
|
|
61
|
+
if (input.queryNames?.length)
|
|
62
|
+
filter.QueryNames = input.queryNames;
|
|
63
|
+
if (input.categoryPath?.trim())
|
|
64
|
+
filter.CategoryPath = input.categoryPath;
|
|
65
|
+
if (input.entityNames?.length)
|
|
66
|
+
filter.EntityNames = input.entityNames;
|
|
67
|
+
if (input.all === true)
|
|
68
|
+
filter.All = true;
|
|
69
|
+
return filter;
|
|
70
|
+
}
|
|
71
|
+
/** Whether a filter selects anything at all. */
|
|
72
|
+
export function HasRefreshSelector(filter) {
|
|
73
|
+
return filter.All === true || !!filter.QueryIDs?.length || !!filter.QueryNames?.length || !!filter.CategoryPath
|
|
74
|
+
|| !!filter.EntityNames?.length || !!filter.EntityIDs?.length;
|
|
75
|
+
}
|
|
76
|
+
/** A service run as the operation's output. */
|
|
77
|
+
export function RefreshOutputOf(run) {
|
|
78
|
+
return {
|
|
79
|
+
dryRun: run.DryRun,
|
|
80
|
+
reports: run.Reports.map(reportOutputOf),
|
|
81
|
+
cycles: run.Cycles.map(c => [...c]),
|
|
82
|
+
summary: FormatQueryRefreshSummary(run),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function reportOutputOf(report) {
|
|
86
|
+
return {
|
|
87
|
+
queryID: report.QueryID,
|
|
88
|
+
queryName: report.QueryName,
|
|
89
|
+
outcome: report.Outcome,
|
|
90
|
+
...(report.ExtractionStatus ? { extractionStatus: report.ExtractionStatus } : {}),
|
|
91
|
+
queryColumns: [...report.QueryColumns],
|
|
92
|
+
added: report.Added.map(changeOutputOf),
|
|
93
|
+
updated: report.Updated.map(changeOutputOf),
|
|
94
|
+
removed: report.Removed.map(changeOutputOf),
|
|
95
|
+
flagged: [...report.Flagged],
|
|
96
|
+
diagnostics: [...report.Diagnostics],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function changeOutputOf(change) {
|
|
100
|
+
return { collection: change.Collection, name: change.Name, changedColumns: [...change.ChangedColumns] };
|
|
101
|
+
}
|
|
102
|
+
/** Keeps the operation in the bundle: import and call it where the class must be registered. */
|
|
103
|
+
export function LoadQueryRefreshExtractionServerOperation() {
|
|
104
|
+
// The import is what registers the class.
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=QueryRefreshExtractionServerOperation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueryRefreshExtractionServerOperation.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/QueryRefreshExtractionServerOperation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { QueryExtractionService } from "./QueryExtractionService.js";
|
|
2
|
+
export { UserCanRefreshQueries, UserCanSeeQuerySources, EntityIDsOfExtraction, QueryRefreshUnavailableMessage, QUERY_REFRESH_CHILD_ENTITIES, } from "./refreshAuthorization.js";
|
|
3
|
+
export { OrderForRefresh } from "./refreshOrder.js";
|
|
4
|
+
export type { OrderableQuery, CompositionEdge, RefreshOrder } from "./refreshOrder.js";
|
|
5
|
+
export { SelectQueriesForRefresh } from "./refreshSelection.js";
|
|
6
|
+
export type { RefreshSelection } from "./refreshSelection.js";
|
|
7
|
+
export type { QueryRefreshProvider, QueryRefreshFilter, QueryRefreshOptions, QueryRefreshProgress, QueryRefreshOutcome, QueryRefreshReport, QueryRefreshRunReport, QueryRefreshContext, } from "./refreshTypes.js";
|
|
8
|
+
export { RebindQueriesForChangedEntities, DefinerRightsDriftMessage } from "./queryRebind.js";
|
|
9
|
+
export type { QueryRebindResult, QueryDefinerRightsDrift } from "./queryRebind.js";
|
|
10
|
+
export { FormatQueryRefreshRun, FormatQueryRefreshReport, FormatQueryRefreshSummary, QueryRefreshTotalsOf } from "./refreshFormat.js";
|
|
11
|
+
export type { QueryRefreshTotals } from "./refreshFormat.js";
|
|
12
|
+
export { IsQueryRefreshProvider } from "./refreshTypes.js";
|
|
13
|
+
export { QueryRefreshExtractionServerOperation, LoadQueryRefreshExtractionServerOperation, RefreshFilterOf, HasRefreshSelector, RefreshOutputOf, } from "./QueryRefreshExtractionServerOperation.js";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EACH,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,8BAA8B,EAC9B,4BAA4B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,YAAY,EACR,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,+BAA+B,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC3F,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACnI,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACH,qCAAqC,EACrC,yCAAyC,EACzC,eAAe,EACf,kBAAkB,EAClB,eAAe,GAClB,MAAM,yCAAyC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Query refresh — re-establishing saved queries' facts on demand, and its authorization
|
|
2
|
+
export { QueryExtractionService } from "./QueryExtractionService.js";
|
|
3
|
+
export { UserCanRefreshQueries, UserCanSeeQuerySources, EntityIDsOfExtraction, QueryRefreshUnavailableMessage, QUERY_REFRESH_CHILD_ENTITIES, } from "./refreshAuthorization.js";
|
|
4
|
+
export { OrderForRefresh } from "./refreshOrder.js";
|
|
5
|
+
export { SelectQueriesForRefresh } from "./refreshSelection.js";
|
|
6
|
+
export { RebindQueriesForChangedEntities, DefinerRightsDriftMessage } from "./queryRebind.js";
|
|
7
|
+
export { FormatQueryRefreshRun, FormatQueryRefreshReport, FormatQueryRefreshSummary, QueryRefreshTotalsOf } from "./refreshFormat.js";
|
|
8
|
+
export { IsQueryRefreshProvider } from "./refreshTypes.js";
|
|
9
|
+
export { QueryRefreshExtractionServerOperation, LoadQueryRefreshExtractionServerOperation, RefreshFilterOf, HasRefreshSelector, RefreshOutputOf, } from "./QueryRefreshExtractionServerOperation.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EACH,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,8BAA8B,EAC9B,4BAA4B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAY7D,OAAO,EAAE,+BAA+B,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEnI,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACH,qCAAqC,EACrC,yCAAyC,EACzC,eAAe,EACf,kBAAkB,EAClB,eAAe,GAClB,MAAM,yCAAyC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { QueryRefreshContext, QueryRefreshRunReport } from "./refreshTypes.js";
|
|
2
|
+
/** A query that runs with definer's rights and gained a column field-level security restricts. */
|
|
3
|
+
export interface QueryDefinerRightsDrift {
|
|
4
|
+
readonly QueryID: string;
|
|
5
|
+
readonly QueryName: string;
|
|
6
|
+
/** The added output columns, each sourced from a field that has field permissions. */
|
|
7
|
+
readonly Fields: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
/** What a re-bind pass did. */
|
|
10
|
+
export interface QueryRebindResult {
|
|
11
|
+
/** The changed entities the pass was given. */
|
|
12
|
+
readonly EntityCount: number;
|
|
13
|
+
readonly Run: QueryRefreshRunReport;
|
|
14
|
+
readonly Drift: readonly QueryDefinerRightsDrift[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Re-binds the saved queries that read entities whose fields changed (a column added, dropped, or
|
|
18
|
+
* changed in type or nullability): each query that reads one of them (its `MJ: Query Entities`
|
|
19
|
+
* rows) is refreshed, with no description pass, so its fields follow the schema again. The SQL is
|
|
20
|
+
* never edited: a query the change broke is recorded with status `Error` and the database
|
|
21
|
+
* server's message.
|
|
22
|
+
*
|
|
23
|
+
* A query with `MJ: Query Permissions` rows runs with definer's rights: its role list alone
|
|
24
|
+
* authorizes a run, whatever the SQL reads. When such a query gains an output column (a `*` over
|
|
25
|
+
* a view that gained one) sourced from a field that has field permissions, the column now reaches
|
|
26
|
+
* every role on the list. The pass records that on the query as a `Warning` and returns it.
|
|
27
|
+
*
|
|
28
|
+
* @param entityIDs - The entities whose fields changed.
|
|
29
|
+
*/
|
|
30
|
+
export declare function RebindQueriesForChangedEntities(context: QueryRefreshContext, entityIDs: readonly string[]): Promise<QueryRebindResult>;
|
|
31
|
+
/** The `ExtractionMessage` line for a definer's-rights query that gained restricted columns. */
|
|
32
|
+
export declare function DefinerRightsDriftMessage(fields: readonly string[]): string;
|
|
33
|
+
//# sourceMappingURL=queryRebind.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryRebind.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/queryRebind.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAsB,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAErG,kGAAkG;AAClG,MAAM,WAAW,uBAAuB;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,+BAA+B;AAC/B,MAAM,WAAW,iBAAiB;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,qBAAqB,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,uBAAuB,EAAE,CAAC;CACtD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,+BAA+B,CAAC,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAO5I;AAkDD,gGAAgG;AAChG,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAI3E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { LogError } from "@memberjunction/core";
|
|
2
|
+
import { EscapeSQLString, NormalizeUUID } from "@memberjunction/global";
|
|
3
|
+
import { QueryExtractionService } from "./QueryExtractionService.js";
|
|
4
|
+
/**
|
|
5
|
+
* Re-binds the saved queries that read entities whose fields changed (a column added, dropped, or
|
|
6
|
+
* changed in type or nullability): each query that reads one of them (its `MJ: Query Entities`
|
|
7
|
+
* rows) is refreshed, with no description pass, so its fields follow the schema again. The SQL is
|
|
8
|
+
* never edited: a query the change broke is recorded with status `Error` and the database
|
|
9
|
+
* server's message.
|
|
10
|
+
*
|
|
11
|
+
* A query with `MJ: Query Permissions` rows runs with definer's rights: its role list alone
|
|
12
|
+
* authorizes a run, whatever the SQL reads. When such a query gains an output column (a `*` over
|
|
13
|
+
* a view that gained one) sourced from a field that has field permissions, the column now reaches
|
|
14
|
+
* every role on the list. The pass records that on the query as a `Warning` and returns it.
|
|
15
|
+
*
|
|
16
|
+
* @param entityIDs - The entities whose fields changed.
|
|
17
|
+
*/
|
|
18
|
+
export async function RebindQueriesForChangedEntities(context, entityIDs) {
|
|
19
|
+
const unique = [...new Set(entityIDs.map(NormalizeUUID))];
|
|
20
|
+
if (unique.length === 0)
|
|
21
|
+
return { EntityCount: 0, Run: { DryRun: false, Reports: [], Cycles: [] }, Drift: [] };
|
|
22
|
+
const run = await new QueryExtractionService(context).RefreshQueries({ EntityIDs: unique }, { WithAI: false });
|
|
23
|
+
const drift = await findDefinerRightsDrift(context, run.Reports);
|
|
24
|
+
for (const item of drift)
|
|
25
|
+
await recordDrift(context, item);
|
|
26
|
+
return { EntityCount: unique.length, Run: run, Drift: drift };
|
|
27
|
+
}
|
|
28
|
+
/** The refreshed queries that run with definer's rights and gained a column sourced from a field with field permissions. */
|
|
29
|
+
async function findDefinerRightsDrift(context, reports) {
|
|
30
|
+
const restricted = restrictedFieldIDs(context.Provider.Entities);
|
|
31
|
+
const candidates = reports
|
|
32
|
+
.map(r => ({ Report: r, Fields: r.Added.filter(c => c.Collection === 'Fields' && c.SourceEntityFieldID && restricted.has(NormalizeUUID(c.SourceEntityFieldID))) }))
|
|
33
|
+
.filter(c => c.Fields.length > 0);
|
|
34
|
+
if (candidates.length === 0)
|
|
35
|
+
return [];
|
|
36
|
+
const definerRights = await queriesWithPermissions(context, candidates.map(c => c.Report.QueryID));
|
|
37
|
+
return candidates
|
|
38
|
+
.filter(c => definerRights.has(NormalizeUUID(c.Report.QueryID)))
|
|
39
|
+
.map(c => ({ QueryID: c.Report.QueryID, QueryName: c.Report.QueryName, Fields: c.Fields.map(f => f.Name) }));
|
|
40
|
+
}
|
|
41
|
+
/** The IDs of fields that have field permissions, or that belong to an entity field-level security applies to. */
|
|
42
|
+
function restrictedFieldIDs(entities) {
|
|
43
|
+
const ids = new Set();
|
|
44
|
+
for (const entity of entities) {
|
|
45
|
+
for (const field of entity.Fields) {
|
|
46
|
+
if (field.HasFieldPermissions || entity.FieldLevelSecurityApplies)
|
|
47
|
+
ids.add(NormalizeUUID(field.ID));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return ids;
|
|
51
|
+
}
|
|
52
|
+
async function queriesWithPermissions(context, queryIDs) {
|
|
53
|
+
const list = queryIDs.map(id => `'${EscapeSQLString(id)}'`).join(',');
|
|
54
|
+
const result = await context.Provider.RunView({
|
|
55
|
+
EntityName: 'MJ: Query Permissions', ExtraFilter: `QueryID IN (${list})`, Fields: ['QueryID'], ResultType: 'simple', BypassCache: true, IgnoreMaxRows: true,
|
|
56
|
+
}, context.User);
|
|
57
|
+
if (!result.Success)
|
|
58
|
+
throw new Error(`the query permissions could not be read: ${result.ErrorMessage}`);
|
|
59
|
+
return new Set(result.Results.map(r => NormalizeUUID(r.QueryID)));
|
|
60
|
+
}
|
|
61
|
+
/** Records the drift on the query: status `Warning` (unless it is already `Error`) and a message line. */
|
|
62
|
+
async function recordDrift(context, drift) {
|
|
63
|
+
const message = DefinerRightsDriftMessage(drift.Fields);
|
|
64
|
+
try {
|
|
65
|
+
const query = await context.Provider.GetEntityObject('MJ: Queries', context.User);
|
|
66
|
+
if (!await query.Load(drift.QueryID))
|
|
67
|
+
throw new Error('the query could not be loaded');
|
|
68
|
+
if (query.ExtractionStatus !== 'Error')
|
|
69
|
+
query.ExtractionStatus = 'Warning';
|
|
70
|
+
query.ExtractionMessage = query.ExtractionMessage ? `${query.ExtractionMessage}\n${message}` : message;
|
|
71
|
+
query.ExtractionOverride = 'Skip';
|
|
72
|
+
if (!await query.Save())
|
|
73
|
+
throw new Error(query.LatestResult?.CompleteMessage ?? 'the save failed');
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
LogError(`[RebindQueriesForChangedEntities] the definer's-rights warning for query "${drift.QueryName}" could not be recorded: ${e instanceof Error ? e.message : String(e)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** The `ExtractionMessage` line for a definer's-rights query that gained restricted columns. */
|
|
80
|
+
export function DefinerRightsDriftMessage(fields) {
|
|
81
|
+
return `The query's output gained ${fields.map(f => `"${f}"`).join(', ')}, sourced from fields restricted by field-level security. ` +
|
|
82
|
+
'The query is authorized by its Query Permissions role list alone, so every role on that list can now read these columns. ' +
|
|
83
|
+
'Review the role list, or name the columns the query returns instead of using *.';
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=queryRebind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryRebind.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/queryRebind.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { EntityInfo, UserInfo } from "@memberjunction/core";
|
|
2
|
+
import type { QueryExtractionResult } from "../query-extraction/index.js";
|
|
3
|
+
/** The child entities a refresh creates, updates and deletes rows of. */
|
|
4
|
+
export declare const QUERY_REFRESH_CHILD_ENTITIES: readonly string[];
|
|
5
|
+
/**
|
|
6
|
+
* Whether `user` may refresh queries at all: Update on `MJ: Queries`, and Create, Update and
|
|
7
|
+
* Delete on each of its derived child entities. A dry run needs the same, since its report
|
|
8
|
+
* discloses the same metadata. Run permissions (`MJ: Query Permissions`) play no part: refreshing
|
|
9
|
+
* is an authoring act.
|
|
10
|
+
*/
|
|
11
|
+
export declare function UserCanRefreshQueries(user: UserInfo, entities: readonly EntityInfo[]): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether `user` may see what a refresh of one query reveals: the column names, types and
|
|
14
|
+
* sources of every entity the query reads. True only when the user can read every one of those
|
|
15
|
+
* entities and is denied no field on any of them.
|
|
16
|
+
*
|
|
17
|
+
* @param entityIDs - The entities the query reads: its stored links and field sources, and the ones
|
|
18
|
+
* the refresh found ({@link EntityIDsOfExtraction}).
|
|
19
|
+
*/
|
|
20
|
+
export declare function UserCanSeeQuerySources(user: UserInfo, entityIDs: Iterable<string>, entities: readonly EntityInfo[]): boolean;
|
|
21
|
+
/** The entities a refresh's facts link the query to: the entities it reads and its fields' sources. */
|
|
22
|
+
export declare function EntityIDsOfExtraction(extraction: QueryExtractionResult): string[];
|
|
23
|
+
/**
|
|
24
|
+
* The message for a refresh of a query that does not exist or that the caller may not refresh.
|
|
25
|
+
* The two are worded identically, so a refusal does not confirm that the query, or anything it
|
|
26
|
+
* reads, exists. It echoes only the identifier the caller supplied.
|
|
27
|
+
*/
|
|
28
|
+
export declare function QueryRefreshUnavailableMessage(label: string): string;
|
|
29
|
+
//# sourceMappingURL=refreshAuthorization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshAuthorization.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshAuthorization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,yEAAyE;AACzE,eAAO,MAAM,4BAA4B,EAAE,SAAS,MAAM,EAKzD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAU9F;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAS5H;AAED,uGAAuG;AACvG,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,qBAAqB,GAAG,MAAM,EAAE,CAMjF;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { NormalizeUUID } from "@memberjunction/global";
|
|
2
|
+
/** The child entities a refresh creates, updates and deletes rows of. */
|
|
3
|
+
export const QUERY_REFRESH_CHILD_ENTITIES = [
|
|
4
|
+
'MJ: Query Fields',
|
|
5
|
+
'MJ: Query Parameters',
|
|
6
|
+
'MJ: Query Entities',
|
|
7
|
+
'MJ: Query Dependencies',
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Whether `user` may refresh queries at all: Update on `MJ: Queries`, and Create, Update and
|
|
11
|
+
* Delete on each of its derived child entities. A dry run needs the same, since its report
|
|
12
|
+
* discloses the same metadata. Run permissions (`MJ: Query Permissions`) play no part: refreshing
|
|
13
|
+
* is an authoring act.
|
|
14
|
+
*/
|
|
15
|
+
export function UserCanRefreshQueries(user, entities) {
|
|
16
|
+
const byName = new Map(entities.map(e => [e.Name.toLowerCase(), e]));
|
|
17
|
+
const query = byName.get('mj: queries');
|
|
18
|
+
if (!query || !query.GetUserPermisions(user)?.CanUpdate)
|
|
19
|
+
return false;
|
|
20
|
+
return QUERY_REFRESH_CHILD_ENTITIES.every(name => {
|
|
21
|
+
const entity = byName.get(name.toLowerCase());
|
|
22
|
+
if (!entity)
|
|
23
|
+
return false;
|
|
24
|
+
const permissions = entity.GetUserPermisions(user);
|
|
25
|
+
return !!permissions && permissions.CanCreate && permissions.CanUpdate && permissions.CanDelete;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether `user` may see what a refresh of one query reveals: the column names, types and
|
|
30
|
+
* sources of every entity the query reads. True only when the user can read every one of those
|
|
31
|
+
* entities and is denied no field on any of them.
|
|
32
|
+
*
|
|
33
|
+
* @param entityIDs - The entities the query reads: its stored links and field sources, and the ones
|
|
34
|
+
* the refresh found ({@link EntityIDsOfExtraction}).
|
|
35
|
+
*/
|
|
36
|
+
export function UserCanSeeQuerySources(user, entityIDs, entities) {
|
|
37
|
+
const byID = new Map(entities.map(e => [NormalizeUUID(e.ID), e]));
|
|
38
|
+
for (const id of entityIDs) {
|
|
39
|
+
const entity = byID.get(NormalizeUUID(id));
|
|
40
|
+
if (!entity)
|
|
41
|
+
return false;
|
|
42
|
+
if (!entity.GetUserPermisions(user)?.CanRead)
|
|
43
|
+
return false;
|
|
44
|
+
if (entity.FieldLevelSecurityApplies && entity.GetDeniedReadFields(user).size > 0)
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
/** The entities a refresh's facts link the query to: the entities it reads and its fields' sources. */
|
|
50
|
+
export function EntityIDsOfExtraction(extraction) {
|
|
51
|
+
const ids = (extraction.Entities ?? []).map(e => e.EntityID);
|
|
52
|
+
for (const field of extraction.Fields ?? []) {
|
|
53
|
+
if (field.SourceEntityID)
|
|
54
|
+
ids.push(field.SourceEntityID);
|
|
55
|
+
}
|
|
56
|
+
return ids;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The message for a refresh of a query that does not exist or that the caller may not refresh.
|
|
60
|
+
* The two are worded identically, so a refusal does not confirm that the query, or anything it
|
|
61
|
+
* reads, exists. It echoes only the identifier the caller supplied.
|
|
62
|
+
*/
|
|
63
|
+
export function QueryRefreshUnavailableMessage(label) {
|
|
64
|
+
return `Query '${label}' does not exist or you do not have permission to refresh it.`;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=refreshAuthorization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshAuthorization.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshAuthorization.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,yEAAyE;AACzE,MAAM,CAAC,MAAM,4BAA4B,GAAsB;IAC3D,kBAAkB;IAClB,sBAAsB;IACtB,oBAAoB;IACpB,wBAAwB;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAc,EAAE,QAA+B;IACjF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,SAAS;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,4BAA4B,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;QAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,WAAW,GAAG,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC,WAAW,IAAI,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,CAAC;IACpG,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAc,EAAE,SAA2B,EAAE,QAA+B;IAC/G,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3D,IAAI,MAAM,CAAC,yBAAyB,IAAI,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IACpG,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,qBAAqB,CAAC,UAAiC;IACnE,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC7D,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,KAAK,CAAC,cAAc;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,KAAa;IACxD,OAAO,UAAU,KAAK,+DAA+D,CAAC;AAC1F,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { QueryRefreshOutcome, QueryRefreshReport, QueryRefreshRunReport } from "./refreshTypes.js";
|
|
2
|
+
/** How many queries of a run ended in each outcome. */
|
|
3
|
+
export type QueryRefreshTotals = Record<QueryRefreshOutcome, number>;
|
|
4
|
+
/** The number of reports per outcome. */
|
|
5
|
+
export declare function QueryRefreshTotalsOf(run: QueryRefreshRunReport): QueryRefreshTotals;
|
|
6
|
+
/** One line summing a run up: `12 refreshed, 19 unchanged, 0 failed (dry run)`. */
|
|
7
|
+
export declare function FormatQueryRefreshSummary(run: QueryRefreshRunReport): string;
|
|
8
|
+
/**
|
|
9
|
+
* The lines of a run's report: one heading per query, then its row changes, flagged parameters
|
|
10
|
+
* and diagnostics, then any composition cycles and the summary. With `changedOnly`, unchanged
|
|
11
|
+
* queries with no diagnostics are left out.
|
|
12
|
+
*/
|
|
13
|
+
export declare function FormatQueryRefreshRun(run: QueryRefreshRunReport, changedOnly?: boolean): string[];
|
|
14
|
+
/** The lines of one query's report. */
|
|
15
|
+
export declare function FormatQueryRefreshReport(report: QueryRefreshReport): string[];
|
|
16
|
+
//# sourceMappingURL=refreshFormat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshFormat.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshFormat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAErG,uDAAuD;AACvD,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;AAErE,yCAAyC;AACzC,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,qBAAqB,GAAG,kBAAkB,CAInF;AAED,mFAAmF;AACnF,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAS5E;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,qBAAqB,EAAE,WAAW,UAAQ,GAAG,MAAM,EAAE,CAS/F;AAED,uCAAuC;AACvC,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAW7E"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** The number of reports per outcome. */
|
|
2
|
+
export function QueryRefreshTotalsOf(run) {
|
|
3
|
+
const totals = { Refreshed: 0, Unchanged: 0, NotPermitted: 0, Unavailable: 0, Failed: 0 };
|
|
4
|
+
for (const report of run.Reports)
|
|
5
|
+
totals[report.Outcome]++;
|
|
6
|
+
return totals;
|
|
7
|
+
}
|
|
8
|
+
/** One line summing a run up: `12 refreshed, 19 unchanged, 0 failed (dry run)`. */
|
|
9
|
+
export function FormatQueryRefreshSummary(run) {
|
|
10
|
+
const t = QueryRefreshTotalsOf(run);
|
|
11
|
+
const verb = run.DryRun ? 'would be refreshed' : 'refreshed';
|
|
12
|
+
const parts = [`${t.Refreshed} ${verb}`, `${t.Unchanged} unchanged`, `${t.Failed} failed`];
|
|
13
|
+
if (t.NotPermitted > 0)
|
|
14
|
+
parts.push(`${t.NotPermitted} skipped (not permitted)`);
|
|
15
|
+
if (t.Unavailable > 0)
|
|
16
|
+
parts.push(`${t.Unavailable} unavailable`);
|
|
17
|
+
const withErrors = run.Reports.filter(r => r.ExtractionStatus === 'Error').length;
|
|
18
|
+
if (withErrors > 0)
|
|
19
|
+
parts.push(`${withErrors} with extraction errors`);
|
|
20
|
+
return `${run.Reports.length} queries: ${parts.join(', ')}${run.DryRun ? ' (dry run: nothing was written)' : ''}.`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The lines of a run's report: one heading per query, then its row changes, flagged parameters
|
|
24
|
+
* and diagnostics, then any composition cycles and the summary. With `changedOnly`, unchanged
|
|
25
|
+
* queries with no diagnostics are left out.
|
|
26
|
+
*/
|
|
27
|
+
export function FormatQueryRefreshRun(run, changedOnly = false) {
|
|
28
|
+
const lines = [];
|
|
29
|
+
for (const report of run.Reports) {
|
|
30
|
+
if (changedOnly && report.Outcome === 'Unchanged' && report.Diagnostics.length === 0)
|
|
31
|
+
continue;
|
|
32
|
+
lines.push(...FormatQueryRefreshReport(report));
|
|
33
|
+
}
|
|
34
|
+
for (const cycle of run.Cycles)
|
|
35
|
+
lines.push(`Composition cycle (each refreshed once): ${cycle.join(' -> ')}`);
|
|
36
|
+
lines.push(FormatQueryRefreshSummary(run));
|
|
37
|
+
return lines;
|
|
38
|
+
}
|
|
39
|
+
/** The lines of one query's report. */
|
|
40
|
+
export function FormatQueryRefreshReport(report) {
|
|
41
|
+
const name = report.QueryName ? `"${report.QueryName}" (${report.QueryID})` : report.QueryID;
|
|
42
|
+
const status = report.ExtractionStatus ? ` [${report.ExtractionStatus}]` : '';
|
|
43
|
+
const lines = [`${outcomeLabel(report)} ${name}${status}`];
|
|
44
|
+
if (report.QueryColumns.length > 0)
|
|
45
|
+
lines.push(` ~ query (${report.QueryColumns.join(', ')})`);
|
|
46
|
+
for (const change of report.Added)
|
|
47
|
+
lines.push(` + ${changeLabel(change)}`);
|
|
48
|
+
for (const change of report.Updated)
|
|
49
|
+
lines.push(` ~ ${changeLabel(change)}${change.ChangedColumns.length > 0 ? ` (${change.ChangedColumns.join(', ')})` : ''}`);
|
|
50
|
+
for (const change of report.Removed)
|
|
51
|
+
lines.push(` - ${changeLabel(change)}`);
|
|
52
|
+
for (const name of report.Flagged)
|
|
53
|
+
lines.push(` ! parameter ${name}: authored, no longer used by the SQL (kept)`);
|
|
54
|
+
for (const line of report.Diagnostics)
|
|
55
|
+
lines.push(` ${line}`);
|
|
56
|
+
return lines;
|
|
57
|
+
}
|
|
58
|
+
function outcomeLabel(report) {
|
|
59
|
+
if (report.Outcome === 'Refreshed')
|
|
60
|
+
return report.DryRun ? 'WOULD REFRESH' : 'REFRESHED';
|
|
61
|
+
return report.Outcome.toUpperCase();
|
|
62
|
+
}
|
|
63
|
+
function changeLabel(change) {
|
|
64
|
+
const kind = { Fields: 'field', Parameters: 'parameter', Entities: 'entity', Dependencies: 'dependency' }[change.Collection];
|
|
65
|
+
return `${kind} ${change.Name}`;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=refreshFormat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshFormat.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshFormat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** A query to order, by ID, with a name for reporting. */
|
|
2
|
+
export interface OrderableQuery {
|
|
3
|
+
readonly ID: string;
|
|
4
|
+
readonly Name: string;
|
|
5
|
+
}
|
|
6
|
+
/** A composition edge: `QueryID` composes `DependsOnQueryID`. */
|
|
7
|
+
export interface CompositionEdge {
|
|
8
|
+
readonly QueryID: string;
|
|
9
|
+
readonly DependsOnQueryID: string;
|
|
10
|
+
}
|
|
11
|
+
/** The order to refresh in, and the composition cycles found. */
|
|
12
|
+
export interface RefreshOrder {
|
|
13
|
+
readonly Order: readonly OrderableQuery[];
|
|
14
|
+
/** Each cycle's members, by name. */
|
|
15
|
+
readonly Cycles: readonly (readonly string[])[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Orders `queries` so each comes after every selected query it composes (dependencies first),
|
|
19
|
+
* keeping the given order among queries that do not depend on each other. Queries in a
|
|
20
|
+
* composition cycle (stored by an older version that allowed one), and those that depend on
|
|
21
|
+
* them, come last in their given order; each is listed once, so a cycle is never followed round.
|
|
22
|
+
* Edges to queries outside the selection are ignored.
|
|
23
|
+
*/
|
|
24
|
+
export declare function OrderForRefresh(queries: readonly OrderableQuery[], edges: readonly CompositionEdge[]): RefreshOrder;
|
|
25
|
+
//# sourceMappingURL=refreshOrder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshOrder.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshOrder.ts"],"names":[],"mappings":"AAEA,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACrC;AAED,iEAAiE;AACjE,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAC1C,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;CACnD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,YAAY,CAmBnH"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { NormalizeUUID } from "@memberjunction/global";
|
|
2
|
+
/**
|
|
3
|
+
* Orders `queries` so each comes after every selected query it composes (dependencies first),
|
|
4
|
+
* keeping the given order among queries that do not depend on each other. Queries in a
|
|
5
|
+
* composition cycle (stored by an older version that allowed one), and those that depend on
|
|
6
|
+
* them, come last in their given order; each is listed once, so a cycle is never followed round.
|
|
7
|
+
* Edges to queries outside the selection are ignored.
|
|
8
|
+
*/
|
|
9
|
+
export function OrderForRefresh(queries, edges) {
|
|
10
|
+
const ids = queries.map(q => NormalizeUUID(q.ID));
|
|
11
|
+
const selected = new Set(ids);
|
|
12
|
+
const dependsOn = new Map(ids.map(id => [id, new Set()]));
|
|
13
|
+
for (const edge of edges) {
|
|
14
|
+
const from = NormalizeUUID(edge.QueryID);
|
|
15
|
+
const to = NormalizeUUID(edge.DependsOnQueryID);
|
|
16
|
+
if (selected.has(from) && selected.has(to) && from !== to)
|
|
17
|
+
dependsOn.get(from)?.add(to);
|
|
18
|
+
if (from === to && selected.has(from))
|
|
19
|
+
dependsOn.get(from)?.add(to);
|
|
20
|
+
}
|
|
21
|
+
const ordered = topologicalOrder(ids, dependsOn);
|
|
22
|
+
const placed = new Set(ordered);
|
|
23
|
+
const rest = ids.filter(id => !placed.has(id));
|
|
24
|
+
const byID = new Map(queries.map(q => [NormalizeUUID(q.ID), q]));
|
|
25
|
+
const nameOf = (id) => byID.get(id)?.Name ?? id;
|
|
26
|
+
return {
|
|
27
|
+
Order: [...ordered, ...rest].map(id => byID.get(id)).filter((q) => q !== undefined),
|
|
28
|
+
Cycles: findCycles(rest, dependsOn).map(cycle => cycle.map(nameOf)),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Kahn's algorithm, stable in the given order; nodes on or behind a cycle are left out. */
|
|
32
|
+
function topologicalOrder(ids, dependsOn) {
|
|
33
|
+
const done = new Set();
|
|
34
|
+
const ordered = [];
|
|
35
|
+
let progressed = true;
|
|
36
|
+
while (progressed) {
|
|
37
|
+
progressed = false;
|
|
38
|
+
for (const id of ids) {
|
|
39
|
+
if (done.has(id))
|
|
40
|
+
continue;
|
|
41
|
+
const deps = dependsOn.get(id) ?? new Set();
|
|
42
|
+
if ([...deps].every(d => done.has(d))) {
|
|
43
|
+
done.add(id);
|
|
44
|
+
ordered.push(id);
|
|
45
|
+
progressed = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return ordered;
|
|
50
|
+
}
|
|
51
|
+
/** The strongly connected components among `ids` that are cycles (more than one member, or a self-reference). */
|
|
52
|
+
function findCycles(ids, dependsOn) {
|
|
53
|
+
const within = new Set(ids);
|
|
54
|
+
const index = new Map();
|
|
55
|
+
const low = new Map();
|
|
56
|
+
const stack = [];
|
|
57
|
+
const onStack = new Set();
|
|
58
|
+
const cycles = [];
|
|
59
|
+
let counter = 0;
|
|
60
|
+
const visit = (id) => {
|
|
61
|
+
index.set(id, counter);
|
|
62
|
+
low.set(id, counter++);
|
|
63
|
+
stack.push(id);
|
|
64
|
+
onStack.add(id);
|
|
65
|
+
for (const dep of dependsOn.get(id) ?? []) {
|
|
66
|
+
if (!within.has(dep))
|
|
67
|
+
continue;
|
|
68
|
+
if (!index.has(dep)) {
|
|
69
|
+
visit(dep);
|
|
70
|
+
low.set(id, Math.min(low.get(id) ?? 0, low.get(dep) ?? 0));
|
|
71
|
+
}
|
|
72
|
+
else if (onStack.has(dep)) {
|
|
73
|
+
low.set(id, Math.min(low.get(id) ?? 0, index.get(dep) ?? 0));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (low.get(id) !== index.get(id))
|
|
77
|
+
return;
|
|
78
|
+
const component = [];
|
|
79
|
+
let member;
|
|
80
|
+
do {
|
|
81
|
+
member = stack.pop();
|
|
82
|
+
if (member === undefined)
|
|
83
|
+
break;
|
|
84
|
+
onStack.delete(member);
|
|
85
|
+
component.push(member);
|
|
86
|
+
} while (member !== id);
|
|
87
|
+
if (component.length > 1 || dependsOn.get(id)?.has(id))
|
|
88
|
+
cycles.push(component.reverse());
|
|
89
|
+
};
|
|
90
|
+
for (const id of ids)
|
|
91
|
+
if (!index.has(id))
|
|
92
|
+
visit(id);
|
|
93
|
+
return cycles;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=refreshOrder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshOrder.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshOrder.ts"],"names":[],"mappings":"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"}
|