masterrecord 0.0.24 → 0.0.25

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.
@@ -0,0 +1,138 @@
1
+
2
+ var tools = require('./Tools');
3
+ class InsertManager {
4
+ // TODO: WE are using the relationshipTable and the object name stick to one so that hasMany doesnt have to give a name
5
+ constructor(sqlEngine, errorModel, allEntities ){
6
+ this._SQLEngine = sqlEngine;
7
+ this._errorModel = errorModel;
8
+ this._allEntities = allEntities;
9
+ }
10
+
11
+ init(currentModel){
12
+ this.runQueries(currentModel);
13
+ }
14
+
15
+ runQueries(currentModel){
16
+ var $that = this;
17
+ var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel.__entity);
18
+ this.validateEntity(cleanCurrentModel, currentModel, currentModel.__entity);
19
+ if(this._errorModel.isValid){
20
+
21
+ var modelEntity = currentModel.__entity;
22
+ // TODO: if you try to add belongs to you must have a tag added first. if you dont throw error
23
+ currentModel = this.belongsToInsert(currentModel, modelEntity);
24
+ var SQL = this._SQLEngine.insert(currentModel);
25
+ var primaryKey = tools.getPrimaryKeyObject(currentModel.__entity);
26
+ // return all fields that have auto and dont have a value to the current model on insert
27
+ if(currentModel.__entity[primaryKey].auto === true){
28
+ var getQueryModel = this._SQLEngine.get(`select * from ${currentModel.__entity.__name} where ${primaryKey} = ${ SQL.id }`);
29
+ currentModel[primaryKey] = getQueryModel[primaryKey];
30
+ }
31
+
32
+ const modelKeys = Object.keys(currentModel);
33
+ // loop through model properties
34
+ for (const property of modelKeys) {
35
+ var propertyModel = currentModel[property];
36
+ var entityProperty = modelEntity[property] ? modelEntity[property] : {};
37
+ if(entityProperty.type === "hasOne"){
38
+ // make sure property model is an object not a primary data type like number or string
39
+ if(typeof(propertyModel) === "object"){
40
+ // check if model has its own entity
41
+ if(modelEntity){
42
+ propertyModel.__entity = tools.getEntity(property, $that._allEntities);
43
+ propertyModel[currentModel.__entity.__name] = SQL.id;
44
+ $that.runQueries(propertyModel);
45
+ }
46
+ else{
47
+ throw `Relationship "${entityProperty.name}" could not be found please check if object has correct spelling or if it has been added to the context class`
48
+ }
49
+ }
50
+ }
51
+
52
+ if(entityProperty.type === "hasMany"){
53
+ if(Array.isArray(propertyModel)){
54
+ const propertyKeys = Object.keys(propertyModel);
55
+ for (const propertykey of propertyKeys) {
56
+ if(propertyModel[propertykey]){
57
+ propertyModel[propertykey].__entity = tools.getEntity(property, $that._allEntities);
58
+ propertyModel[propertykey][currentModel.__entity.__name] = SQL.id;
59
+ $that.runQueries(propertyModel[propertykey]);
60
+ }
61
+ }
62
+ }
63
+ else{
64
+ throw `Relationship "${entityProperty.name}" must be an array`;
65
+ }
66
+ }
67
+
68
+ }
69
+ }
70
+ else{
71
+ var name = currentModel.__entity.__name;
72
+ console.log(`entity ${name} not valid`);
73
+ }
74
+ }
75
+
76
+ // will insert belongs to row first and return the id so that next call can be make correctly
77
+ belongsToInsert(currentModel, modelEntity){
78
+ var $that = this;
79
+ for(var entity in modelEntity) {
80
+ if(modelEntity[entity].type === "belongsTo"){
81
+ var foreignKey = modelEntity[entity].foreignKey === undefined ? modelEntity[entity].name : modelEntity[entity].foreignKey;
82
+ var newPropertyModel = currentModel[foreignKey];
83
+ // check if model is a value because if so then skip we dont need to make an ajax call
84
+ if(typeof newPropertyModel === 'object'){
85
+ newPropertyModel.__entity = tools.getEntity(entity, $that._allEntities);
86
+ var propertyCleanCurrentModel = tools.removePrimarykeyandVirtual(newPropertyModel, newPropertyModel.__entity);
87
+ this.validateEntity(propertyCleanCurrentModel, newPropertyModel, newPropertyModel.__entity);
88
+ var propertySQL = this._SQLEngine.insert(newPropertyModel);
89
+ currentModel[foreignKey] = propertySQL.id;
90
+ }
91
+ }
92
+ }
93
+ // todo:
94
+ // loop through all modelEntity and find all the belongs to
95
+ // if belongs to is true then make sql call to insert
96
+ // update the currentModel.
97
+ return currentModel;
98
+ }
99
+ // validate entity for nullable fields
100
+ validateEntity(currentModel, currentRealModel, entityModel){
101
+ for(var entity in entityModel) {
102
+ var currentEntity = entityModel[entity];
103
+ if (entityModel.hasOwnProperty(entity)) {
104
+ // check if there is a default value
105
+ if(currentEntity.default){
106
+ if(!currentRealModel[entity]){
107
+ // if its empty add the default value
108
+ currentRealModel[entity] = currentEntity.default;
109
+ }
110
+ }
111
+
112
+ // SKIP belongs too
113
+ if(currentEntity.type !== "belongsTo"){
114
+ // primary is always null in an insert so validation insert must be null
115
+ if(currentEntity.nullable === false && !currentEntity.primary){
116
+ // if it doesnt have a get method then call error
117
+ if(currentEntity.get === undefined){
118
+ if(currentModel[entity] === undefined || currentModel[entity] === null || currentModel[entity] === "" ){
119
+ this._errorModel.isValid = false;
120
+ var errorMessage = `Entity ${currentModel.__entity.__name} column ${entity} is a required Field`;
121
+ this._errorModel.errors.push(errorMessage);
122
+ throw errorMessage;
123
+ }
124
+ }
125
+ else{
126
+ currentRealModel[entity] = currentEntity.get(currentModel[entity]);
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ }
133
+ }
134
+
135
+ }
136
+
137
+
138
+ module.exports = InsertManager;
package/MYSQLEngine.js ADDED
@@ -0,0 +1,409 @@
1
+ var tools = require('masterrecord/Tools');
2
+
3
+ class SQLLiteEngine {
4
+
5
+ unsupportedWords = ["order"]
6
+
7
+ update(query){
8
+ var sqlQuery = ` UPDATE [${query.tableName}]
9
+ SET ${query.arg}
10
+ WHERE [${query.tableName}].[${query.primaryKey}] = ${query.primaryKeyValue}` // primary key for that table =
11
+ return this._run(sqlQuery);
12
+ }
13
+
14
+ delete(queryObject){
15
+ var sqlObject = this._buildDeleteObject(queryObject);
16
+ var sqlQuery = `DELETE FROM [${sqlObject.tableName}] WHERE [${sqlObject.tableName}].[${sqlObject.primaryKey}] = ${sqlObject.value}`;
17
+ return this._execute(sqlQuery);
18
+ }
19
+
20
+ insert(queryObject){
21
+ var sqlObject = this._buildSQLInsertObject(queryObject, queryObject.__entity);
22
+ var query = `INSERT INTO [${sqlObject.tableName}] (${sqlObject.columns})
23
+ VALUES (${sqlObject.values})`;
24
+ var queryObj = this._run(query);
25
+ var open = {
26
+ "id": queryObj.lastInsertRowid
27
+ };
28
+ return open;
29
+ }
30
+
31
+ get(query, entity, context){
32
+ var queryString = {};
33
+ try {
34
+ if(query.raw){
35
+ queryString.query = query.raw;
36
+ }
37
+ else{
38
+ queryString = this.buildQuery(query, entity, context);
39
+ }
40
+ if(queryString.query){
41
+ console.log("SQL:", queryString.query);
42
+ var queryReturn = this.db.prepare(queryString.query).get();
43
+ return queryReturn;
44
+ }
45
+ return null;
46
+ } catch (err) {
47
+ console.error(err);
48
+ return null;
49
+ }
50
+ }
51
+
52
+ getCount(queryObject, entity, context){
53
+ var query = queryObject.script;
54
+ var queryString = {};
55
+ try {
56
+ if(query.raw){
57
+ queryString.query = query.raw;
58
+ }
59
+ else{
60
+ queryString = this.buildQuery(query, entity, context);
61
+ }
62
+ if(queryString.query){
63
+ var queryCount = queryObject.count(queryString.query)
64
+ console.log("SQL:", queryCount );
65
+ var queryReturn = this.db.prepare(queryCount ).get();
66
+ return queryReturn;
67
+ }
68
+ return null;
69
+ } catch (err) {
70
+ console.error(err);
71
+ return null;
72
+ }
73
+ }
74
+
75
+ all(query, entity, context){
76
+ var selectQuery = {};
77
+ try {
78
+ if(query.raw){
79
+ selectQuery.query = query.raw;
80
+ }
81
+ else{
82
+ selectQuery = this.buildQuery(query, entity, context);
83
+ }
84
+ if(selectQuery.query){
85
+ console.log("SQL:", selectQuery.query);
86
+ var queryReturn = this.db.prepare(selectQuery.query).all();
87
+ return queryReturn;
88
+ }
89
+ return null;
90
+ } catch (err) {
91
+ console.error(err);
92
+ return null;
93
+ }
94
+ }
95
+
96
+
97
+ buildQuery(query, entity, context){
98
+
99
+ var queryObject = {};
100
+ queryObject.entity = this.getEntity(entity.__name, query.entityMap);
101
+ queryObject.select = this.buildSelect(query, entity);
102
+ queryObject.from = this.buildFrom(query, entity);
103
+ queryObject.include = this.buildInclude(query, entity, context, queryObject);
104
+ queryObject.where = this.buildWhere(query, entity);
105
+
106
+ var queryString = `${queryObject.select} ${queryObject.from} ${queryObject.include} ${queryObject.where}`;
107
+ return {
108
+ query : queryString,
109
+ entity : this.getEntity(entity.__name, query.entityMap)
110
+ }
111
+
112
+ }
113
+
114
+ buildWhere(query, mainQuery){
115
+ var whereEntity = query.where;
116
+ var strQuery = "";
117
+ var $that = this;
118
+ if(whereEntity){
119
+ var entity = this.getEntity(query.parentName, query.entityMap);
120
+ for (let part in whereEntity[query.parentName]) {
121
+ var item = whereEntity[query.parentName][part];
122
+ for (let exp in item.expressions) {
123
+ var field = tools.capitalizeFirstLetter(item.expressions[exp].field);
124
+ if(mainQuery[field]){
125
+ if(mainQuery[field].isNavigational){
126
+ entity = $that.getEntity(field, query.entityMap);
127
+ field = item.fields[1];
128
+ }
129
+ }
130
+ if(strQuery === ""){
131
+ strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
132
+ }
133
+ else{
134
+ strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return strQuery;
140
+ }
141
+
142
+ buildInclude( query, entity, context){
143
+ var includeQuery = "";
144
+ for (let part in query.include) {
145
+ var includeEntity = query.include[part];
146
+ var $that = this;
147
+ if(includeEntity){
148
+ var parentObj = includeEntity[query.parentName];
149
+ var currentContext = "";
150
+ if(includeEntity.selectFields){
151
+ currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
152
+ }
153
+
154
+ if(parentObj){
155
+ parentObj.entityMap = query.entityMap;
156
+ var foreignKey = $that.getForeignKey(entity.__name, currentContext.__entity);
157
+ var mainPrimaryKey = $that.getPrimarykey(entity);
158
+ var mainEntity = $that.getEntity(entity.__name, query.entityMap);
159
+ if(currentContext.__entity[entity.__name].type === "hasManyThrough"){
160
+ var foreignTable = tools.capitalizeFirstLetter(currentContext.__entity[entity.__name].foreignTable); //to uppercase letter
161
+ foreignKey = $that.getPrimarykey(currentContext.__entity);
162
+ mainPrimaryKey = context[foreignTable].__entity[currentContext.__entity.__name].foreignKey;
163
+ var mainEntity = $that.getEntity(foreignTable,query.entityMap);
164
+ }
165
+ // add foreign key to select so that it picks it up
166
+ if(parentObj.select){
167
+ parentObj.select.selectFields.push(foreignKey);
168
+ }else{
169
+ parentObj.select = {};
170
+ parentObj.select.selectFields = [];
171
+ parentObj.select.selectFields.push(foreignKey);
172
+ }
173
+
174
+ var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
175
+
176
+ includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
177
+
178
+ }
179
+ }
180
+ }
181
+ return includeQuery;
182
+ }
183
+
184
+ buildFrom(query, entity){
185
+ var entityName = this.getEntity(entity.__name, query.entityMap);
186
+ if(entityName ){
187
+ return `FROM ${entity.__name } AS ${entityName}`;
188
+ }
189
+ else{ return "" }
190
+ }
191
+
192
+ buildSelect(query, entity){
193
+ // this means that there is a select statement
194
+ var select = "SELECT";
195
+ var arr = "";
196
+ var $that = this;
197
+ if(query.select){
198
+ for (const item in query.select.selectFields) {
199
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
200
+ };
201
+
202
+ }
203
+ else{
204
+ var entityList = this.getEntityList(entity);
205
+ for (const item in entityList) {
206
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
207
+ };
208
+ }
209
+ arr = arr.replace(/,\s*$/, "");
210
+ return `${select} ${arr} `;
211
+ }
212
+
213
+ getForeignKey(name, entity){
214
+ if(entity && name){
215
+ return entity[name].foreignKey;
216
+ }
217
+ }
218
+
219
+ getPrimarykey(entity){
220
+ for (const item in entity) {
221
+ if(entity[item].primary){
222
+ if(entity[item].primary === true){
223
+ return entity[item].name;
224
+ }
225
+ }
226
+ };
227
+ }
228
+
229
+ getForeignTable(name, entity){
230
+ if(entity && name){
231
+ return entity[name].foreignTable;
232
+ }
233
+ }
234
+
235
+ getInclude(name, query){
236
+ var include = query.include;
237
+ if(include){
238
+ for (let part in include) {
239
+ if(tools.capitalizeFirstLetter(include[part].selectFields[0]) === name){
240
+ return include[part];
241
+ }
242
+ }
243
+ }
244
+ else{
245
+ return "";
246
+ }
247
+ }
248
+
249
+ getEntity(name, maps){
250
+ for (let item in maps) {
251
+ var map = maps[item];
252
+ if(tools.capitalizeFirstLetter(name) === map.name){
253
+ return map.entity
254
+ }
255
+ }
256
+ return "";
257
+ }
258
+
259
+ // return a list of entity names and skip foreign keys and underscore.
260
+ getEntityList(entity){
261
+ var entitiesList = [];
262
+ var $that = this;
263
+ for (var ent in entity) {
264
+ if(!ent.startsWith("_")){
265
+ if(!entity[ent].foreignKey){
266
+ if(entity[ent].relationshipTable){
267
+ if($that.chechUnsupportedWords(entity[ent].relationshipTable)){
268
+ entitiesList.push(`'${entity[ent].relationshipTable}'`);
269
+ }
270
+ else{
271
+ entitiesList.push(entity[ent].relationshipTable);
272
+ }
273
+ }
274
+ else{
275
+ if($that.chechUnsupportedWords(ent)){
276
+ entitiesList.push(`'${ent}'`);
277
+ }
278
+ else{
279
+ entitiesList.push(ent);
280
+ }
281
+ }
282
+ }
283
+ }
284
+ }
285
+ return entitiesList
286
+ }
287
+
288
+ chechUnsupportedWords(word){
289
+ for (var item in this.unsupportedWords) {
290
+ var text = this.unsupportedWords[item];
291
+ if(text === word){
292
+ return true
293
+ }
294
+ }
295
+ return false;
296
+ }
297
+
298
+ startTransaction(){
299
+ this.db.prepare('BEGIN').run();
300
+ }
301
+
302
+ endTransaction(){
303
+ this.db.prepare('COMMIT').run();
304
+ }
305
+
306
+ errorTransaction(){
307
+ this.db.prepare('ROLLBACK').run();
308
+ }
309
+
310
+ _buildSQLEqualTo(model){
311
+ var $that = this;
312
+ var argument = null;
313
+ var dirtyFields = model.__dirtyFields;
314
+
315
+ for (var column in dirtyFields) {
316
+ // TODO Boolean value is a string with a letter
317
+ switch(model.__entity[dirtyFields[column]].type){
318
+ case "integer" :
319
+ argument = argument === null ? `[${dirtyFields[column]}] = ${model[dirtyFields[column]]},` : `${argument} [${dirtyFields[column]}] = ${model[dirtyFields[column]]},`;
320
+ break;
321
+ case "string" :
322
+ argument = argument === null ? `[${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',` : `${argument} [${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',`;
323
+ break;
324
+ default:
325
+ argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
326
+ }
327
+ }
328
+ return argument.replace(/,\s*$/, "");
329
+ }
330
+
331
+
332
+ _buildDeleteObject(currentModel){
333
+ var primaryKey = currentModel.__Key === undefined ? tools.getPrimaryKeyObject(currentModel.__entity) : currentModel.__Key;
334
+ var value = currentModel.__value === undefined ? currentModel[primaryKey] : currentModel.__value;
335
+ var tableName = currentModel.__tableName === undefined ? currentModel.__entity.__name : currentModel.__tableName;
336
+ return {tableName: tableName, primaryKey : primaryKey, value : value};
337
+ }
338
+
339
+
340
+ // return columns and value strings
341
+ _buildSQLInsertObject(fields, modelEntity){
342
+ var $that = this;
343
+ var columns = null;
344
+ var values = null;
345
+ for (var column in modelEntity) {
346
+ // column1 = value1, column2 = value2, ...
347
+ if(column.indexOf("__") === -1 ){
348
+ // call the get method if avlable
349
+ var fieldColumn = "";
350
+ // check if get function is avaliable if so use that
351
+ fieldColumn = fields[column];
352
+
353
+ if((fieldColumn !== undefined && fieldColumn !== null && fieldColumn !== "" ) && typeof(fieldColumn) !== "object"){
354
+ switch(modelEntity[column].type){
355
+ case "belongsTo" :
356
+ column = modelEntity[column].relationshipTable === undefined ? column : modelEntity[column].relationshipTable;
357
+ break;
358
+ case "string" :
359
+ fieldColumn = `'${$that._santizeSingleQuotes(fields[column])}'`;
360
+ break;
361
+ case "time" :
362
+ fieldColumn = fields[column];
363
+ break;
364
+ }
365
+
366
+ columns = columns === null ? `'${column}',` : `${columns} '${column}',`;
367
+ values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
368
+
369
+ }
370
+ }
371
+ }
372
+ return {tableName: modelEntity.__name, columns: columns.replace(/,\s*$/, ""), values: values.replace(/,\s*$/, "")};
373
+
374
+ }
375
+
376
+ // will add double single quotes to allow sting to be saved.
377
+ _santizeSingleQuotes(string){
378
+ return string.replace(/'/g, "''");
379
+ }
380
+
381
+ // converts any object into SQL parameter select string
382
+ _convertEntityToSelectParameterString(obj, entityName){
383
+ // todo: loop throgh object and append string with comma to
384
+ var mainString = "";
385
+ const entries = Object.keys(obj);
386
+
387
+ for (const [name] of entries) {
388
+ mainString += `${mainString}, ${entityName}.${name}`;
389
+ }
390
+ return mainString;;
391
+ }
392
+
393
+ _execute(query){
394
+ console.log("SQL:", query);
395
+ return this.db.exec(query);
396
+ }
397
+
398
+ _run(query){
399
+ console.log("SQL:", query);
400
+ return this.db.prepare(query).run();
401
+ }
402
+
403
+ setDB(db, type){
404
+ this.db = db;
405
+ this.dbType = type; // this will let us know which type of sqlengine to use.
406
+ }
407
+ }
408
+
409
+ module.exports = SQLLiteEngine;