@onehat/data 1.23.2 → 1.23.5

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.
@@ -942,6 +942,7 @@ describe('Repository Base', function() {
942
942
 
943
943
  describe('deleting', function() {
944
944
 
945
+
945
946
  it('clear', function() {
946
947
  this.repository.clear();
947
948
  expect(this.repository.entities.length).to.be.eq(0);
@@ -967,6 +968,21 @@ describe('Repository Base', function() {
967
968
  expect(this.repository.entities.length).to.be.eq(5);
968
969
  });
969
970
 
971
+ it('removeById', function() {
972
+ const entity = this.repository.entities[0];
973
+ const id = entity.id;
974
+ this.repository.removeById(id);
975
+ expect(this.repository.entities.includes(entity)).to.be.false;
976
+ });
977
+
978
+ it('removeDestroyedEntities', function() {
979
+ const entity = this.repository.entities[0];
980
+ expect(this.repository.entities.includes(entity)).to.be.true;
981
+ entity.destroy();
982
+ this.repository.removeDestroyedEntities();
983
+ expect(this.repository.entities.includes(entity)).to.be.false;
984
+ });
985
+
970
986
 
971
987
  // I'm going to skip these, as they're just combinations of other, tested functions
972
988
  // deleteByIx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.23.2",
3
+ "version": "1.23.5",
4
4
  "description": "JS data modeling package with adapters for many storage mediums.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -225,6 +225,7 @@ class AjaxRepository extends Repository {
225
225
  * @param {string} name - Param name to set.
226
226
  * @param {any} value - Param value to set.
227
227
  * @param {boolean} isBaseParam - Whether param is a base param (to be sent on every request).
228
+ * @returns {boolean} - Returns true if the param was changed, false otherwise.
228
229
  */
229
230
  setParam(name, value, isBaseParam = false) {
230
231
  const
@@ -235,25 +236,45 @@ class AjaxRepository extends Repository {
235
236
  if (matches) { // name has array notation like 'conditions[username]'
236
237
  const
237
238
  first = matches[1],
238
- second = matches[2];
239
- if (paramsToChange && !paramsToChange.hasOwnProperty(first)) {
240
- paramsToChange[first] = {};
241
- }
242
- if (_.isNil(value) && paramsToChange[first] && paramsToChange[first].hasOwnProperty(second)) {
239
+ second = matches[2],
240
+ hasFirst = paramsToChange && paramsToChange.hasOwnProperty(first),
241
+ hasSecond = hasFirst && paramsToChange[first] && paramsToChange[first].hasOwnProperty(second);
242
+
243
+ if (_.isNil(value)) {
244
+ if (!hasSecond) {
245
+ return false;
246
+ }
243
247
  delete paramsToChange[first][second];
244
248
  if (_.isEmpty(paramsToChange[first])) {
245
249
  delete paramsToChange[first];
246
250
  }
247
- return;
251
+ return true;
252
+ }
253
+
254
+ if (!hasFirst) {
255
+ paramsToChange[first] = {};
256
+ }
257
+
258
+ if (hasSecond && _.isEqual(paramsToChange[first][second], value)) {
259
+ return false;
248
260
  }
249
261
  paramsToChange[first][second] = value;
250
- return;
262
+ return true;
251
263
  }
252
- if (_.isNil(value) && paramsToChange && paramsToChange.hasOwnProperty(name)) {
264
+ if (_.isNil(value)) {
265
+ if (!paramsToChange || !paramsToChange.hasOwnProperty(name)) {
266
+ return false;
267
+ }
253
268
  delete paramsToChange[name];
254
- return;
269
+ return true;
270
+ }
271
+
272
+ if (paramsToChange && paramsToChange.hasOwnProperty(name) && _.isEqual(paramsToChange[name], value)) {
273
+ return false;
255
274
  }
275
+
256
276
  paramsToChange[name] = value;
277
+ return true;
257
278
  }
258
279
 
259
280
  /**
@@ -261,6 +282,7 @@ class AjaxRepository extends Repository {
261
282
  * Sets a single query param.
262
283
  * @param {string} name - Param name to set.
263
284
  * @param {boolean} isBaseParam - Whether param is a base param (to be sent on every request).
285
+ * @returns {boolean} - Returns true if the param was changed, false otherwise.
264
286
  */
265
287
  setValuelessParam(name, isBaseParam = false) {
266
288
  const
@@ -273,27 +295,37 @@ class AjaxRepository extends Repository {
273
295
  first = matches[1],
274
296
  second = matches[2];
275
297
  if (paramsToChange && !paramsToChange.hasOwnProperty(first)) {
276
- paramsToChange[first] = [];
298
+ paramsToChange[first] = {};
277
299
  }
278
- if (paramsToChange[first] && paramsToChange[first].hasOwnProperty(second)) {
279
- delete paramsToChange[first][second];
280
- return;
300
+ if (paramsToChange[first] && paramsToChange[first].hasOwnProperty(second) && paramsToChange[first][second] === true) {
301
+ return false;
281
302
  }
282
- paramsToChange[first][ paramsToChange[first].length ] = second;
283
- return;
303
+ paramsToChange[first][second] = true;
304
+ return true;
284
305
  }
285
- paramsToChange[paramsToChange.length] = second;
306
+
307
+ if (paramsToChange && paramsToChange.hasOwnProperty(name) && paramsToChange[name] === true) {
308
+ return false;
309
+ }
310
+
311
+ paramsToChange[name] = true;
312
+ return true;
286
313
  }
287
314
 
288
315
  /**
289
316
  * Sets query params
290
317
  * @param {object} params - Params to set. Key is parameter name, value is parameter value
318
+ * @returns {boolean} - Returns true if any param was changed, false otherwise.
291
319
  */
292
320
  setParams(params) {
293
321
  const oThis = this;
322
+ let isChanged = false;
294
323
  _.each(params, (value, name) => {
295
- oThis.setParam(name, value);
324
+ if (oThis.setParam(name, value)) {
325
+ isChanged = true;
326
+ }
296
327
  });
328
+ return isChanged;
297
329
  }
298
330
 
299
331
  /**
@@ -407,20 +439,26 @@ class AjaxRepository extends Repository {
407
439
  * Sets base query param
408
440
  * @param {string} name - Param name to set.
409
441
  * @param {any} value - Param value to set.
442
+ * @returns {boolean} - Returns true if the base param was changed, false otherwise.
410
443
  */
411
444
  setBaseParam(name, value) {
412
- this.setParam(name, value, true);
445
+ return this.setParam(name, value, true);
413
446
  }
414
447
 
415
448
  /**
416
449
  * Sets base query params. These params are sent on every request.
417
450
  * @param {object} params - Base params to set. Key is parameter name, value is parameter value
451
+ * @returns {boolean} - Returns true if any base param was changed, false otherwise.
418
452
  */
419
453
  setBaseParams(params) {
420
454
  const oThis = this;
455
+ let isChanged = false;
421
456
  _.each(params, (value, name) => {
422
- oThis.setBaseParam(name, value);
457
+ if (oThis.setBaseParam(name, value)) {
458
+ isChanged = true;
459
+ }
423
460
  });
461
+ return isChanged;
424
462
  }
425
463
 
426
464
  /**
@@ -442,39 +480,51 @@ class AjaxRepository extends Repository {
442
480
  * Sets sort and direction params.
443
481
  * Only one sorter is allowed with this Repository type.
444
482
  * Refreshes entities.
483
+ * @returns {boolean} - Returns true if the sort params were changed, false otherwise.
445
484
  */
446
485
  _onChangeSorters() {
447
486
  const sorter = this.sorters[0];
448
- this.setBaseParam(this.paramSort, sorter.name);
449
- this.setBaseParam(this.paramDirection, sorter.direction);
487
+ let isChanged = false;
488
+ isChanged = this.setBaseParam(this.paramSort, sorter.name) || isChanged;
489
+ isChanged = this.setBaseParam(this.paramDirection, sorter.direction) || isChanged;
450
490
 
451
491
  if (this.isLoaded && !this.eventsPaused) {
452
492
  return this.reload();
453
493
  }
494
+
495
+ return isChanged;
454
496
  }
455
497
 
456
498
  /**
457
499
  * Sets filter params.
458
500
  * Refreshes entities.
501
+ * @returns {boolean} - Returns true if the filter params were changed, false otherwise.
459
502
  */
460
503
  _onChangeFilters() {
461
504
  const oThis = this;
505
+ let isChanged = false;
462
506
  _.each(this.filters, (value, name) => {
463
- oThis.setParam(name, value);
507
+ if (oThis.setParam(name, value)) {
508
+ isChanged = true;
509
+ }
464
510
  });
465
511
 
466
512
  if (this.isLoaded && !this.eventsPaused) {
467
513
  return this.reload();
468
514
  }
515
+
516
+ return isChanged;
469
517
  }
470
518
 
471
519
  /**
472
520
  * Sets pagination params.
473
521
  * Refreshes entities.
522
+ * @returns {boolean} - Returns true if the pagination params were changed, false otherwise.
474
523
  */
475
524
  _onChangePagination() {
476
- this.setBaseParam(this.paramPageNum, this.isPaginated ? this.page : null);
477
- this.setBaseParam(this.paramPageSize, this.isPaginated ? this.pageSize : null);
525
+ let isChanged = false;
526
+ isChanged = this.setBaseParam(this.paramPageNum, this.isPaginated ? this.page : null) || isChanged;
527
+ isChanged = this.setBaseParam(this.paramPageSize, this.isPaginated ? this.pageSize : null) || isChanged;
478
528
 
479
529
  if (this.isLoaded && !this.eventsPaused) {
480
530
  const
@@ -495,6 +545,8 @@ class AjaxRepository extends Repository {
495
545
  return this.reload();
496
546
  }
497
547
  }
548
+
549
+ return isChanged;
498
550
  }
499
551
 
500
552
 
@@ -183,6 +183,7 @@ class OneBuildRepository extends AjaxRepository {
183
183
  * Sets "conditions" param.
184
184
  * OneBuild uses a single, multi-dimentional param for filtering.
185
185
  * Refreshes entities.
186
+ * @returns {boolean} - Returns true if the filter params were changed, false otherwise.
186
187
  */
187
188
  _onChangeFilters() {
188
189
  // Clear existing "conditions" params
@@ -193,11 +194,16 @@ class OneBuildRepository extends AjaxRepository {
193
194
  }
194
195
 
195
196
  const oThis = this;
197
+ let isChanged = false;
196
198
  _.each(this.filters, (filter, ix) => {
197
199
  if (_.includes(nonConditionFilters, filter.name)) {
198
- oThis.setParam(filter.name, filter.value);
200
+ if (oThis.setParam(filter.name, filter.value)) {
201
+ isChanged = true;
202
+ }
199
203
  } else {
200
- oThis.setParam('conditions[' + filter.name + ']', filter.value);
204
+ if (oThis.setParam('conditions[' + filter.name + ']', filter.value)) {
205
+ isChanged = true;
206
+ }
201
207
  }
202
208
  });
203
209
 
@@ -208,21 +214,25 @@ class OneBuildRepository extends AjaxRepository {
208
214
  return this.reload();
209
215
  }
210
216
  }
217
+
218
+ return isChanged;
211
219
  }
212
220
 
213
221
  /**
214
222
  * Sets "order" param.
215
223
  * OneBuild uses a single order param, rather than separate name & direction params.
216
224
  * Refreshes entities.
225
+ * @returns {boolean} - Returns true if the sort params were changed, false otherwise.
217
226
  */
218
227
  _onChangeSorters() {
219
228
  let sorterStrings = [];
229
+ let isChanged = false;
220
230
  _.each(this.sorters, (sorter) => {
221
231
  sorterStrings.push(sorter.name + ' ' + sorter.direction);
222
232
  });
223
233
 
224
234
  if (!_.isEmpty(sorterStrings)) {
225
- this.setBaseParam('order', sorterStrings.join(','));
235
+ isChanged = this.setBaseParam('order', sorterStrings.join(',')) || isChanged;
226
236
  }
227
237
 
228
238
  if (!this.eventsPaused) {
@@ -240,6 +250,8 @@ class OneBuildRepository extends AjaxRepository {
240
250
  this.emit('changeSorters');
241
251
  }
242
252
  }
253
+
254
+ return isChanged;
243
255
  }
244
256
 
245
257
  /**
@@ -418,33 +430,69 @@ class OneBuildRepository extends AjaxRepository {
418
430
  this.throwError('this.getSingleEntityFromServer is no longer valid. Repository has been destroyed.');
419
431
  return;
420
432
  }
433
+ return this.getMultipleEntitiesFromServer([id])
434
+ .then(entities => {
435
+ if (!entities || entities.length === 0) {
436
+ return null;
437
+ }
438
+ return entities[0];
439
+ });
440
+ }
441
+
442
+ async getMultipleEntitiesFromServer(ids) {
443
+ if (this.isDestroyed) {
444
+ this.throwError('this.getMultipleEntitiesFromServer is no longer valid. Repository has been destroyed.');
445
+ return;
446
+ }
421
447
  if (!this.api.get) {
422
448
  this.throwError('No "get" api endpoint defined.');
423
449
  return;
424
450
  }
425
451
 
426
- if (!id) {
452
+ if (!ids || !Array.isArray(ids) || ids.length === 0) {
427
453
  return null;
428
454
  }
429
455
 
456
+ const entitiesById = new Map();
457
+ _.each(this.entities, (entity) => {
458
+ entitiesById.set(entity.id, entity);
459
+ });
460
+
461
+ const idsToFetch = [];
462
+ const idsToFetchSet = new Set();
463
+ _.each(ids, (id) => {
464
+ if (entitiesById.has(id)) {
465
+ return;
466
+ }
467
+ if (idsToFetchSet.has(id)) {
468
+ return;
469
+ }
470
+ idsToFetchSet.add(id);
471
+ idsToFetch.push(id);
472
+ });
473
+
474
+ if (!idsToFetch.length) {
475
+ return [];
476
+ }
477
+
430
478
  this.markLoading();
431
479
 
432
480
  const idPropertyName = this.getSchema().model.idProperty;
433
481
  const params = {};
434
- params['conditions[' + idPropertyName + ']'] = id;
482
+ params['conditions[' + idPropertyName + ' IN]'] = idsToFetch;
435
483
 
436
484
  const
437
485
  url = this.getModel() + '/' + this.api.get,
438
486
  data = _.merge(params, this._baseParams);
439
487
 
440
488
  if (this.debugMode) {
441
- console.log('getSingleEntityFromServer', data);
489
+ console.log('getMultipleEntitiesFromServer', data);
442
490
  }
443
491
 
444
492
  return this._send(this.methods.get, url, data)
445
493
  .then(result => {
446
494
  if (this.debugMode) {
447
- console.log('Response for getSingleEntityFromServer for ' + this.name, result);
495
+ console.log('Response for getMultipleEntitiesFromServer for ' + this.name, result);
448
496
  }
449
497
 
450
498
  if (this.isDestroyed) {
@@ -461,18 +509,48 @@ class OneBuildRepository extends AjaxRepository {
461
509
  } = this._processServerResponse(result);
462
510
 
463
511
  if (!root[0]) {
464
- return null;
512
+ return [];
513
+ }
514
+
515
+ const newEntities = [];
516
+ _.each(root, (item) => {
517
+ const entity = this.createStandaloneEntity(item);
518
+
519
+ if (entitiesById.has(entity.id)) {
520
+ // If an entity with the same ID already exists, we don't want to add a duplicate.
521
+ entity.destroy();
522
+ return;
523
+ }
524
+
525
+ entity.isPersisted = true;
526
+ entity.isRemotePhantom = false;
527
+ this._relayEntityEvents(entity);
528
+ entitiesById.set(entity.id, entity);
529
+ newEntities.push(entity);
530
+ });
531
+
532
+ // Add any new entities to the repository's main entities array.
533
+ if (newEntities.length) {
534
+ this.entities = this.entities.concat(newEntities);
465
535
  }
536
+ if (this.isTree) {
537
+ this.assembleTreeNodes();
538
+ // pagination vars shouldn't be affected by the loading of additional entities
539
+ }
540
+
541
+ // I don't want to do the following because there might be event handler
542
+ // side-effects of loading these!
543
+ // this.markLoaded();
544
+ // this.rehash();
545
+ // this.emit('load', this);
466
546
 
467
- const entity = this.createStandaloneEntity(root[0]);
468
- entity.isPersisted = true;
469
- entity.isRemotePhantom = false;
470
- return entity;
547
+ return newEntities;
471
548
  })
472
549
  .finally(() => {
473
550
  this.markLoading(false);
474
551
  });
475
552
 
553
+
476
554
  }
477
555
 
478
556
  async getLastModifiedDate() {
@@ -2074,6 +2074,24 @@ export default class Repository extends EventEmitter {
2074
2074
  }
2075
2075
  }
2076
2076
 
2077
+ /**
2078
+ * Removes an Entity from the current page by ID
2079
+ */
2080
+ removeById(id) {
2081
+ const entity = this.getById(id);
2082
+ if (entity) {
2083
+ this.removeEntity(entity);
2084
+ }
2085
+ }
2086
+
2087
+ /**
2088
+ * Removes all entities that have been destroyed from the current page.
2089
+ * This is an optimized version of removeById, specifically targeting destroyed entities.
2090
+ */
2091
+ removeDestroyedEntities() {
2092
+ this.entities = _.filter(this.entities, (entity) => !entity.isDestroyed);
2093
+ }
2094
+
2077
2095
  /**
2078
2096
  * Deletes a single Entity by its index (zero-indexed) on the current page
2079
2097
  * @param {integer} ix - Index