@memberjunction/global 2.112.0 → 2.113.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/package.json +1 -1
- package/dist/Core.d.ts +0 -29
- package/dist/Core.d.ts.map +0 -1
- package/dist/Core.js +0 -58
- package/dist/Core.js.map +0 -1
- package/dist/generic/QueryCache.d.ts +0 -85
- package/dist/generic/QueryCache.d.ts.map +0 -1
- package/dist/generic/QueryCache.js +0 -198
- package/dist/generic/QueryCache.js.map +0 -1
- package/dist/generic/QueryCacheConfig.d.ts +0 -72
- package/dist/generic/QueryCacheConfig.d.ts.map +0 -1
- package/dist/generic/QueryCacheConfig.js +0 -3
- package/dist/generic/QueryCacheConfig.js.map +0 -1
- package/dist/generic/applicationInfo.d.ts +0 -138
- package/dist/generic/applicationInfo.d.ts.map +0 -1
- package/dist/generic/applicationInfo.js +0 -177
- package/dist/generic/applicationInfo.js.map +0 -1
- package/dist/generic/authEvaluator.d.ts +0 -25
- package/dist/generic/authEvaluator.d.ts.map +0 -1
- package/dist/generic/authEvaluator.js +0 -49
- package/dist/generic/authEvaluator.js.map +0 -1
- package/dist/generic/authTypes.d.ts +0 -193
- package/dist/generic/authTypes.d.ts.map +0 -1
- package/dist/generic/authTypes.js +0 -19
- package/dist/generic/authTypes.js.map +0 -1
- package/dist/generic/baseEngine.d.ts +0 -260
- package/dist/generic/baseEngine.d.ts.map +0 -1
- package/dist/generic/baseEngine.js +0 -510
- package/dist/generic/baseEngine.js.map +0 -1
- package/dist/generic/baseEntity.d.ts +0 -691
- package/dist/generic/baseEntity.d.ts.map +0 -1
- package/dist/generic/baseEntity.js +0 -1688
- package/dist/generic/baseEntity.js.map +0 -1
- package/dist/generic/baseInfo.d.ts +0 -24
- package/dist/generic/baseInfo.d.ts.map +0 -1
- package/dist/generic/baseInfo.js +0 -53
- package/dist/generic/baseInfo.js.map +0 -1
- package/dist/generic/compositeKey.d.ts +0 -206
- package/dist/generic/compositeKey.d.ts.map +0 -1
- package/dist/generic/compositeKey.js +0 -412
- package/dist/generic/compositeKey.js.map +0 -1
- package/dist/generic/databaseProviderBase.d.ts +0 -46
- package/dist/generic/databaseProviderBase.d.ts.map +0 -1
- package/dist/generic/databaseProviderBase.js +0 -14
- package/dist/generic/databaseProviderBase.js.map +0 -1
- package/dist/generic/entityInfo.d.ts +0 -983
- package/dist/generic/entityInfo.d.ts.map +0 -1
- package/dist/generic/entityInfo.js +0 -1401
- package/dist/generic/entityInfo.js.map +0 -1
- package/dist/generic/explorerNavigationItem.d.ts +0 -20
- package/dist/generic/explorerNavigationItem.d.ts.map +0 -1
- package/dist/generic/explorerNavigationItem.js +0 -29
- package/dist/generic/explorerNavigationItem.js.map +0 -1
- package/dist/generic/interfaces.d.ts +0 -610
- package/dist/generic/interfaces.d.ts.map +0 -1
- package/dist/generic/interfaces.js +0 -211
- package/dist/generic/interfaces.js.map +0 -1
- package/dist/generic/libraryInfo.d.ts +0 -40
- package/dist/generic/libraryInfo.d.ts.map +0 -1
- package/dist/generic/libraryInfo.js +0 -56
- package/dist/generic/libraryInfo.js.map +0 -1
- package/dist/generic/logging.d.ts +0 -179
- package/dist/generic/logging.d.ts.map +0 -1
- package/dist/generic/logging.js +0 -382
- package/dist/generic/logging.js.map +0 -1
- package/dist/generic/metadata.d.ts +0 -305
- package/dist/generic/metadata.d.ts.map +0 -1
- package/dist/generic/metadata.js +0 -454
- package/dist/generic/metadata.js.map +0 -1
- package/dist/generic/metadataUtil.d.ts +0 -8
- package/dist/generic/metadataUtil.d.ts.map +0 -1
- package/dist/generic/metadataUtil.js +0 -36
- package/dist/generic/metadataUtil.js.map +0 -1
- package/dist/generic/providerBase.d.ts +0 -546
- package/dist/generic/providerBase.d.ts.map +0 -1
- package/dist/generic/providerBase.js +0 -999
- package/dist/generic/providerBase.js.map +0 -1
- package/dist/generic/queryInfo.d.ts +0 -460
- package/dist/generic/queryInfo.d.ts.map +0 -1
- package/dist/generic/queryInfo.js +0 -633
- package/dist/generic/queryInfo.js.map +0 -1
- package/dist/generic/querySQLFilters.d.ts +0 -54
- package/dist/generic/querySQLFilters.d.ts.map +0 -1
- package/dist/generic/querySQLFilters.js +0 -84
- package/dist/generic/querySQLFilters.js.map +0 -1
- package/dist/generic/runQuery.d.ts +0 -96
- package/dist/generic/runQuery.d.ts.map +0 -1
- package/dist/generic/runQuery.js +0 -66
- package/dist/generic/runQuery.js.map +0 -1
- package/dist/generic/runQuerySQLFilterImplementations.d.ts +0 -51
- package/dist/generic/runQuerySQLFilterImplementations.d.ts.map +0 -1
- package/dist/generic/runQuerySQLFilterImplementations.js +0 -238
- package/dist/generic/runQuerySQLFilterImplementations.js.map +0 -1
- package/dist/generic/runReport.d.ts +0 -25
- package/dist/generic/runReport.d.ts.map +0 -1
- package/dist/generic/runReport.js +0 -42
- package/dist/generic/runReport.js.map +0 -1
- package/dist/generic/securityInfo.d.ts +0 -355
- package/dist/generic/securityInfo.d.ts.map +0 -1
- package/dist/generic/securityInfo.js +0 -425
- package/dist/generic/securityInfo.js.map +0 -1
- package/dist/generic/transactionGroup.d.ts +0 -184
- package/dist/generic/transactionGroup.d.ts.map +0 -1
- package/dist/generic/transactionGroup.js +0 -357
- package/dist/generic/transactionGroup.js.map +0 -1
- package/dist/generic/util.d.ts +0 -81
- package/dist/generic/util.d.ts.map +0 -1
- package/dist/generic/util.js +0 -301
- package/dist/generic/util.js.map +0 -1
- package/dist/views/runView.d.ts +0 -150
- package/dist/views/runView.d.ts.map +0 -1
- package/dist/views/runView.js +0 -100
- package/dist/views/runView.js.map +0 -1
- package/dist/views/viewInfo.d.ts +0 -121
- package/dist/views/viewInfo.d.ts.map +0 -1
- package/dist/views/viewInfo.js +0 -182
- package/dist/views/viewInfo.js.map +0 -1
|
@@ -1,999 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var _a;
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.ProviderBase = exports.AllMetadataArrays = exports.MetadataFromSimpleObjectWithoutUser = exports.MetadataFromSimpleObject = void 0;
|
|
5
|
-
const baseEntity_1 = require("./baseEntity");
|
|
6
|
-
const entityInfo_1 = require("./entityInfo");
|
|
7
|
-
const interfaces_1 = require("./interfaces");
|
|
8
|
-
const applicationInfo_1 = require("../generic/applicationInfo");
|
|
9
|
-
const securityInfo_1 = require("./securityInfo");
|
|
10
|
-
const logging_1 = require("./logging");
|
|
11
|
-
const queryInfo_1 = require("./queryInfo");
|
|
12
|
-
const libraryInfo_1 = require("./libraryInfo");
|
|
13
|
-
const compositeKey_1 = require("./compositeKey");
|
|
14
|
-
const explorerNavigationItem_1 = require("./explorerNavigationItem");
|
|
15
|
-
const metadata_1 = require("./metadata");
|
|
16
|
-
const runView_1 = require("../views/runView");
|
|
17
|
-
const Global_1 = require("../Global");
|
|
18
|
-
/**
|
|
19
|
-
* Creates a new instance of AllMetadata from a simple object.
|
|
20
|
-
* Handles deserialization and proper instantiation of all metadata classes.
|
|
21
|
-
* @param data - The raw metadata object to convert
|
|
22
|
-
* @param md - The metadata provider for context
|
|
23
|
-
* @returns A fully populated AllMetadata instance with proper type instances
|
|
24
|
-
*/
|
|
25
|
-
function MetadataFromSimpleObject(data, md) {
|
|
26
|
-
try {
|
|
27
|
-
const newObject = MetadataFromSimpleObjectWithoutUser(data, md);
|
|
28
|
-
newObject.CurrentUser = data.CurrentUser ? new securityInfo_1.UserInfo(md, data.CurrentUser) : null;
|
|
29
|
-
return newObject;
|
|
30
|
-
}
|
|
31
|
-
catch (e) {
|
|
32
|
-
(0, logging_1.LogError)(e);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
exports.MetadataFromSimpleObject = MetadataFromSimpleObject;
|
|
36
|
-
/**
|
|
37
|
-
* Creates a new instance of AllMetadata from a simple object, but does NOT set the CurrentUser property
|
|
38
|
-
* Handles deserialization and proper instantiation of all metadata classes.
|
|
39
|
-
* @param data - The raw metadata object to convert
|
|
40
|
-
* @param md - The metadata provider for context
|
|
41
|
-
* @returns A fully populated AllMetadata instance with proper type instances
|
|
42
|
-
*/
|
|
43
|
-
function MetadataFromSimpleObjectWithoutUser(data, md) {
|
|
44
|
-
try {
|
|
45
|
-
const returnMetadata = new interfaces_1.AllMetadata();
|
|
46
|
-
// now iterate through the AllMetadataMapping array and construct the return type
|
|
47
|
-
for (let m of exports.AllMetadataArrays) {
|
|
48
|
-
let simpleKey = m.key;
|
|
49
|
-
if (!data.hasOwnProperty(simpleKey)) {
|
|
50
|
-
simpleKey = simpleKey.substring(3); // remove the All prefix
|
|
51
|
-
}
|
|
52
|
-
if (data.hasOwnProperty(simpleKey)) {
|
|
53
|
-
// at this point, only do this particular property if we have a match, it is either prefixed with All or not
|
|
54
|
-
// for example in our strongly typed AllMetadata class we have AllQueryCategories, but in the simple allMetadata object we have QueryCategories
|
|
55
|
-
// so we need to check for both which is what the above is doing.
|
|
56
|
-
// Build the array of the correct type and initialize with the simple object
|
|
57
|
-
returnMetadata[m.key] = data[simpleKey].map((d) => new m.class(d, md));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return returnMetadata;
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
(0, logging_1.LogError)(e);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
exports.MetadataFromSimpleObjectWithoutUser = MetadataFromSimpleObjectWithoutUser;
|
|
67
|
-
/**
|
|
68
|
-
* This is a list of all metadata classes that are used in the AllMetadata class.
|
|
69
|
-
* Used to automatically determine the class type when deserializing the metadata and
|
|
70
|
-
* for iterating through all metadata collections.
|
|
71
|
-
* Each entry maps a property key to its corresponding class constructor.
|
|
72
|
-
*/
|
|
73
|
-
exports.AllMetadataArrays = [
|
|
74
|
-
{ key: 'AllEntities', class: entityInfo_1.EntityInfo },
|
|
75
|
-
{ key: 'AllApplications', class: applicationInfo_1.ApplicationInfo },
|
|
76
|
-
{ key: 'AllRoles', class: securityInfo_1.RoleInfo },
|
|
77
|
-
{ key: 'AllRowLevelSecurityFilters', class: securityInfo_1.RowLevelSecurityFilterInfo },
|
|
78
|
-
{ key: 'AllAuditLogTypes', class: securityInfo_1.AuditLogTypeInfo },
|
|
79
|
-
{ key: 'AllAuthorizations', class: securityInfo_1.AuthorizationInfo },
|
|
80
|
-
{ key: 'AllQueryCategories', class: queryInfo_1.QueryCategoryInfo },
|
|
81
|
-
{ key: 'AllQueries', class: queryInfo_1.QueryInfo },
|
|
82
|
-
{ key: 'AllQueryFields', class: queryInfo_1.QueryFieldInfo },
|
|
83
|
-
{ key: 'AllQueryPermissions', class: queryInfo_1.QueryPermissionInfo },
|
|
84
|
-
{ key: 'AllQueryEntities', class: queryInfo_1.QueryEntityInfo },
|
|
85
|
-
{ key: 'AllQueryParameters', class: queryInfo_1.QueryParameterInfo },
|
|
86
|
-
{ key: 'AllEntityDocumentTypes', class: entityInfo_1.EntityDocumentTypeInfo },
|
|
87
|
-
{ key: 'AllLibraries', class: libraryInfo_1.LibraryInfo },
|
|
88
|
-
{ key: 'AllExplorerNavigationItems', class: explorerNavigationItem_1.ExplorerNavigationItem },
|
|
89
|
-
];
|
|
90
|
-
/**
|
|
91
|
-
* Base class for all metadata providers in MemberJunction.
|
|
92
|
-
* Implements common functionality for metadata caching, refresh, and dataset management.
|
|
93
|
-
* Subclasses must implement abstract methods for provider-specific operations.
|
|
94
|
-
*/
|
|
95
|
-
class ProviderBase {
|
|
96
|
-
constructor() {
|
|
97
|
-
this._localMetadata = new interfaces_1.AllMetadata();
|
|
98
|
-
this._refresh = false;
|
|
99
|
-
this._cachedVisibleExplorerNavigationItems = null;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Used to check to see if the entity in question is active or not
|
|
103
|
-
* If it is not active, it will throw an exception or log a warning depending on the status of the entity being
|
|
104
|
-
* either Deprecated or Disabled.
|
|
105
|
-
* @param entityName
|
|
106
|
-
* @param callerName
|
|
107
|
-
*/
|
|
108
|
-
async EntityStatusCheck(params, callerName) {
|
|
109
|
-
const entityName = await runView_1.RunView.GetEntityNameFromRunViewParams(params, this);
|
|
110
|
-
const entity = this.Entities.find((e) => e.Name.trim().toLowerCase() === entityName?.trim().toLowerCase());
|
|
111
|
-
if (!entity) {
|
|
112
|
-
throw new Error(`Entity ${entityName} not found in metadata`);
|
|
113
|
-
}
|
|
114
|
-
entityInfo_1.EntityInfo.AssertEntityActiveStatus(entity, callerName);
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Base class pre-processor that all sub-classes should call before they start their RunView process
|
|
118
|
-
* @param params
|
|
119
|
-
* @param contextUser
|
|
120
|
-
*/
|
|
121
|
-
async PreProcessRunView(params, contextUser) {
|
|
122
|
-
await this.EntityStatusCheck(params, 'PreProcessRunView');
|
|
123
|
-
// FIRST, if the resultType is entity_object, we need to run the view with ALL fields in the entity
|
|
124
|
-
// so that we can get the data to populate the entity object with.
|
|
125
|
-
if (params.ResultType === 'entity_object') {
|
|
126
|
-
// we need to get the entity definition and then get all the fields for it
|
|
127
|
-
const entity = this.Entities.find((e) => e.Name.trim().toLowerCase() === params.EntityName.trim().toLowerCase());
|
|
128
|
-
if (!entity)
|
|
129
|
-
throw new Error(`Entity ${params.EntityName} not found in metadata`);
|
|
130
|
-
params.Fields = entity.Fields.map((f) => f.Name); // just override whatever was passed in with all the fields - or if nothing was passed in, we set it. For loading the entity object, we need ALL the fields.
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Base class post-processor that all sub-classes should call after they finish their RunView process
|
|
135
|
-
* @param params
|
|
136
|
-
* @param contextUser
|
|
137
|
-
* @returns
|
|
138
|
-
*/
|
|
139
|
-
async PostProcessRunView(result, params, contextUser) {
|
|
140
|
-
// Transform the result set into BaseEntity-derived objects, if needed
|
|
141
|
-
await this.TransformSimpleObjectToEntityObject(params, result, contextUser);
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Base class implementation for handling pre-processing of RunViews() each sub-class should call this
|
|
145
|
-
* within their RunViews() method implementation
|
|
146
|
-
* @param params
|
|
147
|
-
* @param contextUser
|
|
148
|
-
* @returns
|
|
149
|
-
*/
|
|
150
|
-
async PreProcessRunViews(params, contextUser) {
|
|
151
|
-
if (params && params.length > 0) {
|
|
152
|
-
for (const param of params) {
|
|
153
|
-
this.EntityStatusCheck(param, 'PreProcessRunViews');
|
|
154
|
-
// FIRST, if the resultType is entity_object, we need to run the view with ALL fields in the entity
|
|
155
|
-
// so that we can get the data to populate the entity object with.
|
|
156
|
-
if (param.ResultType === 'entity_object') {
|
|
157
|
-
// we need to get the entity definition and then get all the fields for it
|
|
158
|
-
const entity = this.Entities.find((e) => e.Name.trim().toLowerCase() === param.EntityName.trim().toLowerCase());
|
|
159
|
-
if (!entity) {
|
|
160
|
-
throw new Error(`Entity ${param.EntityName} not found in metadata`);
|
|
161
|
-
}
|
|
162
|
-
param.Fields = entity.Fields.map((f) => f.Name); // just override whatever was passed in with all the fields - or if nothing was passed in, we set it. For loading the entity object, we need ALL the fields.
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Base class utilty method that should be called after each sub-class handles its internal RunViews() process before returning results
|
|
169
|
-
* This handles the optional conversion of simple objects to entity objects for each requested view depending on if the params requests
|
|
170
|
-
* a result_type === 'entity_object'
|
|
171
|
-
* @param results
|
|
172
|
-
* @param params
|
|
173
|
-
* @param contextUser
|
|
174
|
-
*/
|
|
175
|
-
async PostProcessRunViews(results, params, contextUser) {
|
|
176
|
-
if (params && params.length > 0) {
|
|
177
|
-
const promises = [];
|
|
178
|
-
for (let i = 0; i < results.length; i++) {
|
|
179
|
-
promises.push(this.TransformSimpleObjectToEntityObject(params[i], results[i], contextUser));
|
|
180
|
-
}
|
|
181
|
-
// await the promises for all transformations
|
|
182
|
-
await Promise.all(promises);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Transforms the result set from simple objects to entity objects if needed.
|
|
187
|
-
* @param param - The RunViewParams used for the request
|
|
188
|
-
* @param result - The RunViewResult returned from the request
|
|
189
|
-
* @param contextUser - The user context for permissions
|
|
190
|
-
*/
|
|
191
|
-
async TransformSimpleObjectToEntityObject(param, result, contextUser) {
|
|
192
|
-
// only if needed (e.g. ResultType==='entity_object'), transform the result set into BaseEntity-derived objects
|
|
193
|
-
if (param.ResultType === 'entity_object' && result && result.Success) {
|
|
194
|
-
// we need to transform each of the items in the result set into a BaseEntity-derived object
|
|
195
|
-
// Create entities and load data in parallel for better performance
|
|
196
|
-
const entityPromises = result.Results.map(async (item) => {
|
|
197
|
-
if (item instanceof baseEntity_1.BaseEntity || typeof item.Save === 'function') {
|
|
198
|
-
// the second check is a "duck-typing" check in case we have different runtime
|
|
199
|
-
// loading sources where the instanceof will fail
|
|
200
|
-
return item;
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
// not a base entity sub-class already so convert
|
|
204
|
-
const entity = await this.GetEntityObject(param.EntityName, contextUser);
|
|
205
|
-
await entity.LoadFromData(item);
|
|
206
|
-
return entity;
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
result.Results = await Promise.all(entityPromises);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Returns the currently loaded local metadata from within the instance
|
|
214
|
-
*/
|
|
215
|
-
get AllMetadata() {
|
|
216
|
-
return this._localMetadata;
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Configures the provider with the specified configuration data.
|
|
220
|
-
* Handles metadata refresh if needed and initializes the provider.
|
|
221
|
-
* @param data - Configuration including schema filters and connection info
|
|
222
|
-
* @returns True if configuration was successful
|
|
223
|
-
*/
|
|
224
|
-
async Config(data, providerToUse) {
|
|
225
|
-
this._ConfigData = data;
|
|
226
|
-
// first, let's check to see if we have an existing Metadata.Provider registered, if so
|
|
227
|
-
// unless our data.IgnoreExistingMetadata is set to true, we will not refresh the metadata
|
|
228
|
-
if (metadata_1.Metadata.Provider && !data.IgnoreExistingMetadata) {
|
|
229
|
-
// we have an existing globally registered provider AND we are not
|
|
230
|
-
// requested to ignore the existing metadata, so we will not refresh it
|
|
231
|
-
if (this.CopyMetadataFromGlobalProvider()) {
|
|
232
|
-
return true; // we're done, if we fail here, we keep going and do normal logic
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
if (this._refresh || (await this.CheckToSeeIfRefreshNeeded(providerToUse))) {
|
|
236
|
-
// either a hard refresh flag was set within Refresh(), or LocalMetadata is Obsolete
|
|
237
|
-
// first, make sure we reset the flag to false so that if another call to this function happens
|
|
238
|
-
// while we are waiting for the async call to finish, we dont do it again
|
|
239
|
-
this._refresh = false;
|
|
240
|
-
// start with fresh metadata
|
|
241
|
-
this._localMetadata = new interfaces_1.AllMetadata();
|
|
242
|
-
this._cachedVisibleExplorerNavigationItems = null; // reset this so it gets rebuilt next time it is requested
|
|
243
|
-
const start = new Date().getTime();
|
|
244
|
-
const res = await this.GetAllMetadata(providerToUse);
|
|
245
|
-
const end = new Date().getTime();
|
|
246
|
-
(0, logging_1.LogStatus)(`GetAllMetadata() took ${end - start} ms`);
|
|
247
|
-
if (res) {
|
|
248
|
-
this.UpdateLocalMetadata(res);
|
|
249
|
-
this._latestLocalMetadataTimestamps = this._latestRemoteMetadataTimestamps; // update this since we just used server to get all the stuff
|
|
250
|
-
await this.SaveLocalMetadataToStorage();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return true;
|
|
254
|
-
}
|
|
255
|
-
CloneAllMetadata(toClone) {
|
|
256
|
-
// we need to create a copy but can't do it the standard way becuase we need object instances
|
|
257
|
-
// for various things like EntityInfo
|
|
258
|
-
const newmd = MetadataFromSimpleObjectWithoutUser(toClone, this);
|
|
259
|
-
newmd.CurrentUser = this.CurrentUser;
|
|
260
|
-
return newmd;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Copies metadata from the global provider to the local instance.
|
|
264
|
-
* This is used to ensure that the local instance has the latest metadata
|
|
265
|
-
* information available without having to reload it from the server.
|
|
266
|
-
*/
|
|
267
|
-
CopyMetadataFromGlobalProvider() {
|
|
268
|
-
try {
|
|
269
|
-
if (metadata_1.Metadata.Provider && metadata_1.Metadata.Provider !== this && metadata_1.Metadata.Provider.AllMetadata) {
|
|
270
|
-
this._localMetadata = this.CloneAllMetadata(metadata_1.Metadata.Provider.AllMetadata);
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
275
|
-
catch (e) {
|
|
276
|
-
(0, logging_1.LogError)(`Failed to copy metadata from global provider: ${e.message}`);
|
|
277
|
-
return false; // if we fail to copy the metadata, we will return false
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Builds dataset filters based on the provider configuration.
|
|
282
|
-
* Ensures MJ Core schema is always included and never excluded.
|
|
283
|
-
* @returns Array of filters to apply when loading metadata
|
|
284
|
-
*/
|
|
285
|
-
BuildDatasetFilterFromConfig() {
|
|
286
|
-
// setup the schema filters as needed
|
|
287
|
-
const f = [];
|
|
288
|
-
// make sure that the MJ Core schema is always included if includeSchemas are provided because if the user doesn't include them stuff will break
|
|
289
|
-
const includeSchemaList = this.ConfigData.IncludeSchemas;
|
|
290
|
-
const excludeSchemaList = this.ConfigData.ExcludeSchemas;
|
|
291
|
-
const mjcSchema = this.ConfigData.MJCoreSchemaName;
|
|
292
|
-
// check to see if the MJ Core schema is already in the list, if not add it
|
|
293
|
-
// TODO: The logic here doesn't match the comment above
|
|
294
|
-
if (includeSchemaList && includeSchemaList.length > 0 && includeSchemaList.indexOf(mjcSchema) === -1)
|
|
295
|
-
includeSchemaList.push(mjcSchema);
|
|
296
|
-
// check to make sure that if exclude schemas are provided, the list DOES NOT include the MJ Core schema, if it does, remove it
|
|
297
|
-
if (excludeSchemaList && excludeSchemaList.length > 0 && excludeSchemaList.indexOf(mjcSchema) !== -1) {
|
|
298
|
-
const index = excludeSchemaList.indexOf(mjcSchema);
|
|
299
|
-
excludeSchemaList.splice(index, 1);
|
|
300
|
-
(0, logging_1.LogStatus)(`Removed MJ Core schema (${mjcSchema}) from ExcludeSchemas list because it is required for the API to function correctly`);
|
|
301
|
-
}
|
|
302
|
-
let schemaFilter = '';
|
|
303
|
-
if (includeSchemaList && includeSchemaList.length > 0) {
|
|
304
|
-
schemaFilter = 'SchemaName IN (' + includeSchemaList.map((s) => `'${s}'`).join(',') + ')';
|
|
305
|
-
}
|
|
306
|
-
if (excludeSchemaList && excludeSchemaList.length > 0) {
|
|
307
|
-
schemaFilter =
|
|
308
|
-
(schemaFilter.length > 0 ? ' AND ' : '') + 'SchemaName NOT IN (' + excludeSchemaList.map((s) => `'${s}'`).join(',') + ')';
|
|
309
|
-
}
|
|
310
|
-
if (schemaFilter.length > 0) {
|
|
311
|
-
f.push({ ItemCode: 'Entities', Filter: schemaFilter });
|
|
312
|
-
f.push({ ItemCode: 'EntityFields', Filter: schemaFilter });
|
|
313
|
-
}
|
|
314
|
-
return f;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Retrieves all metadata from the server and constructs typed instances.
|
|
318
|
-
* Uses the MJ_Metadata dataset for efficient bulk loading.
|
|
319
|
-
* @returns Complete metadata collection with all relationships
|
|
320
|
-
*/
|
|
321
|
-
async GetAllMetadata(providerToUse) {
|
|
322
|
-
try {
|
|
323
|
-
// we are now using datasets instead of the custom metadata to GraphQL to simplify GraphQL's work as it was very slow preivously
|
|
324
|
-
//const start1 = new Date().getTime();
|
|
325
|
-
const f = this.BuildDatasetFilterFromConfig();
|
|
326
|
-
// Get the dataset and cache it for anyone else who wants to use it
|
|
327
|
-
const d = await this.GetDatasetByName(_a._mjMetadataDatasetName, f.length > 0 ? f : null, this.CurrentUser, providerToUse);
|
|
328
|
-
if (d && d.Success) {
|
|
329
|
-
// cache the dataset for anyone who wants to use it
|
|
330
|
-
await this.CacheDataset(_a._mjMetadataDatasetName, f.length > 0 ? f : null, d);
|
|
331
|
-
// got the results, let's build our response in the format we need
|
|
332
|
-
const simpleMetadata = {};
|
|
333
|
-
for (let r of d.Results) {
|
|
334
|
-
simpleMetadata[r.Code] = r.Results;
|
|
335
|
-
}
|
|
336
|
-
// Post Process Entities because there's some special handling of the sub-objects
|
|
337
|
-
simpleMetadata.AllEntities = this.PostProcessEntityMetadata(simpleMetadata.Entities, simpleMetadata.EntityFields, simpleMetadata.EntityFieldValues, simpleMetadata.EntityPermissions, simpleMetadata.EntityRelationships, simpleMetadata.EntitySettings);
|
|
338
|
-
// Post Process the Applications, because we want to handle the sub-objects properly.
|
|
339
|
-
simpleMetadata.AllApplications = simpleMetadata.Applications.map((a) => {
|
|
340
|
-
a.ApplicationEntities = simpleMetadata.ApplicationEntities.filter((ae) => ae.ApplicationID === a.ID);
|
|
341
|
-
a.ApplicationSettings = simpleMetadata.ApplicationSettings.filter((as) => as.ApplicationID === a.ID);
|
|
342
|
-
return new applicationInfo_1.ApplicationInfo(a, this);
|
|
343
|
-
});
|
|
344
|
-
// now we need to construct our return type. The way the return type works, which is an instance of AllMetadata, we have to
|
|
345
|
-
// 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
|
|
346
|
-
// rather than just plain JavaScript objects that we have in the allMetadata object.
|
|
347
|
-
// build the base return type
|
|
348
|
-
const returnMetadata = MetadataFromSimpleObjectWithoutUser(simpleMetadata, this);
|
|
349
|
-
returnMetadata.CurrentUser = await this.GetCurrentUser();
|
|
350
|
-
return returnMetadata;
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
(0, logging_1.LogError)('GetAllMetadata() - Error getting metadata from server' + (d ? ': ' + d.Status : ''));
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
catch (e) {
|
|
357
|
-
(0, logging_1.LogError)(e);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Post-processes entity metadata to establish relationships between entities and their child objects.
|
|
362
|
-
* Links fields, permissions, relationships, and settings to their parent entities.
|
|
363
|
-
* @param entities - Array of entity metadata
|
|
364
|
-
* @param fields - Array of entity field metadata
|
|
365
|
-
* @param fieldValues - Array of entity field value metadata
|
|
366
|
-
* @param permissions - Array of entity permission metadata
|
|
367
|
-
* @param relationships - Array of entity relationship metadata
|
|
368
|
-
* @param settings - Array of entity settings metadata
|
|
369
|
-
* @returns Processed array of EntityInfo instances with all relationships established
|
|
370
|
-
*/
|
|
371
|
-
PostProcessEntityMetadata(entities, fields, fieldValues, permissions, relationships, settings) {
|
|
372
|
-
const result = [];
|
|
373
|
-
if (fieldValues && fieldValues.length > 0)
|
|
374
|
-
for (let f of fields) {
|
|
375
|
-
// populate the field values for each field, if we have them
|
|
376
|
-
f.EntityFieldValues = fieldValues.filter((fv) => fv.EntityFieldID === f.ID);
|
|
377
|
-
}
|
|
378
|
-
for (let e of entities) {
|
|
379
|
-
e.EntityFields = fields.filter((f) => f.EntityID === e.ID).sort((a, b) => a.Sequence - b.Sequence);
|
|
380
|
-
e.EntityPermissions = permissions.filter((p) => p.EntityID === e.ID);
|
|
381
|
-
e.EntityRelationships = relationships.filter((r) => r.EntityID === e.ID);
|
|
382
|
-
e.EntitySettings = settings.filter((s) => s.EntityID === e.ID);
|
|
383
|
-
result.push(new entityInfo_1.EntityInfo(e));
|
|
384
|
-
}
|
|
385
|
-
return result;
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Gets the configuration data that was provided to the provider.
|
|
389
|
-
* @returns The provider configuration including schema filters
|
|
390
|
-
*/
|
|
391
|
-
get ConfigData() {
|
|
392
|
-
return this._ConfigData;
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Gets all entity metadata in the system.
|
|
396
|
-
* @returns Array of EntityInfo objects representing all entities
|
|
397
|
-
*/
|
|
398
|
-
get Entities() {
|
|
399
|
-
return this._localMetadata.AllEntities;
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Gets all application metadata in the system.
|
|
403
|
-
* @returns Array of ApplicationInfo objects representing all applications
|
|
404
|
-
*/
|
|
405
|
-
get Applications() {
|
|
406
|
-
return this._localMetadata.AllApplications;
|
|
407
|
-
}
|
|
408
|
-
/**
|
|
409
|
-
* Gets the current user's information including roles and permissions.
|
|
410
|
-
* @returns UserInfo object for the authenticated user
|
|
411
|
-
*/
|
|
412
|
-
get CurrentUser() {
|
|
413
|
-
return this._localMetadata.CurrentUser;
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Gets all security roles defined in the system.
|
|
417
|
-
* @returns Array of RoleInfo objects representing all roles
|
|
418
|
-
*/
|
|
419
|
-
get Roles() {
|
|
420
|
-
return this._localMetadata.AllRoles;
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Gets all row-level security filters defined in the system.
|
|
424
|
-
* @returns Array of RowLevelSecurityFilterInfo objects for data access control
|
|
425
|
-
*/
|
|
426
|
-
get RowLevelSecurityFilters() {
|
|
427
|
-
return this._localMetadata.AllRowLevelSecurityFilters;
|
|
428
|
-
}
|
|
429
|
-
/**
|
|
430
|
-
* Gets all audit log types defined for tracking system activities.
|
|
431
|
-
* @returns Array of AuditLogTypeInfo objects
|
|
432
|
-
*/
|
|
433
|
-
get AuditLogTypes() {
|
|
434
|
-
return this._localMetadata.AllAuditLogTypes;
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Gets all authorization definitions in the system.
|
|
438
|
-
* @returns Array of AuthorizationInfo objects defining permissions
|
|
439
|
-
*/
|
|
440
|
-
get Authorizations() {
|
|
441
|
-
return this._localMetadata.AllAuthorizations;
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Gets all saved queries in the system.
|
|
445
|
-
* @returns Array of QueryInfo objects representing stored queries
|
|
446
|
-
*/
|
|
447
|
-
get Queries() {
|
|
448
|
-
return this._localMetadata.AllQueries;
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Gets all query category definitions.
|
|
452
|
-
* @returns Array of QueryCategoryInfo objects for query organization
|
|
453
|
-
*/
|
|
454
|
-
get QueryCategories() {
|
|
455
|
-
return this._localMetadata.AllQueryCategories;
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* Gets all query field definitions.
|
|
459
|
-
* @returns Array of QueryFieldInfo objects defining query result columns
|
|
460
|
-
*/
|
|
461
|
-
get QueryFields() {
|
|
462
|
-
return this._localMetadata.AllQueryFields;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Gets all query permission assignments.
|
|
466
|
-
* @returns Array of QueryPermissionInfo objects defining query access
|
|
467
|
-
*/
|
|
468
|
-
get QueryPermissions() {
|
|
469
|
-
return this._localMetadata.AllQueryPermissions;
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* Gets all query entity associations.
|
|
473
|
-
* @returns Array of QueryEntityInfo objects linking queries to entities
|
|
474
|
-
*/
|
|
475
|
-
get QueryEntities() {
|
|
476
|
-
return this._localMetadata.AllQueryEntities;
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Gets all query parameter definitions.
|
|
480
|
-
* @returns Array of QueryParameterInfo objects for parameterized queries
|
|
481
|
-
*/
|
|
482
|
-
get QueryParameters() {
|
|
483
|
-
return this._localMetadata.AllQueryParameters;
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* Gets all library definitions in the system.
|
|
487
|
-
* @returns Array of LibraryInfo objects representing code libraries
|
|
488
|
-
*/
|
|
489
|
-
get Libraries() {
|
|
490
|
-
return this._localMetadata.AllLibraries;
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Gets all explorer navigation items including inactive ones.
|
|
494
|
-
* @returns Array of all ExplorerNavigationItem objects
|
|
495
|
-
*/
|
|
496
|
-
get AllExplorerNavigationItems() {
|
|
497
|
-
return this._localMetadata.AllExplorerNavigationItems;
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Gets only active explorer navigation items sorted by sequence.
|
|
501
|
-
* Results are cached for performance.
|
|
502
|
-
* @returns Array of active ExplorerNavigationItem objects
|
|
503
|
-
*/
|
|
504
|
-
get VisibleExplorerNavigationItems() {
|
|
505
|
-
// filter and sort once and cache
|
|
506
|
-
if (!this._cachedVisibleExplorerNavigationItems)
|
|
507
|
-
this._cachedVisibleExplorerNavigationItems = this._localMetadata.AllExplorerNavigationItems.filter((e) => e.IsActive).sort((a, b) => a.Sequence - b.Sequence);
|
|
508
|
-
return this._cachedVisibleExplorerNavigationItems;
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* Refreshes all metadata from the server.
|
|
512
|
-
* Respects the AllowRefresh flag from subclasses.
|
|
513
|
-
* @returns True if refresh was initiated or allowed
|
|
514
|
-
*/
|
|
515
|
-
async Refresh(providerToUse) {
|
|
516
|
-
// do nothing here, but set a _refresh flag for next time things are requested
|
|
517
|
-
if (this.AllowRefresh) {
|
|
518
|
-
this._refresh = true;
|
|
519
|
-
return this.Config(this._ConfigData, providerToUse);
|
|
520
|
-
}
|
|
521
|
-
else
|
|
522
|
-
return true; // subclass is telling us not to do any refresh ops right now
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Checks if local metadata is out of date and needs refreshing.
|
|
526
|
-
* Compares local timestamps with server timestamps.
|
|
527
|
-
* @returns True if refresh is needed, false otherwise
|
|
528
|
-
*/
|
|
529
|
-
async CheckToSeeIfRefreshNeeded(providerToUse) {
|
|
530
|
-
if (this.AllowRefresh) {
|
|
531
|
-
await this.RefreshRemoteMetadataTimestamps(providerToUse); // get the latest timestamps from the server first
|
|
532
|
-
await this.LoadLocalMetadataFromStorage(); // then, attempt to load before we check to see if it is obsolete
|
|
533
|
-
return this.LocalMetadataObsolete();
|
|
534
|
-
} //subclass is telling us not to do any refresh ops right now
|
|
535
|
-
else
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
/**
|
|
539
|
-
* Refreshes metadata only if needed based on timestamp comparison.
|
|
540
|
-
* Combines check and refresh into a single operation.
|
|
541
|
-
* @returns True if refresh was successful or not needed
|
|
542
|
-
*/
|
|
543
|
-
async RefreshIfNeeded(providerToUse) {
|
|
544
|
-
if (await this.CheckToSeeIfRefreshNeeded(providerToUse))
|
|
545
|
-
return this.Refresh(providerToUse);
|
|
546
|
-
else
|
|
547
|
-
return true;
|
|
548
|
-
}
|
|
549
|
-
async GetEntityObject(entityName, loadKeyOrContextUser, contextUser) {
|
|
550
|
-
try {
|
|
551
|
-
// Determine which overload was called
|
|
552
|
-
let actualLoadKey;
|
|
553
|
-
let actualContextUser;
|
|
554
|
-
if (loadKeyOrContextUser instanceof compositeKey_1.CompositeKey) {
|
|
555
|
-
// Second overload: entityName, loadKey, contextUser
|
|
556
|
-
actualLoadKey = loadKeyOrContextUser;
|
|
557
|
-
actualContextUser = contextUser;
|
|
558
|
-
}
|
|
559
|
-
else if (contextUser !== undefined) {
|
|
560
|
-
// Second overload with null/undefined loadKey: entityName, null/undefined, contextUser
|
|
561
|
-
actualLoadKey = undefined;
|
|
562
|
-
actualContextUser = contextUser;
|
|
563
|
-
}
|
|
564
|
-
else {
|
|
565
|
-
// First overload: entityName, contextUser
|
|
566
|
-
actualContextUser = loadKeyOrContextUser;
|
|
567
|
-
}
|
|
568
|
-
const entity = this.Metadata.Entities.find((e) => e.Name == entityName);
|
|
569
|
-
if (entity) {
|
|
570
|
-
// 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
|
|
571
|
-
// type reference registration by any module via MJ Global is the way to go as it is reliable across all platforms.
|
|
572
|
-
try {
|
|
573
|
-
const newObject = Global_1.MJGlobal.Instance.ClassFactory.CreateInstance(baseEntity_1.BaseEntity, entityName, entity, this);
|
|
574
|
-
await newObject.Config(actualContextUser);
|
|
575
|
-
if (actualLoadKey) {
|
|
576
|
-
// Load existing record
|
|
577
|
-
const loadResult = await newObject.InnerLoad(actualLoadKey);
|
|
578
|
-
if (!loadResult) {
|
|
579
|
-
throw new Error(`Failed to load ${entityName} with key: ${actualLoadKey.ToString()}`);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
// whenever we create a new object we want it to start
|
|
584
|
-
// out as a new record, so we call NewRecord() on it
|
|
585
|
-
newObject.NewRecord();
|
|
586
|
-
}
|
|
587
|
-
return newObject;
|
|
588
|
-
}
|
|
589
|
-
catch (e) {
|
|
590
|
-
(0, logging_1.LogError)(e);
|
|
591
|
-
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!`);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
else
|
|
595
|
-
throw new Error(`Entity ${entityName} not found in metadata`);
|
|
596
|
-
}
|
|
597
|
-
catch (ex) {
|
|
598
|
-
(0, logging_1.LogError)(ex);
|
|
599
|
-
return null;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
/**
|
|
603
|
-
* Returns a list of entity dependencies, basically metadata that tells you the links to this entity from all other entities.
|
|
604
|
-
* @param entityName
|
|
605
|
-
* @returns
|
|
606
|
-
*/
|
|
607
|
-
async GetEntityDependencies(entityName) {
|
|
608
|
-
// using our metadata, find all of the foreign keys that point to this entity
|
|
609
|
-
// go through each entity and find all the fields that have a RelatedEntity = entityName
|
|
610
|
-
try {
|
|
611
|
-
const eName = entityName.trim().toLowerCase();
|
|
612
|
-
const result = [];
|
|
613
|
-
for (let re of this.Entities) {
|
|
614
|
-
const relatedFields = re.Fields.filter((f) => f.RelatedEntity?.trim().toLowerCase() === eName);
|
|
615
|
-
// we now have all the fields, so let's create the EntityDependency objects
|
|
616
|
-
relatedFields.map((f) => {
|
|
617
|
-
result.push({
|
|
618
|
-
EntityName: entityName,
|
|
619
|
-
RelatedEntityName: re.Name,
|
|
620
|
-
FieldName: f.Name,
|
|
621
|
-
});
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
return result;
|
|
625
|
-
}
|
|
626
|
-
catch (e) {
|
|
627
|
-
(0, logging_1.LogError)(e);
|
|
628
|
-
throw e;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* Gets a database by name, if required, and caches it in a format available to the client (e.g. IndexedDB, LocalStorage, File, etc). The cache method is Provider specific
|
|
633
|
-
* If itemFilters are provided, the combination of datasetName and the filters are used to determine a match in the cache
|
|
634
|
-
* @param datasetName
|
|
635
|
-
* @param itemFilters
|
|
636
|
-
*/
|
|
637
|
-
async GetAndCacheDatasetByName(datasetName, itemFilters, contextUser, providerToUse) {
|
|
638
|
-
// first see if we have anything in cache at all, no reason to check server dates if we dont
|
|
639
|
-
if (await this.IsDatasetCached(datasetName, itemFilters)) {
|
|
640
|
-
// compare the local version, if exists to the server version dates
|
|
641
|
-
if (await this.IsDatasetCacheUpToDate(datasetName, itemFilters)) {
|
|
642
|
-
// we're up to date, all we need to do is get the local cache and return it
|
|
643
|
-
return this.GetCachedDataset(datasetName, itemFilters);
|
|
644
|
-
}
|
|
645
|
-
else {
|
|
646
|
-
// we're out of date, so get the dataset from the server
|
|
647
|
-
const dataset = await this.GetDatasetByName(datasetName, itemFilters, contextUser, providerToUse);
|
|
648
|
-
// cache it
|
|
649
|
-
await this.CacheDataset(datasetName, itemFilters, dataset);
|
|
650
|
-
return dataset;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
else {
|
|
654
|
-
// get the dataset from the server
|
|
655
|
-
const dataset = await this.GetDatasetByName(datasetName, itemFilters, contextUser, providerToUse);
|
|
656
|
-
// cache it
|
|
657
|
-
await this.CacheDataset(datasetName, itemFilters, dataset);
|
|
658
|
-
return dataset;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Returns the timestamp of the local cached version of a given datasetName or null if there is no local cache for the
|
|
663
|
-
* specified dataset
|
|
664
|
-
* @param datasetName the name of the dataset to check
|
|
665
|
-
* @param itemFilters optional filters to apply to the dataset
|
|
666
|
-
*/
|
|
667
|
-
async GetLocalDatasetTimestamp(datasetName, itemFilters) {
|
|
668
|
-
const ls = this.LocalStorageProvider;
|
|
669
|
-
if (ls) {
|
|
670
|
-
const key = this.GetDatasetCacheKey(datasetName, itemFilters);
|
|
671
|
-
const dateKey = key + '_date';
|
|
672
|
-
const val = await ls.GetItem(dateKey);
|
|
673
|
-
if (val) {
|
|
674
|
-
return new Date(val);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* This routine checks to see if the local cache version of a given datasetName/itemFilters combination is up to date with the server or not
|
|
680
|
-
* @param datasetName
|
|
681
|
-
* @param itemFilters
|
|
682
|
-
* @returns
|
|
683
|
-
*/
|
|
684
|
-
async IsDatasetCacheUpToDate(datasetName, itemFilters) {
|
|
685
|
-
const localDate = await this.GetLocalDatasetTimestamp(datasetName, itemFilters);
|
|
686
|
-
if (localDate) {
|
|
687
|
-
// we have a local cached timestamp, so compare it to the server timestamp
|
|
688
|
-
const status = await this.GetDatasetStatusByName(datasetName, itemFilters);
|
|
689
|
-
if (status) {
|
|
690
|
-
const serverTimestamp = status.LatestUpdateDate.getTime();
|
|
691
|
-
if (localDate.getTime() >= serverTimestamp) {
|
|
692
|
-
// this situation means our local cache timestamp is >= the server timestamp, so we're most likely up to date
|
|
693
|
-
// in this situation, the last thing we check is for each entity, if the rowcount is the same as the server, if it is, we're good
|
|
694
|
-
// iterate through all of the entities and check the row counts
|
|
695
|
-
const localDataset = await this.GetCachedDataset(datasetName, itemFilters);
|
|
696
|
-
for (const eu of status.EntityUpdateDates) {
|
|
697
|
-
const localEntity = localDataset.Results.find((e) => e.EntityID === eu.EntityID);
|
|
698
|
-
if (!localEntity || localEntity.Results.length !== eu.RowCount) {
|
|
699
|
-
// we either couldn't find the entity in the local cache or the row count is different, so we're out of date
|
|
700
|
-
// the RowCount being different picks up on DELETED rows. The UpdatedAt check which is handled above would pick up
|
|
701
|
-
// on any new rows or updated rows. This approach makes sure we detect deleted rows and refresh the cache.
|
|
702
|
-
return false;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
// if we get here that means that the row counts are the same for all entities and we're up to date
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
// our local cache timestamp is < the server timestamp, so we're out of date
|
|
710
|
-
return false;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
else {
|
|
714
|
-
// we couldn't get the server status, so we're out of date
|
|
715
|
-
return false;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
// we don't have a local cache timestamp, so we're out of date
|
|
720
|
-
return false;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* This routine gets the local cached version of a given datasetName/itemFilters combination, it does NOT check the server status first and does not fall back on the server if there isn't a local cache version of this dataset/itemFilters combination
|
|
725
|
-
* @param datasetName
|
|
726
|
-
* @param itemFilters
|
|
727
|
-
* @returns
|
|
728
|
-
*/
|
|
729
|
-
async GetCachedDataset(datasetName, itemFilters) {
|
|
730
|
-
const ls = this.LocalStorageProvider;
|
|
731
|
-
if (ls) {
|
|
732
|
-
const key = this.GetDatasetCacheKey(datasetName, itemFilters);
|
|
733
|
-
const val = await ls.GetItem(key);
|
|
734
|
-
if (val) {
|
|
735
|
-
const dataset = JSON.parse(val);
|
|
736
|
-
return dataset;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Stores a dataset in the local cache. If itemFilters are provided, the combination of datasetName and the filters are used to build a key and determine a match in the cache
|
|
742
|
-
* @param datasetName
|
|
743
|
-
* @param itemFilters
|
|
744
|
-
* @param dataset
|
|
745
|
-
*/
|
|
746
|
-
async CacheDataset(datasetName, itemFilters, dataset) {
|
|
747
|
-
const ls = this.LocalStorageProvider;
|
|
748
|
-
if (ls) {
|
|
749
|
-
const key = this.GetDatasetCacheKey(datasetName, itemFilters);
|
|
750
|
-
const val = JSON.stringify(dataset);
|
|
751
|
-
await ls.SetItem(key, val);
|
|
752
|
-
const dateKey = key + '_date';
|
|
753
|
-
const dateVal = dataset.LatestUpdateDate.toISOString();
|
|
754
|
-
await ls.SetItem(dateKey, dateVal);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* Determines if a given datasetName/itemFilters combination is cached locally or not
|
|
759
|
-
* @param datasetName
|
|
760
|
-
* @param itemFilters
|
|
761
|
-
* @returns
|
|
762
|
-
*/
|
|
763
|
-
async IsDatasetCached(datasetName, itemFilters) {
|
|
764
|
-
const ls = this.LocalStorageProvider;
|
|
765
|
-
if (ls) {
|
|
766
|
-
const key = this.GetDatasetCacheKey(datasetName, itemFilters);
|
|
767
|
-
const val = await ls.GetItem(key);
|
|
768
|
-
return val !== null && val !== undefined;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* Creates a unique key for the given datasetName and itemFilters combination coupled with the instance connection string to ensure uniqueness when 2+ connections exist
|
|
773
|
-
* @param datasetName
|
|
774
|
-
* @param itemFilters
|
|
775
|
-
* @returns
|
|
776
|
-
*/
|
|
777
|
-
GetDatasetCacheKey(datasetName, itemFilters) {
|
|
778
|
-
return (this.LocalStoragePrefix +
|
|
779
|
-
_a.localStorageRootKey +
|
|
780
|
-
this.InstanceConnectionString +
|
|
781
|
-
'__DATASET__' +
|
|
782
|
-
datasetName +
|
|
783
|
-
this.ConvertItemFiltersToUniqueKey(itemFilters));
|
|
784
|
-
}
|
|
785
|
-
/**
|
|
786
|
-
* Converts dataset item filters into a unique string key for caching.
|
|
787
|
-
* @param itemFilters - Array of filters to convert
|
|
788
|
-
* @returns JSON-formatted string representing the filters
|
|
789
|
-
*/
|
|
790
|
-
ConvertItemFiltersToUniqueKey(itemFilters) {
|
|
791
|
-
if (itemFilters) {
|
|
792
|
-
const key = '{' + itemFilters.map((f) => `"${f.ItemCode}":"${f.Filter}"`).join(',') + '}'; // this is a unique key for the item filters
|
|
793
|
-
return key;
|
|
794
|
-
}
|
|
795
|
-
else
|
|
796
|
-
return '';
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* If the specified datasetName is cached, this method will clear the cache. If itemFilters are provided, the combination of datasetName and the filters are used to determine a match in the cache
|
|
800
|
-
* @param datasetName
|
|
801
|
-
* @param itemFilters
|
|
802
|
-
*/
|
|
803
|
-
async ClearDatasetCache(datasetName, itemFilters) {
|
|
804
|
-
const ls = this.LocalStorageProvider;
|
|
805
|
-
if (ls) {
|
|
806
|
-
const key = this.GetDatasetCacheKey(datasetName, itemFilters);
|
|
807
|
-
await ls.Remove(key);
|
|
808
|
-
const dateKey = key + '_date';
|
|
809
|
-
await ls.Remove(dateKey);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* Gets the latest metadata timestamps from the remote server.
|
|
814
|
-
* Used to determine if local cache is out of date.
|
|
815
|
-
* @returns Array of metadata timestamp information
|
|
816
|
-
*/
|
|
817
|
-
get LatestRemoteMetadata() {
|
|
818
|
-
return this._latestRemoteMetadataTimestamps;
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* Gets the latest metadata timestamps from local cache.
|
|
822
|
-
* Used for comparison with remote timestamps.
|
|
823
|
-
* @returns Array of locally cached metadata timestamps
|
|
824
|
-
*/
|
|
825
|
-
get LatestLocalMetadata() {
|
|
826
|
-
return this._latestLocalMetadataTimestamps;
|
|
827
|
-
}
|
|
828
|
-
/**
|
|
829
|
-
* Retrieves the latest metadata update timestamps from the server.
|
|
830
|
-
* @returns Array of metadata update information
|
|
831
|
-
*/
|
|
832
|
-
async GetLatestMetadataUpdates(providerToUse) {
|
|
833
|
-
const f = this.BuildDatasetFilterFromConfig();
|
|
834
|
-
const d = await this.GetDatasetStatusByName(_a._mjMetadataDatasetName, f.length > 0 ? f : null, this.CurrentUser, providerToUse);
|
|
835
|
-
if (d && d.Success) {
|
|
836
|
-
const ret = d.EntityUpdateDates.map((e) => {
|
|
837
|
-
return {
|
|
838
|
-
ID: e.EntityID,
|
|
839
|
-
Type: e.EntityName,
|
|
840
|
-
UpdatedAt: e.UpdateDate,
|
|
841
|
-
RowCount: e.RowCount,
|
|
842
|
-
};
|
|
843
|
-
});
|
|
844
|
-
// combine the entityupdate dates with a single top level entry for the dataset itself
|
|
845
|
-
ret.push({
|
|
846
|
-
ID: '',
|
|
847
|
-
Type: 'All Entity Metadata',
|
|
848
|
-
UpdatedAt: d.LatestUpdateDate,
|
|
849
|
-
RowCount: d.EntityUpdateDates.reduce((a, b) => a + b.RowCount, 0),
|
|
850
|
-
});
|
|
851
|
-
return ret;
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
/**
|
|
855
|
-
* Refreshes the remote metadata timestamps from the server.
|
|
856
|
-
* Updates the internal cache of remote timestamps.
|
|
857
|
-
* @returns True if timestamps were successfully refreshed
|
|
858
|
-
*/
|
|
859
|
-
async RefreshRemoteMetadataTimestamps(providerToUse) {
|
|
860
|
-
const mdTimeStamps = await this.GetLatestMetadataUpdates(providerToUse);
|
|
861
|
-
if (mdTimeStamps) {
|
|
862
|
-
this._latestRemoteMetadataTimestamps = mdTimeStamps;
|
|
863
|
-
return true;
|
|
864
|
-
}
|
|
865
|
-
else
|
|
866
|
-
return false;
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* Checks if local metadata is obsolete compared to remote metadata.
|
|
870
|
-
* Compares timestamps and row counts to detect changes.
|
|
871
|
-
* @param type - Optional specific metadata type to check
|
|
872
|
-
* @returns True if local metadata is out of date
|
|
873
|
-
*/
|
|
874
|
-
LocalMetadataObsolete(type) {
|
|
875
|
-
const mdLocal = this.LatestLocalMetadata;
|
|
876
|
-
const mdRemote = this.LatestRemoteMetadata;
|
|
877
|
-
if (!mdLocal || !mdRemote || !mdLocal.length || !mdRemote.length || mdLocal.length === 0 || mdRemote.length === 0)
|
|
878
|
-
return true;
|
|
879
|
-
for (let i = 0; i < mdRemote.length; ++i) {
|
|
880
|
-
let bProcess = true;
|
|
881
|
-
if (type && type.length > 0)
|
|
882
|
-
bProcess = mdRemote[i].Type.toLowerCase().trim() === type.trim().toLowerCase();
|
|
883
|
-
if (bProcess) {
|
|
884
|
-
const l = mdLocal.find((md) => md.Type.trim().toLowerCase() === mdRemote[i].Type.trim().toLowerCase());
|
|
885
|
-
if (!l)
|
|
886
|
-
return true; // no match, obsolete in this case
|
|
887
|
-
else {
|
|
888
|
-
// we have a match, now test various things
|
|
889
|
-
if (!l.UpdatedAt && !mdRemote[i].UpdatedAt) {
|
|
890
|
-
// both are null, so we're good
|
|
891
|
-
// do nothing, keep on truckin'
|
|
892
|
-
// console.log('TEST: both are null, so we\'re good')
|
|
893
|
-
}
|
|
894
|
-
else if (l.UpdatedAt && mdRemote[i].UpdatedAt) {
|
|
895
|
-
// both are not null, so we need to compare them
|
|
896
|
-
const localTime = new Date(l.UpdatedAt);
|
|
897
|
-
const remoteTime = new Date(mdRemote[i].UpdatedAt);
|
|
898
|
-
if (localTime.getTime() !== remoteTime.getTime()) {
|
|
899
|
-
return true; // we can short circuit the entire rest of the function
|
|
900
|
-
// as one obsolete is good enough to obsolete the entire local metadata
|
|
901
|
-
}
|
|
902
|
-
else {
|
|
903
|
-
// here we have a match for the local and remote timestamps, so we need to check the row counts
|
|
904
|
-
// if the row counts are different, we're obsolete
|
|
905
|
-
if (l.RowCount !== mdRemote[i].RowCount) {
|
|
906
|
-
return true;
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
else
|
|
911
|
-
return true; // one is null and the other is not, so we're obsolete without even comparing
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
// if we get here, we're not obsolete!!
|
|
916
|
-
return false;
|
|
917
|
-
}
|
|
918
|
-
/**
|
|
919
|
-
* Updates the local metadata cache with new data.
|
|
920
|
-
* @param res - The new metadata to store locally
|
|
921
|
-
*/
|
|
922
|
-
UpdateLocalMetadata(res) {
|
|
923
|
-
this._localMetadata = res;
|
|
924
|
-
}
|
|
925
|
-
/**
|
|
926
|
-
* Loads metadata from local storage if available.
|
|
927
|
-
* Deserializes and reconstructs typed metadata objects.
|
|
928
|
-
*/
|
|
929
|
-
async LoadLocalMetadataFromStorage() {
|
|
930
|
-
try {
|
|
931
|
-
const ls = this.LocalStorageProvider;
|
|
932
|
-
if (ls) {
|
|
933
|
-
// execution environment supports local storage, use it
|
|
934
|
-
this._latestLocalMetadataTimestamps = JSON.parse(await ls.GetItem(this.LocalStoragePrefix + _a.localStorageTimestampsKey));
|
|
935
|
-
const temp = JSON.parse(await ls.GetItem(this.LocalStoragePrefix + _a.localStorageAllMetadataKey)); // we now have a simple object for all the metadata
|
|
936
|
-
if (temp) {
|
|
937
|
-
// we have local metadata
|
|
938
|
-
(0, logging_1.LogStatus)('Metadata loaded from local storage');
|
|
939
|
-
const metadata = MetadataFromSimpleObject(temp, this); // create a new object to start this up
|
|
940
|
-
this.UpdateLocalMetadata(metadata);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
catch (e) {
|
|
945
|
-
// some enviroments don't support local storage
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
/**
|
|
949
|
-
* 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
|
|
950
|
-
* 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
|
|
951
|
-
* based on the connection or other distinct identifier.
|
|
952
|
-
*/
|
|
953
|
-
get LocalStoragePrefix() {
|
|
954
|
-
return '';
|
|
955
|
-
}
|
|
956
|
-
/**
|
|
957
|
-
* Saves current metadata to local storage for caching.
|
|
958
|
-
* Serializes both timestamps and full metadata collections.
|
|
959
|
-
*/
|
|
960
|
-
async SaveLocalMetadataToStorage() {
|
|
961
|
-
try {
|
|
962
|
-
const ls = this.LocalStorageProvider;
|
|
963
|
-
if (ls) {
|
|
964
|
-
// execution environment supports local storage, use it
|
|
965
|
-
await ls.SetItem(this.LocalStoragePrefix + _a.localStorageTimestampsKey, JSON.stringify(this._latestLocalMetadataTimestamps));
|
|
966
|
-
// now persist the AllMetadata object
|
|
967
|
-
await ls.SetItem(this.LocalStoragePrefix + _a.localStorageAllMetadataKey, JSON.stringify(this._localMetadata));
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
catch (e) {
|
|
971
|
-
// some enviroments don't support local storage
|
|
972
|
-
(0, logging_1.LogError)(e);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* Removes all cached metadata from local storage.
|
|
977
|
-
* Clears both timestamps and metadata collections.
|
|
978
|
-
*/
|
|
979
|
-
async RemoveLocalMetadataFromStorage() {
|
|
980
|
-
try {
|
|
981
|
-
const ls = this.LocalStorageProvider;
|
|
982
|
-
for (let i = 0; i < _a.localStorageKeys.length; i++) {
|
|
983
|
-
await ls.Remove(this.LocalStoragePrefix + _a.localStorageKeys[i]);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
catch (e) {
|
|
987
|
-
// some enviroments don't support local storage
|
|
988
|
-
(0, logging_1.LogError)(e);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
exports.ProviderBase = ProviderBase;
|
|
993
|
-
_a = ProviderBase;
|
|
994
|
-
ProviderBase._mjMetadataDatasetName = 'MJ_Metadata';
|
|
995
|
-
ProviderBase.localStorageRootKey = '___MJCore_Metadata';
|
|
996
|
-
ProviderBase.localStorageTimestampsKey = _a.localStorageRootKey + '_Timestamps';
|
|
997
|
-
ProviderBase.localStorageAllMetadataKey = _a.localStorageRootKey + '_AllMetadata';
|
|
998
|
-
ProviderBase.localStorageKeys = [_a.localStorageTimestampsKey, _a.localStorageAllMetadataKey];
|
|
999
|
-
//# sourceMappingURL=providerBase.js.map
|