@reldens/storage 0.10.0-beta.29 → 0.10.0-beta.33

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")
package/index.js CHANGED
@@ -11,6 +11,7 @@ const { ObjectionJsDataServer } = require('./lib/objection-js/objection-js-data-
11
11
  const { Model } = require('objection');
12
12
  const { MikroOrmDriver } = require('./lib/mikro-orm/mikro-orm-driver');
13
13
  const { MikroOrmDataServer } = require('./lib/mikro-orm/mikro-orm-data-server');
14
+ const MikroOrmCore = require('@mikro-orm/core');
14
15
 
15
16
  module.exports = {
16
17
  // base:
@@ -21,6 +22,7 @@ module.exports = {
21
22
  ObjectionJsDriver: ObjectionJsDriver,
22
23
  ObjectionJsRawModel: Model,
23
24
  // mikro-orm:
25
+ MikroOrmCore,
24
26
  MikroOrmDataServer: MikroOrmDataServer,
25
27
  MikroOrmDriver: MikroOrmDriver
26
28
  };
@@ -12,21 +12,36 @@ class BaseDataServer
12
12
 
13
13
  constructor(props)
14
14
  {
15
- this.config = sc.get(props, 'config', false);
16
- this.client = sc.get(props, 'client', false);
17
- this.poolConfig = sc.get(props, 'poolConfig', false);
18
- this.connectString = sc.get(props, 'connectString', false);
15
+ this.config = sc.get(props, 'config', {});
16
+ if(!this.config.host){
17
+ this.config.host = 'localhost';
18
+ }
19
+ this.client = sc.get(props, 'client', '');
20
+ this.poolConfig = sc.get(props, 'poolConfig', {});
21
+ this.connectStringOptions = sc.get(props, 'connectStringOptions', '');
22
+ this.connectString = sc.get(props, 'connectString', this.createConnectionString());
19
23
  this.rawModel = sc.get(props, 'rawModel', false);
20
24
  this.name = sc.get(props, 'name', false);
21
25
  this.initialized = sc.get(props, 'initialized', false);
22
26
  this.rawEntities = props.rawEntities;
23
- let entities = props.entities || props.rawEntities ? this.generateEntities(props.rawEntities) : {};
24
- this.entityManager = new EntityManager({entities});
27
+ this.entities = props.entities || {};
28
+ this.entityManager = new EntityManager({entities: this.entities});
25
29
  }
26
30
 
27
- connect()
31
+ createConnectionString()
28
32
  {
29
- ErrorManager.error('BaseDriver connect() not implemented.');
33
+ return (this.client || 'client-not-specified')+'://'
34
+ +(this.config.user || 'user-not-specified')
35
+ +(this.config.password ? ':'+this.config.password : '')
36
+ +'@'+this.config.host
37
+ +':'+(this.config.port || 3306)
38
+ +(this.config.database ? '/'+this.config.database : '')
39
+ +(this.connectStringOptions ? '?'+this.connectStringOptions : '');
40
+ }
41
+
42
+ getEntity(entityName)
43
+ {
44
+ return this.entityManager.get(entityName);
30
45
  }
31
46
 
32
47
  generateEntities(rawEntities)
@@ -34,9 +49,9 @@ class BaseDataServer
34
49
  ErrorManager.error('BaseDriver generateEntities() not implemented.');
35
50
  }
36
51
 
37
- getEntity(entityName)
52
+ async connect()
38
53
  {
39
- return this.entityManager.get(entityName);
54
+ ErrorManager.error('BaseDriver connect() not implemented.');
40
55
  }
41
56
 
42
57
  }
@@ -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.');
@@ -4,15 +4,64 @@
4
4
  *
5
5
  */
6
6
 
7
+ const { MikroOrmDriver } = require('./mikro-orm-driver');
7
8
  const { BaseDataServer } = require('../base-data-server');
