@reldens/storage 0.10.0-beta.32 → 0.10.0-beta.36

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/README.md CHANGED
@@ -1,7 +1,58 @@
1
- [![Reldens - GitHub - Release](https://www.dwdeveloper.com/media/reldens/reldens-mmorpg-platform.png)](https://github.com/damian-pastorini/reldens)
1
+ [![Reldens - GitHub - Release](https://www.dwdeveloper.com/media/reldens/reldens-mmorpg-platform.png)](https://www.reldens.com/)
2
2
 
3
3
  # Reldens - Storage
4
4
 
5
- ### [Reldens](https://github.com/damian-pastorini/reldens/ "Reldens")
5
+ ## About this package
6
+ This package is designed to provide a standard drivers support for managing Reldens project data.
7
+
8
+ This way in Reldens project we can trust that any driver implement will have the exact same methods available to fetch and manage the project entities.
9
+
10
+ Every time you need to load, create, update or delete data in Reldens, the drivers from this package will be used.
11
+
12
+ ## Current features
13
+ The current drivers are to support two other ORMs:
14
+ - Objection JS (with Knex), for everything that's SQL related.
15
+ ```javascript
16
+ let server = new ObjectionJsDataServer({
17
+ client: 'mysql',
18
+ config: {
19
+ user: 'reldens',
20
+ password: 'reldens',
21
+ database: 'reldens',
22
+ port : 3306,
23
+ }
24
+ });
25
+ ```
26
+ - Mikro-ORM, to offer support for MongoDB.
27
+ ```javascript
28
+ let server = new MikroOrmDataServer({
29
+ client: 'mongodb',
30
+ config: {
31
+ user: 'reldens',
32
+ password: 'reldens',
33
+ database: 'test',
34
+ port : 27017,
35
+ },
36
+ connectStringOptions: 'authSource=reldens&readPreference=primary&appname=MongoDB%20Compass&ssl=false',
37
+ autoGenerateEntities: false,
38
+ rawEntities: Object.values(rawRegisteredEntities)
39
+ });
40
+ ```
41
+
42
+ ## Custom drivers
43
+ Through this package you can also create your own drivers (to support your own storage system) and later implement it on any Reldens project.
44
+
45
+ In order to do this, just extend the BaseDataServer and BaseDriver classes and fill all the required methods.
46
+
47
+ For last, on your Reldens implementation you just need to pass an instance of your data server to the server manager and you are done:
48
+ ```
49
+ const appServer = new ServerManager(serverConfig, eventsManager, yourDriverInstance);
50
+ ```
51
+
52
+ You can also check the [skeleton implementation](https://github.com/damian-pastorini/reldens-skeleton/) to see how it works.
53
+
54
+ For reference see Reldens GitHub: [https://github.com/damian-pastorini/reldens/tree/master](https://github.com/damian-pastorini/reldens/tree/master)
55
+
56
+ ### [Reldens](https://www.reldens.com/ "Reldens")
6
57
 
7
58
  ##### [By DwDeveloper](https://www.dwdeveloper.com/ "DwDeveloper")
@@ -71,11 +71,16 @@ class BaseDriver
71
71
  ErrorManager.error('BaseDriver updateById() not implemented.');
72
72
  }
73
73
 
74
- delete(id)
74
+ delete(filters)
75
75
  {
76
76
  ErrorManager.error('BaseDriver delete() not implemented.');
77
77
  }
78
78
 
79
+ deleteById(id)
80
+ {
81
+ ErrorManager.error('BaseDriver deleteById() not implemented.');
82
+ }
83
+
79
84
  count(filters)
80
85
  {
81
86
  ErrorManager.error('BaseDriver count() not implemented.');
@@ -25,13 +25,9 @@ class MikroOrmDataServer extends BaseDataServer
25
25
  }
26
26
  this.orm = await MikroORM.init({
27
27
  entities: Object.values(this.rawEntities),
28
- dbName: this.config.database,
29
28
  type: this.clientsMapped[(this.client || 'mongo')],
30
29
  clientUrl: this.connectString,
31
- host: this.config.host,
32
- port: this.config.port,
33
- user: this.config.user,
34
- password: this.config.password
30
+ allowGlobalContext: true
35
31
  });
36
32
  this.initialized = Date.now();
37
33
  return this.initialized;
@@ -55,7 +51,8 @@ class MikroOrmDataServer extends BaseDataServer
55
51
  id: i,
56
52
  name: i,
57
53
  config: this.orm.driver.connection.options,
58
- orm: this.orm
54
+ orm: this.orm,
55
+ server: this
59
56
  });
60
57
  }
61
58
  this.entityManager.setEntities(this.entities);
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  const { BaseDriver } = require('../base-driver');
8
- const { ErrorManager, sc } = require('@reldens/utils');
8
+ const { ErrorManager, Logger, sc } = require('@reldens/utils');
9
9
 
10
10
  class MikroOrmDriver extends BaseDriver
11
11
  {
@@ -16,10 +16,14 @@ class MikroOrmDriver extends BaseDriver
16
16
  if(!props.orm){
17
17
  ErrorManager.error('Missing ORM on Mikro ORM driver.');
18
18
  }
19
+ if(!props.server){
20
+ ErrorManager.error('Missing Server Driver on Mikro ORM driver.');
21
+ }
19
22
  if(!this.rawModel){
20
23
  ErrorManager.error('Missing raw entity on Mikro ORM driver.');
21
24
  }
22
25
  this.orm = props.orm;
26
+ this.server = props.server;
23
27
  this.repository = this.orm.em.getRepository(this.rawModel.entity);
24
28
  }
25
29
 
@@ -30,17 +34,17 @@ class MikroOrmDriver extends BaseDriver
30
34
 
31
35
  id()
32
36
  {
33
- return this.rawModel.entity || this.name();
37
+ return this.rawModel.entity.name || this.name();
34
38
  }
35
39
 
36
40
  name()
37
41
  {
38
- return this.rawModel.entity || this.config.dbName;
42
+ return this.rawModel.entity.name || this.config.dbName;
39
43
  }
40
44
 
41
45
  tableName()
42
46
  {
43
- return this.rawModel.entity;
47
+ return this.rawModel.entity.name;
44
48
  }
45
49
 
46
50
  property(propertyName)
@@ -48,183 +52,271 @@ class MikroOrmDriver extends BaseDriver
48
52
  return this.rawModel[propertyName] || null;
49
53
  }
50
54
 
51
- create(params)
55
+ async create(params)
52
56
  {
53
- return this.orm.em.create(this.rawModel, params);
57
+ let newInstance = this.rawModel.entity.createByProps(params);
58
+ await this.repository.persist(newInstance).flush();
59
+ return newInstance;
54
60
  }
55
61
 
56
- createWithRelations(params, relations)
62
+ async createWithRelations(params, relations)
57
63
  {
58
- return this.create(params, relations);
64
+ let newInstance = await this.create(params, relations);
65
+ await this.createNested(newInstance, params);
66
+ return newInstance;
59
67
  }
60
68
 
61
- update(filters, updatePatch)
69
+ async update(filters, updatePatch)
62
70
  {
63
- let queryBuilder = this.queryBuilder(true, true, true);
64
- this.appendFilters(queryBuilder, filters);
65
- return queryBuilder.patch(updatePatch);
71
+ let entities = await this.repository.find(filters, this.queryBuilder(true, true, true));
72
+ if(0 === entities.length){
73
+ return false;
74
+ }
75
+ for(let entity of entities){
76
+ Object.assign(entity, updatePatch);
77
+ this.repository.persist(entity).flush();
78
+ }
79
+ return entities;
66
80
  }
67
81
 
68
- updateBy(field, fieldValue, updatePatch, operator = null)
82
+ async updateBy(field, fieldValue, updatePatch, operator = null)
69
83
  {
70
- let queryBuilder = this.queryBuilder(true, true, true);
71
- this.appendSingleFilter(queryBuilder, field, fieldValue, operator);
72
- return queryBuilder.patch(updatePatch);
84
+ let filter = this.createSingleFilter(field, fieldValue, operator);
85
+ let entities = await this.repository.find(filter, this.queryBuilder(true, true, true));
86
+ if(0 === entities.length){
87
+ return false;
88
+ }
89
+ for(let entity of entities){
90
+ Object.assign(entity, updatePatch);
91
+ this.repository.persist(entity).flush();
92
+ }
93
+ return entities;
73
94
  }
74
95
 
75
96
  updateById(id, params)
76
97
  {
77
- return this.queryBuilder().patchAndFetchById(id, params);
98
+ return this.update({id}, params);
99
+ }
100
+
101
+ async delete(filters = {})
102
+ {
103
+ let entries = await this.load(filters);
104
+ if (!entries){
105
+ return false;
106
+ }
107
+ for(let entry of Object.keys(entries)){
108
+ await this.repository.nativeDelete(entry.id);
109
+ }
78
110
  }
79
111
 
80
- delete(id)
112
+ deleteById(id)
81
113
  {
82
- return this.queryBuilder().deleteById(id);
114
+ return this.repository.nativeDelete(id);
83
115
  }
84
116
 
85
117
  async count(filters)
86
118
  {
87
- let queryBuilder = this.queryBuilder(true, true, true);
88
- this.appendFilters(queryBuilder, filters);
89
- let count = await queryBuilder.count().first();
90
- return count ? count['count(*)'] : 0;
119
+ return this.repository.count(filters);
91
120
  }
92
121
 
93
122
  async countWithRelations(filters, relations)
94
123
  {
95
- let queryBuilder = this.queryBuilder(true, true, true);
96
- this.appendFilters(queryBuilder, filters);
97
- let count = await this.appendRelationsToQuery(queryBuilder, relations).count().first();
98
- return count ? count['count(*)'] : 0;
124
+ // @TODO - BETA - Look a way to filter by sub-entities properties before count.
125
+ return this.count(filters);
99
126
  }
100
127
 
101
128
  loadAll()
102
129
  {
103
- return this.queryBuilder();
130
+ return this.repository.findAll();
104
131
  }
105
132
 
106
- loadAllWithRelations(relations)
133
+ async loadAllWithRelations(relations)
107
134
  {
108
- return this.appendRelationsToQuery(this.queryBuilder(), relations);
135
+ let entities = await this.repository.find(filter, this.queryBuilder(true, true, true));
136
+ return await this.appendRelationsToCollection(entities, relations);
109
137
  }
110
138
 
111
139
  load(filters)
112
140
  {
113
- let queryBuilder = this.queryBuilder(true, true, true);
114
- this.appendFilters(queryBuilder, filters);
115
- return queryBuilder;
141
+ return this.repository.find(filters, this.queryBuilder(true, true, true));
116
142
  }
117
143
 
118
- loadWithRelations(filters, relations)
144
+ async loadWithRelations(filters, relations)
119
145
  {
120
- let queryBuilder = this.queryBuilder(true, true, true);
121
- this.appendFilters(queryBuilder, filters);
122
- return this.appendRelationsToQuery(queryBuilder, relations);
146
+ let entitiesCollection = await this.repository.findAll(filters, this.queryBuilder(true, true, true));
147
+ return await this.appendRelationsToCollection(entitiesCollection, relations);
123
148
  }
124
149
 
125
150
  loadBy(field, fieldValue, operator = null)
126
151
  {
127
- let queryBuilder = this.queryBuilder(true, true, true);
128
- this.appendSingleFilter(queryBuilder, field, fieldValue, operator);
129
- return queryBuilder;
152
+ let filter = this.createSingleFilter(field, fieldValue, operator);
153
+ return this.repository.find(filter, this.queryBuilder(true, true, true));
130
154
  }
131
155
 
132
- loadByWithRelations(field, fieldValue, relations, operator = null)
156
+ async loadByWithRelations(field, fieldValue, relations, operator = null)
133
157
  {
134
- let queryBuilder = this.queryBuilder(true, true, true);
135
- this.appendSingleFilter(queryBuilder, field, fieldValue, operator);
136
- return this.appendRelationsToQuery(queryBuilder, relations);
158
+ let filter = this.createSingleFilter(field, fieldValue, operator);
159
+ let entitiesCollection = await this.repository.find(filter, this.queryBuilder(true, true, true));
160
+ return await this.appendRelationsToCollection(entitiesCollection, relations);
137
161
  }
138
162
 
139
163
  loadById(id)
140
164
  {
141
- return this.queryBuilder().findById(id);
165
+ return this.loadBy({id});
142
166
  }
143
167
 
144
- loadByIdWithRelations(id, relations)
168
+ async loadByIdWithRelations(id, relations)
145
169
  {
146
- return this.appendRelationsToQuery(this.queryBuilder().findById(id), relations);
170
+ let entity = await this.loadBy({id});
171
+ return await this.appendRelationsToCollection(entity, relations);
147
172
  }
148
173
 
149
174
  loadByIds(ids)
150
175
  {
151
- return this.queryBuilder().findByIds(ids);
176
+ this.repository.find({id: {$in: ids}});
152
177
  }
153
178
 
154
179
  loadOne(filters)
155
180
  {
156
- let queryBuilder = this.queryBuilder(false, true, true);
157
- this.appendFilters(queryBuilder, filters);
158
- return queryBuilder.limit(1).first();
181
+ return this.repository.findOne(filters, this.queryBuilder(false, true, true));
159
182
  }
160
183
 
161
- loadOneWithRelations(filters, relations)
184
+ async loadOneWithRelations(filters, relations)
162
185
  {
163
- let queryBuilder = this.queryBuilder(false, true, true);
164
- this.appendFilters(queryBuilder, filters);
165
- return this.appendRelationsToQuery(queryBuilder, relations).limit(1).first();
186
+ let entitiesCollection = await this.loadOne(filters);
187
+ return this.appendRelationsToCollection(entitiesCollection, relations);
166
188
  }
167
189
 
168
190
  loadOneBy(field, fieldValue, operator = null)
169
191
  {
170
- let queryBuilder = this.queryBuilder(false, true, true);
171
- this.appendSingleFilter(queryBuilder, field, fieldValue, operator);
172
- return queryBuilder.limit(1).first();
192
+ let filter = this.createSingleFilter(field, fieldValue, operator);
193
+ return this.repository.findOne(filter, this.queryBuilder(false, true, true));
173
194
  }
174
195
 
175
- loadOneByWithRelations(field, fieldValue, relations, operator = null)
196
+ async loadOneByWithRelations(field, fieldValue, relations, operator = null)
176
197
  {
177
- let queryBuilder = this.queryBuilder(false, true, true);
178
- this.appendSingleFilter(queryBuilder, field, fieldValue, operator);
179
- return this.appendRelationsToQuery(queryBuilder, relations).limit(1).first();
198
+ let filter = this.createSingleFilter(field, fieldValue, operator);
199
+ let entitiesCollection = await this.repository.findOne(filter, this.queryBuilder(false, true, true));
200
+ return this.appendRelationsToCollection(entitiesCollection, relations);
180
201
  }
181
202
 
182
203
  queryBuilder(useLimit = false, useOffset = false, useSort = false)
183
204
  {
184
- let queryBuilder = this.orm.em.getKnex();
205
+ let queryBuilder = {};
185
206
  if(useLimit && 0 !== this.limit){
186
- queryBuilder.limit(this.limit)
207
+ queryBuilder.limit = this.limit;
187
208
  }
188
209
  if(useOffset && 0 !== this.offset){
189
- queryBuilder.offset(this.offset)
210
+ queryBuilder.offset = this.offset;
190
211
  }
191
212
  if(useSort && false !== this.sortBy && false !== this.sortDirection){
192
- queryBuilder.orderBy(this.sortBy, this.sortDirection)
213
+ queryBuilder.orderBy = {
214
+ [this.sortBy]: this.sortDirection,
215
+ };
193
216
  }
194
217
  return queryBuilder;
195
218
  }
196
219
 
197
- appendSingleFilter(queryBuilder, field, fieldValue, operator = null)
220
+ createSingleFilter(field, fieldValue, operator = null)
198
221
  {
199
- (null === operator) ? queryBuilder.where(field, fieldValue) : queryBuilder.where(field, operator, fieldValue);
200
- return queryBuilder;
222
+ let filter = {};
223
+ if(null === operator) {
224
+ filter[field] = fieldValue;
225
+ return filter;
226
+ }
227
+ filter[field] = {};
228
+ filter[field][operator] = fieldValue;
229
+ return filter;
201
230
  }
202
231
 
203
- appendFilters(queryBuilder, filters)
232
+ async appendRelationsToCollection(entitiesCollection, relations)
204
233
  {
205
- let filtersKeys = Object.keys(filters);
206
- if(0 === filtersKeys.length){
207
- return queryBuilder;
234
+ // @TODO - BETA - Refactor. I would like to use populate but it may not work if the driver is not Mongo DB.
235
+ if(typeof this.rawModel.entity.relationsMappings !== 'function'){
236
+ return false;
208
237
  }
209
- for(let i of filtersKeys){
210
- let filter = filters[i];
211
- sc.hasOwn(filter, 'operator')
212
- ? queryBuilder.where(i, filter.operator, filter.value)
213
- : queryBuilder.where(i, filter);
238
+ if(!sc.isArray(relations) || 0 === relations.length){
239
+ relations = Object.keys(this.rawModel.entity.relationsMappings() || {});
214
240
  }
215
- return queryBuilder;
241
+ if(0 === relations.length){
242
+ return entitiesCollection;
243
+ }
244
+ for(let entity of entitiesCollection){
245
+ await this.appendRelatedEntities(entity, relations);
246
+ }
247
+ return entitiesCollection;
216
248
  }
217
249
 
218
- async appendRelationsToQuery(queryBuilder, relations)
250
+ async appendRelatedEntities(entity, relations)
219
251
  {
220
- if(!sc.isArray(relations) || 0 === relations.length){
221
- relations = Object.keys(this.rawModel.relationMappings || {});
252
+ // @TODO - BETA - Refactor and improve.
253
+ if(typeof this.rawModel.entity.relationsMappings !== 'function'){
254
+ return false;
222
255
  }
223
- if(0 < relations.length){
224
- let result = await queryBuilder;
225
- return this.orm.em.populate(result, relations);
256
+ let relationsMappings = this.rawModel.entity.relationsMappings();
257
+ for(let i of relations){
258
+ let relation = relationsMappings[i];
259
+ let relationRepository = this.server.getEntity(relation.entityName);
260
+ let isManyToOne = 'm:1' === relation.reference;
261
+ let isOneToMany = '1:m' === relation.reference;
262
+ if(isManyToOne){
263
+ entity[i] = await relationRepository.loadOneBy(relation.join.to, entity[relation.join.from]);
264
+ }
265
+ if(isOneToMany){
266
+ entity[i] = await relationRepository.loadBy(relation.join.to, entity[relation.join.from]);
267
+ }
226
268
  }
227
- return queryBuilder;
269
+ }
270
+
271
+ async createNested(newInstance, params)
272
+ {
273
+ if(typeof this.rawModel.entity.relationsMappings !== 'function'){
274
+ return false;
275
+ }
276
+ let relationsMappings = this.rawModel.entity.relationsMappings();
277
+ for(let i of Object.keys(relationsMappings)){
278
+ // @TODO - BETA - Refactor and improve.
279
+ let relationData = relationsMappings[i];
280
+ let relationEntity = this.server.entityManager.get(relationData.entityName).rawModel.entity;
281
+ if(!relationEntity){
282
+ Logger.warning('Factory not found for relation definition:', relationData);
283
+ continue;
284
+ }
285
+ let isManyToOne = 'm:1' === relationData.reference;
286
+ let isOneToMany = '1:m' === relationData.reference;
287
+ if(isManyToOne){
288
+ if(!sc.hasOwn(params, relationData.join.from) || !params[relationData.join.from]){
289
+ continue;
290
+ }
291
+ await this.createOne(params, i, relationEntity, newInstance, relationData);
292
+ continue;
293
+ }
294
+ if(isOneToMany){
295
+ await this.createMany(params, i, relationEntity, newInstance, relationData);
296
+ }
297
+ }
298
+ }
299
+
300
+ async createOne(params, i, relationEntity, newInstance, relationData)
301
+ {
302
+ params[i] = newInstance[relationData.join.from];
303
+ let nestedObject = relationEntity.createByProps(params[i]);
304
+ await this.repository.persist(nestedObject).flush();
305
+ await this.orm.em.flush();
306
+ newInstance[i] = nestedObject;
307
+ }
308
+
309
+ async createMany(params, i, relationEntity, newInstance, relationData)
310
+ {
311
+ let nestedArray = [];
312
+ for(let objectData of params[i]){
313
+ objectData[relationData.join.to] = newInstance[relationData.join.from];
314
+ let nestedObject = relationEntity.createByProps(objectData);
315
+ await this.repository.persist(nestedObject).flush();
316
+ await this.orm.em.flush();
317
+ nestedArray.push(nestedObject);
318
+ }
319
+ newInstance[i] = nestedArray;
228
320
  }
229
321
 
230
322
  }
@@ -64,7 +64,14 @@ class ObjectionJsDriver extends BaseDriver
64
64
  return this.queryBuilder().patchAndFetchById(id, params);
65
65
  }
66
66
 
67
- delete(id)
67
+ delete(filters)
68
+ {
69
+ let queryBuilder = this.queryBuilder(true, true, true);
70
+ this.appendFilters(queryBuilder, filters);
71
+ return queryBuilder.delete();
72
+ }
73
+
74
+ deleteById(id)
68
75
  {
69
76
  return this.queryBuilder().deleteById(id);
70
77
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.10.0-beta.32",
4
+ "version": "0.10.0-beta.36",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -44,10 +44,10 @@
44
44
  "url": "https://github.com/damian-pastorini/reldens-storage/issues"
45
45
  },
46
46
  "dependencies": {
47
- "@mikro-orm/core": "^4.5.10",
48
- "@mikro-orm/mongodb": "^4.5.10",
49
- "@reldens/utils": "^0.10.0-beta.3",
50
- "knex": "^1.0.1",
47
+ "@mikro-orm/core": "^5.0.1",
48
+ "@mikro-orm/mongodb": "^5.0.1",
49
+ "@reldens/utils": "^0.10.0-beta.4",
50
+ "knex": "^1.0.3",
51
51
  "mysql": "^2.18.1",
52
52
  "objection": "^3.0.1"
53
53
  }