@onehat/data 1.6.4 → 1.6.8

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.
@@ -25,6 +25,13 @@ const GroupsUsers = {
25
25
  { name: 'users__last_login', mapping: 'user.last_login', type: 'datetime' },
26
26
  ],
27
27
 
28
+ associations: {
29
+ hasOne: [
30
+ 'Groups',
31
+ 'Users',
32
+ ],
33
+ },
34
+
28
35
  },
29
36
 
30
37
  entity: {
@@ -35,7 +42,10 @@ const GroupsUsers = {
35
42
  },
36
43
  },
37
44
 
38
- repository: 'onebuild',
45
+ repository: {
46
+ type: 'onebuild',
47
+ autoSave: false,
48
+ },
39
49
 
40
50
  };
41
51
 
@@ -1,4 +1,10 @@
1
1
  import { OneHatData } from '../../src/OneHatData';
2
+ import GroupsDefinition from '../fixtures/Definitions/Groups';
3
+ import GroupsUsersDefinition from '../fixtures/Definitions/GroupsUsers';
4
+ import UsersDefinition from '../fixtures/Definitions/Users';
5
+ import groupsUserData from '../fixtures/Data/GroupsUser';
6
+ import KeyValues from '../../src/Schema/KeyValues';
7
+
2
8
 
3
9
  // NOTE: Cypress can't handle async functions for beforeEach,
4
10
  // so we have to manually apply it to every test. Ugh!
