pict-section-recordset 1.0.23 → 1.0.26
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/eslint.config.mjs +10 -0
- package/example_applications/simple_entity/Simple-RecordSet-Application.js +170 -110
- package/package.json +11 -11
- package/source/application/Pict-Application-RecordSet.js +8 -0
- package/source/providers/RecordSet-DynamicRecordsetSolver.js +1 -0
- package/source/providers/RecordSet-Link-Manager.js +2 -2
- package/source/providers/RecordSet-RecordProvider-Base.js +9 -2
- package/source/providers/RecordSet-RecordProvider-MeadowEndpoints.js +118 -16
- package/source/services/RecordsSet-MetaController.js +41 -19
- package/source/templates/Pict-Template-FilterInstanceViews.js +222 -0
- package/source/templates/Pict-Template-FilterView.js +4 -2
- package/source/views/RecordSet-Filters.js +449 -0
- package/source/views/dashboard/RecordSet-Dashboard.js +66 -9
- package/source/views/filters/RecordSet-Filter-Base-Range.js +38 -0
- package/source/views/filters/RecordSet-Filter-Base.js +88 -0
- package/source/views/filters/RecordSet-Filter-DateMatch.js +56 -0
- package/source/views/filters/RecordSet-Filter-DateRange.js +75 -0
- package/source/views/filters/RecordSet-Filter-ExternalJoinDateMatch.js +55 -0
- package/source/views/filters/RecordSet-Filter-ExternalJoinDateRange.js +57 -0
- package/source/views/filters/RecordSet-Filter-ExternalJoinNumericMatch.js +54 -0
- package/source/views/filters/RecordSet-Filter-ExternalJoinNumericRange.js +57 -0
- package/source/views/filters/RecordSet-Filter-ExternalJoinStringMatch.js +45 -0
- package/source/views/filters/RecordSet-Filter-ExternalJoinStringRange.js +46 -0
- package/source/views/filters/RecordSet-Filter-InternalJoinDateMatch.js +55 -0
- package/source/views/filters/RecordSet-Filter-InternalJoinDateRange.js +57 -0
- package/source/views/filters/RecordSet-Filter-InternalJoinNumericMatch.js +55 -0
- package/source/views/filters/RecordSet-Filter-InternalJoinNumericRange.js +57 -0
- package/source/views/filters/RecordSet-Filter-InternalJoinStringMatch.js +45 -0
- package/source/views/filters/RecordSet-Filter-InternalJoinStringRange.js +46 -0
- package/source/views/filters/RecordSet-Filter-NumericMatch.js +55 -0
- package/source/views/filters/RecordSet-Filter-NumericRange.js +57 -0
- package/source/views/filters/RecordSet-Filter-StringMatch.js +45 -0
- package/source/views/filters/RecordSet-Filter-StringRange.js +46 -0
- package/source/views/filters/index.js +27 -0
- package/source/views/list/RecordSet-List.js +44 -6
- package/test/PictSectionRecordSet-RecordProvider-Meadow_tests.js +11 -0
- package/types/application/Pict-Application-RecordSet.d.ts.map +1 -1
- package/types/providers/RecordSet-DynamicRecordsetSolver.d.ts +10 -8
- package/types/providers/RecordSet-DynamicRecordsetSolver.d.ts.map +1 -1
- package/types/providers/RecordSet-Link-Manager.d.ts +3 -3
- package/types/providers/RecordSet-RecordProvider-Base.d.ts +9 -0
- package/types/providers/RecordSet-RecordProvider-Base.d.ts.map +1 -1
- package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts +30 -4
- package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts.map +1 -1
- package/types/services/RecordsSet-MetaController.d.ts +2 -2
- package/types/services/RecordsSet-MetaController.d.ts.map +1 -1
- package/types/templates/Pict-Template-FilterInstanceViews.d.ts +19 -0
- package/types/templates/Pict-Template-FilterInstanceViews.d.ts.map +1 -0
- package/types/templates/Pict-Template-FilterView.d.ts.map +1 -1
- package/types/views/RecordSet-Filters.d.ts +84 -0
- package/types/views/RecordSet-Filters.d.ts.map +1 -0
- package/types/views/dashboard/RecordSet-Dashboard.d.ts +4 -2
- package/types/views/dashboard/RecordSet-Dashboard.d.ts.map +1 -1
- package/types/views/filters/RecordSet-Filter-Base-Range.d.ts +5 -0
- package/types/views/filters/RecordSet-Filter-Base-Range.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-Base.d.ts +29 -0
- package/types/views/filters/RecordSet-Filter-Base.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-DateMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-DateMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-DateRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-DateRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinDateMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinDateMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinDateRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinDateRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinNumericMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinNumericMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinNumericRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinNumericRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinStringMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinStringMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinStringRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-ExternalJoinStringRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinDateMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinDateMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinDateRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinDateRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinNumericMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinNumericMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinNumericRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinNumericRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinStringMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinStringMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinStringRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-InternalJoinStringRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-NumericMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-NumericMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-NumericRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-NumericRange.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-StringMatch.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-StringMatch.d.ts.map +1 -0
- package/types/views/filters/RecordSet-Filter-StringRange.d.ts +9 -0
- package/types/views/filters/RecordSet-Filter-StringRange.d.ts.map +1 -0
- package/types/views/filters/index.d.ts +20 -0
- package/types/views/filters/index.d.ts.map +1 -0
- package/types/views/list/RecordSet-List.d.ts +12 -2
- package/types/views/list/RecordSet-List.d.ts.map +1 -1
- package/source/views/RecordSet-Filter.js +0 -159
- package/types/views/RecordSet-Filter.d.ts +0 -39
- package/types/views/RecordSet-Filter.d.ts.map +0 -1
|
@@ -14,7 +14,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
14
14
|
{
|
|
15
15
|
/**
|
|
16
16
|
* Creates an instance of RecordSetProvider.
|
|
17
|
-
* @param {import('
|
|
17
|
+
* @param {import('pict')} pFable - The Fable object.
|
|
18
18
|
* @param {Record<string, any>} [pOptions] - Custom options for the provider.
|
|
19
19
|
* @param {string} [pServiceHash] - The service hash.
|
|
20
20
|
*/
|
|
@@ -24,8 +24,6 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
24
24
|
|
|
25
25
|
/** @type {Record<string, any>} */
|
|
26
26
|
this.options;
|
|
27
|
-
/** @type {import('fable')} */
|
|
28
|
-
this.fable;
|
|
29
27
|
/** @type {import('pict') & {
|
|
30
28
|
* log: any,
|
|
31
29
|
* services:
|
|
@@ -37,6 +35,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
37
35
|
* PictSectionRecordSet: InstanceType<import('../Pict-Section-RecordSet.js')>
|
|
38
36
|
* }} */
|
|
39
37
|
this.pict;
|
|
38
|
+
this.fable = this.pict;
|
|
40
39
|
/** @type {string} */
|
|
41
40
|
this.Hash;
|
|
42
41
|
/** @type {string} */
|
|
@@ -44,6 +43,10 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
44
43
|
//TODO: make this typedef better
|
|
45
44
|
/** @type {Record<string, any>} */
|
|
46
45
|
this._Schema = { };
|
|
46
|
+
/** @type {Record<string, Record<string, any>>} */
|
|
47
|
+
this._Experiences = { };
|
|
48
|
+
/** @type {Record<string, Record<string, any>>} */
|
|
49
|
+
this._FiltersByField = { };
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
/** @return {import('pict/types/source/Pict-Meadow-EntityProvider.js')} */
|
|
@@ -59,11 +62,6 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
59
62
|
return this._EntityProvider;
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
/**
|
|
63
|
-
* @typedef {(error?: Error, result?: T) => void} RecordSetCallback
|
|
64
|
-
* @template T = Record<string, any>
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
65
|
/**
|
|
68
66
|
* Get a record by its ID or GUID.
|
|
69
67
|
*
|
|
@@ -129,6 +127,25 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
129
127
|
});
|
|
130
128
|
}
|
|
131
129
|
|
|
130
|
+
_prepareFilterState(pEntity, pOptions, pFilterExperienceResultAddress = 'Records')
|
|
131
|
+
{
|
|
132
|
+
const tmpClauses = [].concat(this.pict.Bundle._ActiveFilterState?.[pOptions.Entity || this.options.Entity]?.FilterClauses || []);
|
|
133
|
+
const tmpExperience = Object.assign({}, this.pict.Bundle._ActiveFilterState?.[pOptions.Entity || this.options.Entity]?.Experience || {});
|
|
134
|
+
if (!tmpExperience.ResultDestinationAddress)
|
|
135
|
+
{
|
|
136
|
+
tmpExperience.ResultDestinationAddress = `Bundle._ActiveFilterState[\`${this.options.RecordSet}\`].${pFilterExperienceResultAddress}`;
|
|
137
|
+
}
|
|
138
|
+
if (!tmpExperience.Entity)
|
|
139
|
+
{
|
|
140
|
+
tmpExperience.Entity = pEntity;
|
|
141
|
+
}
|
|
142
|
+
if (pOptions.FilterString && typeof pOptions.FilterString === 'string' && pOptions.FilterString.trim().length > 0)
|
|
143
|
+
{
|
|
144
|
+
tmpClauses.push({ Type: 'RawFilter', Value: pOptions.FilterString });
|
|
145
|
+
}
|
|
146
|
+
return [ tmpClauses, tmpExperience ];
|
|
147
|
+
}
|
|
148
|
+
|
|
132
149
|
/**
|
|
133
150
|
* Read records from the provider.
|
|
134
151
|
*
|
|
@@ -148,15 +165,16 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
148
165
|
}
|
|
149
166
|
return new Promise((resolve, reject) =>
|
|
150
167
|
{
|
|
151
|
-
|
|
152
|
-
this.
|
|
168
|
+
const [ tmpClauses, tmpExperience ] = this._prepareFilterState(tmpEntity, pOptions);
|
|
169
|
+
this.pict.providers.FilterManager.loadRecordPageByFilter(tmpClauses, tmpExperience, pOptions.Offset || 0, pOptions.PageSize || 250, (pError) =>
|
|
153
170
|
{
|
|
154
171
|
if (pError)
|
|
155
172
|
{
|
|
156
173
|
return reject(pError);
|
|
157
174
|
}
|
|
158
|
-
resolve({ Records:
|
|
175
|
+
resolve({ Records: this.pict.resolveStateFromAddress(tmpExperience.ResultDestinationAddress), Facets: { } });
|
|
159
176
|
});
|
|
177
|
+
// using a space here, otherwise you get a `//` in the URL which breaks some stuff
|
|
160
178
|
});
|
|
161
179
|
}
|
|
162
180
|
|
|
@@ -192,13 +210,14 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
192
210
|
//TODO: lite support / other variants?
|
|
193
211
|
return new Promise((resolve, reject) =>
|
|
194
212
|
{
|
|
195
|
-
this.
|
|
213
|
+
const [ tmpClauses, tmpExperience ] = this._prepareFilterState(tmpEntity, pOptions, 'Count');
|
|
214
|
+
this.pict.providers.FilterManager.countRecordsByFilter(tmpClauses, tmpExperience, (pError) =>
|
|
196
215
|
{
|
|
197
216
|
if (pError)
|
|
198
217
|
{
|
|
199
218
|
return reject(pError);
|
|
200
219
|
}
|
|
201
|
-
resolve({ Count:
|
|
220
|
+
resolve({ Count: this.pict.resolveStateFromAddress(tmpExperience.ResultDestinationAddress) });
|
|
202
221
|
});
|
|
203
222
|
});
|
|
204
223
|
}
|
|
@@ -331,6 +350,89 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
331
350
|
* @param {(error?: Error) => void} fCallback - The callback function.
|
|
332
351
|
*/
|
|
333
352
|
onInitializeAsync(fCallback)
|
|
353
|
+
{
|
|
354
|
+
super.onInitializeAsync((pError) =>
|
|
355
|
+
{
|
|
356
|
+
if (this.options.FilterExperiences && typeof this.options.FilterExperiences === 'object' && !Array.isArray(this.options.FilterExperiences))
|
|
357
|
+
{
|
|
358
|
+
this.pict.Bundle._ActiveFilterState = this.pict.Bundle._ActiveFilterState || {};
|
|
359
|
+
this.pict.Bundle._ActiveFilterState[this.options.RecordSet] = this.pict.Bundle._ActiveFilterState[this.options.RecordSet] || {};
|
|
360
|
+
const tmpEntityFilterState = this.pict.Bundle._ActiveFilterState[this.options.RecordSet];
|
|
361
|
+
tmpEntityFilterState.Experience = tmpEntityFilterState.Experience || {};
|
|
362
|
+
for (const tmpKey of Object.keys(this.options.FilterExperiences))
|
|
363
|
+
{
|
|
364
|
+
const tmpExperience = this.options.FilterExperiences[tmpKey];
|
|
365
|
+
if (!tmpExperience.FilterCriteriaHash && !Array.isArray(tmpExperience.FilterClauses))
|
|
366
|
+
{
|
|
367
|
+
this.log.warn(`Filter experience ${tmpKey} has invalid Criteria`, { Experience: tmpExperience });
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (tmpExperience.FilterCriteriaHash)
|
|
371
|
+
{
|
|
372
|
+
// load from hash
|
|
373
|
+
const tmpClauses = this.pict.providers.FilterManager.getFilterCriteria(tmpExperience.FilterCriteriaHash);
|
|
374
|
+
if (!tmpClauses)
|
|
375
|
+
{
|
|
376
|
+
this.pict.log.warn(`Filter experience ${tmpKey} filter criteria hash ${tmpExperience.FilterCriteriaHash} not found`, { Experience: tmpExperience });
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
//TODO: handle Ordinal / generate if missing
|
|
380
|
+
this._Experiences[tmpKey] = JSON.parse(JSON.stringify(tmpExperience));
|
|
381
|
+
this._Experiences[tmpKey].FilterClauses = JSON.parse(JSON.stringify(tmpClauses));
|
|
382
|
+
for (const tmpClause of this._Experiences[tmpKey].FilterClauses)
|
|
383
|
+
{
|
|
384
|
+
tmpClause.FilterCriteriaHash = tmpExperience.FilterCriteriaHash;
|
|
385
|
+
if (!tmpClause.FilterByColumn && !tmpClause.CoreConnectionColumn && !tmpClause.JoinInternalConnectionColumn)
|
|
386
|
+
{
|
|
387
|
+
this.log.warn(`Filter experience ${tmpKey} clause does not have filter by column configured`, { Clause: tmpClause });
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (tmpClause.FilterDefinitionHash)
|
|
391
|
+
{
|
|
392
|
+
const tmpFilter = this.pict.providers.FilterManager.getFilter(tmpClause.FilterDefinitionHash);
|
|
393
|
+
if (!tmpFilter)
|
|
394
|
+
{
|
|
395
|
+
this.pict.log.warn(`Filter experience ${tmpKey} filter criteria hash ${tmpClause.FilterDefinitionHash} not found`, { Experience: tmpExperience });
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
Object.assign(tmpClause, tmpFilter); //TODO: is there a risk of leakage here?
|
|
399
|
+
if (tmpClause.FilterByColumn && typeof tmpClause.Type === 'string')
|
|
400
|
+
{
|
|
401
|
+
if (tmpClause.Type.startsWith('InternalJoin'))
|
|
402
|
+
{
|
|
403
|
+
tmpClause.JoinInternalConnectionColumn = tmpClause.FilterByColumn;
|
|
404
|
+
}
|
|
405
|
+
else if (tmpClause.Type.startsWith('ExternalJoin'))
|
|
406
|
+
{
|
|
407
|
+
tmpClause.CoreConnectionColumn = tmpClause.FilterByColumn;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (tmpExperience.Default && !tmpEntityFilterState.FilterClauses)
|
|
413
|
+
{
|
|
414
|
+
tmpEntityFilterState.FilterClauses = JSON.parse(JSON.stringify(this._Experiences[tmpKey].FilterClauses));
|
|
415
|
+
}
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
this._Experiences[tmpKey] = JSON.parse(JSON.stringify(tmpExperience));
|
|
419
|
+
if (tmpExperience.Default && !tmpEntityFilterState.FilterClauses)
|
|
420
|
+
{
|
|
421
|
+
tmpEntityFilterState.FilterClauses = JSON.parse(JSON.stringify(tmpExperience.FilterClauses));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
this.initializeEntitySchema(() =>
|
|
426
|
+
{
|
|
427
|
+
return fCallback(pError);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* @param {(error?: Error) => void} fCallback - The callback function.
|
|
434
|
+
*/
|
|
435
|
+
initializeEntitySchema(fCallback)
|
|
334
436
|
{
|
|
335
437
|
this.fable.log.info('Initializing RecordSetProvider-MeadowEndpoints');
|
|
336
438
|
const checkSession = this.pict.services.PictSectionRecordSet ? this.pict.services.PictSectionRecordSet.checkSession.bind(this.pict.services.PictSectionRecordSet) : async () => true;
|
|
@@ -361,7 +463,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
361
463
|
{
|
|
362
464
|
if (!this._Schema)
|
|
363
465
|
{
|
|
364
|
-
await new Promise((resolve, reject) => this.
|
|
466
|
+
await new Promise((resolve, reject) => this.initializeEntitySchema((pError) =>
|
|
365
467
|
{
|
|
366
468
|
if (pError)
|
|
367
469
|
{
|
|
@@ -390,8 +492,8 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
|
|
|
390
492
|
this.pict.log.error('RecordDecorationConfiguration is not an array', { RecordDecorationConfiguration: this.options.RecordDecorationConfiguration });
|
|
391
493
|
return;
|
|
392
494
|
}
|
|
393
|
-
this.pict.
|
|
394
|
-
const config = [{ Type: 'SetStateAddress', StateAddress: `
|
|
495
|
+
this.pict.Bundle[this.Hash] = { CoreEntityRecordSubset: pRecords };
|
|
496
|
+
const config = [{ Type: 'SetStateAddress', StateAddress: `Bundle[${this.Hash}]` }].concat(this.options.RecordDecorationConfiguration);
|
|
395
497
|
|
|
396
498
|
try
|
|
397
499
|
{
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
3
|
+
const ViewDefinitionRecordSetErrorNotFound = require('../views/error/RecordSet-Error-NotFound.json');
|
|
4
|
+
const ViewRecordSetList = require('../views/list/RecordSet-List.js');
|
|
5
|
+
const ViewRecordSetEdit = require('../views/edit/RecordSet-Edit.js');
|
|
6
|
+
const ViewRecordSetRead = require('../views/read/RecordSet-Read.js');
|
|
7
|
+
const ViewRecordSetDashboard = require('../views/dashboard/RecordSet-Dashboard.js');
|
|
8
8
|
|
|
9
9
|
//_Pict.addProvider('BooksProvider', { Entity: 'Book', URLPrefix: 'http://www.datadebase.com:8086/1.0/' }, require('../source/providers/RecordSet-RecordProvider-MeadowEndpoints.js'));
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const ProviderBase = require('../providers/RecordSet-RecordProvider-Base.js');
|
|
11
|
+
const ProviderMeadowEndpoints = require('../providers/RecordSet-RecordProvider-MeadowEndpoints.js');
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const ProviderLinkManager = require('../providers/RecordSet-Link-Manager.js');
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const ProviderRouter = require('../providers/RecordSet-Router.js');
|
|
16
16
|
|
|
17
17
|
/** @type {Record<string, any>} */
|
|
18
18
|
const _DEFAULT_CONFIGURATION =
|
|
@@ -20,6 +20,8 @@ const _DEFAULT_CONFIGURATION =
|
|
|
20
20
|
DefaultMeadowURLPrefix: '/1.0/'
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const libFormsTemplateProvider = require('pict-section-form').PictFormTemplateProvider;
|
|
24
|
+
|
|
23
25
|
class RecordSetMetacontroller extends libFableServiceProviderBase
|
|
24
26
|
{
|
|
25
27
|
constructor(pFable, pOptions, pServiceHash)
|
|
@@ -61,6 +63,9 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
|
|
|
61
63
|
this.manifests = { Default: this.pict.manifest };
|
|
62
64
|
|
|
63
65
|
this.has_initialized = false;
|
|
66
|
+
|
|
67
|
+
//FIXME: this duplicates a behavior in pict-section-form - figure out how we want this coupled dependency to work.
|
|
68
|
+
this.fable.addProviderSingleton('PictFormSectionDefaultTemplateProvider', libFormsTemplateProvider.default_configuration, libFormsTemplateProvider);
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
/*
|
|
@@ -163,7 +168,7 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
|
|
|
163
168
|
{
|
|
164
169
|
providerConfiguration.URLPrefix = '/1.0/';
|
|
165
170
|
}
|
|
166
|
-
tmpProvider = this.recordSetProviders[pRecordSetConfiguration.RecordSet] = this.fable.addProvider(providerConfiguration.Hash, providerConfiguration,
|
|
171
|
+
tmpProvider = this.recordSetProviders[pRecordSetConfiguration.RecordSet] = this.fable.addProvider(providerConfiguration.Hash, providerConfiguration, ProviderMeadowEndpoints);
|
|
167
172
|
break;
|
|
168
173
|
default:
|
|
169
174
|
case 'Custom':
|
|
@@ -177,12 +182,12 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
|
|
|
177
182
|
}
|
|
178
183
|
else
|
|
179
184
|
{
|
|
180
|
-
tmpProvider = this.recordSetProviders[providerConfiguration.RecordSet] = this.fable.addProvider(providerConfiguration.Hash, providerConfiguration,
|
|
185
|
+
tmpProvider = this.recordSetProviders[providerConfiguration.RecordSet] = this.fable.addProvider(providerConfiguration.Hash, providerConfiguration, ProviderBase);
|
|
181
186
|
}
|
|
182
187
|
}
|
|
183
188
|
else
|
|
184
189
|
{
|
|
185
|
-
tmpProvider = this.recordSetProviders[providerConfiguration.RecordSet] = this.fable.addProvider(providerConfiguration.Hash, providerConfiguration,
|
|
190
|
+
tmpProvider = this.recordSetProviders[providerConfiguration.RecordSet] = this.fable.addProvider(providerConfiguration.Hash, providerConfiguration, ProviderBase);
|
|
186
191
|
}
|
|
187
192
|
break;
|
|
188
193
|
}
|
|
@@ -302,15 +307,16 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
|
|
|
302
307
|
return this;
|
|
303
308
|
}
|
|
304
309
|
|
|
305
|
-
this.fable.addProvider('RecordSetLinkManager', {},
|
|
310
|
+
this.fable.addProvider('RecordSetLinkManager', {}, ProviderLinkManager);
|
|
306
311
|
|
|
307
312
|
// Add the subviews internally and externally
|
|
308
313
|
this.pict.addTemplate(require('../templates/Pict-Template-FilterView.js'));
|
|
309
|
-
this.
|
|
310
|
-
this.childViews.
|
|
311
|
-
this.childViews.
|
|
312
|
-
this.childViews.
|
|
313
|
-
this.childViews.
|
|
314
|
+
this.pict.addTemplate(require('../templates/Pict-Template-FilterInstanceViews.js'));
|
|
315
|
+
this.childViews.errorNotFound = this.fable.addView('RSP-RecordSet-Error-NotFound', ViewDefinitionRecordSetErrorNotFound);
|
|
316
|
+
this.childViews.list = this.fable.addView('RSP-RecordSet-List', this.options, ViewRecordSetList);
|
|
317
|
+
this.childViews.edit = this.fable.addView('RSP-RecordSet-Edit', this.options, ViewRecordSetEdit);
|
|
318
|
+
this.childViews.read = this.fable.addView('RSP-RecordSet-Read', this.options, ViewRecordSetRead);
|
|
319
|
+
this.childViews.dashboard = this.fable.addView('RSP-RecordSet-Dashboard', this.options, ViewRecordSetDashboard);
|
|
314
320
|
|
|
315
321
|
// Initialize the subviews
|
|
316
322
|
this.childViews.list.initialize();
|
|
@@ -320,6 +326,22 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
|
|
|
320
326
|
|
|
321
327
|
// Now initialize the router
|
|
322
328
|
|
|
329
|
+
if (this.fable.settings.hasOwnProperty('Filters') && typeof this.fable.settings.Filters === 'object')
|
|
330
|
+
{
|
|
331
|
+
for (const tmpFilterKey of Object.keys(this.fable.settings.Filters))
|
|
332
|
+
{
|
|
333
|
+
this.pict.providers.FilterManager.addFilter(tmpFilterKey, this.fable.settings.Filters[tmpFilterKey]);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (this.fable.settings.hasOwnProperty('FilterCriteria') && typeof this.fable.settings.FilterCriteria === 'object')
|
|
338
|
+
{
|
|
339
|
+
for (const tmpFilterCriterionKey of Object.keys(this.fable.settings.FilterCriteria))
|
|
340
|
+
{
|
|
341
|
+
this.pict.providers.FilterManager.addFilterCriteria(tmpFilterCriterionKey, this.fable.settings.FilterCriteria[tmpFilterCriterionKey]);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
323
345
|
if (this.fable.settings.hasOwnProperty('DefaultRecordSetConfigurations'))
|
|
324
346
|
{
|
|
325
347
|
this.loadRecordSetConfigurationArray(this.fable.settings.DefaultRecordSetConfigurations);
|
|
@@ -346,7 +368,7 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
|
|
|
346
368
|
// Load pict-router if it isn't loaded
|
|
347
369
|
if (!('RecordSetRouter' in this.fable.providers))
|
|
348
370
|
{
|
|
349
|
-
this.fable.addProvider('RecordSetRouter', {},
|
|
371
|
+
this.fable.addProvider('RecordSetRouter', {}, ProviderRouter);
|
|
350
372
|
this.fable.providers.RecordSetRouter.initialize();
|
|
351
373
|
}
|
|
352
374
|
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
const libPictTemplate = require('pict-template');
|
|
2
|
+
const libFilterViews = require('../views/filters/index.js');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Specialized instruction for rendering the filter view and plumbing in required context.
|
|
6
|
+
*
|
|
7
|
+
* Based on the Pict base {~V:...~} template instruction.
|
|
8
|
+
*/
|
|
9
|
+
class PictTemplateFilterInstanceViewInstruction extends libPictTemplate
|
|
10
|
+
{
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} pFable - The Fable Framework instance
|
|
13
|
+
* @param {Object} pOptions - The options for the service
|
|
14
|
+
* @param {String} pServiceHash - The hash of the service
|
|
15
|
+
*/
|
|
16
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
17
|
+
{
|
|
18
|
+
super(pFable, pOptions, pServiceHash);
|
|
19
|
+
|
|
20
|
+
/** @type {any} */
|
|
21
|
+
this.log;
|
|
22
|
+
|
|
23
|
+
this.addPattern('{~FIV:', '~}');
|
|
24
|
+
this.addPattern('{~FilterInstanceViews:', '~}');
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if (!('__TemplateOutputCache' in this.pict))
|
|
28
|
+
{
|
|
29
|
+
this.pict.__TemplateOutputCache = {};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_getViewForFilterClauses(pClauses)
|
|
34
|
+
{
|
|
35
|
+
const tmpViewHash =`PSRS-FilterType-${pClauses.Type}`;
|
|
36
|
+
/** @type {import('../views/filters/RecordSet-Filter-Base.js')} */
|
|
37
|
+
let tmpView = this.pict.views[tmpViewHash];
|
|
38
|
+
if (!tmpView)
|
|
39
|
+
{
|
|
40
|
+
const tmpViewPrototype = libFilterViews[pClauses.Type] || libFilterViews.Base;
|
|
41
|
+
if (!tmpViewPrototype)
|
|
42
|
+
{
|
|
43
|
+
this.pict.log.error(`Pict: Filter Instance Views Template Render: No view prototype found for filter type [${pClauses.Type}]`);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
//FIXME: is this safe? will this view get rendered at other times?
|
|
47
|
+
tmpView = this.pict.addView(tmpViewHash, { }, tmpViewPrototype);
|
|
48
|
+
}
|
|
49
|
+
return tmpView;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Render a template expression, returning a string with the resulting content.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} pTemplateHash - The hash contents of the template (what's between the template start and stop tags)
|
|
56
|
+
* @param {any} pRecord - The json object to be used as the Record for the template render
|
|
57
|
+
* @param {Array<any>} pContextArray - An array of context objects accessible from the template; safe to leave empty
|
|
58
|
+
* @param {any} [pScope] - A sticky scope that can be used to carry state and simplify template
|
|
59
|
+
*
|
|
60
|
+
* @return {string} The rendered template
|
|
61
|
+
*/
|
|
62
|
+
render(pTemplateHash, pRecord, pContextArray, pScope)
|
|
63
|
+
{
|
|
64
|
+
const tmpRecordSet = pRecord.RecordSet || '';
|
|
65
|
+
if (!tmpRecordSet)
|
|
66
|
+
{
|
|
67
|
+
this.pict.log.error(`Pict: Filter Instance Views Template Render: No record set specified in record`);
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
const tmpRecordSetConfiguration = this.pict.PictSectionRecordSet.recordSetProviderConfigurations?.[tmpRecordSet];
|
|
71
|
+
if (!tmpRecordSetConfiguration)
|
|
72
|
+
{
|
|
73
|
+
this.pict.log.error(`Pict: Filter Instance Views Template Render: No record set configuration found for [${tmpRecordSet}]`);
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
let tmpViewContext = pRecord.ViewContext;
|
|
77
|
+
if (tmpViewContext)
|
|
78
|
+
{
|
|
79
|
+
tmpViewContext = tmpViewContext.trim();
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
{
|
|
83
|
+
tmpViewContext = 'Default';
|
|
84
|
+
}
|
|
85
|
+
pRecord = pRecord || {};
|
|
86
|
+
if (!pRecord.RecordSet)
|
|
87
|
+
{
|
|
88
|
+
pRecord.RecordSet = tmpRecordSet;
|
|
89
|
+
}
|
|
90
|
+
if (!pRecord.RecordSetConfiguration)
|
|
91
|
+
{
|
|
92
|
+
pRecord.RecordSetConfiguration = tmpRecordSetConfiguration;
|
|
93
|
+
}
|
|
94
|
+
if (!pRecord.ViewContext)
|
|
95
|
+
{
|
|
96
|
+
pRecord.ViewContext = pRecord.DashboardHash ? `${tmpViewContext}-${pRecord.DashboardHash}` : tmpViewContext;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let tmpRenderGUID = this.pict.getUUID();
|
|
100
|
+
let tmpResult = '';
|
|
101
|
+
|
|
102
|
+
const tmpClauses = this.pict.Bundle._ActiveFilterState?.[pRecord.RecordSet]?.FilterClauses || [];
|
|
103
|
+
//FIXME: lookup by hash instead?
|
|
104
|
+
for (let i = 0; i < tmpClauses.length; i++)
|
|
105
|
+
//for (const tmpClauses of this.pict.Bundle._ActiveFilterState?.[pRecord.RecordSet]?.FilterClauses || [])
|
|
106
|
+
{
|
|
107
|
+
const tmpClause = tmpClauses[i];
|
|
108
|
+
/** @type {import('../views/filters/RecordSet-Filter-Base.js')} */
|
|
109
|
+
const tmpView = this._getViewForFilterClauses(tmpClause);
|
|
110
|
+
if (!tmpView)
|
|
111
|
+
{
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const tmpRecord = Object.assign({}, pRecord, tmpClause);
|
|
116
|
+
//tmpRecord.ClauseAddress = `Bundle._ActiveFilterState['${pRecord.RecordSet}'].FilterClauses[${i}]`;
|
|
117
|
+
tmpRecord.ClauseAddress = `_ActiveFilterState[\`${pRecord.RecordSet}\`].FilterClauses[${i}]`;
|
|
118
|
+
tmpView.prepareRecord(tmpRecord);
|
|
119
|
+
tmpView.renderWithScope(tmpClause, '__Virtual', `__TemplateOutputCache.${tmpRenderGUID}`, tmpRecord);
|
|
120
|
+
|
|
121
|
+
tmpResult += this.pict.__TemplateOutputCache[tmpRenderGUID];
|
|
122
|
+
// TODO: Uncomment this when we like how it's working
|
|
123
|
+
//delete this.pict.__TemplateOutputCache[tmpRenderGUID];
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
return tmpResult;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {string} pTemplateHash - The hash contents of the template (what's between the template start and stop tags)
|
|
131
|
+
* @param {any} pRecord - The json object to be used as the Record for the template
|
|
132
|
+
* @param {(pError?: Error, pResult?: string) => void} fCallback - The callback function to call with the result
|
|
133
|
+
* @param {Array<any>} pContextArray - An array of context objects accessible from the template; safe to leave empty
|
|
134
|
+
* @param {any} [pScope] - A sticky scope that can be used to carry state and simplify template
|
|
135
|
+
* @return {void}
|
|
136
|
+
*/
|
|
137
|
+
renderAsync(pTemplateHash, pRecord, fCallback, pContextArray, pScope)
|
|
138
|
+
{
|
|
139
|
+
const tmpRecordSet = pRecord.RecordSet || '';
|
|
140
|
+
if (!tmpRecordSet)
|
|
141
|
+
{
|
|
142
|
+
this.pict.log.error(`Pict: Filter Instance Views Template Render: No record set specified in record`);
|
|
143
|
+
return fCallback(null, '');
|
|
144
|
+
}
|
|
145
|
+
const tmpRecordSetConfiguration = this.pict.PictSectionRecordSet.recordSetProviderConfigurations?.[tmpRecordSet];
|
|
146
|
+
if (!tmpRecordSetConfiguration)
|
|
147
|
+
{
|
|
148
|
+
this.pict.log.error(`Pict: Filter Instance Views Template Render: No record set configuration found for [${tmpRecordSet}]`);
|
|
149
|
+
return fCallback(null, '');
|
|
150
|
+
}
|
|
151
|
+
let tmpViewContext = pRecord.ViewContext;
|
|
152
|
+
if (tmpViewContext)
|
|
153
|
+
{
|
|
154
|
+
tmpViewContext = tmpViewContext.trim();
|
|
155
|
+
}
|
|
156
|
+
else
|
|
157
|
+
{
|
|
158
|
+
tmpViewContext = 'Default';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
pRecord = pRecord || {};
|
|
162
|
+
if (!pRecord.RecordSet)
|
|
163
|
+
{
|
|
164
|
+
pRecord.RecordSet = tmpRecordSet;
|
|
165
|
+
}
|
|
166
|
+
if (!pRecord.RecordSetConfiguration)
|
|
167
|
+
{
|
|
168
|
+
pRecord.RecordSetConfiguration = tmpRecordSetConfiguration;
|
|
169
|
+
}
|
|
170
|
+
if (!pRecord.ViewContext)
|
|
171
|
+
{
|
|
172
|
+
pRecord.ViewContext = pRecord.DashboardHash ? `${tmpViewContext}-${pRecord.DashboardHash}` : tmpViewContext;
|
|
173
|
+
}
|
|
174
|
+
const tmpAnticipate = this.pict.newAnticipate();
|
|
175
|
+
let tmpResult = '';
|
|
176
|
+
|
|
177
|
+
const tmpClauses = this.pict.Bundle._ActiveFilterState?.[pRecord.RecordSet]?.FilterClauses || [];
|
|
178
|
+
//FIXME: lookup by hash instead?
|
|
179
|
+
for (let i = 0; i < tmpClauses.length; i++)
|
|
180
|
+
{
|
|
181
|
+
const tmpClause = tmpClauses[i];
|
|
182
|
+
/** @type {import('../views/filters/RecordSet-Filter-Base.js')} */
|
|
183
|
+
const tmpView = this._getViewForFilterClauses(tmpClause);
|
|
184
|
+
if (!tmpView)
|
|
185
|
+
{
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const tmpRenderGUID = this.pict.getUUID();
|
|
190
|
+
tmpAnticipate.anticipate((fNext) =>
|
|
191
|
+
{
|
|
192
|
+
const tmpRecord = Object.assign({}, pRecord, tmpClause);
|
|
193
|
+
tmpRecord.ClauseAddress = `_ActiveFilterState[${pRecord.RecordSet}].FilterClauses[${i}]`;
|
|
194
|
+
tmpView.prepareRecord(tmpRecord);
|
|
195
|
+
|
|
196
|
+
return tmpView.renderWithScopeAsync(tmpClause, '__Virtual', `__TemplateOutputCache.${tmpRenderGUID}`, tmpRecord,
|
|
197
|
+
(pError, pResult) =>
|
|
198
|
+
{
|
|
199
|
+
if (pError)
|
|
200
|
+
{
|
|
201
|
+
this.log.warn(`Pict: Filter Instance Views Template Render: Error rendering view [${tmpView.Hash}]`, pError);
|
|
202
|
+
//TODO: should we fail the whole render?
|
|
203
|
+
return fNext();
|
|
204
|
+
//return fNext(pError);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
tmpResult += this.pict.__TemplateOutputCache[tmpRenderGUID];
|
|
208
|
+
// TODO: Uncomment this when we like how it's working
|
|
209
|
+
//delete this.pict.__TemplateOutputCache[tmpRenderGUID];
|
|
210
|
+
|
|
211
|
+
return fNext(null);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return tmpAnticipate.wait((pError) =>
|
|
216
|
+
{
|
|
217
|
+
return fCallback(pError, tmpResult);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = PictTemplateFilterInstanceViewInstruction;
|
|
@@ -35,10 +35,11 @@ class PictTemplateFilterViewInstruction extends libPictTemplate
|
|
|
35
35
|
* @param {string} pTemplateHash - The hash contents of the template (what's between the template start and stop tags)
|
|
36
36
|
* @param {any} pRecord - The json object to be used as the Record for the template render
|
|
37
37
|
* @param {Array<any>} pContextArray - An array of context objects accessible from the template; safe to leave empty
|
|
38
|
+
* @param {any} [pScope] - A sticky scope that can be used to carry state and simplify template
|
|
38
39
|
*
|
|
39
40
|
* @return {string} The rendered template
|
|
40
41
|
*/
|
|
41
|
-
render(pTemplateHash, pRecord, pContextArray)
|
|
42
|
+
render(pTemplateHash, pRecord, pContextArray, pScope)
|
|
42
43
|
{
|
|
43
44
|
let [ tmpViewHash, tmpViewContext ] = pTemplateHash.split(':');
|
|
44
45
|
tmpViewHash = tmpViewHash.trim();
|
|
@@ -101,9 +102,10 @@ class PictTemplateFilterViewInstruction extends libPictTemplate
|
|
|
101
102
|
* @param {any} pRecord - The json object to be used as the Record for the template
|
|
102
103
|
* @param {(pError?: Error, pResult?: string) => void} fCallback - The callback function to call with the result
|
|
103
104
|
* @param {Array<any>} pContextArray - An array of context objects accessible from the template; safe to leave empty
|
|
105
|
+
* @param {any} [pScope] - A sticky scope that can be used to carry state and simplify template
|
|
104
106
|
* @return {void}
|
|
105
107
|
*/
|
|
106
|
-
renderAsync(pTemplateHash, pRecord, fCallback, pContextArray)
|
|
108
|
+
renderAsync(pTemplateHash, pRecord, fCallback, pContextArray, pScope)
|
|
107
109
|
{
|
|
108
110
|
let [ tmpViewHash, tmpViewContext ] = pTemplateHash.split(':');
|
|
109
111
|
tmpViewHash = tmpViewHash.trim();
|