@memberjunction/core 3.4.0 → 4.1.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/dist/generic/InMemoryLocalStorageProvider.d.ts +1 -1
- package/dist/generic/InMemoryLocalStorageProvider.js +2 -6
- package/dist/generic/InMemoryLocalStorageProvider.js.map +1 -1
- package/dist/generic/QueryCache.d.ts +1 -1
- package/dist/generic/QueryCache.js +6 -10
- package/dist/generic/QueryCache.js.map +1 -1
- package/dist/generic/QueryCacheConfig.js +1 -2
- package/dist/generic/RegisterForStartup.d.ts +2 -2
- package/dist/generic/RegisterForStartup.js +7 -12
- package/dist/generic/RegisterForStartup.js.map +1 -1
- package/dist/generic/applicationInfo.d.ts +3 -3
- package/dist/generic/applicationInfo.js +4 -10
- package/dist/generic/applicationInfo.js.map +1 -1
- package/dist/generic/authEvaluator.d.ts +1 -1
- package/dist/generic/authEvaluator.js +4 -8
- package/dist/generic/authEvaluator.js.map +1 -1
- package/dist/generic/authTypes.js +1 -4
- package/dist/generic/authTypes.js.map +1 -1
- package/dist/generic/baseEngine.d.ts +5 -5
- package/dist/generic/baseEngine.js +51 -56
- package/dist/generic/baseEngine.js.map +1 -1
- package/dist/generic/baseEngineRegistry.js +13 -17
- package/dist/generic/baseEngineRegistry.js.map +1 -1
- package/dist/generic/baseEntity.d.ts +171 -5
- package/dist/generic/baseEntity.d.ts.map +1 -1
- package/dist/generic/baseEntity.js +651 -121
- package/dist/generic/baseEntity.js.map +1 -1
- package/dist/generic/baseInfo.js +3 -7
- package/dist/generic/baseInfo.js.map +1 -1
- package/dist/generic/compositeKey.d.ts +2 -2
- package/dist/generic/compositeKey.js +5 -11
- package/dist/generic/compositeKey.js.map +1 -1
- package/dist/generic/databaseProviderBase.d.ts +2 -2
- package/dist/generic/databaseProviderBase.js +2 -6
- package/dist/generic/databaseProviderBase.js.map +1 -1
- package/dist/generic/entityInfo.d.ts +84 -5
- package/dist/generic/entityInfo.d.ts.map +1 -1
- package/dist/generic/entityInfo.js +235 -108
- package/dist/generic/entityInfo.js.map +1 -1
- package/dist/generic/explorerNavigationItem.d.ts +1 -1
- package/dist/generic/explorerNavigationItem.js +2 -6
- package/dist/generic/explorerNavigationItem.js.map +1 -1
- package/dist/generic/graphqlTypeNames.d.ts +1 -1
- package/dist/generic/graphqlTypeNames.js +4 -9
- package/dist/generic/graphqlTypeNames.js.map +1 -1
- package/dist/generic/interfaces.d.ts +104 -14
- package/dist/generic/interfaces.d.ts.map +1 -1
- package/dist/generic/interfaces.js +28 -30
- package/dist/generic/interfaces.js.map +1 -1
- package/dist/generic/libraryInfo.d.ts +1 -1
- package/dist/generic/libraryInfo.js +2 -6
- package/dist/generic/libraryInfo.js.map +1 -1
- package/dist/generic/localCacheManager.d.ts +2 -2
- package/dist/generic/localCacheManager.js +44 -48
- package/dist/generic/localCacheManager.js.map +1 -1
- package/dist/generic/logging.d.ts.map +1 -1
- package/dist/generic/logging.js +54 -67
- package/dist/generic/logging.js.map +1 -1
- package/dist/generic/metadata.d.ts +12 -12
- package/dist/generic/metadata.d.ts.map +1 -1
- package/dist/generic/metadata.js +21 -25
- package/dist/generic/metadata.js.map +1 -1
- package/dist/generic/metadataUtil.d.ts +1 -1
- package/dist/generic/metadataUtil.js +3 -7
- package/dist/generic/metadataUtil.js.map +1 -1
- package/dist/generic/providerBase.d.ts +63 -16
- package/dist/generic/providerBase.d.ts.map +1 -1
- package/dist/generic/providerBase.js +253 -130
- package/dist/generic/providerBase.js.map +1 -1
- package/dist/generic/queryInfo.d.ts +5 -5
- package/dist/generic/queryInfo.js +21 -30
- package/dist/generic/queryInfo.js.map +1 -1
- package/dist/generic/queryInfoInterfaces.js +1 -2
- package/dist/generic/queryInfoInterfaces.js.map +1 -1
- package/dist/generic/querySQLFilters.js +5 -10
- package/dist/generic/querySQLFilters.js.map +1 -1
- package/dist/generic/runQuery.d.ts +2 -2
- package/dist/generic/runQuery.js +5 -9
- package/dist/generic/runQuery.js.map +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.d.ts +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.js +4 -8
- package/dist/generic/runQuerySQLFilterImplementations.js.map +1 -1
- package/dist/generic/runReport.d.ts +2 -2
- package/dist/generic/runReport.js +5 -9
- package/dist/generic/runReport.js.map +1 -1
- package/dist/generic/securityInfo.d.ts +2 -2
- package/dist/generic/securityInfo.js +10 -20
- package/dist/generic/securityInfo.js.map +1 -1
- package/dist/generic/telemetryManager.js +20 -32
- package/dist/generic/telemetryManager.js.map +1 -1
- package/dist/generic/transactionGroup.d.ts +1 -1
- package/dist/generic/transactionGroup.d.ts.map +1 -1
- package/dist/generic/transactionGroup.js +11 -19
- package/dist/generic/transactionGroup.js.map +1 -1
- package/dist/generic/util.js +15 -31
- package/dist/generic/util.js.map +1 -1
- package/dist/index.d.ts +34 -34
- package/dist/index.js +45 -63
- package/dist/index.js.map +1 -1
- package/dist/views/runView.d.ts +3 -3
- package/dist/views/runView.js +6 -11
- package/dist/views/runView.js.map +1 -1
- package/dist/views/viewInfo.d.ts +3 -3
- package/dist/views/viewInfo.js +10 -17
- package/dist/views/viewInfo.js.map +1 -1
- package/package.json +11 -10
- package/readme.md +871 -1271
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const compositeKey_1 = require("./compositeKey");
|
|
17
|
-
const explorerNavigationItem_1 = require("./explorerNavigationItem");
|
|
18
|
-
const metadata_1 = require("./metadata");
|
|
19
|
-
const runView_1 = require("../views/runView");
|
|
1
|
+
import { BaseEntity } from "./baseEntity.js";
|
|
2
|
+
import { EntityDocumentTypeInfo, EntityInfo } from "./entityInfo.js";
|
|
3
|
+
import { AllMetadata } from "./interfaces.js";
|
|
4
|
+
import { LocalCacheManager } from "./localCacheManager.js";
|
|
5
|
+
import { ApplicationInfo } from "../generic/applicationInfo.js";
|
|
6
|
+
import { AuditLogTypeInfo, AuthorizationInfo, RoleInfo, RowLevelSecurityFilterInfo, UserInfo } from "./securityInfo.js";
|
|
7
|
+
import { MJGlobal } from "@memberjunction/global";
|
|
8
|
+
import { TelemetryManager } from "./telemetryManager.js";
|
|
9
|
+
import { LogError, LogStatus, LogStatusEx } from "./logging.js";
|
|
10
|
+
import { QueryCategoryInfo, QueryFieldInfo, QueryInfo, QueryPermissionInfo, QueryEntityInfo, QueryParameterInfo } from "./queryInfo.js";
|
|
11
|
+
import { LibraryInfo } from "./libraryInfo.js";
|
|
12
|
+
import { CompositeKey } from "./compositeKey.js";
|
|
13
|
+
import { ExplorerNavigationItem } from "./explorerNavigationItem.js";
|
|
14
|
+
import { Metadata } from "./metadata.js";
|
|
15
|
+
import { RunView } from "../views/runView.js";
|
|
20
16
|
/**
|
|
21
17
|
* Creates a new instance of AllMetadata from a simple object.
|
|
22
18
|
* Handles deserialization and proper instantiation of all metadata classes.
|
|
@@ -24,17 +20,16 @@ const runView_1 = require("../views/runView");
|
|
|
24
20
|
* @param md - The metadata provider for context
|
|
25
21
|
* @returns A fully populated AllMetadata instance with proper type instances
|
|
26
22
|
*/
|
|
27
|
-
function MetadataFromSimpleObject(data, md) {
|
|
23
|
+
export function MetadataFromSimpleObject(data, md) {
|
|
28
24
|
try {
|
|
29
25
|
const newObject = MetadataFromSimpleObjectWithoutUser(data, md);
|
|
30
|
-
newObject.CurrentUser = data.CurrentUser ? new
|
|
26
|
+
newObject.CurrentUser = data.CurrentUser ? new UserInfo(md, data.CurrentUser) : null;
|
|
31
27
|
return newObject;
|
|
32
28
|
}
|
|
33
29
|
catch (e) {
|
|
34
|
-
|
|
30
|
+
LogError(e);
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
|
-
exports.MetadataFromSimpleObject = MetadataFromSimpleObject;
|
|
38
33
|
/**
|
|
39
34
|
* Creates a new instance of AllMetadata from a simple object, but does NOT set the CurrentUser property
|
|
40
35
|
* Handles deserialization and proper instantiation of all metadata classes.
|
|
@@ -42,11 +37,11 @@ exports.MetadataFromSimpleObject = MetadataFromSimpleObject;
|
|
|
42
37
|
* @param md - The metadata provider for context
|
|
43
38
|
* @returns A fully populated AllMetadata instance with proper type instances
|
|
44
39
|
*/
|
|
45
|
-
function MetadataFromSimpleObjectWithoutUser(data, md) {
|
|
40
|
+
export function MetadataFromSimpleObjectWithoutUser(data, md) {
|
|
46
41
|
try {
|
|
47
|
-
const returnMetadata = new
|
|
42
|
+
const returnMetadata = new AllMetadata();
|
|
48
43
|
// now iterate through the AllMetadataMapping array and construct the return type
|
|
49
|
-
for (let m of
|
|
44
|
+
for (let m of AllMetadataArrays) {
|
|
50
45
|
let simpleKey = m.key;
|
|
51
46
|
if (!data.hasOwnProperty(simpleKey)) {
|
|
52
47
|
simpleKey = simpleKey.substring(3); // remove the All prefix
|
|
@@ -62,44 +57,164 @@ function MetadataFromSimpleObjectWithoutUser(data, md) {
|
|
|
62
57
|
return returnMetadata;
|
|
63
58
|
}
|
|
64
59
|
catch (e) {
|
|
65
|
-
|
|
60
|
+
LogError(e);
|
|
66
61
|
}
|
|
67
62
|
}
|
|
68
|
-
exports.MetadataFromSimpleObjectWithoutUser = MetadataFromSimpleObjectWithoutUser;
|
|
69
63
|
/**
|
|
70
64
|
* This is a list of all metadata classes that are used in the AllMetadata class.
|
|
71
65
|
* Used to automatically determine the class type when deserializing the metadata and
|
|
72
66
|
* for iterating through all metadata collections.
|
|
73
67
|
* Each entry maps a property key to its corresponding class constructor.
|
|
74
68
|
*/
|
|
75
|
-
|
|
76
|
-
{ key: 'AllEntities', class:
|
|
77
|
-
{ key: 'AllApplications', class:
|
|
78
|
-
{ key: 'AllRoles', class:
|
|
79
|
-
{ key: 'AllRowLevelSecurityFilters', class:
|
|
80
|
-
{ key: 'AllAuditLogTypes', class:
|
|
81
|
-
{ key: 'AllAuthorizations', class:
|
|
82
|
-
{ key: 'AllQueryCategories', class:
|
|
83
|
-
{ key: 'AllQueries', class:
|
|
84
|
-
{ key: 'AllQueryFields', class:
|
|
85
|
-
{ key: 'AllQueryPermissions', class:
|
|
86
|
-
{ key: 'AllQueryEntities', class:
|
|
87
|
-
{ key: 'AllQueryParameters', class:
|
|
88
|
-
{ key: 'AllEntityDocumentTypes', class:
|
|
89
|
-
{ key: 'AllLibraries', class:
|
|
90
|
-
{ key: 'AllExplorerNavigationItems', class:
|
|
69
|
+
export const AllMetadataArrays = [
|
|
70
|
+
{ key: 'AllEntities', class: EntityInfo },
|
|
71
|
+
{ key: 'AllApplications', class: ApplicationInfo },
|
|
72
|
+
{ key: 'AllRoles', class: RoleInfo },
|
|
73
|
+
{ key: 'AllRowLevelSecurityFilters', class: RowLevelSecurityFilterInfo },
|
|
74
|
+
{ key: 'AllAuditLogTypes', class: AuditLogTypeInfo },
|
|
75
|
+
{ key: 'AllAuthorizations', class: AuthorizationInfo },
|
|
76
|
+
{ key: 'AllQueryCategories', class: QueryCategoryInfo },
|
|
77
|
+
{ key: 'AllQueries', class: QueryInfo },
|
|
78
|
+
{ key: 'AllQueryFields', class: QueryFieldInfo },
|
|
79
|
+
{ key: 'AllQueryPermissions', class: QueryPermissionInfo },
|
|
80
|
+
{ key: 'AllQueryEntities', class: QueryEntityInfo },
|
|
81
|
+
{ key: 'AllQueryParameters', class: QueryParameterInfo },
|
|
82
|
+
{ key: 'AllEntityDocumentTypes', class: EntityDocumentTypeInfo },
|
|
83
|
+
{ key: 'AllLibraries', class: LibraryInfo },
|
|
84
|
+
{ key: 'AllExplorerNavigationItems', class: ExplorerNavigationItem }
|
|
91
85
|
];
|
|
92
86
|
/**
|
|
93
87
|
* Base class for all metadata providers in MemberJunction.
|
|
94
88
|
* Implements common functionality for metadata caching, refresh, and dataset management.
|
|
95
89
|
* Subclasses must implement abstract methods for provider-specific operations.
|
|
96
90
|
*/
|
|
97
|
-
class ProviderBase {
|
|
91
|
+
export class ProviderBase {
|
|
98
92
|
constructor() {
|
|
99
|
-
this._localMetadata = new
|
|
93
|
+
this._localMetadata = new AllMetadata();
|
|
94
|
+
this._entityRecordNameCache = new Map();
|
|
100
95
|
this._refresh = false;
|
|
101
96
|
this._cachedVisibleExplorerNavigationItems = null;
|
|
102
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Helper to generate cache key for entity record names
|
|
100
|
+
*/
|
|
101
|
+
getCacheKey(entityName, compositeKey) {
|
|
102
|
+
return `${entityName}|${compositeKey.ToString()}`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Asynchronous lookup of a cached entity record name. Returns the cached name if available, or undefined if not cached.
|
|
106
|
+
* Use this for synchronous contexts (like template rendering) where you can't await GetEntityRecordName().
|
|
107
|
+
* @param entityName - The name of the entity
|
|
108
|
+
* @param compositeKey - The primary key value(s) for the record
|
|
109
|
+
* @param loadIfNeeded - If set to true, will load from database if not already cached
|
|
110
|
+
* @returns The cached display name, or undefined if not in cache
|
|
111
|
+
*/
|
|
112
|
+
async GetCachedRecordName(entityName, compositeKey, loadIfNeeded) {
|
|
113
|
+
let cachedEntry = this._entityRecordNameCache.get(this.getCacheKey(entityName, compositeKey));
|
|
114
|
+
if (!cachedEntry && loadIfNeeded) {
|
|
115
|
+
cachedEntry = await this.GetEntityRecordName(entityName, compositeKey);
|
|
116
|
+
}
|
|
117
|
+
return cachedEntry;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Stores a record name in the cache for later synchronous retrieval via GetCachedRecordName().
|
|
121
|
+
* Called automatically by BaseEntity after Load(), LoadFromData(), and Save() operations.
|
|
122
|
+
* @param entityName - The name of the entity
|
|
123
|
+
* @param compositeKey - The primary key value(s) for the record
|
|
124
|
+
* @param recordName - The display name to cache
|
|
125
|
+
*/
|
|
126
|
+
SetCachedRecordName(entityName, compositeKey, recordName) {
|
|
127
|
+
this._entityRecordNameCache.set(this.getCacheKey(entityName, compositeKey), recordName);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Gets the display name for a single entity record with caching.
|
|
131
|
+
* Uses the entity's IsNameField or falls back to 'Name' field if available.
|
|
132
|
+
* @param entityName - The name of the entity
|
|
133
|
+
* @param compositeKey - The primary key value(s) for the record
|
|
134
|
+
* @param contextUser - Optional user context for permissions
|
|
135
|
+
* @param forceRefresh - If true, bypasses cache and queries database
|
|
136
|
+
* @returns The display name of the record or null if not found
|
|
137
|
+
*/
|
|
138
|
+
async GetEntityRecordName(entityName, compositeKey, contextUser, forceRefresh = false) {
|
|
139
|
+
const cacheKey = this.getCacheKey(entityName, compositeKey);
|
|
140
|
+
// Check cache unless forceRefresh
|
|
141
|
+
if (!forceRefresh) {
|
|
142
|
+
const cached = this._entityRecordNameCache.get(cacheKey);
|
|
143
|
+
if (cached !== undefined) {
|
|
144
|
+
return cached;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Fetch from database via provider-specific implementation
|
|
148
|
+
const name = await this.InternalGetEntityRecordName(entityName, compositeKey, contextUser);
|
|
149
|
+
if (name) {
|
|
150
|
+
this._entityRecordNameCache.set(cacheKey, name);
|
|
151
|
+
}
|
|
152
|
+
return name;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Gets display names for multiple entity records in a single operation with caching.
|
|
156
|
+
* More efficient than multiple GetEntityRecordName calls.
|
|
157
|
+
* @param info - Array of entity/key pairs to lookup
|
|
158
|
+
* @param contextUser - Optional user context for permissions
|
|
159
|
+
* @param forceRefresh - If true, bypasses cache and queries database for all records
|
|
160
|
+
* @returns Array of results with names and status for each requested record
|
|
161
|
+
*/
|
|
162
|
+
async GetEntityRecordNames(info, contextUser, forceRefresh = false) {
|
|
163
|
+
if (!forceRefresh) {
|
|
164
|
+
// Check cache for each item, collect uncached items
|
|
165
|
+
const results = [];
|
|
166
|
+
const uncachedInfo = [];
|
|
167
|
+
const uncachedIndexes = [];
|
|
168
|
+
for (let i = 0; i < info.length; i++) {
|
|
169
|
+
const item = info[i];
|
|
170
|
+
const cacheKey = this.getCacheKey(item.EntityName, item.CompositeKey);
|
|
171
|
+
const cached = this._entityRecordNameCache.get(cacheKey);
|
|
172
|
+
if (cached !== undefined) {
|
|
173
|
+
// Cache hit
|
|
174
|
+
results[i] = {
|
|
175
|
+
EntityName: item.EntityName,
|
|
176
|
+
CompositeKey: item.CompositeKey,
|
|
177
|
+
Status: 'cached',
|
|
178
|
+
Success: true,
|
|
179
|
+
RecordName: cached
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Cache miss - need to fetch
|
|
184
|
+
uncachedInfo.push(item);
|
|
185
|
+
uncachedIndexes.push(i);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Fetch uncached items from database
|
|
189
|
+
if (uncachedInfo.length > 0) {
|
|
190
|
+
const uncachedResults = await this.InternalGetEntityRecordNames(uncachedInfo, contextUser);
|
|
191
|
+
// Merge results and update cache
|
|
192
|
+
for (let i = 0; i < uncachedResults.length; i++) {
|
|
193
|
+
const result = uncachedResults[i];
|
|
194
|
+
const originalIndex = uncachedIndexes[i];
|
|
195
|
+
results[originalIndex] = result;
|
|
196
|
+
// Cache successful results
|
|
197
|
+
if (result.Success && result.RecordName) {
|
|
198
|
+
const cacheKey = this.getCacheKey(result.EntityName, result.CompositeKey);
|
|
199
|
+
this._entityRecordNameCache.set(cacheKey, result.RecordName);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return results;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// Force refresh - bypass cache entirely
|
|
207
|
+
const results = await this.InternalGetEntityRecordNames(info, contextUser);
|
|
208
|
+
// Update cache with fresh results
|
|
209
|
+
for (const result of results) {
|
|
210
|
+
if (result.Success && result.RecordName) {
|
|
211
|
+
const cacheKey = this.getCacheKey(result.EntityName, result.CompositeKey);
|
|
212
|
+
this._entityRecordNameCache.set(cacheKey, result.RecordName);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return results;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
103
218
|
// ========================================================================
|
|
104
219
|
// PUBLIC API METHODS - Orchestrate Pre → Cache → Internal → Post flow
|
|
105
220
|
// ========================================================================
|
|
@@ -135,7 +250,7 @@ class ProviderBase {
|
|
|
135
250
|
// Check for cached results - if all are cached, end telemetry and return early
|
|
136
251
|
if (preResult.allCached && preResult.cachedResults) {
|
|
137
252
|
const totalResults = preResult.cachedResults.reduce((sum, r) => sum + (r.Results?.length ?? 0), 0);
|
|
138
|
-
|
|
253
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
139
254
|
cacheHit: true,
|
|
140
255
|
allCached: true,
|
|
141
256
|
batchSize: params.length,
|
|
@@ -166,7 +281,7 @@ class ProviderBase {
|
|
|
166
281
|
const preResult = await this.PreRunQuery(params, contextUser);
|
|
167
282
|
// Check for cached result - end telemetry with cache hit info
|
|
168
283
|
if (preResult.cachedResult) {
|
|
169
|
-
|
|
284
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
170
285
|
cacheHit: true,
|
|
171
286
|
cacheStatus: preResult.cacheStatus,
|
|
172
287
|
resultCount: preResult.cachedResult.Results?.length ?? 0
|
|
@@ -192,7 +307,7 @@ class ProviderBase {
|
|
|
192
307
|
// Check for cached results - if all are cached, end telemetry and return early
|
|
193
308
|
if (preResult.allCached && preResult.cachedResults) {
|
|
194
309
|
const totalResults = preResult.cachedResults.reduce((sum, r) => sum + (r.Results?.length ?? 0), 0);
|
|
195
|
-
|
|
310
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
196
311
|
cacheHit: true,
|
|
197
312
|
allCached: true,
|
|
198
313
|
batchSize: params.length,
|
|
@@ -218,12 +333,12 @@ class ProviderBase {
|
|
|
218
333
|
* @param callerName
|
|
219
334
|
*/
|
|
220
335
|
async EntityStatusCheck(params, callerName) {
|
|
221
|
-
const entityName = await
|
|
336
|
+
const entityName = await RunView.GetEntityNameFromRunViewParams(params, this);
|
|
222
337
|
const entity = this.Entities.find(e => e.Name.trim().toLowerCase() === entityName?.trim().toLowerCase());
|
|
223
338
|
if (!entity) {
|
|
224
339
|
throw new Error(`Entity ${entityName} not found in metadata`);
|
|
225
340
|
}
|
|
226
|
-
|
|
341
|
+
EntityInfo.AssertEntityActiveStatus(entity, callerName);
|
|
227
342
|
}
|
|
228
343
|
// Type aliases for cleaner code
|
|
229
344
|
get PreRunViewResult() { return this._preRunViewResultType; }
|
|
@@ -244,7 +359,7 @@ class ProviderBase {
|
|
|
244
359
|
const preViewStart = performance.now();
|
|
245
360
|
// Start telemetry tracking
|
|
246
361
|
const telemetryStart = performance.now();
|
|
247
|
-
const telemetryEventId =
|
|
362
|
+
const telemetryEventId = TelemetryManager.Instance.StartEvent('RunView', 'ProviderBase.RunView', {
|
|
248
363
|
EntityName: params.EntityName,
|
|
249
364
|
ViewID: params.ViewID,
|
|
250
365
|
ViewName: params.ViewName,
|
|
@@ -275,9 +390,9 @@ class ProviderBase {
|
|
|
275
390
|
let cacheStatus = 'disabled';
|
|
276
391
|
let cachedResult;
|
|
277
392
|
let fingerprint;
|
|
278
|
-
if (params.CacheLocal &&
|
|
279
|
-
fingerprint =
|
|
280
|
-
const cached = await
|
|
393
|
+
if (params.CacheLocal && LocalCacheManager.Instance.IsInitialized) {
|
|
394
|
+
fingerprint = LocalCacheManager.Instance.GenerateRunViewFingerprint(params, this.InstanceConnectionString);
|
|
395
|
+
const cached = await LocalCacheManager.Instance.GetRunViewResult(fingerprint);
|
|
281
396
|
if (cached) {
|
|
282
397
|
// Reconstruct RunViewResult from cached data
|
|
283
398
|
cachedResult = {
|
|
@@ -299,7 +414,7 @@ class ProviderBase {
|
|
|
299
414
|
const cacheCheckTime = performance.now() - cacheCheckStart;
|
|
300
415
|
const totalPreTime = performance.now() - preViewStart;
|
|
301
416
|
if (totalPreTime > 50) {
|
|
302
|
-
|
|
417
|
+
LogStatus(`[PERF-PRE] PreRunView ${params.EntityName}: ${totalPreTime.toFixed(1)}ms (telemetry=${telemetryTime.toFixed(1)}ms, entityCheck=${entityCheckTime.toFixed(1)}ms, entityLookup=${entityLookupTime.toFixed(1)}ms, cache=${cacheCheckTime.toFixed(1)}ms)`);
|
|
303
418
|
}
|
|
304
419
|
return {
|
|
305
420
|
telemetryEventId,
|
|
@@ -318,7 +433,7 @@ class ProviderBase {
|
|
|
318
433
|
async PreRunViews(params, contextUser) {
|
|
319
434
|
// Start telemetry tracking for batch operation
|
|
320
435
|
const fromEngine = params.some(p => p._fromEngine);
|
|
321
|
-
const telemetryEventId =
|
|
436
|
+
const telemetryEventId = TelemetryManager.Instance.StartEvent('RunView', 'ProviderBase.RunViews', {
|
|
322
437
|
BatchSize: params.length,
|
|
323
438
|
Entities: params.map(p => p.EntityName || p.ViewName || p.ViewID).filter(Boolean),
|
|
324
439
|
_fromEngine: fromEngine
|
|
@@ -326,7 +441,7 @@ class ProviderBase {
|
|
|
326
441
|
// Check if any params have CacheLocal enabled - smart caching is always used when caching locally
|
|
327
442
|
const useSmartCacheCheck = params.some(p => p.CacheLocal);
|
|
328
443
|
// If local caching is enabled, use smart cache check flow
|
|
329
|
-
if (useSmartCacheCheck &&
|
|
444
|
+
if (useSmartCacheCheck && LocalCacheManager.Instance.IsInitialized) {
|
|
330
445
|
return this.prepareSmartCacheCheckParams(params, telemetryEventId, contextUser);
|
|
331
446
|
}
|
|
332
447
|
// Traditional caching flow
|
|
@@ -347,9 +462,9 @@ class ProviderBase {
|
|
|
347
462
|
param.Fields = entity.Fields.map(f => f.Name);
|
|
348
463
|
}
|
|
349
464
|
// Check local cache if enabled
|
|
350
|
-
if (param.CacheLocal &&
|
|
351
|
-
const fingerprint =
|
|
352
|
-
const cached = await
|
|
465
|
+
if (param.CacheLocal && LocalCacheManager.Instance.IsInitialized) {
|
|
466
|
+
const fingerprint = LocalCacheManager.Instance.GenerateRunViewFingerprint(param, this.InstanceConnectionString);
|
|
467
|
+
const cached = await LocalCacheManager.Instance.GetRunViewResult(fingerprint);
|
|
353
468
|
if (cached) {
|
|
354
469
|
const cachedViewResult = {
|
|
355
470
|
Success: true,
|
|
@@ -404,9 +519,9 @@ class ProviderBase {
|
|
|
404
519
|
}
|
|
405
520
|
// Build the cache check param with optional cache status
|
|
406
521
|
let cacheStatus;
|
|
407
|
-
if (param.CacheLocal &&
|
|
408
|
-
const fingerprint =
|
|
409
|
-
const cached = await
|
|
522
|
+
if (param.CacheLocal && LocalCacheManager.Instance.IsInitialized) {
|
|
523
|
+
const fingerprint = LocalCacheManager.Instance.GenerateRunViewFingerprint(param, this.InstanceConnectionString);
|
|
524
|
+
const cached = await LocalCacheManager.Instance.GetRunViewResult(fingerprint);
|
|
410
525
|
if (cached) {
|
|
411
526
|
cacheStatus = {
|
|
412
527
|
maxUpdatedAt: cached.maxUpdatedAt,
|
|
@@ -442,8 +557,8 @@ class ProviderBase {
|
|
|
442
557
|
const response = await provider.RunViewsWithCacheCheck(preResult.smartCacheCheckParams, contextUser);
|
|
443
558
|
if (!response.success) {
|
|
444
559
|
// If the smart cache check failed, log and return empty results
|
|
445
|
-
|
|
446
|
-
|
|
560
|
+
LogError(`SmartCacheCheck failed: ${response.errorMessage}`);
|
|
561
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
447
562
|
smartCacheCheck: true,
|
|
448
563
|
success: false,
|
|
449
564
|
errorMessage: response.errorMessage
|
|
@@ -471,7 +586,7 @@ class ProviderBase {
|
|
|
471
586
|
cacheMisses++;
|
|
472
587
|
}
|
|
473
588
|
// End telemetry
|
|
474
|
-
|
|
589
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
475
590
|
smartCacheCheck: true,
|
|
476
591
|
success: true,
|
|
477
592
|
cacheHits,
|
|
@@ -503,8 +618,8 @@ class ProviderBase {
|
|
|
503
618
|
}
|
|
504
619
|
if (checkResult.status === 'current') {
|
|
505
620
|
// Cache is current - use cached data
|
|
506
|
-
const fingerprint =
|
|
507
|
-
const cached = await
|
|
621
|
+
const fingerprint = LocalCacheManager.Instance.GenerateRunViewFingerprint(param, this.InstanceConnectionString);
|
|
622
|
+
const cached = await LocalCacheManager.Instance.GetRunViewResult(fingerprint);
|
|
508
623
|
if (cached) {
|
|
509
624
|
const cachedResult = {
|
|
510
625
|
Success: true,
|
|
@@ -539,13 +654,13 @@ class ProviderBase {
|
|
|
539
654
|
}
|
|
540
655
|
else if (checkResult.status === 'differential') {
|
|
541
656
|
// Cache is stale but we have differential data - merge with cached data
|
|
542
|
-
const fingerprint =
|
|
657
|
+
const fingerprint = LocalCacheManager.Instance.GenerateRunViewFingerprint(param, this.InstanceConnectionString);
|
|
543
658
|
// Get entity info for primary key field name
|
|
544
659
|
const entity = this.Entities.find(e => e.Name.trim().toLowerCase() === param.EntityName?.trim().toLowerCase());
|
|
545
660
|
const primaryKeyFieldName = entity?.FirstPrimaryKey?.Name || 'ID';
|
|
546
661
|
// Apply differential update to cache
|
|
547
|
-
if (param.CacheLocal && checkResult.differentialData &&
|
|
548
|
-
const merged = await
|
|
662
|
+
if (param.CacheLocal && checkResult.differentialData && LocalCacheManager.Instance.IsInitialized) {
|
|
663
|
+
const merged = await LocalCacheManager.Instance.ApplyDifferentialUpdate(fingerprint, param, checkResult.differentialData.updatedRows, checkResult.differentialData.deletedRecordIDs, primaryKeyFieldName, checkResult.maxUpdatedAt || new Date().toISOString(), checkResult.rowCount || 0, checkResult.aggregateResults // Pass fresh aggregate results (can't be differentially computed)
|
|
549
664
|
);
|
|
550
665
|
if (merged) {
|
|
551
666
|
const mergedResult = {
|
|
@@ -584,12 +699,12 @@ class ProviderBase {
|
|
|
584
699
|
AggregateResults: checkResult.aggregateResults // Include fresh aggregate results
|
|
585
700
|
};
|
|
586
701
|
// Update the local cache with fresh data (don't await - fire and forget for performance)
|
|
587
|
-
if (param.CacheLocal && checkResult.maxUpdatedAt &&
|
|
588
|
-
const fingerprint =
|
|
702
|
+
if (param.CacheLocal && checkResult.maxUpdatedAt && LocalCacheManager.Instance.IsInitialized) {
|
|
703
|
+
const fingerprint = LocalCacheManager.Instance.GenerateRunViewFingerprint(param, this.InstanceConnectionString);
|
|
589
704
|
// Note: We don't await here to avoid blocking the response
|
|
590
705
|
// Cache update happens in background
|
|
591
|
-
|
|
592
|
-
).catch(e =>
|
|
706
|
+
LocalCacheManager.Instance.SetRunViewResult(fingerprint, param, checkResult.results || [], checkResult.maxUpdatedAt, checkResult.aggregateResults // Include aggregate results in cache
|
|
707
|
+
).catch(e => LogError(`Failed to update cache: ${e}`));
|
|
593
708
|
}
|
|
594
709
|
// Transform to entity objects if needed
|
|
595
710
|
await this.TransformSimpleObjectToEntityObject(param, freshResult, contextUser);
|
|
@@ -621,7 +736,7 @@ class ProviderBase {
|
|
|
621
736
|
*/
|
|
622
737
|
async PreRunQuery(params, contextUser) {
|
|
623
738
|
// Start telemetry tracking
|
|
624
|
-
const telemetryEventId =
|
|
739
|
+
const telemetryEventId = TelemetryManager.Instance.StartEvent('RunQuery', 'ProviderBase.RunQuery', {
|
|
625
740
|
QueryID: params.QueryID,
|
|
626
741
|
QueryName: params.QueryName,
|
|
627
742
|
CategoryPath: params.CategoryPath,
|
|
@@ -648,7 +763,7 @@ class ProviderBase {
|
|
|
648
763
|
*/
|
|
649
764
|
async PreRunQueries(params, contextUser) {
|
|
650
765
|
// Start telemetry tracking for batch operation
|
|
651
|
-
const telemetryEventId =
|
|
766
|
+
const telemetryEventId = TelemetryManager.Instance.StartEvent('RunQuery', 'ProviderBase.RunQueries', {
|
|
652
767
|
BatchSize: params.length,
|
|
653
768
|
Queries: params.map(p => p.QueryName || p.QueryID).filter(Boolean)
|
|
654
769
|
}, contextUser?.ID);
|
|
@@ -676,15 +791,15 @@ class ProviderBase {
|
|
|
676
791
|
// Transform the result set into BaseEntity-derived objects, if needed
|
|
677
792
|
await this.TransformSimpleObjectToEntityObject(params, result, contextUser);
|
|
678
793
|
// Store in local cache if enabled and we have a successful result
|
|
679
|
-
if (params.CacheLocal && result.Success && preResult.fingerprint &&
|
|
794
|
+
if (params.CacheLocal && result.Success && preResult.fingerprint && LocalCacheManager.Instance.IsInitialized) {
|
|
680
795
|
// Extract maxUpdatedAt from results if available
|
|
681
796
|
const maxUpdatedAt = this.extractMaxUpdatedAt(result.Results);
|
|
682
|
-
await
|
|
797
|
+
await LocalCacheManager.Instance.SetRunViewResult(preResult.fingerprint, params, result.Results, maxUpdatedAt, result.AggregateResults // Include aggregate results in cache
|
|
683
798
|
);
|
|
684
799
|
}
|
|
685
800
|
// End telemetry tracking with cache miss info
|
|
686
801
|
if (preResult.telemetryEventId) {
|
|
687
|
-
|
|
802
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
688
803
|
cacheHit: false,
|
|
689
804
|
cacheStatus: preResult.cacheStatus,
|
|
690
805
|
resultCount: result.Results?.length ?? 0,
|
|
@@ -706,10 +821,10 @@ class ProviderBase {
|
|
|
706
821
|
for (let i = 0; i < results.length; i++) {
|
|
707
822
|
promises.push(this.TransformSimpleObjectToEntityObject(params[i], results[i], contextUser));
|
|
708
823
|
// Store in local cache if enabled
|
|
709
|
-
if (params[i].CacheLocal && results[i].Success &&
|
|
710
|
-
const fingerprint =
|
|
824
|
+
if (params[i].CacheLocal && results[i].Success && LocalCacheManager.Instance.IsInitialized) {
|
|
825
|
+
const fingerprint = LocalCacheManager.Instance.GenerateRunViewFingerprint(params[i], this.InstanceConnectionString);
|
|
711
826
|
const maxUpdatedAt = this.extractMaxUpdatedAt(results[i].Results);
|
|
712
|
-
promises.push(
|
|
827
|
+
promises.push(LocalCacheManager.Instance.SetRunViewResult(fingerprint, params[i], results[i].Results, maxUpdatedAt, results[i].AggregateResults // Include aggregate results in cache
|
|
713
828
|
));
|
|
714
829
|
}
|
|
715
830
|
}
|
|
@@ -720,7 +835,7 @@ class ProviderBase {
|
|
|
720
835
|
const cachedCount = preResult.cacheStatusMap
|
|
721
836
|
? [...preResult.cacheStatusMap.values()].filter(s => s.status === 'hit').length
|
|
722
837
|
: 0;
|
|
723
|
-
|
|
838
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
724
839
|
cacheHit: false,
|
|
725
840
|
allCached: false,
|
|
726
841
|
batchSize: params.length,
|
|
@@ -742,7 +857,7 @@ class ProviderBase {
|
|
|
742
857
|
// Query caching is handled internally by the provider
|
|
743
858
|
// End telemetry tracking with cache miss info
|
|
744
859
|
if (preResult.telemetryEventId) {
|
|
745
|
-
|
|
860
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
746
861
|
cacheHit: false,
|
|
747
862
|
cacheStatus: preResult.cacheStatus,
|
|
748
863
|
resultCount: result.Results?.length ?? 0,
|
|
@@ -766,7 +881,7 @@ class ProviderBase {
|
|
|
766
881
|
const cachedCount = preResult.cacheStatusMap
|
|
767
882
|
? [...preResult.cacheStatusMap.values()].filter(s => s.status === 'hit').length
|
|
768
883
|
: 0;
|
|
769
|
-
|
|
884
|
+
TelemetryManager.Instance.EndEvent(preResult.telemetryEventId, {
|
|
770
885
|
cacheHit: false,
|
|
771
886
|
allCached: false,
|
|
772
887
|
batchSize: params.length,
|
|
@@ -856,7 +971,7 @@ class ProviderBase {
|
|
|
856
971
|
*/
|
|
857
972
|
async PreProcessRunView(params, contextUser) {
|
|
858
973
|
// Start telemetry tracking
|
|
859
|
-
const eventId =
|
|
974
|
+
const eventId = TelemetryManager.Instance.StartEvent('RunView', 'ProviderBase.RunView', {
|
|
860
975
|
EntityName: params.EntityName,
|
|
861
976
|
ViewID: params.ViewID,
|
|
862
977
|
ViewName: params.ViewName,
|
|
@@ -892,7 +1007,7 @@ class ProviderBase {
|
|
|
892
1007
|
// End telemetry tracking
|
|
893
1008
|
const eventId = params._telemetryEventId;
|
|
894
1009
|
if (eventId) {
|
|
895
|
-
|
|
1010
|
+
TelemetryManager.Instance.EndEvent(eventId);
|
|
896
1011
|
delete params._telemetryEventId;
|
|
897
1012
|
}
|
|
898
1013
|
}
|
|
@@ -906,7 +1021,7 @@ class ProviderBase {
|
|
|
906
1021
|
async PreProcessRunViews(params, contextUser) {
|
|
907
1022
|
// Start telemetry tracking for batch operation
|
|
908
1023
|
const fromEngine = params.some(p => p._fromEngine);
|
|
909
|
-
const eventId =
|
|
1024
|
+
const eventId = TelemetryManager.Instance.StartEvent('RunView', 'ProviderBase.RunViews', {
|
|
910
1025
|
BatchSize: params.length,
|
|
911
1026
|
Entities: params.map(p => p.EntityName || p.ViewName || p.ViewID).filter(Boolean),
|
|
912
1027
|
_fromEngine: fromEngine
|
|
@@ -950,7 +1065,7 @@ class ProviderBase {
|
|
|
950
1065
|
// End telemetry tracking for batch operation
|
|
951
1066
|
const eventId = params[0]._telemetryBatchEventId;
|
|
952
1067
|
if (eventId) {
|
|
953
|
-
|
|
1068
|
+
TelemetryManager.Instance.EndEvent(eventId);
|
|
954
1069
|
delete params[0]._telemetryBatchEventId;
|
|
955
1070
|
}
|
|
956
1071
|
}
|
|
@@ -967,7 +1082,7 @@ class ProviderBase {
|
|
|
967
1082
|
// we need to transform each of the items in the result set into a BaseEntity-derived object
|
|
968
1083
|
// Create entities and load data in parallel for better performance
|
|
969
1084
|
const entityPromises = result.Results.map(async (item) => {
|
|
970
|
-
if (item instanceof
|
|
1085
|
+
if (item instanceof BaseEntity || (typeof item.Save === 'function')) {
|
|
971
1086
|
// the second check is a "duck-typing" check in case we have different runtime
|
|
972
1087
|
// loading sources where the instanceof will fail
|
|
973
1088
|
return item;
|
|
@@ -998,7 +1113,7 @@ class ProviderBase {
|
|
|
998
1113
|
this._ConfigData = data;
|
|
999
1114
|
// first, let's check to see if we have an existing Metadata.Provider registered, if so
|
|
1000
1115
|
// unless our data.IgnoreExistingMetadata is set to true, we will not refresh the metadata
|
|
1001
|
-
if (
|
|
1116
|
+
if (Metadata.Provider && !data.IgnoreExistingMetadata) {
|
|
1002
1117
|
// we have an existing globally registered provider AND we are not
|
|
1003
1118
|
// requested to ignore the existing metadata, so we will not refresh it
|
|
1004
1119
|
if (this.CopyMetadataFromGlobalProvider()) {
|
|
@@ -1015,7 +1130,7 @@ class ProviderBase {
|
|
|
1015
1130
|
const start = new Date().getTime();
|
|
1016
1131
|
const res = await this.GetAllMetadata(providerToUse);
|
|
1017
1132
|
const end = new Date().getTime();
|
|
1018
|
-
(
|
|
1133
|
+
LogStatusEx({ message: `GetAllMetadata() took ${end - start} ms`, verboseOnly: true });
|
|
1019
1134
|
if (res) {
|
|
1020
1135
|
// Atomic swap via UpdateLocalMetadata: single property assignment is atomic in JavaScript
|
|
1021
1136
|
// Readers now see new metadata instead of old
|
|
@@ -1027,7 +1142,7 @@ class ProviderBase {
|
|
|
1027
1142
|
}
|
|
1028
1143
|
else {
|
|
1029
1144
|
// GetAllMetadata failed - log error but keep existing metadata
|
|
1030
|
-
|
|
1145
|
+
LogError('GetAllMetadata() returned undefined - metadata not updated');
|
|
1031
1146
|
}
|
|
1032
1147
|
}
|
|
1033
1148
|
return true;
|
|
@@ -1046,14 +1161,14 @@ class ProviderBase {
|
|
|
1046
1161
|
*/
|
|
1047
1162
|
CopyMetadataFromGlobalProvider() {
|
|
1048
1163
|
try {
|
|
1049
|
-
if (
|
|
1050
|
-
this._localMetadata = this.CloneAllMetadata(
|
|
1164
|
+
if (Metadata.Provider && Metadata.Provider !== this && Metadata.Provider.AllMetadata) {
|
|
1165
|
+
this._localMetadata = this.CloneAllMetadata(Metadata.Provider.AllMetadata);
|
|
1051
1166
|
return true;
|
|
1052
1167
|
}
|
|
1053
1168
|
return false;
|
|
1054
1169
|
}
|
|
1055
1170
|
catch (e) {
|
|
1056
|
-
|
|
1171
|
+
LogError(`Failed to copy metadata from global provider: ${e.message}`);
|
|
1057
1172
|
return false; // if we fail to copy the metadata, we will return false
|
|
1058
1173
|
}
|
|
1059
1174
|
}
|
|
@@ -1077,7 +1192,7 @@ class ProviderBase {
|
|
|
1077
1192
|
if (excludeSchemaList && excludeSchemaList.length > 0 && excludeSchemaList.indexOf(mjcSchema) !== -1) {
|
|
1078
1193
|
const index = excludeSchemaList.indexOf(mjcSchema);
|
|
1079
1194
|
excludeSchemaList.splice(index, 1);
|
|
1080
|
-
|
|
1195
|
+
LogStatus(`Removed MJ Core schema (${mjcSchema}) from ExcludeSchemas list because it is required for the API to function correctly`);
|
|
1081
1196
|
}
|
|
1082
1197
|
let schemaFilter = '';
|
|
1083
1198
|
if (includeSchemaList && includeSchemaList.length > 0) {
|
|
@@ -1092,6 +1207,7 @@ class ProviderBase {
|
|
|
1092
1207
|
}
|
|
1093
1208
|
return f;
|
|
1094
1209
|
}
|
|
1210
|
+
static { this._mjMetadataDatasetName = 'MJ_Metadata'; }
|
|
1095
1211
|
/**
|
|
1096
1212
|
* Retrieves all metadata from the server and constructs typed instances.
|
|
1097
1213
|
* Uses the MJ_Metadata dataset for efficient bulk loading.
|
|
@@ -1103,10 +1219,10 @@ class ProviderBase {
|
|
|
1103
1219
|
//const start1 = new Date().getTime();
|
|
1104
1220
|
const f = this.BuildDatasetFilterFromConfig();
|
|
1105
1221
|
// Get the dataset and cache it for anyone else who wants to use it
|
|
1106
|
-
const d = await this.GetDatasetByName(
|
|
1222
|
+
const d = await this.GetDatasetByName(ProviderBase._mjMetadataDatasetName, f.length > 0 ? f : null, this.CurrentUser, providerToUse);
|
|
1107
1223
|
if (d && d.Success) {
|
|
1108
1224
|
// cache the dataset for anyone who wants to use it
|
|
1109
|
-
await this.CacheDataset(
|
|
1225
|
+
await this.CacheDataset(ProviderBase._mjMetadataDatasetName, f.length > 0 ? f : null, d);
|
|
1110
1226
|
// got the results, let's build our response in the format we need
|
|
1111
1227
|
const simpleMetadata = {};
|
|
1112
1228
|
for (let r of d.Results) {
|
|
@@ -1118,7 +1234,7 @@ class ProviderBase {
|
|
|
1118
1234
|
simpleMetadata.AllApplications = simpleMetadata.Applications.map((a) => {
|
|
1119
1235
|
a.ApplicationEntities = simpleMetadata.ApplicationEntities.filter((ae) => ae.ApplicationID === a.ID);
|
|
1120
1236
|
a.ApplicationSettings = simpleMetadata.ApplicationSettings.filter((as) => as.ApplicationID === a.ID);
|
|
1121
|
-
return new
|
|
1237
|
+
return new ApplicationInfo(a, this);
|
|
1122
1238
|
});
|
|
1123
1239
|
// now we need to construct our return type. The way the return type works, which is an instance of AllMetadata, we have to
|
|
1124
1240
|
// construst each item so it contains an array of the correct type. This is because the AllMetadata class has an array of each type of metadata
|
|
@@ -1129,11 +1245,11 @@ class ProviderBase {
|
|
|
1129
1245
|
return returnMetadata;
|
|
1130
1246
|
}
|
|
1131
1247
|
else {
|
|
1132
|
-
|
|
1248
|
+
LogError('GetAllMetadata() - Error getting metadata from server' + (d ? ': ' + d.Status : ''));
|
|
1133
1249
|
}
|
|
1134
1250
|
}
|
|
1135
1251
|
catch (e) {
|
|
1136
|
-
|
|
1252
|
+
LogError(e);
|
|
1137
1253
|
}
|
|
1138
1254
|
}
|
|
1139
1255
|
/**
|
|
@@ -1162,7 +1278,7 @@ class ProviderBase {
|
|
|
1162
1278
|
e.EntityPermissions = permissions.filter(p => p.EntityID === e.ID);
|
|
1163
1279
|
e.EntityRelationships = relationships.filter(r => r.EntityID === e.ID);
|
|
1164
1280
|
e.EntitySettings = settings.filter(s => s.EntityID === e.ID);
|
|
1165
|
-
result.push(new
|
|
1281
|
+
result.push(new EntityInfo(e));
|
|
1166
1282
|
}
|
|
1167
1283
|
return result;
|
|
1168
1284
|
}
|
|
@@ -1333,7 +1449,7 @@ class ProviderBase {
|
|
|
1333
1449
|
// Determine which overload was called
|
|
1334
1450
|
let actualLoadKey;
|
|
1335
1451
|
let actualContextUser;
|
|
1336
|
-
if (loadKeyOrContextUser instanceof
|
|
1452
|
+
if (loadKeyOrContextUser instanceof CompositeKey) {
|
|
1337
1453
|
// Second overload: entityName, loadKey, contextUser
|
|
1338
1454
|
actualLoadKey = loadKeyOrContextUser;
|
|
1339
1455
|
actualContextUser = contextUser;
|
|
@@ -1352,8 +1468,10 @@ class ProviderBase {
|
|
|
1352
1468
|
// Use the MJGlobal Class Factory to do our object instantiation - we do NOT use metadata for this anymore, doesn't work well to have file paths with node dynamically at runtime
|
|
1353
1469
|
// type reference registration by any module via MJ Global is the way to go as it is reliable across all platforms.
|
|
1354
1470
|
try {
|
|
1355
|
-
const newObject =
|
|
1471
|
+
const newObject = MJGlobal.Instance.ClassFactory.CreateInstance(BaseEntity, entityName, entity, this);
|
|
1356
1472
|
await newObject.Config(actualContextUser);
|
|
1473
|
+
// Initialize IS-A parent entity composition chain before any data operations
|
|
1474
|
+
await newObject.InitializeParentEntity();
|
|
1357
1475
|
if (actualLoadKey) {
|
|
1358
1476
|
// Load existing record
|
|
1359
1477
|
const loadResult = await newObject.InnerLoad(actualLoadKey);
|
|
@@ -1369,7 +1487,7 @@ class ProviderBase {
|
|
|
1369
1487
|
return newObject;
|
|
1370
1488
|
}
|
|
1371
1489
|
catch (e) {
|
|
1372
|
-
|
|
1490
|
+
LogError(e);
|
|
1373
1491
|
throw new Error(`Entity ${entityName} could not be instantiated via MJGlobal Class Factory. Make sure you have registered the class reference with MJGlobal.Instance.ClassFactory.Register(). ALSO, make sure you call LoadGeneratedEntities() from the GeneratedEntities project within your project as tree-shaking sometimes removes subclasses and could be causing this error!`);
|
|
1374
1492
|
}
|
|
1375
1493
|
}
|
|
@@ -1377,7 +1495,7 @@ class ProviderBase {
|
|
|
1377
1495
|
throw new Error(`Entity ${entityName} not found in metadata`);
|
|
1378
1496
|
}
|
|
1379
1497
|
catch (ex) {
|
|
1380
|
-
|
|
1498
|
+
LogError(ex);
|
|
1381
1499
|
return null;
|
|
1382
1500
|
}
|
|
1383
1501
|
}
|
|
@@ -1406,7 +1524,7 @@ class ProviderBase {
|
|
|
1406
1524
|
return result;
|
|
1407
1525
|
}
|
|
1408
1526
|
catch (e) {
|
|
1409
|
-
|
|
1527
|
+
LogError(e);
|
|
1410
1528
|
throw e;
|
|
1411
1529
|
}
|
|
1412
1530
|
}
|
|
@@ -1557,7 +1675,7 @@ class ProviderBase {
|
|
|
1557
1675
|
* @returns
|
|
1558
1676
|
*/
|
|
1559
1677
|
GetDatasetCacheKey(datasetName, itemFilters) {
|
|
1560
|
-
return this.LocalStoragePrefix +
|
|
1678
|
+
return this.LocalStoragePrefix + ProviderBase.localStorageRootKey + this.InstanceConnectionString + '__DATASET__' + datasetName + this.ConvertItemFiltersToUniqueKey(itemFilters);
|
|
1561
1679
|
}
|
|
1562
1680
|
/**
|
|
1563
1681
|
* Converts dataset item filters into a unique string key for caching.
|
|
@@ -1608,7 +1726,7 @@ class ProviderBase {
|
|
|
1608
1726
|
*/
|
|
1609
1727
|
async GetLatestMetadataUpdates(providerToUse) {
|
|
1610
1728
|
const f = this.BuildDatasetFilterFromConfig();
|
|
1611
|
-
const d = await this.GetDatasetStatusByName(
|
|
1729
|
+
const d = await this.GetDatasetStatusByName(ProviderBase._mjMetadataDatasetName, f.length > 0 ? f : null, this.CurrentUser, providerToUse);
|
|
1612
1730
|
if (d && d.Success) {
|
|
1613
1731
|
const ret = d.EntityUpdateDates.map(e => {
|
|
1614
1732
|
return {
|
|
@@ -1699,6 +1817,14 @@ class ProviderBase {
|
|
|
1699
1817
|
UpdateLocalMetadata(res) {
|
|
1700
1818
|
this._localMetadata = res;
|
|
1701
1819
|
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Returns the filesystem provider for the current environment.
|
|
1822
|
+
* Default implementation returns null (no filesystem access).
|
|
1823
|
+
* Server-side providers should override this to return a NodeFileSystemProvider.
|
|
1824
|
+
*/
|
|
1825
|
+
get FileSystemProvider() {
|
|
1826
|
+
return null;
|
|
1827
|
+
}
|
|
1702
1828
|
/**
|
|
1703
1829
|
* Loads metadata from local storage if available.
|
|
1704
1830
|
* Deserializes and reconstructs typed metadata objects.
|
|
@@ -1708,8 +1834,8 @@ class ProviderBase {
|
|
|
1708
1834
|
const ls = this.LocalStorageProvider;
|
|
1709
1835
|
if (ls) {
|
|
1710
1836
|
// execution environment supports local storage, use it
|
|
1711
|
-
this._latestLocalMetadataTimestamps = JSON.parse(await ls.GetItem(this.LocalStoragePrefix +
|
|
1712
|
-
const temp = JSON.parse(await ls.GetItem(this.LocalStoragePrefix +
|
|
1837
|
+
this._latestLocalMetadataTimestamps = JSON.parse(await ls.GetItem(this.LocalStoragePrefix + ProviderBase.localStorageTimestampsKey));
|
|
1838
|
+
const temp = JSON.parse(await ls.GetItem(this.LocalStoragePrefix + ProviderBase.localStorageAllMetadataKey)); // we now have a simple object for all the metadata
|
|
1713
1839
|
if (temp) {
|
|
1714
1840
|
// we have local metadata
|
|
1715
1841
|
const metadata = MetadataFromSimpleObject(temp, this); // create a new object to start this up
|
|
@@ -1721,6 +1847,13 @@ class ProviderBase {
|
|
|
1721
1847
|
// some enviroments don't support local storage
|
|
1722
1848
|
}
|
|
1723
1849
|
}
|
|
1850
|
+
static { this.localStorageRootKey = '___MJCore_Metadata'; }
|
|
1851
|
+
static { this.localStorageTimestampsKey = this.localStorageRootKey + '_Timestamps'; }
|
|
1852
|
+
static { this.localStorageAllMetadataKey = this.localStorageRootKey + '_AllMetadata'; }
|
|
1853
|
+
static { this.localStorageKeys = [
|
|
1854
|
+
ProviderBase.localStorageTimestampsKey,
|
|
1855
|
+
ProviderBase.localStorageAllMetadataKey,
|
|
1856
|
+
]; }
|
|
1724
1857
|
/**
|
|
1725
1858
|
* This property will return the prefix to use for local storage keys. This is useful if you have multiple instances of a provider running in the same environment
|
|
1726
1859
|
* and you want to keep their local storage keys separate. The default implementation returns an empty string, but subclasses can override this to return a unique string
|
|
@@ -1738,14 +1871,14 @@ class ProviderBase {
|
|
|
1738
1871
|
const ls = this.LocalStorageProvider;
|
|
1739
1872
|
if (ls) {
|
|
1740
1873
|
// execution environment supports local storage, use it
|
|
1741
|
-
await ls.SetItem(this.LocalStoragePrefix +
|
|
1874
|
+
await ls.SetItem(this.LocalStoragePrefix + ProviderBase.localStorageTimestampsKey, JSON.stringify(this._latestLocalMetadataTimestamps));
|
|
1742
1875
|
// now persist the AllMetadata object
|
|
1743
|
-
await ls.SetItem(this.LocalStoragePrefix +
|
|
1876
|
+
await ls.SetItem(this.LocalStoragePrefix + ProviderBase.localStorageAllMetadataKey, JSON.stringify(this._localMetadata));
|
|
1744
1877
|
}
|
|
1745
1878
|
}
|
|
1746
1879
|
catch (e) {
|
|
1747
1880
|
// some enviroments don't support local storage
|
|
1748
|
-
|
|
1881
|
+
LogError(e);
|
|
1749
1882
|
}
|
|
1750
1883
|
}
|
|
1751
1884
|
/**
|
|
@@ -1755,24 +1888,14 @@ class ProviderBase {
|
|
|
1755
1888
|
async RemoveLocalMetadataFromStorage() {
|
|
1756
1889
|
try {
|
|
1757
1890
|
const ls = this.LocalStorageProvider;
|
|
1758
|
-
for (let i = 0; i <
|
|
1759
|
-
await ls.Remove(this.LocalStoragePrefix +
|
|
1891
|
+
for (let i = 0; i < ProviderBase.localStorageKeys.length; i++) {
|
|
1892
|
+
await ls.Remove(this.LocalStoragePrefix + ProviderBase.localStorageKeys[i]);
|
|
1760
1893
|
}
|
|
1761
1894
|
}
|
|
1762
1895
|
catch (e) {
|
|
1763
1896
|
// some enviroments don't support local storage
|
|
1764
|
-
|
|
1897
|
+
LogError(e);
|
|
1765
1898
|
}
|
|
1766
1899
|
}
|
|
1767
1900
|
}
|
|
1768
|
-
exports.ProviderBase = ProviderBase;
|
|
1769
|
-
_a = ProviderBase;
|
|
1770
|
-
ProviderBase._mjMetadataDatasetName = 'MJ_Metadata';
|
|
1771
|
-
ProviderBase.localStorageRootKey = '___MJCore_Metadata';
|
|
1772
|
-
ProviderBase.localStorageTimestampsKey = _a.localStorageRootKey + '_Timestamps';
|
|
1773
|
-
ProviderBase.localStorageAllMetadataKey = _a.localStorageRootKey + '_AllMetadata';
|
|
1774
|
-
ProviderBase.localStorageKeys = [
|
|
1775
|
-
_a.localStorageTimestampsKey,
|
|
1776
|
-
_a.localStorageAllMetadataKey,
|
|
1777
|
-
];
|
|
1778
1901
|
//# sourceMappingURL=providerBase.js.map
|