@onehat/data 1.6.0 → 1.6.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.
- package/cypress/fixtures/Definitions/GroupsUsers.js +8 -0
- package/cypress/integration/Entity.spec.js +12 -1
- package/cypress/integration/Repository/Repository.spec.js +29 -0
- package/cypress/integration/Schema.spec.js +2 -0
- package/package.json +1 -1
- package/src/Entity.js +43 -0
- package/src/OneHatData.js +3 -3
- package/src/Repository/Ajax.js +1 -10
- package/src/Repository/Repository.js +40 -12
- package/src/Schema/Schema.js +7 -2
|
@@ -17,6 +17,13 @@ describe('Entity', function() {
|
|
|
17
17
|
{ name: 'baz', mapping: 'baz.test.val', type: 'bool', defaultValue: null, },
|
|
18
18
|
],
|
|
19
19
|
},
|
|
20
|
+
entity: {
|
|
21
|
+
methods: {
|
|
22
|
+
testMethod: function() {
|
|
23
|
+
this.bar = 'test me';
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
20
27
|
});
|
|
21
28
|
this.data = {
|
|
22
29
|
foo: 1,
|
|
@@ -71,7 +78,6 @@ describe('Entity', function() {
|
|
|
71
78
|
entity.markSaved();
|
|
72
79
|
expect(entity.isTempId).to.be.false;
|
|
73
80
|
});
|
|
74
|
-
|
|
75
81
|
|
|
76
82
|
it('clone', function() {
|
|
77
83
|
const entity = this.entity;
|
|
@@ -100,6 +106,11 @@ describe('Entity', function() {
|
|
|
100
106
|
expect(entity.properties.bar.name).to.be.eq('bar');
|
|
101
107
|
expect(entity.properties.baz.name).to.be.eq('baz');
|
|
102
108
|
});
|
|
109
|
+
|
|
110
|
+
it('_createMethods', function() {
|
|
111
|
+
this.entity.testMethod();
|
|
112
|
+
expect(this.entity.bar).to.be.eq('test me');
|
|
113
|
+
});
|
|
103
114
|
|
|
104
115
|
it('loadOriginalData', function() {
|
|
105
116
|
const data = {
|
|
@@ -581,6 +581,23 @@ describe('Repository Base', function() {
|
|
|
581
581
|
expect(result.value).to.be.eq('three');
|
|
582
582
|
});
|
|
583
583
|
|
|
584
|
+
it('getById', function() {
|
|
585
|
+
const result = this.repository.getById(3);
|
|
586
|
+
expect(result.value).to.be.eq('three');
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('getBy', function() {
|
|
590
|
+
const result = this.repository.getBy(entity => entity.id === 2 || entity.id === 3);
|
|
591
|
+
expect(result.length).to.be.eq(2);
|
|
592
|
+
expect(result[0].value).to.be.eq('two');
|
|
593
|
+
expect(result[1].value).to.be.eq('three');
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
it('getFirstBy', function() {
|
|
597
|
+
const result = this.repository.getFirstBy(entity => entity.id === 3);
|
|
598
|
+
expect(result.value).to.be.eq('three');
|
|
599
|
+
});
|
|
600
|
+
|
|
584
601
|
it('getByRange', function() {
|
|
585
602
|
const result = this.repository.getByRange(1, 4);
|
|
586
603
|
expect(_.size(result)).to.be.eq(4);
|
|
@@ -668,6 +685,11 @@ describe('Repository Base', function() {
|
|
|
668
685
|
|
|
669
686
|
describe('deleting', function() {
|
|
670
687
|
|
|
688
|
+
it('clear', function() {
|
|
689
|
+
this.repository.clear();
|
|
690
|
+
expect(this.repository.entities.length).to.be.eq(0);
|
|
691
|
+
});
|
|
692
|
+
|
|
671
693
|
it('delete', function() {
|
|
672
694
|
let didFire = false;
|
|
673
695
|
this.repository.on('delete', () => {
|
|
@@ -681,6 +703,13 @@ describe('Repository Base', function() {
|
|
|
681
703
|
expect(didFire).to.be.true;
|
|
682
704
|
});
|
|
683
705
|
|
|
706
|
+
it('delete() / removeEntity', async function() {
|
|
707
|
+
this.repository.setAutoSave(false);
|
|
708
|
+
const entity = await this.repository.add({ value: 'six' });
|
|
709
|
+
this.repository.delete(entity);
|
|
710
|
+
expect(this.repository.entities.length).to.be.eq(5);
|
|
711
|
+
});
|
|
712
|
+
|
|
684
713
|
|
|
685
714
|
// I'm going to skip these, as they're just combinations of other, tested functions
|
|
686
715
|
// deleteByIx
|
|
@@ -10,6 +10,8 @@ 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.model.associations.hasOne).to.be.an('array');
|
|
14
|
+
expect(this.schema.entity.methods.testMethod).to.be.a('function');
|
|
13
15
|
expect(this.schema.repository.type).to.be.eq('onebuild');
|
|
14
16
|
});
|
|
15
17
|
|
package/package.json
CHANGED
package/src/Entity.js
CHANGED
|
@@ -150,10 +150,28 @@ class Entity extends EventEmitter {
|
|
|
150
150
|
|
|
151
151
|
initialize = () => {
|
|
152
152
|
this.properties = this._createProperties();
|
|
153
|
+
this._createMethods();
|
|
153
154
|
this.reset();
|
|
154
155
|
this.isInitialized = true;
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Creates the methods for this Entity, based on Schema.
|
|
160
|
+
* @private
|
|
161
|
+
*/
|
|
162
|
+
_createMethods = () => {
|
|
163
|
+
if (this.isDestroyed) {
|
|
164
|
+
throw Error('this._createMethods is no longer valid. Entity has been destroyed.');
|
|
165
|
+
}
|
|
166
|
+
const methodDefinitions = this.schema.entity.methods;
|
|
167
|
+
if (_.isEmpty(methodDefinitions)) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
_.each(methodDefinitions, (method, name) => {
|
|
171
|
+
this[name] = method; // NOTE: Methods must be defined in schema as "function() {}", not as "() => {}" so "this" will be assigned correctly
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
157
175
|
/**
|
|
158
176
|
* Generates a new unique id and assigns it to this entity.
|
|
159
177
|
*/
|
|
@@ -833,6 +851,31 @@ class Entity extends EventEmitter {
|
|
|
833
851
|
return this._originalData;
|
|
834
852
|
}
|
|
835
853
|
|
|
854
|
+
/**
|
|
855
|
+
* Gets the associated Repository
|
|
856
|
+
* @param {string} repositoryName - Name of the Repository to retrieve
|
|
857
|
+
* @return {boolean} hasProperty
|
|
858
|
+
*/
|
|
859
|
+
getAssociatedRepository = (repositoryName) => {
|
|
860
|
+
if (this.isDestroyed) {
|
|
861
|
+
throw Error('this.getAssociatedRepository is no longer valid. Entity has been destroyed.');
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const schema = this.getSchema();
|
|
865
|
+
if (!schema.associations.hasOne.includes(repositoryName) &&
|
|
866
|
+
!schema.associations.hasMany.includes(repositoryName) &&
|
|
867
|
+
!schema.associations.belongsTo.includes(repositoryName) &&
|
|
868
|
+
!schema.associations.belongsToMany.includes(repositoryName)
|
|
869
|
+
) {
|
|
870
|
+
throw Error(repositoryName + ' is not associated with ' + this.getRepository().name);
|
|
871
|
+
}
|
|
872
|
+
const repository = this.getRepository();
|
|
873
|
+
if (!repository.oneHatData) {
|
|
874
|
+
throw Error('No global oneHatData object');
|
|
875
|
+
}
|
|
876
|
+
return repository.oneHatData.getRepository(repositoryName);
|
|
877
|
+
}
|
|
878
|
+
|
|
836
879
|
|
|
837
880
|
|
|
838
881
|
// _____ __ __
|
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 = _.
|
|
258
|
-
remoteConfig = _.
|
|
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;
|
package/src/Repository/Ajax.js
CHANGED
|
@@ -370,7 +370,7 @@ class AjaxRepository extends Repository {
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
const repository = this;
|
|
373
|
-
const data = _.
|
|
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 => {
|
|
@@ -678,15 +678,6 @@ class AjaxRepository extends Repository {
|
|
|
678
678
|
}));
|
|
679
679
|
}
|
|
680
680
|
|
|
681
|
-
/**
|
|
682
|
-
* Deletes all locally cached entities in repository,
|
|
683
|
-
* usually, the current "page".
|
|
684
|
-
* Does not actually affect anything on the server.
|
|
685
|
-
*/
|
|
686
|
-
clear = async () => {
|
|
687
|
-
this._destroyEntities();
|
|
688
|
-
}
|
|
689
|
-
|
|
690
681
|
}
|
|
691
682
|
|
|
692
683
|
AjaxRepository.className = 'Ajax';
|
|
@@ -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',
|
|
@@ -972,6 +977,15 @@ export default class Repository extends EventEmitter {
|
|
|
972
977
|
this.entities = [];
|
|
973
978
|
}
|
|
974
979
|
|
|
980
|
+
/**
|
|
981
|
+
* Deletes all locally cached entities in repository,
|
|
982
|
+
* usually, the current "page".
|
|
983
|
+
* Does not actually affect anything on the server.
|
|
984
|
+
*/
|
|
985
|
+
clear = async () => {
|
|
986
|
+
this._destroyEntities();
|
|
987
|
+
}
|
|
988
|
+
|
|
975
989
|
/**
|
|
976
990
|
* Gets an array of "submit" values objects for the entities
|
|
977
991
|
* @return {array} map -
|
|
@@ -1064,20 +1078,19 @@ export default class Repository extends EventEmitter {
|
|
|
1064
1078
|
/**
|
|
1065
1079
|
* Get a single Entity by its id
|
|
1066
1080
|
* @param {integer} id - id of record to retrieve
|
|
1067
|
-
* @return {Entity} The Entity with matching id
|
|
1081
|
+
* @return {Entity} The Entity with matching id, or undefined
|
|
1068
1082
|
*/
|
|
1069
1083
|
getById = (id) => {
|
|
1070
1084
|
if (this.isDestroyed) {
|
|
1071
1085
|
throw Error('this.getById is no longer valid. Repository has been destroyed.');
|
|
1072
1086
|
}
|
|
1073
|
-
|
|
1074
|
-
return result.length > 0 ? result[0] : null;
|
|
1087
|
+
return this.getFirstBy(entity => entity.id === id);
|
|
1075
1088
|
}
|
|
1076
1089
|
|
|
1077
1090
|
/**
|
|
1078
1091
|
* Get an array of Entities by supplied filter function
|
|
1079
1092
|
* @param {function} filter - Filter function to apply to all entities
|
|
1080
|
-
* @return {Entity[]} Entities that passed through filter
|
|
1093
|
+
* @return {Entity[]} Entities that passed through filter, or []
|
|
1081
1094
|
*/
|
|
1082
1095
|
getBy = (filter) => {
|
|
1083
1096
|
if (this.isDestroyed) {
|
|
@@ -1093,7 +1106,7 @@ export default class Repository extends EventEmitter {
|
|
|
1093
1106
|
* filters into account. Defaults to false.
|
|
1094
1107
|
*
|
|
1095
1108
|
* @param {function} filter - Filter function to search by
|
|
1096
|
-
* @return {Entity} First Entity found
|
|
1109
|
+
* @return {Entity} First Entity found, or undefined
|
|
1097
1110
|
*/
|
|
1098
1111
|
getFirstBy = (filter) => {
|
|
1099
1112
|
if (this.isDestroyed) {
|
|
@@ -1104,7 +1117,7 @@ export default class Repository extends EventEmitter {
|
|
|
1104
1117
|
|
|
1105
1118
|
/**
|
|
1106
1119
|
* Get all phantom (unsaved) Entities
|
|
1107
|
-
* @return {Entity[]} Array of phantom Entities
|
|
1120
|
+
* @return {Entity[]} Array of phantom Entities, or []
|
|
1108
1121
|
*/
|
|
1109
1122
|
getPhantom = () => {
|
|
1110
1123
|
if (this.isDestroyed) {
|
|
@@ -1115,7 +1128,7 @@ export default class Repository extends EventEmitter {
|
|
|
1115
1128
|
|
|
1116
1129
|
/**
|
|
1117
1130
|
* Get all Entities not yet persisted to a storage medium
|
|
1118
|
-
* @return {Entity[]} Array of dirty Entities
|
|
1131
|
+
* @return {Entity[]} Array of dirty Entities, or []
|
|
1119
1132
|
*/
|
|
1120
1133
|
getNonPersisted = () => {
|
|
1121
1134
|
if (this.isDestroyed) {
|
|
@@ -1128,7 +1141,7 @@ export default class Repository extends EventEmitter {
|
|
|
1128
1141
|
|
|
1129
1142
|
/**
|
|
1130
1143
|
* Get all dirty (having unsaved changes) Entities
|
|
1131
|
-
* @return {Entity[]} Array of dirty Entities
|
|
1144
|
+
* @return {Entity[]} Array of dirty Entities, or []
|
|
1132
1145
|
*/
|
|
1133
1146
|
getDirty = () => {
|
|
1134
1147
|
if (this.isDestroyed) {
|
|
@@ -1141,7 +1154,7 @@ export default class Repository extends EventEmitter {
|
|
|
1141
1154
|
|
|
1142
1155
|
/**
|
|
1143
1156
|
* Get all deleted Entities
|
|
1144
|
-
* @return {Entity[]} Array of deleted Entities
|
|
1157
|
+
* @return {Entity[]} Array of deleted Entities, or []
|
|
1145
1158
|
*/
|
|
1146
1159
|
getDeleted = () => {
|
|
1147
1160
|
if (this.isDestroyed) {
|
|
@@ -1366,7 +1379,7 @@ export default class Repository extends EventEmitter {
|
|
|
1366
1379
|
|
|
1367
1380
|
/**
|
|
1368
1381
|
* Marks entities for deletion from storage medium.
|
|
1369
|
-
* Actual deletion takes place in save()
|
|
1382
|
+
* Actual deletion takes place in save(), unless isPhantom
|
|
1370
1383
|
* @param {object|array} entities - one or more entities to delete
|
|
1371
1384
|
* @fires delete
|
|
1372
1385
|
*/
|
|
@@ -1382,7 +1395,12 @@ export default class Repository extends EventEmitter {
|
|
|
1382
1395
|
return;
|
|
1383
1396
|
}
|
|
1384
1397
|
_.each(entities, (entity) => {
|
|
1385
|
-
entity.
|
|
1398
|
+
if (entity.isPhantom) {
|
|
1399
|
+
// Just auto-remove it. Don't bother saving to storage medium.
|
|
1400
|
+
this.removeEntity(entity);
|
|
1401
|
+
} else {
|
|
1402
|
+
entity.markDeleted(); // Entity is still there, it's just marked for deletion
|
|
1403
|
+
}
|
|
1386
1404
|
});
|
|
1387
1405
|
|
|
1388
1406
|
this.emit('delete', entities);
|
|
@@ -1392,6 +1410,16 @@ export default class Repository extends EventEmitter {
|
|
|
1392
1410
|
}
|
|
1393
1411
|
}
|
|
1394
1412
|
|
|
1413
|
+
/**
|
|
1414
|
+
* Removes an Entity from the current page
|
|
1415
|
+
* Mainly used for phantom Entities
|
|
1416
|
+
* Helper for delete()
|
|
1417
|
+
*/
|
|
1418
|
+
removeEntity = async (entity) => {
|
|
1419
|
+
this.entities = _.filter(this.entities, e => e !== entity);
|
|
1420
|
+
entity.destroy();
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1395
1423
|
/**
|
|
1396
1424
|
* Deletes a single Entity by its index (zero-indexed) on the current page
|
|
1397
1425
|
* @param {integer} ix - Index
|
package/src/Schema/Schema.js
CHANGED
|
@@ -5,11 +5,12 @@ import _ from 'lodash';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Class represents the Schema definition for Model and Source
|
|
8
|
-
* This is basically just a big config object, used to instantiate
|
|
8
|
+
* This is basically just a big config object, used to instantiate an Entity, and Repository.
|
|
9
9
|
* Usage:
|
|
10
10
|
* - const schema = new Schema({
|
|
11
11
|
* name: 'Users',
|
|
12
12
|
* model: {},
|
|
13
|
+
* entity: {},
|
|
13
14
|
* repository: {},
|
|
14
15
|
* });
|
|
15
16
|
*
|
|
@@ -96,6 +97,10 @@ export default class Schema extends EventEmitter {
|
|
|
96
97
|
belongsToMany: [],
|
|
97
98
|
},
|
|
98
99
|
},
|
|
100
|
+
|
|
101
|
+
entity: {
|
|
102
|
+
methods: {}, // NOTE: Methods must be defined as "function() {}", not as "() => {}" so "this" will be assigned correctly
|
|
103
|
+
},
|
|
99
104
|
|
|
100
105
|
/**
|
|
101
106
|
* @member {object|string} repository - Config for Repository
|
|
@@ -104,7 +109,7 @@ export default class Schema extends EventEmitter {
|
|
|
104
109
|
|
|
105
110
|
};
|
|
106
111
|
|
|
107
|
-
this._originalConfig = _.
|
|
112
|
+
this._originalConfig = _.merge({}, defaults, config);
|
|
108
113
|
_.merge(this, this._originalConfig);
|
|
109
114
|
|
|
110
115
|
/**
|