@memberjunction/graphql-dataprovider 1.8.1 → 2.0.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.
@@ -1,1178 +0,0 @@
1
- "use strict";
2
- /**************************************************************************************************************
3
- * The graphQLDataProvider provides a data provider for the entities framework that uses GraphQL to communicate
4
- * with the server.
5
- * In practice - this FILE will NOT exist in the entities library, we need to move to its own separate project
6
- * so it is only included by the consumer of the entities library if they want to use it.
7
- **************************************************************************************************************/
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.GraphQLDataProvider = exports.GraphQLProviderConfigData = void 0;
10
- const core_1 = require("@memberjunction/core");
11
- const core_entities_1 = require("@memberjunction/core-entities");
12
- const graphql_request_1 = require("graphql-request");
13
- const graphQLTransactionGroup_1 = require("./graphQLTransactionGroup");
14
- const idb_1 = require("@tempfix/idb");
15
- const rxjs_1 = require("rxjs");
16
- const graphql_ws_1 = require("graphql-ws");
17
- class GraphQLProviderConfigData extends core_1.ProviderConfigDataBase {
18
- /**
19
- * Token is the JWT token that is used to authenticate the user with the server
20
- */
21
- get Token() { return this.Data.Token; }
22
- set Token(token) { this.Data.Token = token; }
23
- /**
24
- * URL is the URL to the GraphQL endpoint
25
- */
26
- get URL() { return this.Data.URL; }
27
- /**
28
- * WSURL is the URL to the GraphQL websocket endpoint. This is used for subscriptions, if you are not using subscriptions, you can pass in a blank string for this
29
- */
30
- get WSURL() { return this.Data.WSURL; }
31
- /**
32
- * RefreshTokenFunction is a function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired
33
- */
34
- get RefreshTokenFunction() { return this.Data.RefreshFunction; }
35
- /**
36
- *
37
- * @param token Token is the JWT token that is used to authenticate the user with the server
38
- * @param url the URL to the GraphQL endpoint
39
- * @param wsurl the URL to the GraphQL websocket endpoint. This is used for subscriptions, if you are not using subscriptions, you can pass in a blank string for this
40
- * @param refreshTokenFunction is a function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired
41
- * @param MJCoreSchemaName the name of the MJ Core schema, if it is not the default name of __mj
42
- * @param includeSchemas optional, an array of schema names to include in the metadata. If not passed, all schemas are included
43
- * @param excludeSchemas optional, an array of schema names to exclude from the metadata. If not passed, no schemas are excluded
44
- */
45
- constructor(token, url, wsurl, refreshTokenFunction, MJCoreSchemaName, includeSchemas, excludeSchemas) {
46
- super({
47
- Token: token,
48
- URL: url,
49
- WSURL: wsurl,
50
- RefreshTokenFunction: refreshTokenFunction,
51
- }, MJCoreSchemaName, includeSchemas, excludeSchemas);
52
- }
53
- }
54
- exports.GraphQLProviderConfigData = GraphQLProviderConfigData;
55
- // The GraphQLDataProvider implements both the IEntityDataProvider and IMetadataProvider interfaces.
56
- class GraphQLDataProvider extends core_1.ProviderBase {
57
- constructor() {
58
- super(...arguments);
59
- this._innerCurrentUserQueryString = `CurrentUser {
60
- ${this.userInfoString()}
61
- UserRolesArray {
62
- ${this.userRoleInfoString()}
63
- }
64
- }
65
- `;
66
- this._currentUserQuery = (0, graphql_request_1.gql) `query CurrentUserAndRoles {
67
- ${this._innerCurrentUserQueryString}
68
- }`;
69
- this._wsClient = null;
70
- this._pushStatusRequests = [];
71
- }
72
- get ConfigData() { return GraphQLDataProvider._configData; }
73
- GenerateUUID() {
74
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
75
- var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8;
76
- return v.toString(16);
77
- });
78
- }
79
- async Config(configData) {
80
- try {
81
- // FIRST, set up the GraphQL client
82
- if (GraphQLDataProvider._sessionId === undefined)
83
- GraphQLDataProvider._sessionId = this.GenerateUUID();
84
- GraphQLDataProvider._configData = configData;
85
- // now create the new client, if it isn't alreayd created
86
- if (!GraphQLDataProvider._client)
87
- GraphQLDataProvider._client = GraphQLDataProvider.CreateNewGraphQLClient(configData.URL, configData.Token, GraphQLDataProvider._sessionId);
88
- return super.Config(configData); // now parent class can do it's config
89
- }
90
- catch (e) {
91
- (0, core_1.LogError)(e);
92
- throw (e);
93
- }
94
- }
95
- get sessionId() {
96
- return GraphQLDataProvider._sessionId;
97
- }
98
- get AllowRefresh() {
99
- return true; // this provider doesn't have any issues with allowing refreshes at any time
100
- }
101
- async GetCurrentUser() {
102
- const d = await GraphQLDataProvider.ExecuteGQL(this._currentUserQuery, null);
103
- if (d) {
104
- // convert the user and the user roles _mj__*** fields back to __mj_***
105
- const u = this.ConvertBackToMJFields(d.CurrentUser);
106
- const roles = u.UserRolesArray.map(r => this.ConvertBackToMJFields(r));
107
- u.UserRolesArray = roles;
108
- return new core_1.UserInfo(this, { ...u, UserRoles: roles }); // need to pass in the UserRoles as a separate property that is what is expected here
109
- }
110
- }
111
- /**************************************************************************/
112
- // START ---- IRunReportProvider
113
- /**************************************************************************/
114
- async RunReport(params, contextUser) {
115
- const query = (0, graphql_request_1.gql) `
116
- query GetReportDataQuery ($ReportID: Int!) {
117
- GetReportData(ReportID: $ReportID) {
118
- Success
119
- Results
120
- RowCount
121
- ExecutionTime
122
- ErrorMessage
123
- }
124
- }`;
125
- const result = await GraphQLDataProvider.ExecuteGQL(query, { ReportID: params.ReportID });
126
- if (result && result.GetReportData)
127
- return {
128
- ReportID: params.ReportID,
129
- Success: result.GetReportData.Success,
130
- Results: JSON.parse(result.GetReportData.Results),
131
- RowCount: result.GetReportData.RowCount,
132
- ExecutionTime: result.GetReportData.ExecutionTime,
133
- ErrorMessage: result.GetReportData.ErrorMessage,
134
- };
135
- }
136
- /**************************************************************************/
137
- // END ---- IRunReportProvider
138
- /**************************************************************************/
139
- /**************************************************************************/
140
- // START ---- IRunQueryProvider
141
- /**************************************************************************/
142
- async RunQuery(params, contextUser) {
143
- const query = (0, graphql_request_1.gql) `
144
- query GetQueryDataQuery ($QueryID: Int!) {
145
- GetQueryData(QueryID: $QueryID) {
146
- Success
147
- Results
148
- RowCount
149
- ExecutionTime
150
- ErrorMessage
151
- }
152
- }`;
153
- const queryId = typeof params.QueryID === 'string' ? parseInt(params.QueryID) : params.QueryID;
154
- const result = await GraphQLDataProvider.ExecuteGQL(query, { QueryID: queryId });
155
- if (result && result.GetQueryData)
156
- return {
157
- QueryID: params.QueryID,
158
- Success: result.GetQueryData.Success,
159
- Results: JSON.parse(result.GetQueryData.Results),
160
- RowCount: result.GetQueryData.RowCount,
161
- ExecutionTime: result.GetQueryData.ExecutionTime,
162
- ErrorMessage: result.GetQueryData.ErrorMessage,
163
- };
164
- }
165
- /**************************************************************************/
166
- // END ---- IRunReportProvider
167
- /**************************************************************************/
168
- /**************************************************************************/
169
- // START ---- IRunViewProvider
170
- /**************************************************************************/
171
- async RunView(params, contextUser) {
172
- try {
173
- let qName = '';
174
- let paramType = '';
175
- if (params) {
176
- const innerParams = {};
177
- let entity, viewEntity;
178
- if (params.ViewEntity) {
179
- viewEntity = params.ViewEntity;
180
- entity = viewEntity.Entity;
181
- }
182
- else {
183
- const { entityName, v } = await this.getEntityNameAndUserView(params, contextUser);
184
- viewEntity = v;
185
- entity = entityName;
186
- }
187
- // get entity metadata
188
- const e = this.Entities.find(e => e.Name === entity);
189
- if (!e)
190
- throw new Error(`Entity ${entity} not found in metadata`);
191
- let dynamicView = false;
192
- if (params.ViewID) {
193
- qName = `Run${e.ClassName}ViewByID`;
194
- paramType = 'RunViewByIDInput';
195
- innerParams.ViewID = params.ViewID;
196
- }
197
- else if (params.ViewName) {
198
- qName = `Run${e.ClassName}ViewByName`;
199
- paramType = 'RunViewByNameInput';
200
- innerParams.ViewName = params.ViewName;
201
- }
202
- else {
203
- dynamicView = true;
204
- qName = `Run${e.ClassName}DynamicView`;
205
- paramType = 'RunDynamicViewInput';
206
- innerParams.EntityName = params.EntityName;
207
- }
208
- innerParams.ExtraFilter = params.ExtraFilter ? params.ExtraFilter : '';
209
- innerParams.OrderBy = params.OrderBy ? params.OrderBy : '';
210
- innerParams.UserSearchString = params.UserSearchString ? params.UserSearchString : '';
211
- innerParams.Fields = params.Fields; // pass it straight through, either null or array of strings
212
- innerParams.IgnoreMaxRows = params.IgnoreMaxRows ? params.IgnoreMaxRows : false;
213
- innerParams.MaxRows = params.MaxRows ? params.MaxRows : 0;
214
- innerParams.ForceAuditLog = params.ForceAuditLog ? params.ForceAuditLog : false;
215
- innerParams.ResultType = params.ResultType ? params.ResultType : 'simple';
216
- if (params.AuditLogDescription && params.AuditLogDescription.length > 0)
217
- innerParams.AuditLogDescription = params.AuditLogDescription;
218
- if (!dynamicView) {
219
- innerParams.ExcludeUserViewRunID = params.ExcludeUserViewRunID ? params.ExcludeUserViewRunID : -1;
220
- innerParams.ExcludeDataFromAllPriorViewRuns = params.ExcludeDataFromAllPriorViewRuns ? params.ExcludeDataFromAllPriorViewRuns : false;
221
- innerParams.OverrideExcludeFilter = params.OverrideExcludeFilter ? params.OverrideExcludeFilter : '';
222
- innerParams.SaveViewResults = params.SaveViewResults ? params.SaveViewResults : false;
223
- }
224
- const fieldList = this.getViewRunTimeFieldList(e, viewEntity, params, dynamicView);
225
- const query = (0, graphql_request_1.gql) `
226
- query RunViewQuery ($input: ${paramType}!) {
227
- ${qName}(input: $input) {
228
- Results {
229
- ${fieldList.join("\n ")}
230
- }
231
- UserViewRunID
232
- RowCount
233
- TotalRowCount
234
- ExecutionTime
235
- Success
236
- ErrorMessage
237
- }
238
- }`;
239
- const viewData = await GraphQLDataProvider.ExecuteGQL(query, { input: innerParams });
240
- if (viewData && viewData[qName]) {
241
- // now, if we have any results in viewData that are for the CodeName, we need to convert them to the Name
242
- // so that the caller gets back what they expect
243
- const results = viewData[qName].Results;
244
- if (results && results.length > 0) {
245
- const codeNameDiffFields = e.Fields.filter(f => f.CodeName !== f.Name && f.CodeName !== undefined);
246
- results.forEach(r => {
247
- // for _mj__ results, we need to convert them back to the Name
248
- this.ConvertBackToMJFields(r);
249
- codeNameDiffFields.forEach(f => {
250
- r[f.Name] = r[f.CodeName];
251
- // delete r[f.CodeName]; // Leave the CodeName in the results, it is useful to have both
252
- });
253
- });
254
- }
255
- return viewData[qName];
256
- }
257
- }
258
- else
259
- throw ("No parameters passed to RunView");
260
- return null;
261
- }
262
- catch (e) {
263
- (0, core_1.LogError)(e);
264
- throw (e);
265
- }
266
- }
267
- async getEntityNameAndUserView(params, contextUser) {
268
- let entityName;
269
- let v;
270
- if (!params.EntityName) {
271
- if (params.ViewID) {
272
- v = await core_entities_1.ViewInfo.GetViewEntity(params.ViewID, contextUser);
273
- entityName = v.Entity;
274
- }
275
- else if (params.ViewName) {
276
- v = await core_entities_1.ViewInfo.GetViewEntityByName(params.ViewName, contextUser);
277
- entityName = v.Entity;
278
- }
279
- else
280
- throw new Error(`No EntityName, ViewID or ViewName passed to RunView`);
281
- }
282
- else
283
- entityName = params.EntityName;
284
- return { entityName, v };
285
- }
286
- getViewRunTimeFieldList(e, v, params, dynamicView) {
287
- const fieldList = [];
288
- if (params.Fields) {
289
- for (const kv of e.PrimaryKeys) {
290
- if (params.Fields.find(f => f.trim().toLowerCase() === kv.Name.toLowerCase()) === undefined)
291
- fieldList.push(kv.Name); // always include the primary key fields in view run time field list
292
- }
293
- // now add any other fields that were passed in
294
- params.Fields.forEach(f => {
295
- if (f.trim().toLowerCase().startsWith('__mj_'))
296
- fieldList.push(f.replace('__mj_', '_mj__'));
297
- else
298
- fieldList.push(f);
299
- });
300
- }
301
- else {
302
- // no fields were passed in. So, let's check to see if we are running an dynamic view.
303
- // If so, we need to include all fields since the caller didn't specify the fields they want
304
- // otherwise, we include the fields that are part of the view definition.
305
- if (dynamicView) {
306
- // include all fields since no fields were passed in
307
- e.Fields.forEach(f => {
308
- if (!f.IsBinaryFieldType) {
309
- if (f.CodeName.trim().toLowerCase().startsWith('__mj_'))
310
- fieldList.push(f.CodeName.replace('__mj_', '_mj__'));
311
- else
312
- fieldList.push(f.CodeName);
313
- }
314
- });
315
- }
316
- else {
317
- // NOTE: in the below, c.EntityField SHOULD always exist, however there is a possibility that at some point a VIEW was created that used fields
318
- // and those fields are NO LONGER part of an entity, in that situation we should just remove them, rather than letting the whole view blow up which
319
- // would happen if we dno't check for c.EntityField? in the below
320
- // first make sure we have the primary key field in the view column list, always should, but make sure
321
- for (const kv of e.PrimaryKeys) {
322
- if (fieldList.find(f => f.trim().toLowerCase() === kv.Name.toLowerCase()) === undefined)
323
- fieldList.push(kv.Name); // always include the primary key fields in view run time field list
324
- }
325
- // Now: include the fields that are part of the view definition
326
- v.Columns.forEach(c => {
327
- if (c.hidden === false && !fieldList.find(item => item.trim().toLowerCase() === c.EntityField?.Name.trim().toLowerCase())) { // don't include hidden fields and don't include the pkey field again
328
- if (c.EntityField.CodeName.trim().toLowerCase().startsWith('__mj_'))
329
- fieldList.push(c.EntityField.CodeName.replace('__mj_', '_mj__'));
330
- else
331
- fieldList.push(c.EntityField.CodeName);
332
- }
333
- });
334
- }
335
- }
336
- return fieldList;
337
- }
338
- /**************************************************************************/
339
- // END ---- IRunViewProvider
340
- /**************************************************************************/
341
- /**************************************************************************/
342
- // START ---- IEntityDataProvider
343
- /**************************************************************************/
344
- get ProviderType() {
345
- return core_1.ProviderType.Network;
346
- }
347
- async GetRecordChanges(entityName, primaryKey) {
348
- try {
349
- const p = {
350
- EntityName: 'Record Changes',
351
- ExtraFilter: `RecordID = '${primaryKey.Values()}' AND Entity = '${entityName}'`,
352
- //OrderBy: 'ChangedAt DESC',
353
- };
354
- const result = await this.RunView(p);
355
- if (result) {
356
- // sort the results client side because, for now, the RunViewParams doesn't support OrderBy dynamically like we tried. Later change this to do via the SQL query
357
- return result.Results.sort((a, b) => {
358
- return (a.ChangedAt > b.ChangedAt) ? -1 : 1; // sort descending on the date.... GraphQL passes back the date as time since base date
359
- });
360
- }
361
- else
362
- return null;
363
- }
364
- catch (e) {
365
- (0, core_1.LogError)(e);
366
- throw (e);
367
- }
368
- }
369
- /**
370
- * Returns a list of dependencies - records that are linked to the specified Entity/KeyValuePairs combination. A dependency is as defined by the relationships in the database. The MemberJunction metadata that is used
371
- * for this simply reflects the foreign key relationships that exist in the database. The CodeGen tool is what detects all of the relationships and generates the metadata that is used by MemberJunction. The metadata in question
372
- * is within the EntityField table and specifically the RelatedEntity and RelatedEntityField columns. In turn, this method uses that metadata and queries the database to determine the dependencies. To get the list of entity dependencies
373
- * you can use the utility method GetEntityDependencies(), which doesn't check for dependencies on a specific record, but rather gets the metadata in one shot that can be used for dependency checking.
374
- * @param entityName the name of the entity to check
375
- * @param KeyValuePairs the KeyValuePairs of the record to check
376
- */
377
- async GetRecordDependencies(entityName, primaryKey) {
378
- try {
379
- // execute the gql query to get the dependencies
380
- const query = (0, graphql_request_1.gql) `query GetRecordDependenciesQuery ($entityName: String!, $CompositeKey: CompositeKeyInputType!) {
381
- GetRecordDependencies(entityName: $entityName, CompositeKey: $CompositeKey) {
382
- EntityName
383
- RelatedEntityName
384
- FieldName
385
- CompositeKey {
386
- KeyValuePairs {
387
- FieldName
388
- Value
389
- }
390
- }
391
- }
392
- }`;
393
- // now we have our query built, execute it
394
- const vars = {
395
- entityName: entityName,
396
- CompositeKey: { KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs) }
397
- };
398
- const data = await GraphQLDataProvider.ExecuteGQL(query, vars);
399
- return data?.GetRecordDependencies; // shape of the result should exactly match the RecordDependency type
400
- }
401
- catch (e) {
402
- (0, core_1.LogError)(e);
403
- throw (e);
404
- }
405
- }
406
- ensureKeyValuePairValueIsString(kvps) {
407
- return kvps.map(kv => {
408
- return { FieldName: kv.FieldName, Value: kv.Value.toString() };
409
- });
410
- }
411
- async GetRecordDuplicates(params, contextUser) {
412
- if (!params) {
413
- return null;
414
- }
415
- const query = (0, graphql_request_1.gql) `query GetRecordDuplicatesQuery ($params: PotentialDuplicateRequestType!) {
416
- GetRecordDuplicates(params: $params) {
417
- Status
418
- ErrorMessage
419
- PotentialDuplicateResult {
420
- EntityID
421
- DuplicateRunDetailMatchRecordIDs
422
- RecordPrimaryKeys {
423
- KeyValuePairs {
424
- FieldName
425
- Value
426
- }
427
- }
428
- Duplicates {
429
- ProbabilityScore
430
- KeyValuePairs {
431
- FieldName
432
- Value
433
- }
434
- }
435
- }
436
- }
437
- }`;
438
- let request = {
439
- EntityID: params.EntityID,
440
- EntityDocumentID: params.EntityDocumentID,
441
- ListID: params.ListID,
442
- ProbabilityScore: params.ProbabilityScore,
443
- Options: params.Options,
444
- RecordIDs: params.RecordIDs.map(recordID => {
445
- return recordID.Copy();
446
- })
447
- };
448
- const data = await GraphQLDataProvider.ExecuteGQL(query, { params: request });
449
- if (data && data.GetRecordDuplicates) {
450
- return data.GetRecordDuplicates;
451
- }
452
- }
453
- async MergeRecords(request) {
454
- try {
455
- // execute the gql query to get the dependencies
456
- const mutation = (0, graphql_request_1.gql) `mutation MergeRecordsMutation ($request: RecordMergeRequest!) {
457
- MergeRecords(request: $request) {
458
- Success
459
- OverallStatus
460
- RecordMergeLogID
461
- RecordStatus {
462
- CompositeKey {
463
- KeyValuePairs {
464
- FieldName
465
- Value
466
- }
467
- }
468
- Success
469
- RecordMergeDeletionLogID
470
- Message
471
- }
472
- }
473
- }`;
474
- // create a new request that is compatible with the server's expectations where field maps and also the primary key values are all strings
475
- const newRequest = {
476
- EntityName: request.EntityName,
477
- SurvivingRecordCompositeKey: { KeyValuePairs: this.ensureKeyValuePairValueIsString(request.SurvivingRecordCompositeKey.KeyValuePairs) },
478
- FieldMap: request.FieldMap?.map(fm => {
479
- return {
480
- FieldName: fm.FieldName,
481
- Value: fm.Value.toString() // turn the value into a string, since that is what the server expects
482
- };
483
- }),
484
- RecordsToMerge: request.RecordsToMerge.map(r => {
485
- return r.Copy();
486
- })
487
- };
488
- // now we have our query built, execute it
489
- const data = await GraphQLDataProvider.ExecuteGQL(mutation, { request: newRequest });
490
- return data?.MergeRecords; // shape of the result should exactly match the RecordDependency type
491
- }
492
- catch (e) {
493
- (0, core_1.LogError)(e);
494
- return {
495
- Success: false,
496
- OverallStatus: e && e.message ? e.message : e,
497
- RecordStatus: [],
498
- RecordMergeLogID: -1,
499
- Request: request,
500
- };
501
- throw (e);
502
- }
503
- }
504
- async Save(entity, user, options) {
505
- const result = new core_1.BaseEntityResult();
506
- try {
507
- entity.RegisterTransactionPreprocessing(); // as of the time of writing, this isn't technically needed because we are not doing any async preprocessing, but it is good to have it here for future use in case something is added with async between here and the TransactionItem being added.
508
- const vars = { input: {} };
509
- const type = entity.IsSaved ? "Update" : "Create";
510
- result.StartedAt = new Date();
511
- result.Type = entity.IsSaved ? 'update' : 'create';
512
- result.OriginalValues = entity.Fields.map(f => { return { FieldName: f.CodeName, Value: f.Value }; });
513
- entity.ResultHistory.push(result); // push the new result as we have started a process
514
- // Create the query for the mutation first, we will provide the specific
515
- // input values later in the loop below. Here we are just setting up the mutation
516
- // and the fields that will be returned since the mutation returns back the latest
517
- // values for the entity and we need to have those values to update the entity after the
518
- // save
519
- const mutationName = `${type}${entity.EntityInfo.ClassName}`;
520
- // only pass along writable fields, AND the PKEY value if this is an update
521
- const filteredFields = entity.Fields.filter(f => f.SQLType.trim().toLowerCase() !== 'uniqueidentifier' && (f.ReadOnly === false || (f.IsPrimaryKey && entity.IsSaved)));
522
- const inner = ` ${mutationName}(input: $input) {
523
- ${entity.Fields.map(f => {
524
- if (f.Name.trim().toLowerCase().startsWith('__mj_'))
525
- return f.CodeName.replace('__mj_', '_mj__');
526
- else
527
- return f.CodeName;
528
- }).join("\n ")}
529
- }`;
530
- const outer = (0, graphql_request_1.gql) `mutation ${type}${entity.EntityInfo.ClassName} ($input: ${mutationName}Input!) {
531
- ${inner}
532
- }
533
- `;
534
- for (let i = 0; i < filteredFields.length; i++) {
535
- const f = filteredFields[i];
536
- let val = f.Value;
537
- if (val && f.EntityFieldInfo.TSType === core_1.EntityFieldTSType.Date)
538
- val = val.getTime();
539
- if (val && f.EntityFieldInfo.TSType === core_1.EntityFieldTSType.Boolean && typeof val !== 'boolean')
540
- val = parseInt(val) === 0 ? false : true; // convert to boolean
541
- if (val == null && f.EntityFieldInfo.AllowsNull == false) {
542
- if (f.EntityFieldInfo.DefaultValue != null) {
543
- // no value, but there is a default value, so use that, since field does NOT allow NULL
544
- val = f.EntityFieldInfo.DefaultValue;
545
- }
546
- else {
547
- // no default value, null value and field doesn't allow nulls, so set to either 0 or empty string
548
- if (f.FieldType == core_1.EntityFieldTSType.Number || f.FieldType == core_1.EntityFieldTSType.Boolean)
549
- val = 0;
550
- else
551
- val = '';
552
- }
553
- }
554
- vars.input[f.CodeName] = val;
555
- }
556
- // now add an OldValues prop to the vars IF the type === 'update'
557
- if (type.trim().toLowerCase() === 'update') {
558
- const ov = [];
559
- entity.Fields.forEach(f => {
560
- const val = f.OldValue ? (typeof f.OldValue === 'string' ? f.OldValue : f.OldValue.toString()) : null;
561
- ov.push({ Key: f.CodeName, Value: val }); // pass ALL old values to server, slightly inefficient but we want full record
562
- });
563
- vars.input['OldValues___'] = ov; // add the OldValues prop to the input property that is part of the vars already
564
- }
565
- if (entity.TransactionGroup) {
566
- return new Promise((resolve, reject) => {
567
- const mutationInputTypes = [
568
- {
569
- varName: 'input',
570
- inputType: mutationName + 'Input!'
571
- }
572
- ];
573
- entity.RaiseReadyForTransaction(); // let the entity know we're ready to be part of the transaction
574
- // we are part of a transaction group, so just add our query to the list
575
- // and when the transaction is committed, we will send all the queries at once
576
- entity.TransactionGroup.AddTransaction(new core_1.TransactionItem(entity, inner, vars, { mutationName,
577
- mutationInputTypes: mutationInputTypes }, (results, success) => {
578
- // we get here whenever the transaction group does gets around to committing
579
- // our query. We need to update our entity with the values that were returned
580
- // from the mutation if it was successful.
581
- result.EndedAt = new Date();
582
- if (success && results) {
583
- // got our data, send it back to the caller, which is the entity object
584
- // and that object needs to update itself from this data.
585
- result.Success = true;
586
- resolve(this.ConvertBackToMJFields(results));
587
- }
588
- else {
589
- // the transaction failed, nothing to update, but we need to call Reject so the
590
- // promise resolves with a rejection so our outer caller knows
591
- result.Success = false;
592
- result.Message = 'Transaction failed';
593
- reject();
594
- }
595
- }));
596
- });
597
- }
598
- else {
599
- // not part of a transaction group, so just go for it and send across our GQL
600
- const d = await GraphQLDataProvider.ExecuteGQL(outer, vars);
601
- if (d && d[type + entity.EntityInfo.ClassName]) {
602
- result.Success = true;
603
- result.EndedAt = new Date();
604
- const ret = this.ConvertBackToMJFields(d[type + entity.EntityInfo.ClassName]);
605
- return ret;
606
- }
607
- else
608
- throw new Error(`Save failed for ${entity.EntityInfo.ClassName}`);
609
- }
610
- }
611
- catch (e) {
612
- result.Success = false;
613
- result.EndedAt = new Date();
614
- result.Message = e.response?.errors?.length > 0 ? e.response.errors[0].message : e.message;
615
- (0, core_1.LogError)(e);
616
- return null;
617
- }
618
- }
619
- async Load(entity, primaryKey, EntityRelationshipsToLoad = null, user) {
620
- try {
621
- const vars = {};
622
- let pkeyInnerParamString = '';
623
- let pkeyOuterParamString = '';
624
- for (let i = 0; i < primaryKey.KeyValuePairs.length; i++) {
625
- const field = entity.Fields.find(f => f.Name.trim().toLowerCase() === primaryKey.KeyValuePairs[i].FieldName.trim().toLowerCase()).EntityFieldInfo;
626
- const val = primaryKey.GetValueByIndex(i);
627
- const pkeyGraphQLType = field.GraphQLType;
628
- // build up the param string for the outer query definition
629
- if (pkeyOuterParamString.length > 0)
630
- pkeyOuterParamString += ', ';
631
- pkeyOuterParamString += `$${field.CodeName}: ${pkeyGraphQLType}!`;
632
- // build up the param string for the inner query call
633
- if (pkeyInnerParamString.length > 0)
634
- pkeyInnerParamString += ', ';
635
- pkeyInnerParamString += `${field.CodeName}: $${field.CodeName}`;
636
- // build up the variables we are passing along to the query
637
- if (field.TSType === core_1.EntityFieldTSType.Number) {
638
- if (isNaN(primaryKey.GetValueByIndex(i)))
639
- throw new Error(`Primary Key value ${val} (${field.Name}) is not a valid number`);
640
- vars[field.CodeName] = parseInt(val); // converting to number here for graphql type to work properly
641
- }
642
- else
643
- vars[field.CodeName] = val;
644
- }
645
- const rel = EntityRelationshipsToLoad && EntityRelationshipsToLoad.length > 0 ? this.getRelatedEntityString(entity.EntityInfo, EntityRelationshipsToLoad) : '';
646
- const query = (0, graphql_request_1.gql) `query Single${entity.EntityInfo.ClassName}${rel.length > 0 ? 'Full' : ''} (${pkeyOuterParamString}) {
647
- ${entity.EntityInfo.ClassName}(${pkeyInnerParamString}) {
648
- ${entity.Fields.filter(f => !f.EntityFieldInfo.IsBinaryFieldType).map(f => {
649
- if (f.EntityFieldInfo.Name.trim().toLowerCase().startsWith('__mj_')) {
650
- // fields that start with __mj_ need to be converted to _mj__ for the GraphQL query
651
- return f.CodeName.replace('__mj_', '_mj__');
652
- }
653
- else {
654
- return f.CodeName;
655
- }
656
- }).join("\n ")}
657
- ${rel}
658
- }
659
- }
660
- `;
661
- const d = await GraphQLDataProvider.ExecuteGQL(query, vars);
662
- if (d && d[entity.EntityInfo.ClassName]) {
663
- // the resulting object has all the values in it, but we need to convert any elements that start with _mj__ back to __mj_
664
- return this.ConvertBackToMJFields(d[entity.EntityInfo.ClassName]);
665
- }
666
- else
667
- return null;
668
- }
669
- catch (e) {
670
- (0, core_1.LogError)(e);
671
- return null;
672
- }
673
- }
674
- /**
675
- * This method will convert back any fields that start with _mj__ back to __mj_ so that the entity object can properly update itself with the data that was returned from the server
676
- * @param ret
677
- * @returns
678
- */
679
- ConvertBackToMJFields(ret) {
680
- const keys = Object.keys(ret);
681
- keys.forEach(k => {
682
- if (k.trim().toLowerCase().startsWith('_mj__')) {
683
- const newKey = k.replace('_mj__', '__mj_');
684
- ret[newKey] = ret[k];
685
- delete ret[k]; // DO remove the old key as it has no purpose anymore as it was just for transport since GraphQL wont allow fields that start with __
686
- }
687
- });
688
- return ret; // clean object to pass back here
689
- }
690
- getRelatedEntityString(entityInfo, EntityRelationshipsToLoad) {
691
- let rel = '';
692
- for (let i = 0; i < entityInfo.RelatedEntities.length; i++) {
693
- if (EntityRelationshipsToLoad.indexOf(entityInfo.RelatedEntities[i].RelatedEntity) >= 0) {
694
- const r = entityInfo.RelatedEntities[i];
695
- const re = this.Entities.find(e => e.ID === r.RelatedEntityID);
696
- rel += `
697
- ${re.CodeName} {
698
- ${re.Fields.map(f => f.CodeName).join("\n ")}
699
- }
700
- `;
701
- }
702
- }
703
- return rel;
704
- }
705
- async Delete(entity, options, user) {
706
- const result = new core_1.BaseEntityResult();
707
- try {
708
- entity.RegisterTransactionPreprocessing();
709
- result.StartedAt = new Date();
710
- result.Type = 'delete';
711
- result.OriginalValues = entity.Fields.map(f => { return { FieldName: f.CodeName, Value: f.Value }; });
712
- entity.ResultHistory.push(result); // push the new result as we have started a process
713
- const vars = {};
714
- const mutationInputTypes = [];
715
- let pkeyInnerParamString = '';
716
- let pkeyOuterParamString = '';
717
- let returnValues = '';
718
- for (let kv of entity.PrimaryKey.KeyValuePairs) {
719
- const pk = entity.Fields.find(f => f.Name.trim().toLowerCase() === kv.FieldName.trim().toLowerCase()); // get the field for the primary key field
720
- vars[pk.CodeName] = pk.Value;
721
- mutationInputTypes.push({ varName: pk.CodeName, inputType: pk.EntityFieldInfo.GraphQLType + '!' }); // only used when doing a transaction group, but it is easier to do in this main loop
722
- if (pkeyInnerParamString.length > 0)
723
- pkeyInnerParamString += ', ';
724
- pkeyInnerParamString += `${pk.CodeName}: $${pk.CodeName}`;
725
- if (pkeyOuterParamString.length > 0)
726
- pkeyOuterParamString += ', ';
727
- pkeyOuterParamString += `$${pk.CodeName}: ${pk.EntityFieldInfo.GraphQLType}!`;
728
- if (returnValues.length > 0)
729
- returnValues += '\n ';
730
- returnValues += `${pk.CodeName}`;
731
- }
732
- mutationInputTypes.push({ varName: "options___", inputType: 'DeleteOptionsInput!' }); // only used when doing a transaction group, but it is easier to do in this main loop
733
- vars["options___"] = options ? options : { SkipEntityAIActions: false, SkipEntityActions: false };
734
- const queryName = 'Delete' + entity.EntityInfo.ClassName;
735
- const inner = (0, graphql_request_1.gql) `${queryName}(${pkeyInnerParamString}, options___: $options___) {
736
- ${returnValues}
737
- }
738
- `;
739
- const query = (0, graphql_request_1.gql) `mutation ${queryName} (${pkeyOuterParamString}, $options___: DeleteOptionsInput!) {
740
- ${inner}
741
- }
742
- `;
743
- if (entity.TransactionGroup) {
744
- // we have a transaction group, need to play nice and be part of it
745
- entity.RaiseReadyForTransaction();
746
- return new Promise((resolve, reject) => {
747
- // we are part of a transaction group, so just add our query to the list
748
- // and when the transaction is committed, we will send all the queries at once
749
- entity.TransactionGroup.AddTransaction(new core_1.TransactionItem(entity, inner, vars, { mutationName: queryName,
750
- mutationInputTypes: mutationInputTypes }, (results, success) => {
751
- // we get here whenever the transaction group does gets around to committing
752
- // our query.
753
- result.EndedAt = new Date(); // done processing
754
- if (success && results) {
755
- // success indicated by the entity.PrimaryKey.Value matching the return value of the mutation
756
- let success = true;
757
- for (const pk of entity.PrimaryKey.KeyValuePairs) {
758
- // check each primary key value to see if it matches the return value of the mutation
759
- if (pk.Value !== results[pk.FieldName]) {
760
- success = false;
761
- }
762
- }
763
- if (success) {
764
- result.Success = true;
765
- resolve(true);
766
- }
767
- else {
768
- // the transaction failed, nothing to update, but we need to call Reject so the
769
- // promise resolves with a rejection so our outer caller knows
770
- result.Success = false;
771
- result.Message = 'Transaction failed to commit';
772
- reject();
773
- }
774
- }
775
- else {
776
- // the transaction failed, nothing to update, but we need to call Reject so the
777
- // promise resolves with a rejection so our outer caller knows
778
- result.Success = false;
779
- result.Message = 'Transaction failed to commit';
780
- reject();
781
- }
782
- }));
783
- });
784
- }
785
- else {
786
- // no transaction just go for it
787
- const d = await GraphQLDataProvider.ExecuteGQL(query, vars);
788
- if (d && d[queryName]) {
789
- for (let key of entity.PrimaryKey.KeyValuePairs) {
790
- if (key.Value !== d[queryName][key.FieldName])
791
- throw new Error('Missing primary key value in server Delete response: ' + key.FieldName);
792
- }
793
- result.Success = true;
794
- result.EndedAt = new Date(); // done processing
795
- return true; // all of the return values match the primary key values, so we are good and delete worked
796
- }
797
- else
798
- throw new Error(`Delete failed for ${entity.EntityInfo.Name}: ${entity.PrimaryKey.ToString()} `);
799
- }
800
- }
801
- catch (e) {
802
- result.EndedAt = new Date(); // done processing
803
- result.Success = false;
804
- result.Message = e.response?.errors?.length > 0 ? e.response.errors[0].message : e.message;
805
- (0, core_1.LogError)(e);
806
- return false;
807
- }
808
- }
809
- /**************************************************************************/
810
- // END ---- IEntityDataProvider
811
- /**************************************************************************/
812
- /**************************************************************************/
813
- // START ---- IMetadataProvider
814
- /**************************************************************************/
815
- async GetDatasetByName(datasetName, itemFilters) {
816
- const query = (0, graphql_request_1.gql) `query GetDatasetByName($DatasetName: String!, $ItemFilters: [DatasetItemFilterTypeGQL!]) {
817
- GetDatasetByName(DatasetName: $DatasetName, ItemFilters: $ItemFilters) {
818
- DatasetID
819
- DatasetName
820
- Success
821
- Status
822
- LatestUpdateDate
823
- Results
824
- }
825
- }`;
826
- const data = await GraphQLDataProvider.ExecuteGQL(query, { DatasetName: datasetName, ItemFilters: itemFilters });
827
- if (data && data.GetDatasetByName && data.GetDatasetByName.Success) {
828
- return {
829
- DatasetID: data.GetDatasetByName.DatasetID,
830
- DatasetName: data.GetDatasetByName.DatasetName,
831
- Success: data.GetDatasetByName.Success,
832
- Status: data.GetDatasetByName.Status,
833
- LatestUpdateDate: new Date(data.GetDatasetByName.LatestUpdateDate),
834
- Results: JSON.parse(data.GetDatasetByName.Results)
835
- };
836
- }
837
- else {
838
- return {
839
- DatasetID: 0,
840
- DatasetName: datasetName,
841
- Success: false,
842
- Status: 'Unknown',
843
- LatestUpdateDate: null,
844
- Results: null
845
- };
846
- }
847
- }
848
- async GetDatasetStatusByName(datasetName, itemFilters) {
849
- const query = (0, graphql_request_1.gql) `query GetDatasetStatusByName($DatasetName: String!, $ItemFilters: [DatasetItemFilterTypeGQL!]) {
850
- GetDatasetStatusByName(DatasetName: $DatasetName, ItemFilters: $ItemFilters) {
851
- DatasetID
852
- DatasetName
853
- Success
854
- Status
855
- LatestUpdateDate
856
- EntityUpdateDates
857
- }
858
- }`;
859
- const data = await GraphQLDataProvider.ExecuteGQL(query, { DatasetName: datasetName, ItemFilters: itemFilters });
860
- if (data && data.GetDatasetStatusByName && data.GetDatasetStatusByName.Success) {
861
- return {
862
- DatasetID: data.GetDatasetStatusByName.DatasetID,
863
- DatasetName: data.GetDatasetStatusByName.DatasetName,
864
- Success: data.GetDatasetStatusByName.Success,
865
- Status: data.GetDatasetStatusByName.Status,
866
- LatestUpdateDate: new Date(data.GetDatasetStatusByName.LatestUpdateDate),
867
- EntityUpdateDates: JSON.parse(data.GetDatasetStatusByName.EntityUpdateDates)
868
- };
869
- }
870
- else {
871
- return {
872
- DatasetID: 0,
873
- DatasetName: datasetName,
874
- Success: false,
875
- Status: 'Unknown',
876
- LatestUpdateDate: null,
877
- EntityUpdateDates: null
878
- };
879
- }
880
- }
881
- async CreateTransactionGroup() {
882
- return new graphQLTransactionGroup_1.GraphQLTransactionGroup();
883
- }
884
- async GetRecordFavoriteStatus(userId, entityName, primaryKey) {
885
- const valResult = primaryKey.Validate();
886
- if (!valResult.IsValid)
887
- return false;
888
- const e = this.Entities.find(e => e.Name === entityName);
889
- if (!e)
890
- throw new Error(`Entity ${entityName} not found in metadata`);
891
- const query = (0, graphql_request_1.gql) `query GetRecordFavoriteStatus($params: UserFavoriteSearchParams!) {
892
- GetRecordFavoriteStatus(params: $params) {
893
- Success
894
- IsFavorite
895
- }
896
- }`;
897
- const data = await GraphQLDataProvider.ExecuteGQL(query, { params: {
898
- UserID: userId,
899
- EntityID: e.ID,
900
- CompositeKey: { KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs) }
901
- }
902
- });
903
- if (data && data.GetRecordFavoriteStatus && data.GetRecordFavoriteStatus.Success)
904
- return data.GetRecordFavoriteStatus.IsFavorite;
905
- }
906
- async SetRecordFavoriteStatus(userId, entityName, primaryKey, isFavorite, contextUser) {
907
- const e = this.Entities.find(e => e.Name === entityName);
908
- if (!e) {
909
- throw new Error(`Entity ${entityName} not found in metadata`);
910
- }
911
- const query = (0, graphql_request_1.gql) `mutation SetRecordFavoriteStatus($params: UserFavoriteSetParams!) {
912
- SetRecordFavoriteStatus(params: $params){
913
- Success
914
- }
915
- }`;
916
- const data = await GraphQLDataProvider.ExecuteGQL(query, { params: {
917
- UserID: userId,
918
- EntityID: e.ID,
919
- CompositeKey: { KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs) },
920
- IsFavorite: isFavorite
921
- }
922
- });
923
- if (data && data.SetRecordFavoriteStatus !== null)
924
- return data.SetRecordFavoriteStatus.Success;
925
- }
926
- async GetEntityRecordName(entityName, primaryKey) {
927
- if (!entityName || !primaryKey || primaryKey.KeyValuePairs?.length === 0) {
928
- return null;
929
- }
930
- const query = (0, graphql_request_1.gql) `query GetEntityRecordNameQuery ($EntityName: String!, $CompositeKey: CompositeKeyInputType!) {
931
- GetEntityRecordName(EntityName: $EntityName, CompositeKey: $CompositeKey) {
932
- Success
933
- Status
934
- RecordName
935
- }
936
- }`;
937
- const data = await GraphQLDataProvider.ExecuteGQL(query, {
938
- EntityName: entityName,
939
- CompositeKey: { KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs) }
940
- });
941
- if (data && data.GetEntityRecordName && data.GetEntityRecordName.Success)
942
- return data.GetEntityRecordName.RecordName;
943
- }
944
- async GetEntityRecordNames(info) {
945
- if (!info)
946
- return null;
947
- const query = (0, graphql_request_1.gql) `query GetEntityRecordNamesQuery ($info: [EntityRecordNameInput!]!) {
948
- GetEntityRecordNames(info: $info) {
949
- Success
950
- Status
951
- CompositeKey {
952
- KeyValuePairs {
953
- FieldName
954
- Value
955
- }
956
- }
957
- EntityName
958
- RecordName
959
- }
960
- }`;
961
- const data = await GraphQLDataProvider.ExecuteGQL(query, { info: info.map(i => {
962
- return {
963
- EntityName: i.EntityName,
964
- CompositeKey: { KeyValuePairs: this.ensureKeyValuePairValueIsString(i.CompositeKey.KeyValuePairs) }
965
- };
966
- }) });
967
- if (data && data.GetEntityRecordNames)
968
- return data.GetEntityRecordNames;
969
- }
970
- static async ExecuteGQL(query, variables, refreshTokenIfNeeded = true) {
971
- try {
972
- const data = await GraphQLDataProvider._client.request(query, variables);
973
- return data;
974
- }
975
- catch (e) {
976
- if (e && e.response && e.response.errors?.length > 0) { //e.code === 'JWT_EXPIRED') {
977
- const error = e.response.errors[0];
978
- const code = error?.extensions?.code?.toUpperCase().trim();
979
- if (code === 'JWT_EXPIRED') {
980
- if (refreshTokenIfNeeded) {
981
- // token expired, so we need to refresh it and try again
982
- await GraphQLDataProvider.RefreshToken();
983
- return await GraphQLDataProvider.ExecuteGQL(query, variables, false /*don't attempt to refresh again*/);
984
- }
985
- else {
986
- // token expired but the caller doesn't want a refresh, so just return the error
987
- (0, core_1.LogError)(`JWT_EXPIRED and refreshTokenIfNeeded is false`);
988
- throw e;
989
- }
990
- }
991
- else
992
- throw e;
993
- }
994
- else {
995
- (0, core_1.LogError)(e);
996
- throw e; // force the caller to handle the error
997
- }
998
- }
999
- }
1000
- static async RefreshToken() {
1001
- if (GraphQLDataProvider._configData.Data.RefreshTokenFunction) {
1002
- const newToken = await GraphQLDataProvider._configData.Data.RefreshTokenFunction();
1003
- if (newToken) {
1004
- GraphQLDataProvider._configData.Token = newToken; // update the token
1005
- GraphQLDataProvider._client = this.CreateNewGraphQLClient(GraphQLDataProvider._configData.URL, GraphQLDataProvider._configData.Token, GraphQLDataProvider._sessionId);
1006
- }
1007
- else {
1008
- throw new Error('Refresh token function returned null or undefined token');
1009
- }
1010
- }
1011
- else {
1012
- throw new Error('No refresh token function provided');
1013
- }
1014
- }
1015
- static CreateNewGraphQLClient(url, token, sessionId) {
1016
- return new graphql_request_1.GraphQLClient(url, {
1017
- headers: {
1018
- authorization: 'Bearer ' + token,
1019
- 'x-session-id': sessionId
1020
- }
1021
- });
1022
- }
1023
- userInfoString() {
1024
- return this.infoString(new core_1.UserInfo(null, null));
1025
- }
1026
- userRoleInfoString() {
1027
- return this.infoString(new core_1.UserRoleInfo(null));
1028
- }
1029
- infoString(object) {
1030
- let sOutput = '';
1031
- const keys = Object.keys(object);
1032
- for (const k of keys) {
1033
- if (k.startsWith('__mj_')) {
1034
- sOutput += k.replace('__mj_', '_mj__') + '\n ';
1035
- }
1036
- else if (!k.startsWith('_')) {
1037
- sOutput += k + '\n ';
1038
- }
1039
- }
1040
- return sOutput;
1041
- }
1042
- get LocalStorageProvider() {
1043
- if (!this._localStorageProvider)
1044
- this._localStorageProvider = new BrowserIndexedDBStorageProvider();
1045
- return this._localStorageProvider;
1046
- }
1047
- /**************************************************************************/
1048
- // END ---- IMetadataProvider
1049
- /**************************************************************************/
1050
- get Metadata() {
1051
- return this;
1052
- }
1053
- PushStatusUpdates(sessionId = null) {
1054
- if (!sessionId)
1055
- sessionId = this.sessionId;
1056
- if (!this._wsClient)
1057
- this._wsClient = (0, graphql_ws_1.createClient)({
1058
- url: this.ConfigData.WSURL,
1059
- connectionParams: {
1060
- Authorization: 'Bearer ' + this.ConfigData.Token,
1061
- },
1062
- });
1063
- const existingRequest = this._pushStatusRequests.find(r => r.sessionId === sessionId);
1064
- if (existingRequest)
1065
- return existingRequest.observable;
1066
- const SUBSCRIBE_TO_STATUS = (0, graphql_request_1.gql) `subscription StatusUpdates($sessionId: String!) {
1067
- statusUpdates(sessionId: $sessionId) {
1068
- date
1069
- message
1070
- sessionId
1071
- }
1072
- }
1073
- `;
1074
- const newObservable = new rxjs_1.Observable((observer) => {
1075
- const unsubscribe = this._wsClient.subscribe({ query: SUBSCRIBE_TO_STATUS, variables: { sessionId } }, {
1076
- next: (data) => {
1077
- return observer.next(data.data.statusUpdates);
1078
- },
1079
- error: (error) => {
1080
- return observer.error(error);
1081
- },
1082
- complete: () => {
1083
- return observer.complete();
1084
- },
1085
- });
1086
- return () => {
1087
- // Cleanup logic
1088
- console.log('would unsub here');
1089
- //unsubscribe();
1090
- };
1091
- });
1092
- this._pushStatusRequests.push({ sessionId, observable: newObservable });
1093
- return newObservable;
1094
- }
1095
- }
1096
- exports.GraphQLDataProvider = GraphQLDataProvider;
1097
- // this class implements a simple in-memory only storage as a fallback if the browser doesn't support local storage
1098
- class BrowserStorageProviderBase {
1099
- constructor() {
1100
- this._localStorage = {};
1101
- }
1102
- async getItem(key) {
1103
- return new Promise((resolve) => {
1104
- if (this._localStorage.hasOwnProperty(key))
1105
- resolve(this._localStorage[key]);
1106
- else
1107
- resolve(null);
1108
- });
1109
- }
1110
- async setItem(key, value) {
1111
- return new Promise((resolve) => {
1112
- this._localStorage[key] = value;
1113
- resolve();
1114
- });
1115
- }
1116
- async remove(key) {
1117
- return new Promise((resolve) => {
1118
- if (this._localStorage.hasOwnProperty(key)) {
1119
- delete this._localStorage[key];
1120
- }
1121
- resolve();
1122
- });
1123
- }
1124
- }
1125
- // This implementation just wraps the browser local storage and if for some reason the browser doesn't
1126
- // have a localStorage object, we just use a simple object to store the data in memory.
1127
- class BrowserLocalStorageProvider extends BrowserStorageProviderBase {
1128
- async getItem(key) {
1129
- if (localStorage)
1130
- return localStorage.getItem(key);
1131
- else
1132
- return await super.getItem(key);
1133
- }
1134
- async setItem(key, value) {
1135
- if (localStorage)
1136
- localStorage.setItem(key, value);
1137
- else
1138
- await super.setItem(key, value);
1139
- }
1140
- async remove(key) {
1141
- if (localStorage)
1142
- localStorage.removeItem(key);
1143
- else
1144
- await super.remove(key);
1145
- }
1146
- }
1147
- const IDB_DB_NAME = 'MJ_Metadata';
1148
- const IDB_DB_ObjectStoreName = 'Metadata_KVPairs';
1149
- class BrowserIndexedDBStorageProvider extends BrowserStorageProviderBase {
1150
- constructor() {
1151
- super();
1152
- this.dbPromise = (0, idb_1.openDB)(IDB_DB_NAME, 1, {
1153
- upgrade(db) {
1154
- if (!db.objectStoreNames.contains(IDB_DB_ObjectStoreName)) {
1155
- db.createObjectStore(IDB_DB_ObjectStoreName);
1156
- }
1157
- },
1158
- });
1159
- }
1160
- async setItem(key, value) {
1161
- const db = await this.dbPromise;
1162
- const tx = db.transaction(IDB_DB_ObjectStoreName, 'readwrite');
1163
- await tx.objectStore(IDB_DB_ObjectStoreName).put(value, key);
1164
- await tx.done;
1165
- }
1166
- async getItem(key) {
1167
- const db = await this.dbPromise;
1168
- const value = await db.transaction(IDB_DB_ObjectStoreName).objectStore(IDB_DB_ObjectStoreName).get(key);
1169
- return value;
1170
- }
1171
- async remove(key) {
1172
- const db = await this.dbPromise;
1173
- const tx = db.transaction(IDB_DB_ObjectStoreName, 'readwrite');
1174
- await tx.objectStore(IDB_DB_ObjectStoreName).delete(key);
1175
- await tx.done;
1176
- }
1177
- }
1178
- //# sourceMappingURL=graphQLDataProvider.js.map