9
+ const { MikroORM } = require('@mikro-orm/core');
10
+ const { Logger } = require('@reldens/utils');
8
11
 
9
12
  class MikroOrmDataServer extends BaseDataServer
10
13
  {
11
14
 
12
- constructor(props)
15
+ orm = {};
16
+ clientsMapped = {
17
+ mysql: 'mysql',
18
+ mongodb: 'mongo'
19
+ };
20
+
21
+ async connect()
22
+ {
23
+ if(this.initialized){
24
+ return this.initialized;
25
+ }
26
+ this.orm = await MikroORM.init({
27
+ entities: Object.values(this.rawEntities),
28
+ type: this.clientsMapped[(this.client || 'mongo')],
29
+ clientUrl: this.connectString,
30
+ allowGlobalContext: true
31
+ });
32
+ this.initialized = Date.now();
33
+ return this.initialized;
34
+ }
35
+
36
+ generateEntities(rawEntities)
37
+ {
38
+ if(!this.initialized){
39
+ Logger.critical('In order to generate entities with Mikro ORM driver you need to connect to the server.');
40
+ return {};
41
+ }
42
+ if(!this.rawEntities){
43
+ Logger.warning('Empty raw entities array, none entities generated.');
44
+ return {};
45
+ }
46
+ this.entities = {};
47
+ for(let i of Object.keys(rawEntities)){
48
+ let rawEntity = rawEntities[i];
49
+ this.entities[i] = new MikroOrmDriver({
50
+ rawModel: rawEntity,
51
+ id: i,
52
+ name: i,
53
+ config: this.orm.driver.connection.options,
54
+ orm: this.orm,
55
+ server: this
56
+ });
57
+ }
58
+ this.entityManager.setEntities(this.entities);
59
+ return this.entities;
60
+ }
61
+
62
+ name()
13
63
  {
14
- super(props);
15
- // @TODO: implement.
64
+ return this.name || 'Mikro-ORM Data Server Driver';
16
65
  }
17
66
 
18
67
  }
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  const { BaseDriver } = require('../base-driver');
8
+ const { ErrorManager, Logger, sc } = require('@reldens/utils');
8
9
 
9
10
  class MikroOrmDriver extends BaseDriver