@@ -110,7 +116,7 @@ describe('OneHatData', function() {
110
116
  (async function() {
111
117
  await beforeEach();
112
118
 
113
- const repository = this.oneHatData.createRepository('bar');
119
+ const repository = await this.oneHatData.createRepository('bar');
114
120
  expect(repository.id).to.be.not.eq(this.repository.id);
115
121
 
116
122
  afterEach();
@@ -128,7 +134,7 @@ describe('OneHatData', function() {
128
134
  { name: 'baz', },
129
135
  ]);
130
136
  const schemas = oneHatData.schemas;
131
- oneHatData.createRepositories(schemas);
137
+ await oneHatData.createRepositories(schemas);
132
138
 
133
139
  const result = oneHatData.getAllRepositories();
134
140
  expect(_.size(result)).to.be.eq(3);
@@ -147,7 +153,7 @@ describe('OneHatData', function() {
147
153
  { name: 'bar', },
148
154
  { name: 'baz', },
149
155
  ]);
150
- oneHatData.createBoundRepositories();
156
+ await oneHatData.createBoundRepositories();
151
157
 
152
158
  const schemas = oneHatData.schemas;
153
159
  let bound = 0;
@@ -175,6 +181,43 @@ describe('OneHatData', function() {
175
181
  })();
176
182
  });
177
183
 
184
+ it('hasRepository', function() {
185
+ (async function() {
186
+ await beforeEach();
187
+
188
+ const oneHatData = new OneHatData();
189
+ oneHatData.createSchemas([
190
+ { name: 'foo', },
191
+ ]);
192
+ await oneHatData.createBoundRepositories();
193
+ expect(oneHatData.hasRepository('foo')).to.be.true;
194
+
195
+ afterEach();
196
+ })();
197
+ });
198
+
199
+ it('Entity.getAssociatedRepository', function() {
200
+ (async function() {
201
+ await beforeEach();
202
+
203
+ const oneHatData = new OneHatData();
204
+
205
+ await oneHatData.createSchemas([
206
+ GroupsDefinition,
207
+ GroupsUsersDefinition,
208
+ UsersDefinition,
209
+ ]);
210
+ await oneHatData.createBoundRepositories();
211
+ const GroupsUsers = oneHatData.getRepository('GroupsUsers');
212
+ const groupsUser = await GroupsUsers.add(groupsUserData);
213
+ const Users = groupsUser.getAssociatedRepository('Users');
214
+
215
+ expect(Users).to.be.not.null;
216
+
217
+ afterEach();
218
+ })();
219
+ });
220
+
178
221
  it('hasRepositoryWithId', function() {
179
222
  (async function() {
180
223
  await beforeEach();
@@ -249,9 +292,9 @@ describe('OneHatData', function() {
249
292
  await beforeEach();
250
293
 
251
294
  const oneHatData = this.oneHatData;
252
- oneHatData.createRepository('bar');
253
- oneHatData.createRepository('bar');
254
- oneHatData.createRepository('bar');
295
+ await oneHatData.createRepository('bar');
296
+ await oneHatData.createRepository('bar');
297
+ await oneHatData.createRepository('bar');
255
298
  const result = oneHatData.getRepositoriesBySchema(this.schema);
256
299
  expect(_.size(result)).to.be.eq(4);
257
300
 
@@ -259,22 +302,22 @@ describe('OneHatData', function() {
259
302
  })();
260
303
  });
261
304
 
262
- it('createGlobalErrorHandler', function() {
263
- (async function() {
264
- await beforeEach();
305
+ // it('createGlobalErrorHandler', function() {
306
+ // (async function() {
307
+ // await beforeEach();
265
308
 
266
- let message = '';
267
- const oneHatData = this.oneHatData,
268
- errorHandler = (a, b) => {
269
- debugger;
270
- };
271
- oneHatData.createGlobalErrorHandler(errorHandler);
272
- oneHatData.emitError();
273
- expect(message).to.be.eq('Test here');
309
+ // let message = '';
310
+ // const oneHatData = this.oneHatData,
311
+ // errorHandler = (a, b) => {
312
+ // debugger;
313
+ // };
314
+ // oneHatData.createGlobalErrorHandler(errorHandler);
315
+ // oneHatData.emitError();
316
+ // expect(message).to.be.eq('Test here');
274
317
 
275
- afterEach();
276
- })();
277
- });
318
+ // afterEach();
319
+ // })();
320
+ // });
278
321
 
279
322
  it('setOptionsOnAllRepositories', function() {
280
323
  (async function() {
@@ -308,14 +351,16 @@ describe('OneHatData', function() {
308
351
  await beforeEach();
309
352
 
310
353
  const oneHatData = new OneHatData();
311
- const repositories = oneHatData.createSchemas([
354
+
355
+ await oneHatData.createSchemas([
312
356
  { name: 'foo', },
313
357
  { name: 'bar', },
314
358
  { name: 'baz', },
315
359
  ])
316
- .createBoundRepositories()
317
- .getAllRepositories();
318
-
360
+ .createBoundRepositories();
361
+
362
+ // NOTE: Can't chain getAllRepositories() because we have to wait for createBoundRepositories to finish
363
+ const repositories = oneHatData.getAllRepositories();
319
364
  expect(_.size(repositories)).to.be.eq(3);
320
365
 
321
366
  afterEach();
@@ -334,8 +379,8 @@ describe('OneHatData', function() {
334
379
  { key: '4', value: 'four', },
335
380
  { key: '5', value: 'five', },
336
381
  ],
337
- repository = oneHatData.createRepository({
338
- schema: 'KeyValues',
382
+ repository = await oneHatData.createRepository({
383
+ schema: KeyValues,
339
384
  data,
340
385
  });
341
386
 
@@ -18,12 +18,45 @@ describe('CurrencyProperty', function() {
18
18
  });
19
19
 
20
20
  it('default value', function() {
21
- const property = this.property,
22
- rawValue = property.getDefaultValue();
23
- property.pauseEvents();
24
- property.setValue(rawValue);
25
- property.resumeEvents();
26
21
  expect(this.property.submitValue).to.be.eq('0.00');
22
+
23
+ const rawValue = this.property.getDefaultValue();
24
+ this.property.setValue(rawValue);
25
+ expect(this.property.submitValue).to.be.eq('0.00');
26
+ });
27
+
28
+ it('isZero', function() {
29
+ this.property.setValue('0.00');
30
+ expect(this.property.isZero).to.be.true;
31
+
32
+ this.property.setValue('0.01');
33
+ expect(this.property.isZero).to.be.false;
34
+
35
+ this.property.setValue('-0.01');
36
+ expect(this.property.isZero).to.be.false;
37
+
38
+ this.property.setValue(null);
39
+ expect(this.property.isZero).to.be.false;
40
+
41
+ this.property.setValue();
42
+ expect(this.property.isZero).to.be.false;
43
+ });
44
+
45
+ it('hasValue', function() {
46
+ this.property.setValue('0.00');
47
+ expect(this.property.hasValue).to.be.true;
48
+
49
+ this.property.setValue('0.01');
50
+ expect(this.property.hasValue).to.be.true;
51
+
52
+ this.property.setValue('-0.01');
53
+ expect(this.property.hasValue).to.be.true;
54
+
55
+ this.property.setValue(null);
56
+ expect(this.property.hasValue).to.be.false;
57
+
58
+ this.property.setValue();
59
+ expect(this.property.hasValue).to.be.false;
27
60
  });
28
61
 
29
62
  });
@@ -41,8 +74,8 @@ describe('CurrencyProperty', function() {
41
74
  });
42
75
 
43
76
  it('numeric string', function() {
44
- const parsed = this.property.parse('This is 1234 dollars.');
45
- expect(parsed).to.be.eq(1234);
77
+ const parsed = this.property.parse('This is $1234.56 dollars.');
78
+ expect(parsed).to.be.eq(1234.56);
46
79
  });
47
80
 
48
81
  it('non-numeric string', function() {
@@ -111,6 +111,33 @@ describe('OneBuildRepository', function() {
111
111
  await this.repository.add({ key: 6, value: 'six' });
112
112
  expect(_.size(this.repository.entities)).to.be.eq(1);
113
113
  });
114
+
115
+ it.only('sortInMemory', function() {
116
+
117
+ const repository = this.repository;
118
+
119
+ // Create two phantom records, out of order
120
+ repository.setAutoSave(false);
121
+ repository.add({ key: 5, value: 'Five', });
122
+ repository.add({ key: 4, value: 'One', });
123
+ repository.add({ key: 2, value: 'Two', });
124
+ repository.add({ key: 3, value: 'Three', });
125
+ repository.add({ key: 1, value: 'One', });
126
+
127
+ repository.sorters = [
128
+ { name: 'value', direction: 'DESC', }, // 2,3,4,1,5
129
+ { name: 'key', direction: 'ASC', }, // 2,3,1,4,5
130
+ ];
131
+ repository.sortInMemory();
132
+
133
+ // Check that they are correct order
134
+ const entities = repository.entities;
135
+ expect(entities[0].key).to.be.eq(2);
136
+ expect(entities[1].key).to.be.eq(3);
137
+ expect(entities[2].key).to.be.eq(1);
138
+ expect(entities[3].key).to.be.eq(4);
139
+ expect(entities[4].key).to.be.eq(5);
140
+ });
114
141
 
115
142
  });
116
143
 
@@ -10,8 +10,12 @@ describe('Schema', function() {
10
10
  it('schema is valid', function() {
11
11
  expect(this.schema instanceof Schema).to.be.true;
12
12
  expect(this.schema.name).to.be.eq('GroupsUsers');
13
- expect(this.schema.repository.type).to.be.eq('onebuild');
13
+ expect(this.schema.model.associations.hasOne).to.be.an('array');
14
+ expect(this.schema.model.associations.hasMany).to.be.an('array');
15
+ expect(this.schema.model.associations.belongsTo).to.be.an('array');
16
+ expect(this.schema.model.associations.belongsToMany).to.be.an('array');
14
17
  expect(this.schema.entity.methods.testMethod).to.be.a('function');
18
+ expect(this.schema.repository.type).to.be.eq('onebuild');
15
19
  });
16
20
 
17
21
  it('clone', function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.6.4",
3
+ "version": "1.6.8",
4
4
  "description": "JS data modeling package with adapters for many storage mediums.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/Entity.js CHANGED
@@ -398,6 +398,17 @@ class Entity extends EventEmitter {
398
398
  return this.schema;
399
399
  }
400
400
 
401
+ /**
402
+ * Gets the Repository object
403
+ * @return {Repository} repository
404
+ */
405
+ getRepository = () => {
406
+ if (this.isDestroyed) {
407
+ throw Error('this.getRepository is no longer valid. Entity has been destroyed.');
408
+ }
409
+ return this.repository;
410
+ }
411
+
401
412
  /**
402
413
  * Alias for this.properties
403
414
  */
@@ -851,6 +862,43 @@ class Entity extends EventEmitter {
851
862
  return this._originalData;
852
863
  }
853
864
 
865
+ /**
866
+ * Gets the associated Repository
867
+ * @param {string} repositoryName - Name of the Repository to retrieve
868
+ * @return {boolean} hasProperty
869
+ */
870
+ getAssociatedRepository = (repositoryName) => {
871
+ if (this.isDestroyed) {
872
+ throw Error('this.getAssociatedRepository is no longer valid. Entity has been destroyed.');
873
+ }
874
+
875
+ const schema = this.getSchema();
876
+ if (!schema.model.associations.hasOne.includes(repositoryName) &&
877
+ !schema.model.associations.hasMany.includes(repositoryName) &&
878
+ !schema.model.associations.belongsTo.includes(repositoryName) &&
879
+ !schema.model.associations.belongsToMany.includes(repositoryName)
880
+ ) {
881
+ throw Error(repositoryName + ' is not associated with this schema');
882
+ }
883
+
884
+ const repository = this.getRepository();
885
+ if (!repository) {
886
+ throw Error('No repository on this entity');
887
+ }
888
+
889
+ const oneHatData = repository.oneHatData;
890
+ if (!oneHatData) {
891
+ throw Error('No global oneHatData object');
892
+ }
893
+
894
+ const associatedRepository = oneHatData.getRepository(repositoryName);
895
+ if (!associatedRepository) {
896
+ throw Error('Repository ' + repositoryName + ' cannot be found');
897
+ }
898
+
899
+ return associatedRepository;
900
+ }
901
+
854
902
 
855
903
 
856
904
  // _____ __ __
package/src/OneHatData.js CHANGED
@@ -254,8 +254,8 @@ export class OneHatData extends EventEmitter {
254
254
  }
255
255
 
256
256
  // Apply the general config settings to each specific one
257
- const localConfig = _.assign({}, generalConfig, config.local),
258
- remoteConfig = _.assign({}, generalConfig, config.remote);
257
+ const localConfig = _.merge({}, generalConfig, config.local),
258
+ remoteConfig = _.merge({}, generalConfig, config.remote);
259
259
 
260
260
  // Actually create the local and remote repositories
261
261
  config.local = await this.createRepository(localConfig);
@@ -263,7 +263,7 @@ export class OneHatData extends EventEmitter {
263
263
  }
264
264
 
265
265
  const Repository = this._repositoryTypes[config.type],
266
- repository = new Repository(config);
266
+ repository = new Repository(config, this);
267
267
  await repository.initialize();
268
268
 
269
269
  return repository;
@@ -442,6 +442,19 @@ export class OneHatData extends EventEmitter {
442
442
  return schema.getBoundRepository();
443
443
  }
444
444
 
445
+ /**
446
+ * Checks whether the requested bound Repository exists.
447
+ * @param {string} name - Name of Schema
448
+ * @return {boolean} hasRepository
449
+ */
450
+ hasRepository = (name) => {
451
+ if (this.isDestroyed) {
452
+ throw new Error('this.getRepository is no longer valid. OneHatData has been destroyed.');
453
+ }
454
+ const repository = this.getRepository(name);
455
+ return !!repository;
456
+ }
457
+
445
458
  /**
446
459
  * Get Repositories by a filter function
447
460
  * @param {function} filter - Filter function
@@ -64,6 +64,28 @@ export default class CurrencyProperty extends Property {
64
64
  return this.parsedValue;
65
65
  }
66
66
 
67
+ /**
68
+ * Determines if a currency is equal to 0.00
69
+ * Returns false if no value set.
70
+ */
71
+ get isZero() {
72
+ if (this.isDestroyed) {
73
+ throw Error('this.isZero is no longer valid. Property has been destroyed.');
74
+ }
75
+ return this.parsedValue === 0;
76
+ }
77
+
78
+ /**
79
+ * Determines if a currency has a non-null value
80
+ */
81
+ get hasValue() {
82
+ if (this.isDestroyed) {
83
+ throw Error('this.hasValue is no longer valid. Property has been destroyed.');
84
+ }
85
+ console.log(this.parsedValue);
86
+ return !_.isNil(this.parsedValue);
87
+ }
88
+
67
89
  };
68
90
 
69
91
  CurrencyProperty.className = 'Currency';
@@ -370,7 +370,7 @@ class AjaxRepository extends Repository {
370
370
  }
371
371
 
372
372
  const repository = this;
373
- const data = _.assign({}, this._baseParams, this._params);
373
+ const data = _.merge({}, this._baseParams, this._params);
374
374
 
375
375
  return this._send(this.methods.get, this.api.get, data)
376
376
  .then(result => {
@@ -653,6 +653,24 @@ class AjaxRepository extends Repository {
653
653
  return this.reader.read(result);
654
654
  }
655
655
 
656
+ /**
657
+ * Sorts the items in the current page of memory
658
+ * This is mainly used to sort isPhantom entities,
659
+ * since the server normally sorts, and they haven't yet gone to server.
660
+ */
661
+ sortInMemory = () => {
662
+ const sorters = this.sorters;
663
+ let sortNames = [],
664
+ sortDirections = [];
665
+ _.each(sorters, (sorter) => {
666
+ sortNames.push(sorter.name);
667
+ sortDirections.push(sorter.direction.toLowerCase());
668
+ });
669
+ let entities = this.entities;
670
+ entities = _.orderBy(entities, sortNames, sortDirections);
671
+ this.entities = entities;
672
+ }
673
+
656
674
  /**
657
675
  * Helper for save.
658
676
  * Takes an array of Promises from axios. When they are all resolved,
@@ -23,7 +23,7 @@ export default class Repository extends EventEmitter {
23
23
  * - name {string} - Optional. Defaults to schema.name
24
24
  * - schema - Schema object
25
25
  */
26
- constructor(config = {}) {
26
+ constructor(config = {}, oneHatData = null) {
27
27
  super(...arguments);
28
28
 
29
29
  const { schema } = config;
@@ -214,6 +214,11 @@ export default class Repository extends EventEmitter {
214
214
  */
215
215
  this.isDestroyed = false;
216
216
 
217
+ /**
218
+ * @member {boolean} oneHatData - The global @onehat/data object
219
+ */
220
+ this.oneHatData = oneHatData;
221
+
217
222
  this.registerEvents([
218
223
  'add',
219
224
  'beforeSave',
@@ -109,7 +109,7 @@ export default class Schema extends EventEmitter {
109
109
 
110
110
  };
111
111
 
112
- this._originalConfig = _.assign({}, defaults, config);
112
+ this._originalConfig = _.merge({}, defaults, config);
113
113
  _.merge(this, this._originalConfig);
114
114
 
115
115
  /**