forms-angular 0.12.0-beta.193 → 0.12.0-beta.194

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,64 +1,13 @@
1
1
  "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- if (typeof b !== "function" && b !== null)
11
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
- extendStatics(d, b);
13
- function __() { this.constructor = d; }
14
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
- };
16
- })();
17
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
- return new (P || (P = Promise))(function (resolve, reject) {
20
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
- step((generator = generator.apply(thisArg, _arguments || [])).next());
24
- });
25
- };
26
- var __generator = (this && this.__generator) || function (thisArg, body) {
27
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
- function verb(n) { return function (v) { return step([n, v]); }; }
30
- function step(op) {
31
- if (f) throw new TypeError("Generator is already executing.");
32
- while (_) try {
33
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34
- if (y = 0, t) op = [op[0] & 2, t.value];
35
- switch (op[0]) {
36
- case 0: case 1: t = op; break;
37
- case 4: _.label++; return { value: op[1], done: false };
38
- case 5: _.label++; y = op[1]; op = [0]; continue;
39
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
- default:
41
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
- if (t[2]) _.ops.pop();
46
- _.trys.pop(); continue;
47
- }
48
- op = body.call(thisArg, _);
49
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
- }
52
- };
53
2
  Object.defineProperty(exports, "__esModule", { value: true });
54
3
  exports.FormsAngular = void 0;
55
4
  // This part of forms-angular borrows _very_ heavily from https://github.com/Alexandre-Strzelewicz/angular-bridge
56
5
  // (now https://github.com/Unitech/angular-bridge
57
- var _ = require('lodash');
58
- var util = require('util');
59
- var extend = require('node.extend');
60
- var async = require('async');
61
- var debug = false;
6
+ const _ = require('lodash');
7
+ const util = require('util');
8
+ const extend = require('node.extend');
9
+ const async = require('async');
10
+ let debug = false;
62
11
  function logTheAPICalls(req, res, next) {
63
12
  void (res);
64
13
  console.log('API : ' + req.method + ' ' + req.url + ' [ ' + JSON.stringify(req.body) + ' ]');
@@ -66,8 +15,8 @@ function logTheAPICalls(req, res, next) {
66
15
  }
67
16
  function processArgs(options, array) {
68
17
  if (options.authentication) {
69
- var authArray = _.isArray(options.authentication) ? options.authentication : [options.authentication];
70
- for (var i = authArray.length - 1; i >= 0; i--) {
18
+ let authArray = _.isArray(options.authentication) ? options.authentication : [options.authentication];
19
+ for (let i = authArray.length - 1; i >= 0; i--) {
71
20
  array.splice(1, 0, authArray[i]);
72
21
  }
73
22
  }
@@ -77,8 +26,8 @@ function processArgs(options, array) {
77
26
  array[0] = options.urlPrefix + array[0];
78
27
  return array;
79
28
  }
80
- var FormsAngular = /** @class */ (function () {
81
- function FormsAngular(mongoose, app, options) {
29
+ class FormsAngular {
30
+ constructor(mongoose, app, options) {
82
31
  this.mongoose = mongoose;
83
32
  this.app = app;
84
33
  app.locals.formsAngular = app.locals.formsAngular || [];
@@ -90,7 +39,7 @@ var FormsAngular = /** @class */ (function () {
90
39
  }, options || {});
91
40
  this.resources = [];
92
41
  this.searchFunc = async.forEach;
93
- var search = 'search/', schema = 'schema/', report = 'report/', resourceName = ':resourceName', id = '/:id';
42
+ const search = 'search/', schema = 'schema/', report = 'report/', resourceName = ':resourceName', id = '/:id';
94
43
  this.app.get.apply(this.app, processArgs(this.options, ['models', this.models()]));
95
44
  this.app.get.apply(this.app, processArgs(this.options, [search + resourceName, this.search()]));
96
45
  this.app.get.apply(this.app, processArgs(this.options, [schema + resourceName, this.schema()]));
@@ -99,50 +48,53 @@ var FormsAngular = /** @class */ (function () {
99
48
  this.app.get.apply(this.app, processArgs(this.options, [report + resourceName + '/:reportName', this.report()]));
100
49
  this.app.get.apply(this.app, processArgs(this.options, [resourceName, this.collectionGet()]));
101
50
  this.app.post.apply(this.app, processArgs(this.options, [resourceName, this.collectionPost()]));
51
+ // return the List attributes for all records - used by record-handler's setUpLookupOptions() method, for cases
52
+ // where there's a lookup that doesn't use the fngajax option
53
+ this.app.get.apply(this.app, processArgs(this.options, [resourceName + '/listAll', this.entityListAll()]));
102
54
  this.app.get.apply(this.app, processArgs(this.options, [resourceName + id, this.entityGet()]));
103
55
  this.app.post.apply(this.app, processArgs(this.options, [resourceName + id, this.entityPut()])); // You can POST or PUT to update data
104
56
  this.app.put.apply(this.app, processArgs(this.options, [resourceName + id, this.entityPut()]));
105
57
  this.app.delete.apply(this.app, processArgs(this.options, [resourceName + id, this.entityDelete()]));
106
- // return the List attributes for a record - used by select2
58
+ // return the List attributes for a record - used by fng-ui-select
107
59
  this.app.get.apply(this.app, processArgs(this.options, [resourceName + id + '/list', this.entityList()]));
108
60
  this.app.get.apply(this.app, processArgs(this.options, ['search', this.searchAll()]));
109
- for (var pluginName in this.options.plugins) {
61
+ for (let pluginName in this.options.plugins) {
110
62
  if (this.options.plugins.hasOwnProperty(pluginName)) {
111
- var pluginObj = this.options.plugins[pluginName];
63
+ let pluginObj = this.options.plugins[pluginName];
112
64
  this.options.plugins[pluginName] = Object.assign(this.options.plugins[pluginName], pluginObj.plugin(this, processArgs, pluginObj.options));
113
65
  }
114
66
  }
115
67
  }
116
- FormsAngular.prototype.getListFields = function (resource, doc, cb) {
117
- function getFirstMatchingField(keyList, type) {
118
- for (var i = 0; i < keyList.length; i++) {
119
- var fieldDetails = resource.model.schema['tree'][keyList[i]];
120
- if (fieldDetails.type && (!type || fieldDetails.type.name === type) && keyList[i] !== '_id') {
121
- resource.options.listFields = [{ field: keyList[i] }];
122
- return doc[keyList[i]];
123
- }
68
+ getFirstMatchingField(resource, doc, keyList, type) {
69
+ for (let i = 0; i < keyList.length; i++) {
70
+ let fieldDetails = resource.model.schema['tree'][keyList[i]];
71
+ if (fieldDetails.type && (!type || fieldDetails.type.name === type) && keyList[i] !== '_id') {
72
+ resource.options.listFields = [{ field: keyList[i] }];
73
+ return doc ? doc[keyList[i]] : keyList[i];
124
74
  }
125
75
  }
126
- var that = this;
127
- var display = '';
128
- var listFields = resource.options.listFields;
76
+ }
77
+ getListFields(resource, doc, cb) {
78
+ const that = this;
79
+ let display = '';
80
+ let listFields = resource.options.listFields;
129
81
  if (listFields) {
130
82
  async.map(listFields, function (aField, cbm) {
131
83
  if (typeof doc[aField.field] !== 'undefined') {
132
84
  if (aField.params) {
133
85
  if (aField.params.ref) {
134
- var fieldOptions = resource.model.schema['paths'][aField.field].options;
86
+ let fieldOptions = resource.model.schema['paths'][aField.field].options;
135
87
  if (typeof fieldOptions.ref === 'string') {
136
- var lookupResource_1 = that.getResource(fieldOptions.ref);
137
- if (lookupResource_1) {
138
- var hiddenFields = that.generateHiddenFields(lookupResource_1, false);
88
+ let lookupResource = that.getResource(fieldOptions.ref);
89
+ if (lookupResource) {
90
+ let hiddenFields = that.generateHiddenFields(lookupResource, false);
139
91
  hiddenFields.__v = false;
140
- lookupResource_1.model.findOne({ _id: doc[aField.field] }).select(hiddenFields).exec(function (err, doc2) {
92
+ lookupResource.model.findOne({ _id: doc[aField.field] }).select(hiddenFields).exec(function (err, doc2) {
141
93
  if (err) {
142
94
  cbm(err);
143
95
  }
144
96
  else {
145
- that.getListFields(lookupResource_1, doc2, cbm);
97
+ that.getListFields(lookupResource, doc2, cbm);
146
98
  }
147
99
  });
148
100
  }
@@ -152,7 +104,7 @@ var FormsAngular = /** @class */ (function () {
152
104
  }
153
105
  }
154
106
  else if (aField.params.params === 'timestamp') {
155
- var date = that.extractTimestampFromMongoID(doc[aField.field]);
107
+ let date = that.extractTimestampFromMongoID(doc[aField.field]);
156
108
  cbm(null, date.toLocaleDateString() + ' ' + date.toLocaleTimeString());
157
109
  }
158
110
  }
@@ -178,29 +130,61 @@ var FormsAngular = /** @class */ (function () {
178
130
  });
179
131
  }
180
132
  else {
181
- var keyList = Object.keys(resource.model.schema['tree']);
133
+ const keyList = Object.keys(resource.model.schema['tree']);
182
134
  // No list field specified - use the first String field,
183
- display = getFirstMatchingField(keyList, 'String') ||
135
+ display = this.getFirstMatchingField(resource, doc, keyList, 'String') ||
184
136
  // and if there aren't any then just take the first field
185
- getFirstMatchingField(keyList);
137
+ this.getFirstMatchingField(resource, doc, keyList);
186
138
  cb(null, display.trim());
187
139
  }
188
- };
140
+ }
141
+ ;
142
+ // generate a Mongo projection that can be used to restrict a query to return only those fields from the given
143
+ // resource that are identified as "list" fields (i.e., ones that should appear whenever records of that type are
144
+ // displayed in a list)
145
+ generateListFieldProjection(resource) {
146
+ const projection = {};
147
+ const listFields = resource.options?.listFields;
148
+ // resource.options.listFields will identify all of the fields from resource that have a value for .list.
149
+ // generally, that value will be "true", identifying the corresponding field as one which should be
150
+ // included whenever records of that type appear in a list.
151
+ // occasionally, it will instead be "{ ref: true }"", which means something entirely different -
152
+ // this means that the field requires a lookup translation before it can be displayed on a form.
153
+ // for our purposes, we're interested in only the first of these two cases, so we'll ignore anything where
154
+ // field.params.ref has a truthy value
155
+ if (listFields) {
156
+ for (const field of listFields) {
157
+ if (!field.params?.ref) {
158
+ projection[field.field] = 1;
159
+ }
160
+ }
161
+ }
162
+ else {
163
+ const keyList = Object.keys(resource.model.schema['tree']);
164
+ const firstField = (
165
+ // No list field specified - use the first String field,
166
+ this.getFirstMatchingField(resource, undefined, keyList, 'String') ||
167
+ // and if there aren't any then just take the first field
168
+ this.getFirstMatchingField(resource, undefined, keyList));
169
+ projection[firstField] = 1;
170
+ }
171
+ return projection;
172
+ }
189
173
  ;
190
- FormsAngular.prototype.newResource = function (model, options) {
174
+ newResource(model, options) {
191
175
  options = options || {};
192
176
  options.suppressDeprecatedMessage = true;
193
- var passModel = model;
177
+ let passModel = model;
194
178
  if (typeof model !== 'function') {
195
179
  passModel = model.model;
196
180
  }
197
181
  this.addResource(passModel.modelName, passModel, options);
198
- };
182
+ }
199
183
  ;
200
184
  // Add a resource, specifying the model and any options.
201
185
  // Models may include their own options, which means they can be passed through from the model file
202
- FormsAngular.prototype.addResource = function (resourceName, model, options) {
203
- var resource = {
186
+ addResource(resourceName, model, options) {
187
+ let resource = {
204
188
  resourceName: resourceName,
205
189
  options: options || {}
206
190
  };
@@ -212,7 +196,7 @@ var FormsAngular = /** @class */ (function () {
212
196
  }
213
197
  else {
214
198
  resource.model = model.model;
215
- for (var prop in model) {
199
+ for (const prop in model) {
216
200
  if (model.hasOwnProperty(prop) && prop !== 'model') {
217
201
  resource.options[prop] = model[prop];
218
202
  }
@@ -230,30 +214,89 @@ var FormsAngular = /** @class */ (function () {
230
214
  else {
231
215
  this.resources.push(resource);
232
216
  }
233
- };
217
+ }
234
218
  ;
235
- FormsAngular.prototype.getResource = function (name) {
219
+ getResource(name) {
236
220
  return _.find(this.resources, function (resource) {
237
221
  return resource.resourceName === name;
238
222
  });
239
- };
223
+ }
240
224
  ;
241
- FormsAngular.prototype.getResourceFromCollection = function (name) {
225
+ getResourceFromCollection(name) {
242
226
  return _.find(this.resources, function (resource) {
243
227
  return resource.model.collection.collectionName === name;
244
228
  });
245
- };
229
+ }
246
230
  ;
247
- FormsAngular.prototype.internalSearch = function (req, resourcesToSearch, includeResourceInResults, limit, callback) {
248
- var _a, _b, _c, _d;
231
+ // Using the given (already-populated) AmbiguousRecordStore, generate text suitable for
232
+ // disambiguation of each ambiguous record, and pass that to the given disambiguateItemCallback
233
+ // so our caller can decorate the ambiguous record in whatever way it deems appropriate.
234
+ //
235
+ // The ambiguousRecordStore provided to this function (generated either by a call to
236
+ // buildSingleResourceAmbiguousRecordStore() or buildMultiResourceAmbiguousRecordStore()) will
237
+ // already be grouping records by the resource that should be used to disambiguate them, with
238
+ // the name of that resource being the primary index property of the store.
239
+ //
240
+ // The disambiguation text will be the concatenation (space-seperated) of the list fields for
241
+ // the doc from that resource whose _id matches the value of record[disambiguationField].
242
+ //
243
+ // allRecords should include all of the ambiguous records (also held by AmbiguousRecordStore)
244
+ // as well as those found not to be ambiguous. The final act of this function will be to delete
245
+ // the disambiguation field from those records - it is only going to be there for the purpose
246
+ // of disambiguation, and should not be returned by our caller once disambiguation is complete.
247
+ //
248
+ // The scary-looking templating used here ensures that the objects in allRecords (and also
249
+ // ambiguousRecordStore) include an (optional) string property with the name identified by
250
+ // disambiguationField. For the avoidance of doubt, "prop" here could be anything - "foo in f"
251
+ // would achieve the same result.
252
+ disambiguate(allRecords, ambiguousRecordStore, disambiguationField, disambiguateItemCallback, completionCallback) {
253
+ const that = this;
254
+ async.map(Object.keys(ambiguousRecordStore), function (resourceName, cbm) {
255
+ const resource = that.getResource(resourceName);
256
+ const projection = that.generateListFieldProjection(resource);
257
+ resource.model
258
+ .find({ _id: { $in: ambiguousRecordStore[resourceName].map((sr) => sr[disambiguationField]) } })
259
+ .select(projection)
260
+ .lean()
261
+ .then((disambiguationRecs) => {
262
+ for (const ambiguousResult of ambiguousRecordStore[resourceName]) {
263
+ const disambiguator = disambiguationRecs.find((d) => d._id.toString() === ambiguousResult[disambiguationField].toString());
264
+ if (disambiguator) {
265
+ let suffix = "";
266
+ for (const listField in projection) {
267
+ if (disambiguator[listField]) {
268
+ if (suffix) {
269
+ suffix += " ";
270
+ }
271
+ suffix += disambiguator[listField];
272
+ }
273
+ }
274
+ if (suffix) {
275
+ disambiguateItemCallback(ambiguousResult, suffix);
276
+ }
277
+ }
278
+ }
279
+ cbm(null);
280
+ })
281
+ .catch((err) => {
282
+ cbm(err);
283
+ });
284
+ }, (err) => {
285
+ for (const record of allRecords) {
286
+ delete record[disambiguationField];
287
+ }
288
+ completionCallback(err);
289
+ });
290
+ }
291
+ internalSearch(req, resourcesToSearch, includeResourceInResults, limit, callback) {
249
292
  if (typeof req.query === 'undefined') {
250
293
  req.query = {};
251
294
  }
252
- var timestamps = { sentAt: req.query.sentAt, startedAt: new Date().valueOf(), completedAt: undefined };
253
- var searches = [], resourceCount = resourcesToSearch.length, searchFor = req.query.q || '', filter = req.query.f;
295
+ const timestamps = { sentAt: req.query.sentAt, startedAt: new Date().valueOf(), completedAt: undefined };
296
+ let searches = [], resourceCount = resourcesToSearch.length, searchFor = req.query.q || '', filter = req.query.f;
254
297
  function translate(string, array, context) {
255
298
  if (array) {
256
- var translation = _.find(array, function (fromTo) {
299
+ let translation = _.find(array, function (fromTo) {
257
300
  return fromTo.from === string && (!fromTo.context || fromTo.context === context);
258
301
  });
259
302
  if (translation) {
@@ -264,11 +307,10 @@ var FormsAngular = /** @class */ (function () {
264
307
  }
265
308
  // return a string that determines the sort order of the resultObject
266
309
  function calcResultValue(obj) {
267
- function padLeft(score, reqLength, str) {
268
- if (str === void 0) { str = '0'; }
310
+ function padLeft(score, reqLength, str = '0') {
269
311
  return new Array(1 + reqLength - String(score).length).join(str) + score;
270
312
  }
271
- var sortString = '';
313
+ let sortString = '';
272
314
  sortString += padLeft(obj.addHits || 9, 1);
273
315
  sortString += padLeft(obj.searchImportance || 99, 2);
274
316
  sortString += padLeft(obj.weighting || 9999, 4);
@@ -279,9 +321,9 @@ var FormsAngular = /** @class */ (function () {
279
321
  filter = JSON.parse(filter);
280
322
  }
281
323
  // See if we are narrowing down the resources
282
- var collectionName;
283
- var collectionNameLower;
284
- var colonPos = searchFor.indexOf(':');
324
+ let collectionName;
325
+ let collectionNameLower;
326
+ let colonPos = searchFor.indexOf(':');
285
327
  switch (colonPos) {
286
328
  case -1:
287
329
  // Original behaviour = do nothing different
@@ -298,19 +340,19 @@ var FormsAngular = /** @class */ (function () {
298
340
  }
299
341
  break;
300
342
  }
301
- for (var i = 0; i < resourceCount; i++) {
302
- var resource = resourcesToSearch[i];
303
- if (resourceCount === 1 || (resource.options.searchImportance !== false && (!collectionName || collectionName === resource.resourceName || ((_b = (_a = resource.options) === null || _a === void 0 ? void 0 : _a.synonyms) === null || _b === void 0 ? void 0 : _b.find(function (s) { var _a; return ((_a = s.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === collectionNameLower; }))))) {
304
- var schema = resource.model.schema;
305
- var indexedFields = [];
306
- for (var j = 0; j < schema._indexes.length; j++) {
307
- var attributes = schema._indexes[j][0];
308
- var field = Object.keys(attributes)[0];
343
+ for (let i = 0; i < resourceCount; i++) {
344
+ let resource = resourcesToSearch[i];
345
+ if (resourceCount === 1 || (resource.options.searchImportance !== false && (!collectionName || collectionName === resource.resourceName || resource.options?.synonyms?.find(s => s.name?.toLowerCase() === collectionNameLower)))) {
346
+ let schema = resource.model.schema;
347
+ let indexedFields = [];
348
+ for (let j = 0; j < schema._indexes.length; j++) {
349
+ let attributes = schema._indexes[j][0];
350
+ let field = Object.keys(attributes)[0];
309
351
  if (indexedFields.indexOf(field) === -1) {
310
352
  indexedFields.push(field);
311
353
  }
312
354
  }
313
- for (var path in schema.paths) {
355
+ for (let path in schema.paths) {
314
356
  if (path !== '_id' && schema.paths.hasOwnProperty(path)) {
315
357
  if (schema.paths[path]._index && !schema.paths[path].options.noSearch) {
316
358
  if (indexedFields.indexOf(path) === -1) {
@@ -322,10 +364,10 @@ var FormsAngular = /** @class */ (function () {
322
364
  if (indexedFields.length === 0) {
323
365
  console.log('ERROR: Searching on a collection with no indexes ' + resource.resourceName);
324
366
  }
325
- var synonymObj = (_d = (_c = resource.options) === null || _c === void 0 ? void 0 : _c.synonyms) === null || _d === void 0 ? void 0 : _d.find(function (s) { return s.name.toLowerCase() === collectionNameLower; });
326
- var synonymFilter = synonymObj === null || synonymObj === void 0 ? void 0 : synonymObj.filter;
327
- for (var m = 0; m < indexedFields.length; m++) {
328
- var searchObj = { resource: resource, field: indexedFields[m] };
367
+ let synonymObj = resource.options?.synonyms?.find(s => s.name.toLowerCase() === collectionNameLower);
368
+ const synonymFilter = synonymObj?.filter;
369
+ for (let m = 0; m < indexedFields.length; m++) {
370
+ let searchObj = { resource: resource, field: indexedFields[m] };
329
371
  if (synonymFilter) {
330
372
  searchObj.filter = synonymFilter;
331
373
  }
@@ -333,19 +375,19 @@ var FormsAngular = /** @class */ (function () {
333
375
  }
334
376
  }
335
377
  }
336
- var that = this;
337
- var results = [];
338
- var moreCount = 0;
339
- var searchCriteria;
340
- var searchStrings;
341
- var multiMatchPossible = false;
378
+ const that = this;
379
+ let results = [];
380
+ let moreCount = 0;
381
+ let searchCriteria;
382
+ let searchStrings;
383
+ let multiMatchPossible = false;
342
384
  if (searchFor === '?') {
343
385
  // interpret this as a wildcard (so there is no way to search for ?
344
386
  searchCriteria = null;
345
387
  }
346
388
  else {
347
389
  // Support for searching anywhere in a field by starting with *
348
- var startAnchor = '^';
390
+ let startAnchor = '^';
349
391
  if (searchFor.slice(0, 1) === '*') {
350
392
  startAnchor = '';
351
393
  searchFor = searchFor.slice(1);
@@ -356,19 +398,19 @@ var FormsAngular = /** @class */ (function () {
356
398
  if (multiMatchPossible) {
357
399
  searchStrings = searchFor.split(' ');
358
400
  }
359
- var modifiedSearchStr = multiMatchPossible ? searchStrings.join('|') : searchFor;
401
+ let modifiedSearchStr = multiMatchPossible ? searchStrings.join('|') : searchFor;
360
402
  searchFor = searchFor.toLowerCase(); // For later case-insensitive comparison
361
403
  // Removed the logic that preserved spaces when collection was specified because Louise asked me to.
362
- searchCriteria = { $regex: "".concat(startAnchor, "(").concat(modifiedSearchStr, ")"), $options: 'i' };
404
+ searchCriteria = { $regex: `${startAnchor}(${modifiedSearchStr})`, $options: 'i' };
363
405
  }
364
- var handleSearchResultsFromIndex = function (err, docs, item, cb) {
406
+ let handleSearchResultsFromIndex = function (err, docs, item, cb) {
365
407
  function handleSingleSearchResult(aDoc, cbdoc) {
366
- var thisId = aDoc._id.toString(), resultObject, resultPos;
408
+ let thisId = aDoc._id.toString(), resultObject, resultPos;
367
409
  function handleResultsInList() {
368
410
  if (multiMatchPossible) {
369
411
  resultObject.matched = resultObject.matched || [];
370
412
  // record the index of string that matched, so we don't count it against another field
371
- for (var i = 0; i < searchStrings.length; i++) {
413
+ for (let i = 0; i < searchStrings.length; i++) {
372
414
  if (aDoc[item.field].toLowerCase().indexOf(searchStrings[i]) === 0) {
373
415
  resultObject.matched.push(i);
374
416
  break;
@@ -395,7 +437,7 @@ var FormsAngular = /** @class */ (function () {
395
437
  // If they have already matched then improve their weighting
396
438
  if (multiMatchPossible) {
397
439
  // record the index of string that matched, so we don't count it against another field
398
- for (var i = 0; i < searchStrings.length; i++) {
440
+ for (let i = 0; i < searchStrings.length; i++) {
399
441
  if (!resultObject.matched.includes(i) && aDoc[item.field].toLowerCase().indexOf(searchStrings[i]) === 0) {
400
442
  resultObject.matched.push(i);
401
443
  resultObject.addHits = Math.max((resultObject.addHits || 9) - 1, 0);
@@ -411,19 +453,28 @@ var FormsAngular = /** @class */ (function () {
411
453
  }
412
454
  else {
413
455
  // Otherwise add them new...
414
- var addHits_1;
415
- if (multiMatchPossible)
456
+ let addHits;
457
+ if (multiMatchPossible) {
416
458
  // If they match the whole search phrase in one index they get smaller addHits (so they sort higher)
417
459
  if (aDoc[item.field].toLowerCase().indexOf(searchFor) === 0) {
418
- addHits_1 = 7;
460
+ addHits = 7;
419
461
  }
462
+ }
463
+ let disambiguationId;
464
+ const opts = item.resource.options;
465
+ const disambiguationResource = opts.disambiguation?.resource;
466
+ if (disambiguationResource) {
467
+ disambiguationId = aDoc[opts.disambiguation.field]?.toString();
468
+ }
420
469
  // Use special listings format if defined
421
- var specialListingFormat = item.resource.options.searchResultFormat;
470
+ let specialListingFormat = item.resource.options.searchResultFormat;
422
471
  if (specialListingFormat) {
423
472
  specialListingFormat.apply(aDoc, [req])
424
- .then(function (resultObj) {
473
+ .then((resultObj) => {
425
474
  resultObject = resultObj;
426
- resultObject.addHits = addHits_1;
475
+ resultObject.addHits = addHits;
476
+ resultObject.disambiguationResource = disambiguationResource;
477
+ resultObject.disambiguationId = disambiguationId;
427
478
  handleResultsInList();
428
479
  });
429
480
  }
@@ -436,7 +487,9 @@ var FormsAngular = /** @class */ (function () {
436
487
  resultObject = {
437
488
  id: aDoc._id,
438
489
  weighting: 9999,
439
- addHits: addHits_1,
490
+ addHits,
491
+ disambiguationResource,
492
+ disambiguationId,
440
493
  text: description
441
494
  };
442
495
  if (resourceCount > 1 || includeResourceInResults) {
@@ -456,14 +509,14 @@ var FormsAngular = /** @class */ (function () {
456
509
  }
457
510
  };
458
511
  this.searchFunc(searches, function (item, cb) {
459
- var searchDoc = {};
460
- var searchFilter = filter || item.filter;
512
+ let searchDoc = {};
513
+ let searchFilter = filter || item.filter;
461
514
  if (searchFilter) {
462
515
  that.hackVariables(searchFilter);
463
516
  extend(searchDoc, searchFilter);
464
517
  if (searchFilter[item.field]) {
465
518
  delete searchDoc[item.field];
466
- var obj1 = {}, obj2 = {};
519
+ let obj1 = {}, obj2 = {};
467
520
  obj1[item.field] = searchFilter[item.field];
468
521
  obj2[item.field] = searchCriteria;
469
522
  searchDoc['$and'] = [obj1, obj2];
@@ -512,13 +565,27 @@ var FormsAngular = /** @class */ (function () {
512
565
  moreCount += results.length - limit;
513
566
  results.splice(limit);
514
567
  }
515
- timestamps.completedAt = new Date().valueOf();
516
- callback(null, { results: results, moreCount: moreCount, timestamps: timestamps });
568
+ that.disambiguate(results, that.buildMultiResourceAmbiguousRecordStore(results, ["text"], "disambiguationResource"), "disambiguationId", (item, disambiguationText) => {
569
+ item.text += ` (${disambiguationText})`;
570
+ }, (err) => {
571
+ if (err) {
572
+ callback(err);
573
+ }
574
+ else {
575
+ // the disambiguate() call will have deleted the disambiguationIds but we're responsible for
576
+ // the disambiguationResources, which we shouldn't be returning to the client
577
+ for (const result of results) {
578
+ delete result.disambiguationResource;
579
+ }
580
+ timestamps.completedAt = new Date().valueOf();
581
+ callback(null, { results, moreCount, timestamps });
582
+ }
583
+ });
517
584
  }
518
585
  });
519
- };
586
+ }
520
587
  ;
521
- FormsAngular.prototype.wrapInternalSearch = function (req, res, resourcesToSearch, includeResourceInResults, limit) {
588
+ wrapInternalSearch(req, res, resourcesToSearch, includeResourceInResults, limit) {
522
589
  this.internalSearch(req, resourcesToSearch, includeResourceInResults, limit, function (err, resultsObject) {
523
590
  if (err) {
524
591
  res.status(400, err);
@@ -527,46 +594,46 @@ var FormsAngular = /** @class */ (function () {
527
594
  res.send(resultsObject);
528
595
  }
529
596
  });
530
- };
597
+ }
531
598
  ;
532
- FormsAngular.prototype.search = function () {
599
+ search() {
533
600
  return _.bind(function (req, res, next) {
534
601
  if (!(req.resource = this.getResource(req.params.resourceName))) {
535
602
  return next();
536
603
  }
537
604
  this.wrapInternalSearch(req, res, [req.resource], false, 0);
538
605
  }, this);
539
- };
606
+ }
540
607
  ;
541
- FormsAngular.prototype.searchAll = function () {
608
+ searchAll() {
542
609
  return _.bind(function (req, res) {
543
610
  this.wrapInternalSearch(req, res, this.resources, true, 10);
544
611
  }, this);
545
- };
612
+ }
546
613
  ;
547
- FormsAngular.prototype.models = function () {
548
- var that = this;
614
+ models() {
615
+ const that = this;
549
616
  return function (req, res) {
550
617
  // TODO: Make this less wasteful - we only need to send the resourceNames of the resources
551
618
  // Check for optional modelFilter and call it with the request and current list. Otherwise just return the list.
552
619
  res.send(that.options.modelFilter ? that.options.modelFilter.call(null, req, that.resources) : that.resources);
553
620
  };
554
- };
621
+ }
555
622
  ;
556
- FormsAngular.prototype.renderError = function (err, redirectUrl, req, res) {
557
- res.statusMessage = (err === null || err === void 0 ? void 0 : err.message) || err;
558
- res.status(400).end((err === null || err === void 0 ? void 0 : err.message) || err);
559
- };
623
+ renderError(err, redirectUrl, req, res) {
624
+ res.statusMessage = err?.message || err;
625
+ res.status(400).end(err?.message || err);
626
+ }
560
627
  ;
561
- FormsAngular.prototype.redirect = function (address, req, res) {
628
+ redirect(address, req, res) {
562
629
  res.send(address);
563
- };
630
+ }
564
631
  ;
565
- FormsAngular.prototype.applySchemaSubset = function (vanilla, schema) {
566
- var outPath;
632
+ applySchemaSubset(vanilla, schema) {
633
+ let outPath;
567
634
  if (schema) {
568
635
  outPath = {};
569
- for (var fld in schema) {
636
+ for (let fld in schema) {
570
637
  if (schema.hasOwnProperty(fld)) {
571
638
  if (vanilla[fld]) {
572
639
  outPath[fld] = _.cloneDeep(vanilla[fld]);
@@ -586,7 +653,7 @@ var FormsAngular = /** @class */ (function () {
586
653
  }
587
654
  }
588
655
  outPath[fld].options = outPath[fld].options || {};
589
- for (var override in schema[fld]) {
656
+ for (const override in schema[fld]) {
590
657
  if (schema[fld].hasOwnProperty(override)) {
591
658
  if (override.slice(0, 1) !== '_') {
592
659
  if (schema[fld].hasOwnProperty(override)) {
@@ -605,19 +672,19 @@ var FormsAngular = /** @class */ (function () {
605
672
  outPath = vanilla;
606
673
  }
607
674
  return outPath;
608
- };
675
+ }
609
676
  ;
610
- FormsAngular.prototype.preprocess = function (resource, paths, formSchema) {
611
- var outPath = {}, hiddenFields = [], listFields = [];
677
+ preprocess(resource, paths, formSchema) {
678
+ let outPath = {}, hiddenFields = [], listFields = [];
612
679
  if (resource && resource.options && resource.options.idIsList) {
613
680
  paths['_id'].options = paths['_id'].options || {};
614
681
  paths['_id'].options.list = resource.options.idIsList;
615
682
  }
616
- for (var element in paths) {
683
+ for (let element in paths) {
617
684
  if (paths.hasOwnProperty(element) && element !== '__v') {
618
685
  // check for schemas
619
686
  if (paths[element].schema) {
620
- var subSchemaInfo = this.preprocess(null, paths[element].schema.paths);
687
+ let subSchemaInfo = this.preprocess(null, paths[element].schema.paths);
621
688
  outPath[element] = { schema: subSchemaInfo.paths };
622
689
  if (paths[element].options.form) {
623
690
  outPath[element].options = { form: extend(true, {}, paths[element].options.form) };
@@ -631,10 +698,10 @@ var FormsAngular = /** @class */ (function () {
631
698
  }
632
699
  else {
633
700
  // check for arrays
634
- var realType = paths[element].caster ? paths[element].caster : paths[element];
701
+ let realType = paths[element].caster ? paths[element].caster : paths[element];
635
702
  if (!realType.instance) {
636
703
  if (realType.options.type) {
637
- var type = realType.options.type(), typeType = typeof type;
704
+ let type = realType.options.type(), typeType = typeof type;
638
705
  if (typeType === 'string') {
639
706
  realType.instance = (!isNaN(Date.parse(type))) ? 'Date' : 'String';
640
707
  }
@@ -650,9 +717,9 @@ var FormsAngular = /** @class */ (function () {
650
717
  if (paths[element].options.match) {
651
718
  outPath[element].options.match = paths[element].options.match.source || paths[element].options.match;
652
719
  }
653
- var schemaListInfo = paths[element].options.list;
720
+ let schemaListInfo = paths[element].options.list;
654
721
  if (schemaListInfo) {
655
- var listFieldInfo = { field: element };
722
+ let listFieldInfo = { field: element };
656
723
  if (typeof schemaListInfo === 'object' && Object.keys(schemaListInfo).length > 0) {
657
724
  listFieldInfo.params = schemaListInfo;
658
725
  }
@@ -662,7 +729,7 @@ var FormsAngular = /** @class */ (function () {
662
729
  }
663
730
  }
664
731
  outPath = this.applySchemaSubset(outPath, formSchema);
665
- var returnObj = { paths: outPath };
732
+ let returnObj = { paths: outPath };
666
733
  if (hiddenFields.length > 0) {
667
734
  returnObj.hide = hiddenFields;
668
735
  }
@@ -670,113 +737,101 @@ var FormsAngular = /** @class */ (function () {
670
737
  returnObj.listFields = listFields;
671
738
  }
672
739
  return returnObj;
673
- };
740
+ }
674
741
  ;
675
- FormsAngular.prototype.schema = function () {
742
+ schema() {
676
743
  return _.bind(function (req, res) {
677
744
  if (!(req.resource = this.getResource(req.params.resourceName))) {
678
745
  return res.status(404).end();
679
746
  }
680
- var formSchema = null;
747
+ let formSchema = null;
681
748
  if (req.params.formName) {
682
749
  formSchema = req.resource.model.schema.statics['form'](req.params.formName, req);
683
750
  }
684
- var paths = this.preprocess(req.resource, req.resource.model.schema.paths, formSchema).paths;
751
+ let paths = this.preprocess(req.resource, req.resource.model.schema.paths, formSchema).paths;
685
752
  res.send(paths);
686
753
  }, this);
687
- };
754
+ }
688
755
  ;
689
- FormsAngular.prototype.report = function () {
690
- return _.bind(function (req, res, next) {
691
- return __awaiter(this, void 0, void 0, function () {
692
- var self, reportSchema, fields, key, schemaCopy;
693
- return __generator(this, function (_a) {
694
- switch (_a.label) {
695
- case 0:
696
- if (!(req.resource = this.getResource(req.params.resourceName))) {
697
- return [2 /*return*/, next()];
698
- }
699
- self = this;
700
- if (typeof req.query === 'undefined') {
701
- req.query = {};
702
- }
703
- if (!req.params.reportName) return [3 /*break*/, 2];
704
- return [4 /*yield*/, req.resource.model.schema.statics['report'](req.params.reportName, req)];
705
- case 1:
706
- reportSchema = _a.sent();
707
- return [3 /*break*/, 3];
708
- case 2:
709
- if (req.query.r) {
710
- switch (req.query.r[0]) {
711
- case '[':
712
- reportSchema = { pipeline: JSON.parse(req.query.r) };
713
- break;
714
- case '{':
715
- reportSchema = JSON.parse(req.query.r);
716
- break;
717
- default:
718
- return [2 /*return*/, self.renderError(new Error('Invalid "r" parameter'), null, req, res)];
719
- }
720
- }
721
- else {
722
- fields = {};
723
- for (key in req.resource.model.schema.paths) {
724
- if (req.resource.model.schema.paths.hasOwnProperty(key)) {
725
- if (key !== '__v' && !req.resource.model.schema.paths[key].options.secure) {
726
- if (key.indexOf('.') === -1) {
727
- fields[key] = 1;
728
- }
729
- }
730
- }
731
- }
732
- reportSchema = {
733
- pipeline: [
734
- { $project: fields }
735
- ], drilldown: req.params.resourceName + '/|_id|/edit'
736
- };
756
+ report() {
757
+ return _.bind(async function (req, res, next) {
758
+ if (!(req.resource = this.getResource(req.params.resourceName))) {
759
+ return next();
760
+ }
761
+ const self = this;
762
+ if (typeof req.query === 'undefined') {
763
+ req.query = {};
764
+ }
765
+ let reportSchema;
766
+ if (req.params.reportName) {
767
+ reportSchema = await req.resource.model.schema.statics['report'](req.params.reportName, req);
768
+ }
769
+ else if (req.query.r) {
770
+ switch (req.query.r[0]) {
771
+ case '[':
772
+ reportSchema = { pipeline: JSON.parse(req.query.r) };
773
+ break;
774
+ case '{':
775
+ reportSchema = JSON.parse(req.query.r);
776
+ break;
777
+ default:
778
+ return self.renderError(new Error('Invalid "r" parameter'), null, req, res);
779
+ }
780
+ }
781
+ else {
782
+ let fields = {};
783
+ for (let key in req.resource.model.schema.paths) {
784
+ if (req.resource.model.schema.paths.hasOwnProperty(key)) {
785
+ if (key !== '__v' && !req.resource.model.schema.paths[key].options.secure) {
786
+ if (key.indexOf('.') === -1) {
787
+ fields[key] = 1;
737
788
  }
738
- _a.label = 3;
739
- case 3:
740
- schemaCopy = {};
741
- extend(schemaCopy, reportSchema);
742
- schemaCopy.params = schemaCopy.params || [];
743
- self.reportInternal(req, req.resource, schemaCopy, function (err, result) {
744
- if (err) {
745
- res.send({ success: false, error: err.message || err });
746
- }
747
- else {
748
- res.send(result);
749
- }
750
- });
751
- return [2 /*return*/];
789
+ }
752
790
  }
753
- });
791
+ }
792
+ reportSchema = {
793
+ pipeline: [
794
+ { $project: fields }
795
+ ], drilldown: req.params.resourceName + '/|_id|/edit'
796
+ };
797
+ }
798
+ // Replace parameters in pipeline
799
+ let schemaCopy = {};
800
+ extend(schemaCopy, reportSchema);
801
+ schemaCopy.params = schemaCopy.params || [];
802
+ self.reportInternal(req, req.resource, schemaCopy, function (err, result) {
803
+ if (err) {
804
+ res.send({ success: false, error: err.message || err });
805
+ }
806
+ else {
807
+ res.send(result);
808
+ }
754
809
  });
755
810
  }, this);
756
- };
811
+ }
757
812
  ;
758
- FormsAngular.prototype.hackVariablesInPipeline = function (runPipeline) {
759
- for (var pipelineSection = 0; pipelineSection < runPipeline.length; pipelineSection++) {
813
+ hackVariablesInPipeline(runPipeline) {
814
+ for (let pipelineSection = 0; pipelineSection < runPipeline.length; pipelineSection++) {
760
815
  if (runPipeline[pipelineSection]['$match']) {
761
816
  this.hackVariables(runPipeline[pipelineSection]['$match']);
762
817
  }
763
818
  }
764
- };
819
+ }
765
820
  ;
766
- FormsAngular.prototype.hackVariables = function (obj) {
821
+ hackVariables(obj) {
767
822
  // Replace variables that cannot be serialised / deserialised. Bit of a hack, but needs must...
768
823
  // Anything formatted 1800-01-01T00:00:00.000Z or 1800-01-01T00:00:00.000+0000 is converted to a Date
769
824
  // Only handles the cases I need for now
770
825
  // TODO: handle arrays etc
771
- for (var prop in obj) {
826
+ for (const prop in obj) {
772
827
  if (obj.hasOwnProperty(prop)) {
773
828
  if (typeof obj[prop] === 'string') {
774
- var dateTest = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3})(Z|[+ -]\d{4})$/.exec(obj[prop]);
829
+ const dateTest = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3})(Z|[+ -]\d{4})$/.exec(obj[prop]);
775
830
  if (dateTest) {
776
831
  obj[prop] = new Date(dateTest[1] + 'Z');
777
832
  }
778
833
  else {
779
- var objectIdTest = /^([0-9a-fA-F]{24})$/.exec(obj[prop]);
834
+ const objectIdTest = /^([0-9a-fA-F]{24})$/.exec(obj[prop]);
780
835
  if (objectIdTest) {
781
836
  obj[prop] = new this.mongoose.Types.ObjectId(objectIdTest[1]);
782
837
  }
@@ -787,20 +842,19 @@ var FormsAngular = /** @class */ (function () {
787
842
  }
788
843
  }
789
844
  }
790
- };
845
+ }
791
846
  ;
792
- FormsAngular.prototype.sanitisePipeline = function (aggregationParam, hiddenFields, findFuncQry) {
793
- var _a, _b, _c, _d;
794
- var that = this;
795
- var array = Array.isArray(aggregationParam) ? aggregationParam : [aggregationParam];
796
- var retVal = [];
797
- var doneHiddenFields = false;
847
+ sanitisePipeline(aggregationParam, hiddenFields, findFuncQry) {
848
+ let that = this;
849
+ let array = Array.isArray(aggregationParam) ? aggregationParam : [aggregationParam];
850
+ let retVal = [];
851
+ let doneHiddenFields = false;
798
852
  if (findFuncQry) {
799
853
  retVal.unshift({ $match: findFuncQry });
800
854
  }
801
- var _loop_1 = function (pipelineSection) {
802
- var stage = array[pipelineSection];
803
- var keys = Object.keys(stage);
855
+ for (let pipelineSection = 0; pipelineSection < array.length; pipelineSection++) {
856
+ let stage = array[pipelineSection];
857
+ let keys = Object.keys(stage);
804
858
  if (keys.length !== 1) {
805
859
  throw new Error('Invalid pipeline instruction');
806
860
  }
@@ -812,18 +866,18 @@ var FormsAngular = /** @class */ (function () {
812
866
  /*
813
867
  Sanitise the pipeline we are doing a union with, removing hidden fields from that collection
814
868
  */
815
- var unionCollectionName = stage.$unionWith.coll;
816
- var unionResource = that.getResourceFromCollection(unionCollectionName);
817
- var unionHiddenLookupFields = {};
869
+ const unionCollectionName = stage.$unionWith.coll;
870
+ const unionResource = that.getResourceFromCollection(unionCollectionName);
871
+ let unionHiddenLookupFields = {};
818
872
  if (unionResource) {
819
- if (((_b = (_a = unionResource.options) === null || _a === void 0 ? void 0 : _a.hide) === null || _b === void 0 ? void 0 : _b.length) > 0) {
820
- unionHiddenLookupFields = this_1.generateHiddenFields(unionResource, false);
873
+ if (unionResource.options?.hide?.length > 0) {
874
+ unionHiddenLookupFields = this.generateHiddenFields(unionResource, false);
821
875
  }
822
876
  }
823
877
  stage.$unionWith.pipeline = that.sanitisePipeline(stage.$unionWith.pipeline, unionHiddenLookupFields, findFuncQry);
824
878
  break;
825
879
  case '$match':
826
- this_1.hackVariables(array[pipelineSection]['$match']);
880
+ this.hackVariables(array[pipelineSection]['$match']);
827
881
  retVal.push(array[pipelineSection]);
828
882
  if (!doneHiddenFields && Object.keys(hiddenFields) && Object.keys(hiddenFields).length > 0) {
829
883
  // We can now project out the hidden fields (we wait for the $match to make sure we don't break
@@ -835,20 +889,20 @@ var FormsAngular = /** @class */ (function () {
835
889
  break;
836
890
  case '$lookup':
837
891
  // hide any hiddenfields in the lookup collection
838
- var collectionName = stage.$lookup.from;
839
- var lookupField_1 = stage.$lookup.as;
840
- if ((collectionName + lookupField_1).indexOf('$') !== -1) {
892
+ const collectionName = stage.$lookup.from;
893
+ const lookupField = stage.$lookup.as;
894
+ if ((collectionName + lookupField).indexOf('$') !== -1) {
841
895
  throw new Error('No support for lookups where the "from" or "as" is anything other than a simple string');
842
896
  }
843
- var resource = that.getResourceFromCollection(collectionName);
897
+ const resource = that.getResourceFromCollection(collectionName);
844
898
  if (resource) {
845
- if (((_d = (_c = resource.options) === null || _c === void 0 ? void 0 : _c.hide) === null || _d === void 0 ? void 0 : _d.length) > 0) {
846
- var hiddenLookupFields = this_1.generateHiddenFields(resource, false);
847
- var hiddenFieldsObj_1 = {};
848
- Object.keys(hiddenLookupFields).forEach(function (hf) {
849
- hiddenFieldsObj_1["".concat(lookupField_1, ".").concat(hf)] = false;
899
+ if (resource.options?.hide?.length > 0) {
900
+ const hiddenLookupFields = this.generateHiddenFields(resource, false);
901
+ let hiddenFieldsObj = {};
902
+ Object.keys(hiddenLookupFields).forEach(hf => {
903
+ hiddenFieldsObj[`${lookupField}.${hf}`] = false;
850
904
  });
851
- retVal.push({ $project: hiddenFieldsObj_1 });
905
+ retVal.push({ $project: hiddenFieldsObj });
852
906
  }
853
907
  }
854
908
  break;
@@ -859,21 +913,17 @@ var FormsAngular = /** @class */ (function () {
859
913
  if (stage) {
860
914
  retVal.push(stage);
861
915
  }
862
- };
863
- var this_1 = this;
864
- for (var pipelineSection = 0; pipelineSection < array.length; pipelineSection++) {
865
- _loop_1(pipelineSection);
866
916
  }
867
917
  if (!doneHiddenFields && Object.keys(hiddenFields) && Object.keys(hiddenFields).length > 0) {
868
918
  // If there was no $match we still need to hide the hidden fields
869
919
  retVal.unshift({ $project: hiddenFields });
870
920
  }
871
921
  return retVal;
872
- };
873
- FormsAngular.prototype.reportInternal = function (req, resource, schema, callback) {
874
- var runPipelineStr;
875
- var runPipelineObj;
876
- var self = this;
922
+ }
923
+ reportInternal(req, resource, schema, callback) {
924
+ let runPipelineStr;
925
+ let runPipelineObj;
926
+ let self = this;
877
927
  if (typeof req.query === 'undefined') {
878
928
  req.query = {};
879
929
  }
@@ -883,7 +933,7 @@ var FormsAngular = /** @class */ (function () {
883
933
  }
884
934
  else {
885
935
  runPipelineStr = JSON.stringify(schema.pipeline);
886
- for (var param in req.query) {
936
+ for (let param in req.query) {
887
937
  if (req.query.hasOwnProperty(param)) {
888
938
  if (req.query[param]) {
889
939
  if (param !== 'r') { // we don't want to copy the whole report schema (again!)
@@ -891,7 +941,7 @@ var FormsAngular = /** @class */ (function () {
891
941
  schema.params[param].value = req.query[param];
892
942
  }
893
943
  else {
894
- callback("No such parameter as ".concat(param, " - try one of ").concat(Object.keys(schema.params).join()));
944
+ callback(`No such parameter as ${param} - try one of ${Object.keys(schema.params).join()}`);
895
945
  }
896
946
  }
897
947
  }
@@ -900,7 +950,7 @@ var FormsAngular = /** @class */ (function () {
900
950
  // Replace parameters with the value
901
951
  if (runPipelineStr) {
902
952
  runPipelineStr = runPipelineStr.replace(/"\(.+?\)"/g, function (match) {
903
- var sparam = schema.params[match.slice(2, -2)];
953
+ let sparam = schema.params[match.slice(2, -2)];
904
954
  if (sparam !== undefined) {
905
955
  if (sparam.type === 'number') {
906
956
  return sparam.value;
@@ -916,27 +966,27 @@ var FormsAngular = /** @class */ (function () {
916
966
  }
917
967
  }
918
968
  else {
919
- callback("No such parameter as ".concat(match.slice(2, -2), " - try one of ").concat(Object.keys(schema.params).join()));
969
+ callback(`No such parameter as ${match.slice(2, -2)} - try one of ${Object.keys(schema.params).join()}`);
920
970
  }
921
971
  });
922
972
  }
923
973
  runPipelineObj = JSON.parse(runPipelineStr);
924
- var hiddenFields_1 = self.generateHiddenFields(resource, false);
925
- var toDo = {
974
+ let hiddenFields = self.generateHiddenFields(resource, false);
975
+ let toDo = {
926
976
  runAggregation: function (cb) {
927
- runPipelineObj = self.sanitisePipeline(runPipelineObj, hiddenFields_1, queryObj);
977
+ runPipelineObj = self.sanitisePipeline(runPipelineObj, hiddenFields, queryObj);
928
978
  resource.model.aggregate(runPipelineObj, cb);
929
979
  }
930
980
  };
931
- var translations_1 = []; // array of form {ref:'lookupname',translations:[{value:xx, display:' '}]}
981
+ let translations = []; // array of form {ref:'lookupname',translations:[{value:xx, display:' '}]}
932
982
  // if we need to do any column translations add the function to the tasks list
933
983
  if (schema.columnTranslations) {
934
984
  toDo.applyTranslations = ['runAggregation', function (results, cb) {
935
985
  function doATranslate(column, theTranslation) {
936
986
  results['runAggregation'].forEach(function (resultRow) {
937
- var valToTranslate = resultRow[column.field];
987
+ let valToTranslate = resultRow[column.field];
938
988
  valToTranslate = (valToTranslate ? valToTranslate.toString() : '');
939
- var thisTranslation = _.find(theTranslation.translations, function (option) {
989
+ let thisTranslation = _.find(theTranslation.translations, function (option) {
940
990
  return valToTranslate === option.value.toString();
941
991
  });
942
992
  resultRow[column.field] = thisTranslation ? thisTranslation.display : ' * Missing columnTranslation * ';
@@ -947,7 +997,7 @@ var FormsAngular = /** @class */ (function () {
947
997
  doATranslate(columnTranslation, columnTranslation);
948
998
  }
949
999
  if (columnTranslation.ref) {
950
- var theTranslation = _.find(translations_1, function (translation) {
1000
+ let theTranslation = _.find(translations, function (translation) {
951
1001
  return (translation.ref === columnTranslation.ref);
952
1002
  });
953
1003
  if (theTranslation) {
@@ -960,9 +1010,9 @@ var FormsAngular = /** @class */ (function () {
960
1010
  });
961
1011
  cb(null, null);
962
1012
  }];
963
- var callFuncs = false;
964
- for (var i = 0; i < schema.columnTranslations.length; i++) {
965
- var thisColumnTranslation = schema.columnTranslations[i];
1013
+ let callFuncs = false;
1014
+ for (let i = 0; i < schema.columnTranslations.length; i++) {
1015
+ let thisColumnTranslation = schema.columnTranslations[i];
966
1016
  if (thisColumnTranslation.field) {
967
1017
  // if any of the column translations are adhoc funcs, set up the tasks to perform them
968
1018
  if (thisColumnTranslation.fn) {
@@ -970,28 +1020,28 @@ var FormsAngular = /** @class */ (function () {
970
1020
  }
971
1021
  // if this column translation is a "ref", set up the tasks to look up the values and populate the translations
972
1022
  if (thisColumnTranslation.ref) {
973
- var lookup = self.getResource(thisColumnTranslation.ref);
1023
+ let lookup = self.getResource(thisColumnTranslation.ref);
974
1024
  if (lookup) {
975
1025
  if (!toDo[thisColumnTranslation.ref]) {
976
- var getFunc = function (ref) {
977
- var lookup = ref;
1026
+ let getFunc = function (ref) {
1027
+ let lookup = ref;
978
1028
  return function (cb) {
979
- var translateObject = { ref: lookup.resourceName, translations: [] };
980
- translations_1.push(translateObject);
1029
+ let translateObject = { ref: lookup.resourceName, translations: [] };
1030
+ translations.push(translateObject);
981
1031
  lookup.model.find({}, {}, { lean: true }, function (err, findResults) {
982
1032
  if (err) {
983
1033
  cb(err);
984
1034
  }
985
1035
  else {
986
1036
  // TODO - this ref func can probably be done away with now that list fields can have ref
987
- var j_1 = 0;
1037
+ let j = 0;
988
1038
  async.whilst(function (cbtest) {
989
- cbtest(null, j_1 < findResults.length);
1039
+ cbtest(null, j < findResults.length);
990
1040
  }, function (cbres) {
991
- var theResult = findResults[j_1];
992
- translateObject.translations[j_1] = translateObject.translations[j_1] || {};
993
- var theTranslation = translateObject.translations[j_1];
994
- j_1++;
1041
+ let theResult = findResults[j];
1042
+ translateObject.translations[j] = translateObject.translations[j] || {};
1043
+ let theTranslation = translateObject.translations[j];
1044
+ j++;
995
1045
  self.getListFields(lookup, theResult, function (err, description) {
996
1046
  if (err) {
997
1047
  cbres(err);
@@ -1026,8 +1076,8 @@ var FormsAngular = /** @class */ (function () {
1026
1076
  if (callFuncs) {
1027
1077
  toDo['callFunctions'] = ['runAggregation', function (results, cb) {
1028
1078
  async.each(results.runAggregation, function (row, cb) {
1029
- for (var i = 0; i < schema.columnTranslations.length; i++) {
1030
- var thisColumnTranslation = schema.columnTranslations[i];
1079
+ for (let i = 0; i < schema.columnTranslations.length; i++) {
1080
+ let thisColumnTranslation = schema.columnTranslations[i];
1031
1081
  if (thisColumnTranslation.fn) {
1032
1082
  thisColumnTranslation.fn(row, cb);
1033
1083
  }
@@ -1055,13 +1105,13 @@ var FormsAngular = /** @class */ (function () {
1055
1105
  });
1056
1106
  }
1057
1107
  });
1058
- };
1108
+ }
1059
1109
  ;
1060
- FormsAngular.prototype.saveAndRespond = function (req, res, hiddenFields) {
1110
+ saveAndRespond(req, res, hiddenFields) {
1061
1111
  function internalSave(doc) {
1062
1112
  doc.save(function (err, doc2) {
1063
1113
  if (err) {
1064
- var err2 = { status: 'err' };
1114
+ let err2 = { status: 'err' };
1065
1115
  if (!err.errors) {
1066
1116
  err2.message = err.message;
1067
1117
  }
@@ -1075,12 +1125,12 @@ var FormsAngular = /** @class */ (function () {
1075
1125
  }
1076
1126
  else {
1077
1127
  doc2 = doc2.toObject();
1078
- for (var hiddenField in hiddenFields) {
1128
+ for (const hiddenField in hiddenFields) {
1079
1129
  if (hiddenFields.hasOwnProperty(hiddenField) && hiddenFields[hiddenField]) {
1080
- var parts = hiddenField.split('.');
1081
- var lastPart = parts.length - 1;
1082
- var target = doc2;
1083
- for (var i = 0; i < lastPart; i++) {
1130
+ let parts = hiddenField.split('.');
1131
+ let lastPart = parts.length - 1;
1132
+ let target = doc2;
1133
+ for (let i = 0; i < lastPart; i++) {
1084
1134
  if (target.hasOwnProperty(parts[i])) {
1085
1135
  target = target[parts[i]];
1086
1136
  }
@@ -1094,7 +1144,7 @@ var FormsAngular = /** @class */ (function () {
1094
1144
  }
1095
1145
  });
1096
1146
  }
1097
- var doc = req.doc;
1147
+ let doc = req.doc;
1098
1148
  if (typeof req.resource.options.onSave === 'function') {
1099
1149
  req.resource.options.onSave(doc, req, function (err) {
1100
1150
  if (err) {
@@ -1106,19 +1156,19 @@ var FormsAngular = /** @class */ (function () {
1106
1156
  else {
1107
1157
  internalSave(doc);
1108
1158
  }
1109
- };
1159
+ }
1110
1160
  ;
1111
1161
  /**
1112
1162
  * All entities REST functions have to go through this first.
1113
1163
  */
1114
- FormsAngular.prototype.processCollection = function (req) {
1164
+ processCollection(req) {
1115
1165
  req.resource = this.getResource(req.params.resourceName);
1116
- };
1166
+ }
1117
1167
  ;
1118
1168
  /**
1119
1169
  * Renders a view with the list of docs, which may be modified by query parameters
1120
1170
  */
1121
- FormsAngular.prototype.collectionGet = function () {
1171
+ collectionGet() {
1122
1172
  return _.bind(function (req, res, next) {
1123
1173
  this.processCollection(req);
1124
1174
  if (!req.resource) {
@@ -1128,20 +1178,20 @@ var FormsAngular = /** @class */ (function () {
1128
1178
  req.query = {};
1129
1179
  }
1130
1180
  try {
1131
- var aggregationParam = req.query.a ? JSON.parse(req.query.a) : null;
1132
- var findParam = req.query.f ? JSON.parse(req.query.f) : {};
1133
- var projectParam = req.query.p ? JSON.parse(req.query.p) : {};
1134
- var limitParam = req.query.l ? JSON.parse(req.query.l) : 0;
1135
- var skipParam = req.query.s ? JSON.parse(req.query.s) : 0;
1136
- var orderParam = req.query.o ? JSON.parse(req.query.o) : req.resource.options.listOrder;
1181
+ const aggregationParam = req.query.a ? JSON.parse(req.query.a) : null;
1182
+ const findParam = req.query.f ? JSON.parse(req.query.f) : {};
1183
+ const projectParam = req.query.p ? JSON.parse(req.query.p) : {};
1184
+ const limitParam = req.query.l ? JSON.parse(req.query.l) : 0;
1185
+ const skipParam = req.query.s ? JSON.parse(req.query.s) : 0;
1186
+ const orderParam = req.query.o ? JSON.parse(req.query.o) : req.resource.options.listOrder;
1137
1187
  // Dates in aggregation must be Dates
1138
1188
  if (aggregationParam) {
1139
1189
  this.hackVariablesInPipeline(aggregationParam);
1140
1190
  }
1141
- var self_1 = this;
1191
+ const self = this;
1142
1192
  this.filteredFind(req.resource, req, aggregationParam, findParam, projectParam, orderParam, limitParam, skipParam, function (err, docs) {
1143
1193
  if (err) {
1144
- return self_1.renderError(err, null, req, res);
1194
+ return self.renderError(err, null, req, res);
1145
1195
  }
1146
1196
  else {
1147
1197
  res.send(docs);
@@ -1152,10 +1202,10 @@ var FormsAngular = /** @class */ (function () {
1152
1202
  res.status(400).send(e.message);
1153
1203
  }
1154
1204
  }, this);
1155
- };
1205
+ }
1156
1206
  ;
1157
- FormsAngular.prototype.generateProjection = function (hiddenFields, projectParam) {
1158
- var type;
1207
+ generateProjection(hiddenFields, projectParam) {
1208
+ let type;
1159
1209
  function setSelectType(typeChar, checkChar) {
1160
1210
  if (type === checkChar) {
1161
1211
  throw new Error('Cannot mix include and exclude fields in select');
@@ -1164,11 +1214,11 @@ var FormsAngular = /** @class */ (function () {
1164
1214
  type = typeChar;
1165
1215
  }
1166
1216
  }
1167
- var retVal = hiddenFields;
1217
+ let retVal = hiddenFields;
1168
1218
  if (projectParam) {
1169
- var projection = Object.keys(projectParam);
1219
+ let projection = Object.keys(projectParam);
1170
1220
  if (projection.length > 0) {
1171
- projection.forEach(function (p) {
1221
+ projection.forEach(p => {
1172
1222
  if (projectParam[p] === 0) {
1173
1223
  setSelectType('E', 'I');
1174
1224
  }
@@ -1186,7 +1236,7 @@ var FormsAngular = /** @class */ (function () {
1186
1236
  else {
1187
1237
  // We are selecting fields - make sure none are hidden
1188
1238
  retVal = projectParam;
1189
- for (var h in hiddenFields) {
1239
+ for (let h in hiddenFields) {
1190
1240
  if (hiddenFields.hasOwnProperty(h)) {
1191
1241
  delete retVal[h];
1192
1242
  }
@@ -1195,21 +1245,21 @@ var FormsAngular = /** @class */ (function () {
1195
1245
  }
1196
1246
  }
1197
1247
  return retVal;
1198
- };
1248
+ }
1199
1249
  ;
1200
- FormsAngular.prototype.doFindFunc = function (req, resource, cb) {
1250
+ doFindFunc(req, resource, cb) {
1201
1251
  if (resource.options.findFunc) {
1202
1252
  resource.options.findFunc(req, cb);
1203
1253
  }
1204
1254
  else {
1205
1255
  cb(null);
1206
1256
  }
1207
- };
1257
+ }
1208
1258
  ;
1209
- FormsAngular.prototype.filteredFind = function (resource, req, aggregationParam, findParam, projectParam, sortOrder, limit, skip, callback) {
1210
- var that = this;
1211
- var hiddenFields = this.generateHiddenFields(resource, false);
1212
- var stashAggregationResults;
1259
+ filteredFind(resource, req, aggregationParam, findParam, projectParam, sortOrder, limit, skip, callback) {
1260
+ const that = this;
1261
+ let hiddenFields = this.generateHiddenFields(resource, false);
1262
+ let stashAggregationResults;
1213
1263
  function doAggregation(queryObj, cb) {
1214
1264
  if (aggregationParam) {
1215
1265
  aggregationParam = that.sanitisePipeline(aggregationParam, hiddenFields, queryObj);
@@ -1239,7 +1289,7 @@ var FormsAngular = /** @class */ (function () {
1239
1289
  callback(null, []);
1240
1290
  }
1241
1291
  else {
1242
- var query = resource.model.find(queryObj);
1292
+ let query = resource.model.find(queryObj);
1243
1293
  if (idArray.length > 0) {
1244
1294
  query = query.where('_id').in(idArray);
1245
1295
  }
@@ -1258,10 +1308,10 @@ var FormsAngular = /** @class */ (function () {
1258
1308
  }
1259
1309
  query.exec(function (err, docs) {
1260
1310
  if (!err && stashAggregationResults) {
1261
- docs.forEach(function (obj) {
1311
+ docs.forEach(obj => {
1262
1312
  // Add any fields from the aggregation results whose field name starts __ to the mongoose Document
1263
- var aggObj = stashAggregationResults.find(function (a) { return a._id.toString() === obj._id.toString(); });
1264
- Object.keys(aggObj).forEach(function (k) {
1313
+ let aggObj = stashAggregationResults.find(a => a._id.toString() === obj._id.toString());
1314
+ Object.keys(aggObj).forEach(k => {
1265
1315
  if (k.slice(0, 2) === '__') {
1266
1316
  obj[k] = aggObj[k];
1267
1317
  }
@@ -1274,9 +1324,9 @@ var FormsAngular = /** @class */ (function () {
1274
1324
  });
1275
1325
  }
1276
1326
  });
1277
- };
1327
+ }
1278
1328
  ;
1279
- FormsAngular.prototype.collectionPost = function () {
1329
+ collectionPost() {
1280
1330
  return _.bind(function (req, res, next) {
1281
1331
  this.processCollection(req);
1282
1332
  if (!req.resource) {
@@ -1286,36 +1336,36 @@ var FormsAngular = /** @class */ (function () {
1286
1336
  if (!req.body) {
1287
1337
  throw new Error('Nothing submitted.');
1288
1338
  }
1289
- var cleansedBody = this.cleanseRequest(req);
1339
+ let cleansedBody = this.cleanseRequest(req);
1290
1340
  req.doc = new req.resource.model(cleansedBody);
1291
1341
  this.saveAndRespond(req, res);
1292
1342
  }, this);
1293
- };
1343
+ }
1294
1344
  ;
1295
1345
  /**
1296
1346
  * Generate an object of fields to not expose
1297
1347
  **/
1298
- FormsAngular.prototype.generateHiddenFields = function (resource, state) {
1299
- var hiddenFields = {};
1348
+ generateHiddenFields(resource, state) {
1349
+ let hiddenFields = {};
1300
1350
  if (resource.options['hide'] !== undefined) {
1301
1351
  resource.options.hide.forEach(function (dt) {
1302
1352
  hiddenFields[dt] = state;
1303
1353
  });
1304
1354
  }
1305
1355
  return hiddenFields;
1306
- };
1356
+ }
1307
1357
  ;
1308
1358
  /** Sec issue
1309
1359
  * Cleanse incoming data to avoid overwrite and POST request forgery
1310
1360
  * (name may seem weird but it was in French, so it is some small improvement!)
1311
1361
  */
1312
- FormsAngular.prototype.cleanseRequest = function (req) {
1313
- var reqData = req.body, resource = req.resource;
1362
+ cleanseRequest(req) {
1363
+ let reqData = req.body, resource = req.resource;
1314
1364
  delete reqData.__v; // Don't mess with Mongoose internal field (https://github.com/LearnBoost/mongoose/issues/1933)
1315
1365
  if (typeof resource.options['hide'] === 'undefined') {
1316
1366
  return reqData;
1317
1367
  }
1318
- var hiddenFields = resource.options.hide;
1368
+ let hiddenFields = resource.options.hide;
1319
1369
  _.each(reqData, function (num, key) {
1320
1370
  _.each(hiddenFields, function (fi) {
1321
1371
  if (fi === key) {
@@ -1324,20 +1374,19 @@ var FormsAngular = /** @class */ (function () {
1324
1374
  });
1325
1375
  });
1326
1376
  return reqData;
1327
- };
1377
+ }
1328
1378
  ;
1329
- FormsAngular.prototype.generateQueryForEntity = function (req, resource, id, cb) {
1330
- var that = this;
1331
- var hiddenFields = this.generateHiddenFields(resource, false);
1379
+ generateQueryForEntity(req, resource, id, cb) {
1380
+ let that = this;
1381
+ let hiddenFields = this.generateHiddenFields(resource, false);
1332
1382
  hiddenFields.__v = false;
1333
1383
  that.doFindFunc(req, resource, function (err, queryObj) {
1334
- var _a;
1335
1384
  if (err) {
1336
1385
  cb(err);
1337
1386
  }
1338
1387
  else {
1339
- var idSel = { _id: id };
1340
- var crit = void 0;
1388
+ const idSel = { _id: id };
1389
+ let crit;
1341
1390
  if (queryObj) {
1342
1391
  if (queryObj._id) {
1343
1392
  crit = { $and: [idSel, { _id: queryObj._id }] };
@@ -1353,16 +1402,16 @@ var FormsAngular = /** @class */ (function () {
1353
1402
  else {
1354
1403
  crit = idSel;
1355
1404
  }
1356
- cb(null, resource.model.findOne(crit).select(that.generateProjection(hiddenFields, (_a = req.query) === null || _a === void 0 ? void 0 : _a.p)));
1405
+ cb(null, resource.model.findOne(crit).select(that.generateProjection(hiddenFields, req.query?.p)));
1357
1406
  }
1358
1407
  });
1359
- };
1408
+ }
1360
1409
  ;
1361
1410
  /*
1362
1411
  * Entity request goes here first
1363
1412
  * It retrieves the resource
1364
1413
  */
1365
- FormsAngular.prototype.processEntity = function (req, res, next) {
1414
+ processEntity(req, res, next) {
1366
1415
  if (!(req.resource = this.getResource(req.params.resourceName))) {
1367
1416
  next();
1368
1417
  return;
@@ -1393,14 +1442,14 @@ var FormsAngular = /** @class */ (function () {
1393
1442
  });
1394
1443
  }
1395
1444
  });
1396
- };
1445
+ }
1397
1446
  ;
1398
1447
  /**
1399
1448
  * Gets a single entity
1400
1449
  *
1401
1450
  * @return {Function} The function to use as route
1402
1451
  */
1403
- FormsAngular.prototype.entityGet = function () {
1452
+ entityGet() {
1404
1453
  return _.bind(function (req, res, next) {
1405
1454
  this.processEntity(req, res, function () {
1406
1455
  if (!req.resource) {
@@ -1416,10 +1465,10 @@ var FormsAngular = /** @class */ (function () {
1416
1465
  }
1417
1466
  });
1418
1467
  }, this);
1419
- };
1468
+ }
1420
1469
  ;
1421
- FormsAngular.prototype.replaceHiddenFields = function (record, data) {
1422
- var self = this;
1470
+ replaceHiddenFields(record, data) {
1471
+ const self = this;
1423
1472
  if (record) {
1424
1473
  record._replacingHiddenFields = true;
1425
1474
  _.each(data, function (value, name) {
@@ -1432,11 +1481,11 @@ var FormsAngular = /** @class */ (function () {
1432
1481
  });
1433
1482
  delete record._replacingHiddenFields;
1434
1483
  }
1435
- };
1484
+ }
1436
1485
  ;
1437
- FormsAngular.prototype.entityPut = function () {
1486
+ entityPut() {
1438
1487
  return _.bind(function (req, res, next) {
1439
- var that = this;
1488
+ const that = this;
1440
1489
  this.processEntity(req, res, function () {
1441
1490
  if (!req.resource) {
1442
1491
  next();
@@ -1445,19 +1494,19 @@ var FormsAngular = /** @class */ (function () {
1445
1494
  if (!req.body) {
1446
1495
  throw new Error('Nothing submitted.');
1447
1496
  }
1448
- var cleansedBody = that.cleanseRequest(req);
1497
+ let cleansedBody = that.cleanseRequest(req);
1449
1498
  // Merge
1450
- for (var prop in cleansedBody) {
1499
+ for (let prop in cleansedBody) {
1451
1500
  if (cleansedBody.hasOwnProperty(prop)) {
1452
1501
  req.doc.set(prop, cleansedBody[prop] === '' ? undefined : cleansedBody[prop]);
1453
1502
  }
1454
1503
  }
1455
1504
  if (req.resource.options.hide !== undefined) {
1456
- var hiddenFields_2 = that.generateHiddenFields(req.resource, true);
1457
- hiddenFields_2._id = false;
1458
- req.resource.model.findById(req.doc._id, hiddenFields_2, { lean: true }, function (err, data) {
1505
+ let hiddenFields = that.generateHiddenFields(req.resource, true);
1506
+ hiddenFields._id = false;
1507
+ req.resource.model.findById(req.doc._id, hiddenFields, { lean: true }, function (err, data) {
1459
1508
  that.replaceHiddenFields(req.doc, data);
1460
- that.saveAndRespond(req, res, hiddenFields_2);
1509
+ that.saveAndRespond(req, res, hiddenFields);
1461
1510
  });
1462
1511
  }
1463
1512
  else {
@@ -1465,187 +1514,313 @@ var FormsAngular = /** @class */ (function () {
1465
1514
  }
1466
1515
  });
1467
1516
  }, this);
1468
- };
1517
+ }
1469
1518
  ;
1470
- FormsAngular.prototype.entityDelete = function () {
1471
- var that = this;
1472
- return _.bind(function (req, res, next) {
1473
- return __awaiter(this, void 0, void 0, function () {
1474
- function generateDependencyList(resource) {
1475
- if (resource.options.dependents === undefined) {
1476
- resource.options.dependents = that.resources.reduce(function (acc, r) {
1477
- function searchPaths(schema, prefix) {
1478
- var fldList = [];
1479
- for (var fld in schema.paths) {
1480
- if (schema.paths.hasOwnProperty(fld)) {
1481
- var parts = fld.split('.');
1482
- var schemaType = schema.tree;
1483
- while (parts.length > 0) {
1484
- schemaType = schemaType[parts.shift()];
1519
+ entityDelete() {
1520
+ let that = this;
1521
+ return _.bind(async function (req, res, next) {
1522
+ function generateDependencyList(resource) {
1523
+ if (resource.options.dependents === undefined) {
1524
+ resource.options.dependents = that.resources.reduce(function (acc, r) {
1525
+ function searchPaths(schema, prefix) {
1526
+ var fldList = [];
1527
+ for (var fld in schema.paths) {
1528
+ if (schema.paths.hasOwnProperty(fld)) {
1529
+ var parts = fld.split('.');
1530
+ var schemaType = schema.tree;
1531
+ while (parts.length > 0) {
1532
+ schemaType = schemaType[parts.shift()];
1533
+ }
1534
+ if (schemaType.type) {
1535
+ if (schemaType.type.name === 'ObjectId' && schemaType.ref === resource.resourceName) {
1536
+ fldList.push(prefix + fld);
1485
1537
  }
1486
- if (schemaType.type) {
1487
- if (schemaType.type.name === 'ObjectId' && schemaType.ref === resource.resourceName) {
1488
- fldList.push(prefix + fld);
1489
- }
1490
- else if (_.isArray(schemaType.type)) {
1491
- schemaType.type.forEach(function (t) {
1492
- searchPaths(t, prefix + fld + '.');
1493
- });
1494
- }
1538
+ else if (_.isArray(schemaType.type)) {
1539
+ schemaType.type.forEach(function (t) {
1540
+ searchPaths(t, prefix + fld + '.');
1541
+ });
1495
1542
  }
1496
1543
  }
1497
1544
  }
1498
- if (fldList.length > 0) {
1499
- acc.push({ resource: r, keys: fldList });
1500
- }
1501
1545
  }
1502
- searchPaths(r.model.schema, '');
1503
- return acc;
1504
- }, []);
1505
- for (var pluginName in that.options.plugins) {
1506
- var thisPlugin = that.options.plugins[pluginName];
1507
- if (thisPlugin.dependencyChecks && thisPlugin.dependencyChecks[resource.resourceName]) {
1508
- resource.options.dependents = resource.options.dependents.concat(thisPlugin.dependencyChecks[resource.resourceName]);
1546
+ if (fldList.length > 0) {
1547
+ acc.push({ resource: r, keys: fldList });
1509
1548
  }
1510
1549
  }
1550
+ searchPaths(r.model.schema, '');
1551
+ return acc;
1552
+ }, []);
1553
+ for (let pluginName in that.options.plugins) {
1554
+ let thisPlugin = that.options.plugins[pluginName];
1555
+ if (thisPlugin.dependencyChecks && thisPlugin.dependencyChecks[resource.resourceName]) {
1556
+ resource.options.dependents = resource.options.dependents.concat(thisPlugin.dependencyChecks[resource.resourceName]);
1557
+ }
1511
1558
  }
1512
1559
  }
1513
- function removeDoc(doc, resource) {
1514
- return __awaiter(this, void 0, void 0, function () {
1515
- var promises_1;
1516
- return __generator(this, function (_a) {
1517
- switch (resource.options.handleRemove) {
1518
- case 'allow':
1519
- // old behaviour - no attempt to maintain data integrity
1520
- return [2 /*return*/, doc.remove()];
1521
- case 'cascade':
1522
- generateDependencyList(resource);
1523
- res.status(400).send('"cascade" option not yet supported');
1524
- break;
1525
- default:
1526
- generateDependencyList(resource);
1527
- promises_1 = [];
1528
- resource.options.dependents.forEach(function (collection) {
1529
- collection.keys.forEach(function (key) {
1530
- var _a;
1531
- promises_1.push({
1532
- p: collection.resource.model.find((_a = {}, _a[key] = doc._id, _a)).limit(1).exec(),
1533
- collection: collection,
1534
- key: key
1535
- });
1536
- });
1537
- });
1538
- return [2 /*return*/, Promise.all(promises_1.map(function (p) { return p.p; }))
1539
- .then(function (results) {
1540
- results.forEach(function (r, i) {
1541
- if (r.length > 0) {
1542
- throw new ForeignKeyError(resource.resourceName, promises_1[i].collection.resource.resourceName, promises_1[i].key, r[0]._id);
1543
- }
1544
- });
1545
- return doc.remove();
1546
- })];
1547
- }
1548
- return [2 /*return*/];
1549
- });
1550
- });
1551
- }
1552
- function runDeletion(doc, resource) {
1553
- return __awaiter(this, void 0, void 0, function () {
1554
- return __generator(this, function (_a) {
1555
- return [2 /*return*/, new Promise(function (resolve) {
1556
- if (resource.options.onRemove) {
1557
- resource.options.onRemove(doc, req, function (err) {
1558
- return __awaiter(this, void 0, void 0, function () {
1559
- return __generator(this, function (_a) {
1560
- if (err) {
1561
- throw err;
1562
- }
1563
- resolve(removeDoc(doc, resource));
1564
- return [2 /*return*/];
1565
- });
1566
- });
1567
- });
1568
- }
1569
- else {
1570
- resolve(removeDoc(doc, resource));
1571
- }
1572
- })];
1560
+ }
1561
+ async function removeDoc(doc, resource) {
1562
+ switch (resource.options.handleRemove) {
1563
+ case 'allow':
1564
+ // old behaviour - no attempt to maintain data integrity
1565
+ return doc.remove();
1566
+ case 'cascade':
1567
+ generateDependencyList(resource);
1568
+ res.status(400).send('"cascade" option not yet supported');
1569
+ break;
1570
+ default:
1571
+ generateDependencyList(resource);
1572
+ let promises = [];
1573
+ resource.options.dependents.forEach(collection => {
1574
+ collection.keys.forEach(key => {
1575
+ promises.push({
1576
+ p: collection.resource.model.find({ [key]: doc._id }).limit(1).exec(),
1577
+ collection,
1578
+ key
1579
+ });
1580
+ });
1573
1581
  });
1574
- });
1575
- }
1576
- return __generator(this, function (_a) {
1577
- this.processEntity(req, res, function () {
1578
- return __awaiter(this, void 0, void 0, function () {
1579
- var doc, e_1;
1580
- return __generator(this, function (_a) {
1581
- switch (_a.label) {
1582
- case 0:
1583
- if (!req.resource) {
1584
- next();
1585
- return [2 /*return*/];
1586
- }
1587
- doc = req.doc;
1588
- _a.label = 1;
1589
- case 1:
1590
- _a.trys.push([1, 3, , 4]);
1591
- return [4 /*yield*/, runDeletion(doc, req.resource)];
1592
- case 2:
1593
- void (_a.sent());
1594
- res.status(200).send();
1595
- return [3 /*break*/, 4];
1596
- case 3:
1597
- e_1 = _a.sent();
1598
- if (e_1 instanceof ForeignKeyError) {
1599
- res.status(400).send(e_1.message);
1600
- }
1601
- else {
1602
- res.status(500).send(e_1.message);
1603
- }
1604
- return [3 /*break*/, 4];
1605
- case 4: return [2 /*return*/];
1582
+ return Promise.all(promises.map(p => p.p))
1583
+ .then((results) => {
1584
+ results.forEach((r, i) => {
1585
+ if (r.length > 0) {
1586
+ throw new ForeignKeyError(resource.resourceName, promises[i].collection.resource.resourceName, promises[i].key, r[0]._id);
1606
1587
  }
1607
1588
  });
1589
+ return doc.remove();
1608
1590
  });
1609
- });
1610
- return [2 /*return*/];
1591
+ }
1592
+ }
1593
+ async function runDeletion(doc, resource) {
1594
+ return new Promise((resolve) => {
1595
+ if (resource.options.onRemove) {
1596
+ resource.options.onRemove(doc, req, async function (err) {
1597
+ if (err) {
1598
+ throw err;
1599
+ }
1600
+ resolve(removeDoc(doc, resource));
1601
+ });
1602
+ }
1603
+ else {
1604
+ resolve(removeDoc(doc, resource));
1605
+ }
1611
1606
  });
1607
+ }
1608
+ this.processEntity(req, res, async function () {
1609
+ if (!req.resource) {
1610
+ next();
1611
+ return;
1612
+ }
1613
+ let doc = req.doc;
1614
+ try {
1615
+ void await runDeletion(doc, req.resource);
1616
+ res.status(200).send();
1617
+ }
1618
+ catch (e) {
1619
+ if (e instanceof ForeignKeyError) {
1620
+ res.status(400).send(e.message);
1621
+ }
1622
+ else {
1623
+ res.status(500).send(e.message);
1624
+ }
1625
+ }
1612
1626
  });
1613
1627
  }, this);
1614
- };
1628
+ }
1615
1629
  ;
1616
- FormsAngular.prototype.entityList = function () {
1630
+ entityList() {
1617
1631
  return _.bind(function (req, res, next) {
1618
- var that = this;
1632
+ const that = this;
1619
1633
  this.processEntity(req, res, function () {
1620
1634
  if (!req.resource) {
1621
1635
  return next();
1622
1636
  }
1623
- that.getListFields(req.resource, req.doc, function (err, display) {
1624
- if (err) {
1625
- return res.status(500).send(err);
1637
+ const returnRawParam = req.query?.returnRaw ? !!JSON.parse(req.query.returnRaw) : false;
1638
+ if (returnRawParam) {
1639
+ const result = { _id: req.doc._id };
1640
+ for (const field of req.resource.options.listFields) {
1641
+ result[field.field] = req.doc[field.field];
1642
+ }
1643
+ return res.send(result);
1644
+ }
1645
+ else {
1646
+ that.getListFields(req.resource, req.doc, function (err, display) {
1647
+ if (err) {
1648
+ return res.status(500).send(err);
1649
+ }
1650
+ else {
1651
+ return res.send({ list: display });
1652
+ }
1653
+ });
1654
+ }
1655
+ });
1656
+ }, this);
1657
+ }
1658
+ ;
1659
+ // To disambiguate the contents of items - assumed to be the results of a single resource lookup or search -
1660
+ // pass the result of this function as the second argument to the disambiguate() function.
1661
+ // equalityProps should identify the property(s) of the items that must ALL be equal for two items to
1662
+ // be considered ambiguous.
1663
+ // disambiguationResourceName should identify the resource whose list field(s) should be used to generate
1664
+ // the disambiguation text for ambiguous results later.
1665
+ buildSingleResourceAmbiguousRecordStore(items, equalityProps, disambiguationResourceName) {
1666
+ const ambiguousResults = [];
1667
+ for (let i = 0; i < items.length - 1; i++) {
1668
+ for (let j = i + 1; j < items.length; j++) {
1669
+ if (!equalityProps.some((p) => items[i][p] !== items[j][p])) {
1670
+ if (!ambiguousResults.includes(items[i])) {
1671
+ ambiguousResults.push(items[i]);
1672
+ }
1673
+ if (!ambiguousResults.includes(items[j])) {
1674
+ ambiguousResults.push(items[j]);
1675
+ }
1676
+ }
1677
+ }
1678
+ }
1679
+ return { [disambiguationResourceName]: ambiguousResults };
1680
+ }
1681
+ // An alternative to buildSingleResourceAmbiguousRecordStore() for use when disambiguating the results of a
1682
+ // multi-resource lookup or search. In this case, all items need to include the name of the resource that
1683
+ // will be used (when necessary) to yield their disambiguation text later. The property of items that holds
1684
+ // that resource name should be identified by the disambiguationResourceNameProp parameter.
1685
+ // The scary-looking templating used here ensures that the items really do all have an (optional) string
1686
+ // property with the name identified by disambiguationResourceNameProp. For the avoidance of doubt, "prop"
1687
+ // here could be anything - "foo in f" would achieve the same result.
1688
+ buildMultiResourceAmbiguousRecordStore(items, equalityProps, disambiguationResourceNameProp) {
1689
+ const store = {};
1690
+ for (let i = 0; i < items.length - 1; i++) {
1691
+ for (let j = i + 1; j < items.length; j++) {
1692
+ if (items[i][disambiguationResourceNameProp] &&
1693
+ items[i][disambiguationResourceNameProp] === items[j][disambiguationResourceNameProp] &&
1694
+ !equalityProps.some((p) => items[i][p] !== items[j][p])) {
1695
+ if (!store[items[i][disambiguationResourceNameProp]]) {
1696
+ store[items[i][disambiguationResourceNameProp]] = [];
1697
+ }
1698
+ if (!store[items[i][disambiguationResourceNameProp]].includes(items[i])) {
1699
+ store[items[i][disambiguationResourceNameProp]].push(items[i]);
1700
+ }
1701
+ if (!store[items[i][disambiguationResourceNameProp]].includes(items[j])) {
1702
+ store[items[i][disambiguationResourceNameProp]].push(items[j]);
1703
+ }
1704
+ }
1705
+ }
1706
+ }
1707
+ return store;
1708
+ }
1709
+ // return just the id and list fields for all of the records from req.resource.
1710
+ // list fields are those whose schema entry has a value for the "list" attribute (except where this is { ref: true })
1711
+ // if the resource has no explicit list fields identified, the first string field will be returned. if the resource
1712
+ // doesn't have any string fields either, the first (non-id) field will be returned.
1713
+ // usually, we will respond with an array of ILookupItem objects, where the .text property of those objects is the concatenation
1714
+ // of all of the document's list fields (space-seperated).
1715
+ // to request the documents without this transformation applied, include "c=true" in the query string.
1716
+ // the query string can also be used to filter and order the response, by providing values for "f" (find), "l" (limit),
1717
+ // "s" (skip) and/or "o" (order).
1718
+ // results will be disambiguated if req.resource includes disambiguation parameters in its resource options.
1719
+ // where c=true, the disambiguation will be added as a suffix to the .text property of the returned ILookupItem objects.
1720
+ // otherwise, if the resource has just one list field, the disambiguation will be appended to the values of that field, and if
1721
+ // it has multiple list fields, it will be returned as an additional property of the returned (untransformed) objects
1722
+ internalEntityListAll(req, callback) {
1723
+ const projection = this.generateListFieldProjection(req.resource);
1724
+ const listFields = Object.keys(projection);
1725
+ const aggregationParam = req.query.a ? JSON.parse(req.query.a) : null;
1726
+ const findParam = req.query.f ? JSON.parse(req.query.f) : {};
1727
+ const limitParam = req.query.l ? JSON.parse(req.query.l) : 0;
1728
+ const skipParam = req.query.s ? JSON.parse(req.query.s) : 0;
1729
+ const orderParam = req.query.o ? JSON.parse(req.query.o) : req.resource.options.listOrder;
1730
+ const concatenateParam = req.query.c ? JSON.parse(req.query.c) : true;
1731
+ const resOpts = req.resource.options;
1732
+ let disambiguationField;
1733
+ let disambiguationResourceName;
1734
+ if (resOpts?.disambiguation) {
1735
+ disambiguationField = resOpts.disambiguation.field;
1736
+ if (disambiguationField) {
1737
+ projection[disambiguationField] = 1;
1738
+ disambiguationResourceName = resOpts.disambiguation.resource;
1739
+ }
1740
+ }
1741
+ const that = this;
1742
+ this.filteredFind(req.resource, req, aggregationParam, findParam, projection, orderParam, limitParam, skipParam, function (err, docs) {
1743
+ if (err) {
1744
+ return callback(err);
1745
+ }
1746
+ else {
1747
+ docs = docs.map((d) => d.toObject());
1748
+ if (concatenateParam) {
1749
+ const transformed = docs.map((doc) => {
1750
+ let text = "";
1751
+ for (const field of listFields) {
1752
+ if (doc[field]) {
1753
+ if (text !== "") {
1754
+ text += " ";
1755
+ }
1756
+ text += doc[field];
1757
+ }
1758
+ }
1759
+ const disambiguationId = disambiguationField ? doc[disambiguationField] : undefined;
1760
+ return { id: doc._id, text, disambiguationId };
1761
+ });
1762
+ if (disambiguationResourceName) {
1763
+ that.disambiguate(transformed, that.buildSingleResourceAmbiguousRecordStore(transformed, ["text"], disambiguationResourceName), "disambiguationId", (item, disambiguationText) => {
1764
+ item.text += ` (${disambiguationText})`;
1765
+ }, (err) => {
1766
+ callback(err, transformed);
1767
+ });
1626
1768
  }
1627
1769
  else {
1628
- return res.send({ list: display });
1770
+ return callback(null, transformed);
1629
1771
  }
1630
- });
1772
+ }
1773
+ else {
1774
+ if (disambiguationResourceName) {
1775
+ that.disambiguate(docs, that.buildSingleResourceAmbiguousRecordStore(docs, listFields, disambiguationResourceName), disambiguationField, (item, disambiguationText) => {
1776
+ if (listFields.length === 1) {
1777
+ item[listFields[0]] += ` (${disambiguationText})`;
1778
+ }
1779
+ else {
1780
+ // store the text against hard-coded property name "disambiguation", rather than (say) using
1781
+ // item[disambiguationResourceName], because if disambiguationResourceName === disambiguationField,
1782
+ // that value would end up being deleted again when the this.disambiguate() call (which we have
1783
+ // been called from) does its final tidy-up and deletes [disambiguationField] from all of the items in docs
1784
+ item.disambiguation = disambiguationText;
1785
+ }
1786
+ }, (err) => {
1787
+ callback(err, docs);
1788
+ });
1789
+ }
1790
+ else {
1791
+ return callback(null, docs);
1792
+ }
1793
+ }
1794
+ }
1795
+ });
1796
+ }
1797
+ ;
1798
+ entityListAll() {
1799
+ return _.bind(function (req, res, next) {
1800
+ if (!(req.resource = this.getResource(req.params.resourceName))) {
1801
+ return next();
1802
+ }
1803
+ this.internalEntityListAll(req, function (err, resultsObject) {
1804
+ if (err) {
1805
+ res.status(400, err);
1806
+ }
1807
+ else {
1808
+ res.send(resultsObject);
1809
+ }
1631
1810
  });
1632
1811
  }, this);
1633
- };
1812
+ }
1634
1813
  ;
1635
- FormsAngular.prototype.extractTimestampFromMongoID = function (record) {
1636
- var timestamp = record.toString().substring(0, 8);
1814
+ extractTimestampFromMongoID(record) {
1815
+ let timestamp = record.toString().substring(0, 8);
1637
1816
  return new Date(parseInt(timestamp, 16) * 1000);
1638
- };
1639
- return FormsAngular;
1640
- }());
1817
+ }
1818
+ }
1641
1819
  exports.FormsAngular = FormsAngular;
1642
- var ForeignKeyError = /** @class */ (function (_super) {
1643
- __extends(ForeignKeyError, _super);
1644
- function ForeignKeyError(resourceName, foreignKeyOnResource, foreignItem, id) {
1645
- var _this = _super.call(this, "Cannot delete this ".concat(resourceName, ", as it is the ").concat(foreignItem, " on ").concat(foreignKeyOnResource, " ").concat(id)) || this;
1646
- _this.name = "ForeignKeyError";
1647
- _this.stack = new global.Error('').stack;
1648
- return _this;
1820
+ class ForeignKeyError extends global.Error {
1821
+ constructor(resourceName, foreignKeyOnResource, foreignItem, id) {
1822
+ super(`Cannot delete this ${resourceName}, as it is the ${foreignItem} on ${foreignKeyOnResource} ${id}`);
1823
+ this.name = "ForeignKeyError";
1824
+ this.stack = new global.Error('').stack;
1649
1825
  }
1650
- return ForeignKeyError;
1651
- }(global.Error));
1826
+ }