10
11
  {
@@ -12,7 +13,302 @@ class MikroOrmDriver extends BaseDriver
12
13
  constructor(props)
13
14
  {
14
15
  super(props);
15
- // @TODO: implement.
16
+ if(!props.orm){
17
+ ErrorManager.error('Missing ORM on Mikro ORM driver.');
18
+ }
19
+ if(!props.server){
20
+ ErrorManager.error('Missing Server Driver on Mikro ORM driver.');
21
+ }
22
+ if(!this.rawModel){
23
+ ErrorManager.error('Missing raw entity on Mikro ORM driver.');
24
+ }
25
+ this.orm = props.orm;
26
+ this.server = props.server;
27
+ this.repository = this.orm.em.getRepository(this.rawModel.entity);
28
+ }
29
+
30
+ databaseName()
31
+ {
32
+ return this.config.dbName || '';
33
+ }
34
+
35
+ id()
36
+ {
37
+ return this.rawModel.entity.name || this.name();
38
+ }
39
+
40
+ name()
41
+ {
42
+ return this.rawModel.entity.name || this.config.dbName;
43
+ }
44
+
45
+ tableName()
46
+ {
47
+ return this.rawModel.entity.name;
48
+ }
49
+
50
+ property(propertyName)
51
+ {
52
+ return this.rawModel[propertyName] || null;
53
+ }
54
+
55
+ async create(params)
56
+ {
57
+ let newInstance = this.rawModel.factory.create(params);
58
+ await this.repository.persist(newInstance).flush();
59
+ return newInstance;
60
+ }
61
+
62
+ async createWithRelations(params, relations)
63
+ {
64
+ let newInstance = await this.create(params, relations);
65
+ await this.createNested(newInstance, params);
66
+ return newInstance;
67
+ }
68
+
69
+ async update(filters, updatePatch)
70
+ {
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;
80
+ }
81
+
82
+ async updateBy(field, fieldValue, updatePatch, operator = null)
83
+ {
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;
94
+ }
95
+
96
+ updateById(id, params)
97
+ {
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
+ }
110
+ }
111
+
112
+ deleteById(id)
113
+ {
114
+ return this.repository.nativeDelete(id);
115
+ }
116
+
117
+ async count(filters)
118
+ {
119
+ return this.repository.count(filters);
120
+ }
121
+
122
+ async countWithRelations(filters, relations)
123
+ {
124
+ // @TODO - BETA - Look a way to filter by sub-entities properties before count.
125
+ return this.count(filters);
126
+ }
127
+
128
+ loadAll()
129
+ {
130
+ return this.repository.findAll();
131
+ }
132
+
133
+ async loadAllWithRelations(relations)
134
+ {
135
+ let entities = await this.repository.find(filter, this.queryBuilder(true, true, true));
136
+ return await this.appendRelationsToCollection(entities, relations);
137
+ }
138
+
139
+ load(filters)
140
+ {
141
+ return this.repository.find(filters, this.queryBuilder(true, true, true));
142
+ }
143
+
144
+ async loadWithRelations(filters, relations)
145
+ {
146
+ let entitiesCollection = await this.repository.findAll(filters, this.queryBuilder(true, true, true));
147
+ return await this.appendRelationsToCollection(entitiesCollection, relations);
148
+ }
149
+
150
+ loadBy(field, fieldValue, operator = null)
151
+ {
152
+ let filter = this.createSingleFilter(field, fieldValue, operator);
153
+ return this.repository.find(filter, this.queryBuilder(true, true, true));
154
+ }
155
+
156
+ async loadByWithRelations(field, fieldValue, relations, operator = null)
157
+ {
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);
161
+ }
162
+
163
+ loadById(id)
164
+ {
165
+ return this.loadBy({id});
166
+ }
167
+
168
+ async loadByIdWithRelations(id, relations)
169
+ {
170
+ let entity = await this.loadBy({id});
171
+ return await this.appendRelationsToCollection(entity, relations);
172
+ }
173
+
174
+ loadByIds(ids)
175
+ {
176
+ this.repository.find({id: {$in: ids}});
177
+ }
178
+
179
+ loadOne(filters)
180
+ {
181
+ return this.repository.findOne(filters, this.queryBuilder(false, true, true));
182
+ }
183
+
184
+ async loadOneWithRelations(filters, relations)
185
+ {
186
+ let entitiesCollection = await this.loadOne(filters);
187
+ return this.appendRelationsToCollection(entitiesCollection, relations);
188
+ }
189
+
190
+ loadOneBy(field, fieldValue, operator = null)
191
+ {
192
+ let filter = this.createSingleFilter(field, fieldValue, operator);
193
+ return this.repository.findOne(filter, this.queryBuilder(false, true, true));
194
+ }
195
+
196
+ async loadOneByWithRelations(field, fieldValue, relations, operator = null)
197
+ {
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);
201
+ }
202
+
203
+ queryBuilder(useLimit = false, useOffset = false, useSort = false)
204
+ {
205
+ let queryBuilder = {};
206
+ if(useLimit && 0 !== this.limit){
207
+ queryBuilder.limit = this.limit;
208
+ }
209
+ if(useOffset && 0 !== this.offset){
210
+ queryBuilder.offset = this.offset;
211
+ }
212
+ if(useSort && false !== this.sortBy && false !== this.sortDirection){
213
+ queryBuilder.orderBy = {
214
+ [this.sortBy]: this.sortDirection,
215
+ };
216
+ }
217
+ return queryBuilder;
218
+ }
219
+
220
+ createSingleFilter(field, fieldValue, operator = null)
221
+ {
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;
230
+ }
231
+
232
+ async appendRelationsToCollection(entitiesCollection, relations)
233
+ {
234
+ // @TODO - BETA - Refactor. I would like to use populate but it may not work if the driver is not Mongo DB.
235
+ if(!sc.isArray(relations) || 0 === relations.length){
236
+ relations = Object.keys(this.rawModel.relationsMappings || {});
237
+ }
238
+ if(0 === relations.length){
239
+ return entitiesCollection;
240
+ }
241
+ for(let entity of entitiesCollection){
242
+ await this.appendRelatedEntities(entity, relations);
243
+ }
244
+ return entitiesCollection;
245
+ }
246
+
247
+ async appendRelatedEntities(entity, relations)
248
+ {
249
+ // @TODO - BETA - Refactor and improve.
250
+ for(let i of relations){
251
+ let relation = this.rawModel.relationsMappings[i];
252
+ let relationRepository = this.server.getEntity(relation.entityName);
253
+ let isManyToOne = 'm:1' === relation.reference;
254
+ let isOneToMany = '1:m' === relation.reference;
255
+ if(isManyToOne){
256
+ entity[i] = await relationRepository.loadOneBy(relation.join.to, entity[relation.join.from]);
257
+ }
258
+ if(isOneToMany){
259
+ entity[i] = await relationRepository.loadBy(relation.join.to, entity[relation.join.from]);
260
+ }
261
+ }
262
+ }
263
+
264
+ async createNested(newInstance, params)
265
+ {
266
+ if(!sc.hasOwn(this.rawModel, 'relationsMappings')){
267
+ return false;
268
+ }
269
+ for(let i of Object.keys(this.rawModel.relationsMappings)){
270
+ // @TODO - BETA - Refactor and improve.
271
+ let relationData = this.rawModel.relationsMappings[i];
272
+ let relationFactory = this.server.entityManager.get(relationData.entityName).rawModel.factory;
273
+ if(!relationFactory){
274
+ Logger.warning('Factory not found for relation definition:', relationData);
275
+ continue;
276
+ }
277
+ let isManyToOne = 'm:1' === relationData.reference;
278
+ let isOneToMany = '1:m' === relationData.reference;
279
+ if(isManyToOne){
280
+ if(!sc.hasOwn(params, relationData.join.from) || !params[relationData.join.from]){
281
+ continue;
282
+ }
283
+ await this.createOne(params, i, relationFactory, newInstance, relationData);
284
+ continue;
285
+ }
286
+ if(isOneToMany){
287
+ await this.createMany(params, i, relationFactory, newInstance, relationData);
288
+ }
289
+ }
290
+ }
291
+
292
+ async createOne(params, i, relationFactory, newInstance, relationData)
293
+ {
294
+ params[i] = newInstance[relationData.join.from];
295
+ let nestedObject = relationFactory.create(params[i]);
296
+ await this.repository.persist(nestedObject).flush();
297
+ await this.orm.em.flush();
298
+ newInstance[i] = nestedObject;
299
+ }
300
+
301
+ async createMany(params, i, relationFactory, newInstance, relationData)
302
+ {
303
+ let nestedArray = [];
304
+ for(let objectData of params[i]){
305
+ objectData[relationData.join.to] = newInstance[relationData.join.from];
306
+ let nestedObject = relationFactory.create(objectData);
307
+ await this.repository.persist(nestedObject).flush();
308
+ await this.orm.em.flush();
309
+ nestedArray.push(nestedObject);
310
+ }
311
+ newInstance[i] = nestedArray;
16
312
  }
17
313
 
18
314
  }
@@ -8,6 +8,7 @@ const { BaseDataServer } = require('../base-data-server');
8
8
  const { ObjectionJsDriver } = require('./objection-js-driver');
9
9
  const { Model } = require('objection');
10
10
  const Knex = require('knex');
11
+ const { Logger } = require('@reldens/utils');
11
12
 
12
13
  class ObjectionJsDataServer extends BaseDataServer
13
14
  {
@@ -21,26 +22,32 @@ class ObjectionJsDataServer extends BaseDataServer
21
22
  }
22
23
  }
23
24
 
24
- connect()
25
+ async connect()
25
26
  {
26
27
  if(this.initialized){
27
28
  return this.initialized;
28
29
  }
29
30
  // initialize knex, the query builder:
30
- this.knex = Knex({client: this.client, connection: this.config, pool: this.poolConfig});
31
+ this.knex = await Knex({client: this.client, connection: this.config, pool: this.poolConfig});
31
32
  // give the knex instance to Objection:
32
33
  this.rawModel.knex(this.knex);
33
34
  this.initialized = Date.now();
35
+ return this.initialized;
34
36
  }
35
37
 
36
- generateEntities(rawEntities)
38
+ generateEntities()
37
39
  {
38
- let entities = {};
39
- for(let i of Object.keys(rawEntities)){
40
- let rawEntity = rawEntities[i];
41
- entities[i] = new ObjectionJsDriver({rawModel: rawEntity, id: i, name: i, config: rawEntity.knex});
40
+ if(!this.rawEntities){
41
+ Logger.warning('Empty raw entities array, none entities generated.');
42
+ return {};
42
43
  }
43
- return entities;
44
+ this.entities = {};
45
+ for(let i of Object.keys(this.rawEntities)){
46
+ let rawEntity = this.rawEntities[i];
47
+ this.entities[i] = new ObjectionJsDriver({rawModel: rawEntity, id: i, name: i, config: rawEntity.knex});
48
+ }
49
+ this.entityManager.setEntities(this.entities);
50
+ return this.entities;
44
51
  }
45
52
 
46
53
  name()
@@ -10,11 +10,6 @@ const { sc } = require('@reldens/utils');
10
10
  class ObjectionJsDriver extends BaseDriver
11
11
  {
12
12
 
13
- constructor(props)
14
- {
15
- super(props);
16
- }
17
-
18
13
  databaseName()
19
14
  {
20
15
  return this.rawModel.knex().client.config.connection.database || '';
@@ -69,7 +64,14 @@ class ObjectionJsDriver extends BaseDriver
69
64
  return this.queryBuilder().patchAndFetchById(id, params);
70
65
  }
71
66
 
72
- 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)
73
75
  {
74
76
  return this.queryBuilder().deleteById(id);
75
77
  }
@@ -209,10 +211,10 @@ class ObjectionJsDriver extends BaseDriver
209
211
 
210
212
  appendRelationsToQuery(queryBuilder, relations)
211
213
  {
212
- if(!sc.isArray(relations) || relations.length === 0){
214
+ if(!sc.isArray(relations) || 0 === relations.length){
213
215
  relations = Object.keys(this.rawModel.relationMappings || {});
214
216
  }
215
- if(relations.length > 0){
217
+ if(0 < relations.length){
216
218
  queryBuilder.withGraphFetched('['+relations.join(',')+']');
217
219
  }
218
220
  return queryBuilder;
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.29",
4
+ "version": "0.10.0-beta.33",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -44,9 +44,11 @@
44
44
  "url": "https://github.com/damian-pastorini/reldens-storage/issues"
45
45
  },
46
46
  "dependencies": {
47
- "@reldens/utils": "^0.10.0-beta.3",
48
- "knex": "^0.95.15",
49
- "objection": "^3.0.1",
50
- "mysql": "^2.18.1"
47
+ "@mikro-orm/core": "^5.0.0",
48
+ "@mikro-orm/mongodb": "^5.0.0",
49
+ "@reldens/utils": "^0.10.0-beta.4",
50
+ "knex": "^1.0.3",
51
+ "mysql": "^2.18.1",
52
+ "objection": "^3.0.1"
51
53
  }
52
54
  }