masterrecord 0.1.3 → 0.2.0

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,4646 @@
1
+ /*
2
+
3
+ :binary
4
+ :boolean
5
+ :date
6
+ :datetime
7
+ :decimal
8
+ :float
9
+ :integer
10
+ :bigint
11
+ :primary_key
12
+ :references
13
+ :string
14
+ :text
15
+ :time
16
+ :timestamp
17
+
18
+ */
19
+
20
+ // version 0.0.5
21
+ class EntityModel {
22
+
23
+ constructor(name){
24
+ this.obj = {
25
+ name: name,
26
+ type: null,
27
+ relationshipType: null,
28
+ typeSize : null,
29
+ primary : null,
30
+ default : null,
31
+ virtual : null,
32
+ foreignKey : null,
33
+ nullable : true, // no
34
+ unique : false,
35
+ auto : false,
36
+ cascadeOnDelete : true,
37
+ lazyLoading : true,
38
+ isNavigational : false,
39
+ skipGetFunction :false,
40
+ valueConversion : true
41
+
42
+ }
43
+ }
44
+
45
+ type(type, size){
46
+ this.obj.type = type;
47
+ this.obj.typeSize = size;
48
+ return this;
49
+ }
50
+
51
+ string(){
52
+ this.obj.type = "string";
53
+ return this;
54
+ }
55
+
56
+ integer(){
57
+ this.obj.type = "integer";
58
+ return this;
59
+ }
60
+
61
+ time(){
62
+ this.obj.type = "time";
63
+ return this;
64
+ }
65
+
66
+ boolean(){
67
+ this.obj.type = "boolean";
68
+ return this;
69
+ }
70
+
71
+ // maxLength(amount){
72
+ // this.obj.maxLength = amount;
73
+ // return this;
74
+ // }
75
+
76
+ // will stop cascade delete which means it will stop not auto delete relationship
77
+ stopCascadeOnDelete(){
78
+ this.obj.cascadeOnDelete = false;
79
+ return this;
80
+ }
81
+
82
+ // is this obj a primary key
83
+ primary(){
84
+ this.obj.primary = true;
85
+ this.obj.nullable = false;
86
+ this.obj.unique = true;
87
+ return this;
88
+ }
89
+
90
+ // allows ablity to get back primaryKey on insert automaticlly return on insert
91
+ auto(){
92
+ this.obj.auto = true;
93
+ return this;
94
+ }
95
+
96
+ // sets the default value in the DB
97
+ default(value){
98
+ this.obj.default = value;
99
+ return this;
100
+ }
101
+
102
+ get(func){
103
+ this.obj.get = func;
104
+ return this;
105
+ }
106
+
107
+ set(func){
108
+ this.obj.set = func;
109
+ return this;
110
+ }
111
+
112
+ unique(){
113
+ this.obj.unique = true; // yes
114
+ return this;
115
+
116
+ }
117
+
118
+ // this means that it can be an empty field
119
+ nullable(){
120
+ this.obj.nullable = true; // yes
121
+ return this;
122
+ }
123
+
124
+ notNullable(){
125
+ this.obj.nullable = false; // no
126
+ return this;
127
+ }
128
+
129
+ //allows you to stop lazy loading because lazy loading is added by default
130
+ lazyLoadingOff(){
131
+ this.obj.lazyLoading = false;
132
+ return this;
133
+ }
134
+
135
+ valueConversion(bool){
136
+ this.obj.valueConversion = bool;
137
+ return this;
138
+ }
139
+
140
+ // allows you to add a virtual object that will skipped from being used as sql objects
141
+ virtual(){
142
+ this.obj.virtual = true;
143
+ return this;
144
+ }
145
+
146
+ hasMany(foreignTable, foreignKey){
147
+ if(foreignKey === undefined){
148
+ foreignKey = `${this.obj.name.toLowerCase()}_id`;
149
+ }
150
+ this.obj.relationshipType = "hasMany";
151
+ this.obj.type = "hasMany";
152
+ this.obj.foreignTable = foreignTable;
153
+ this.obj.foreignKey = foreignKey;
154
+ this.obj.isNavigational = true;
155
+ this.obj.nullable = false;
156
+ return this;
157
+ }
158
+
159
+ // DB must have a record or exception will be thrown unless set to nullable
160
+ hasOne(foreignTable, foreignKey){
161
+ if(foreignKey === undefined){
162
+ foreignKey = `${this.obj.name.toLowerCase()}_id`;
163
+ }
164
+ this.obj.relationshipType = "hasOne";
165
+ this.obj.type = "hasOne";
166
+ this.obj.foreignTable = foreignTable;
167
+ this.obj.foreignKey = foreignKey;
168
+ this.obj.isNavigational = true;
169
+ this.obj.nullable = false;
170
+ return this;
171
+ }
172
+
173
+ // will do a inner join with foreignKey
174
+ //hasManyThrough("Tagging", "tag_id") ----- if foreignKey is not provided use the name of the object_id
175
+ hasManyThrough(foreignTable, foreignKey ){
176
+ if(foreignKey === undefined){
177
+ foreignKey = `${this.obj.name.toLowerCase()}_id`;
178
+ };
179
+ this.obj.relationshipType = "hasManyThrough";
180
+ this.obj.type = "hasManyThrough";
181
+ this.obj.foreignTable = foreignTable;// if joinKey is undefined then use name of object.
182
+ this.obj.foreignKey = foreignKey; // Foreign Key table
183
+ this.obj.isNavigational = true;
184
+ return this;
185
+ }
186
+
187
+ // will get info
188
+ belongsTo(foreignTable, foreignKey){
189
+
190
+ if(foreignKey === undefined){
191
+ foreignKey = `${foreignTable.toLowerCase()}_id`;
192
+ }
193
+ // will use table name to find forien key
194
+ this.obj.type = "integer";
195
+ this.obj.relationshipType = "belongsTo";
196
+
197
+ this.obj.foreignTable = foreignTable; // this is the table name of the current table if diffrent from the object name
198
+ this.obj.foreignKey = foreignKey; // this is the table name of the joining table
199
+ this.obj.nullable = false; // this means it cannot be null
200
+ return this
201
+ }
202
+
203
+ foreignKey(foreignKey){
204
+ this.obj.foreignKey = foreignKey;
205
+ this.obj.nullable = false;
206
+ return this
207
+ }
208
+
209
+ foreignTable(foreignTable){
210
+ this.obj.foreignTable = foreignTable;
211
+ this.obj.nullable = false;
212
+ return this
213
+ }
214
+ }
215
+ module.exports = EntityModel;
216
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Entity/entityModel.js **
217
+ // version 0.0.3
218
+
219
+ var modelDB = require('./entityModel');
220
+
221
+ // creates new instance if entity model and calls inner functions to build out a valid entity
222
+ class EntityModelBuilder {
223
+
224
+ static create(model){
225
+ if(model.name === undefined){
226
+ throw "dbset model declaired incorrectly. Check you dbset models for code errors."
227
+ }
228
+ var mod = new model(); //create new instance of Entity Model
229
+ var obj = {};
230
+ var methodNamesArray = Object.getOwnPropertyNames( mod.__proto__ );
231
+ var constructorIndex = methodNamesArray.indexOf("constructor");
232
+ // remove contructor method
233
+ if (constructorIndex > -1) {
234
+ methodNamesArray.splice(constructorIndex, 1);
235
+ }
236
+ // loop through all method names in the entity model
237
+ for (var i = 0; i < methodNamesArray.length; i++) {
238
+ let MDB = new modelDB(model.name); // create a new instance of entity Model class
239
+ mod[methodNamesArray[i]](MDB);
240
+ this.cleanNull(MDB.obj); // remove objects that are null or undefined
241
+ if(Object.keys(MDB.obj).length === 0){
242
+ MDB.obj.virtual = true;
243
+ }
244
+ MDB.obj.name = methodNamesArray[i];
245
+ obj[methodNamesArray[i]] = MDB.obj;
246
+ }
247
+ return obj;
248
+ }
249
+
250
+ static cleanNull(obj) {
251
+ for (var propName in obj) {
252
+ if (obj[propName] === null) {
253
+ delete obj[propName];
254
+ }
255
+ }
256
+ }
257
+
258
+ }
259
+
260
+ module.exports = EntityModelBuilder;
261
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Entity/entityModelBuilder.js **
262
+
263
+ // version : 0.0.7
264
+ var tools = require('../Tools');
265
+ class EntityTrackerModel {
266
+
267
+
268
+ // entity states https://docs.microsoft.com/en-us/dotnet/api/system.data.entitystate?view=netframework-4.7.2
269
+
270
+ // start tracking model
271
+ build(dataModel, currentEntity, context){
272
+ var $that = this;
273
+ var modelClass = this.buildObject(); // build entity with models
274
+ modelClass.__proto__ = {};
275
+ const modelFields = Object.entries(dataModel); /// return array of objects
276
+ modelClass.__entity = currentEntity;
277
+ modelClass.__name = currentEntity.__name;
278
+ modelClass.__context = context;
279
+ this.buildRelationshipModels(modelClass, currentEntity, dataModel);
280
+
281
+ // loop through data model fields
282
+ for (const [modelField, modelFieldValue] of modelFields) {
283
+
284
+ // set the value dynamiclly
285
+ if(!$that._isRelationship(currentEntity[modelField])){
286
+ // current entity has a value then add
287
+ modelClass["__proto__"]["_" + modelField] = modelFieldValue;
288
+
289
+ Object.defineProperty(modelClass,modelField, {
290
+ set: function(value) {
291
+ modelClass.__state = "modified";
292
+ modelClass.__dirtyFields.push(modelField);
293
+ if(typeof currentEntity[modelField].set === "function"){
294
+ this["__proto__"]["_" + modelField] = currentEntity[modelField].set(value);
295
+ }else{
296
+ // Then it will add name to dirty fields
297
+ this["__proto__"]["_" + modelField] = value;
298
+ }
299
+ },
300
+ get:function(){
301
+ // TODO: fix only when updating
302
+ if(currentEntity[modelField]){
303
+ if(!currentEntity[modelField].skipGetFunction){
304
+ if(typeof currentEntity[modelField].get === "function"){
305
+ return currentEntity[modelField].get(this["__proto__"]["_" + modelField]);
306
+ }else{
307
+ return this["__proto__"]["_" + modelField];
308
+ }
309
+ }
310
+ }else{
311
+ return this["__proto__"]["_" + modelField];
312
+ }
313
+ }
314
+ });
315
+ }
316
+ }
317
+
318
+
319
+ return modelClass;
320
+ }
321
+
322
+ buildObject(){
323
+ return {
324
+ __ID : Math.floor((Math.random() * 100000) + 1),
325
+ __dirtyFields : [],
326
+ __state : "track",
327
+ __entity : null,
328
+ __context : null,
329
+ __name : null
330
+ }
331
+ }
332
+
333
+ _isRelationship(entity){
334
+ if(entity){
335
+ if(entity.type === "hasOne" || entity.type === "hasMany" || entity.relationshipType === "belongsTo" || entity.type === "hasManyThrough"){
336
+ return true;
337
+ }
338
+ else{
339
+ return false;
340
+ }
341
+ }else{
342
+ return false;
343
+ }
344
+ }
345
+
346
+ buildRelationshipModels(modelClass, currentEntity, currentModel){
347
+ var $that = this;
348
+ // loop though current entity and add only relationship models to this list
349
+ const entityFields = Object.entries(currentEntity);
350
+ for (const [entityField, entityFieldValue] of entityFields) { // loop through entity values
351
+
352
+ if($that._isRelationship(currentEntity[entityField])){
353
+
354
+
355
+ Object.defineProperty(modelClass, entityField, {
356
+ set: function(value) {
357
+ if(typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" ){
358
+ modelClass.__state = "modified";
359
+ modelClass.__dirtyFields.push(entityField);
360
+ modelClass.__context.__track(modelClass);
361
+ }
362
+ this["__proto__"]["_" + entityField] = value;
363
+ },
364
+ get : function(){
365
+ var ent = tools.findEntity(entityField, this.__context);
366
+ if(!ent){
367
+ var parentEntity = tools.findEntity(this.__name, this.__context);
368
+ if(parentEntity){
369
+ ent = tools.findEntity(parentEntity.__entity[entityField].foreignTable, this.__context);
370
+ if(!ent){
371
+ return `Error - Entity ${parentEntity.__entity[entityField].foreignTable} not found. Please check your context for proper name.`
372
+ }
373
+ }
374
+ else{
375
+ return `Error - Entity ${parentEntity} not found. Please check your context for proper name.`
376
+ }
377
+ }
378
+
379
+
380
+ if(currentEntity[entityField].relationshipType === "belongsTo"){
381
+ if(currentEntity[entityField].lazyLoading){
382
+ // TODO: UPDATE THIS CODE TO USE SOMETHING ELSE - THIS WILL NOT WORK WHEN USING DIFFERENT DATABASES BECAUSE THIS IS USING SQLITE CODE.
383
+
384
+ var name = currentEntity[entityField].foreignKey;
385
+ var priKey = tools.getPrimaryKeyObject(ent.__entity);
386
+
387
+ //var idValue = currentEntity[entityField].foreignKey;
388
+ var currentValue = this.__proto__[`_${name}`];
389
+ var val = this["__proto__"]["_"+entityField];
390
+ var modelValue = null;
391
+ if(!val){
392
+ modelValue = ent.where(`r => r.${priKey} == ${ currentValue }`).single();
393
+
394
+ }
395
+ else{
396
+ modelValue = val;
397
+ }
398
+
399
+ this[entityField] = modelValue;
400
+ }
401
+ else{
402
+ return this["__proto__"]["_" + entityField];
403
+ }
404
+ }
405
+ else{
406
+ // user.tags = gets all tags related to user
407
+ // tag.users = get all users related to tags
408
+ if(currentEntity[entityField].lazyLoading){
409
+ var priKey = tools.getPrimaryKeyObject(this.__entity);
410
+ var entityName = currentEntity[entityField].foreignTable === undefined ? entityField : currentEntity[entityField].foreignTable;
411
+ var tableName = "";
412
+ if(entityName){
413
+ switch(currentEntity[entityField].type){
414
+ // TODO: move the SQL generation part to the SQL builder so that we can later on use many diffrent types of SQL databases.
415
+ case "hasManyThrough" :
416
+ try{
417
+ var joiningEntity = this.__context[tools.capitalize(entityName)];
418
+ var entityFieldJoinName = currentEntity[entityField].foreignTable === undefined? entityField : currentEntity[entityField].foreignTable;
419
+ var thirdEntity = this.__context[tools.capitalize(entityFieldJoinName)];
420
+ var firstJoiningID = joiningEntity.__entity[this.__entity.__name].foreignTable;
421
+ var secondJoiningID = joiningEntity.__entity[entityField].foreignTable;
422
+ if(firstJoiningID && secondJoiningID )
423
+ {
424
+ var modelValue = ent.include(`p => p.${entityFieldJoinName}.select(j => j.${joiningEntity.__entity[this.__entity.__name].foreignKey})`).include(`p =>p.${this.__entity.__name}`).where(`r =>r.${this.__entity.__name}.${priKey} = ${this[priKey]}`).toList();
425
+ // var modelQuery = `select ${selectParameter} from ${this.__entity.__name} INNER JOIN ${entityName} ON ${this.__entity.__name}.${priKey} = ${entityName}.${firstJoiningID} INNER JOIN ${entityField} ON ${entityField}.${joinTablePriKey} = ${entityName}.${secondJoiningID} WHERE ${this.__entity.__name}.${priKey} = ${ this[priKey]}`;
426
+ // var modelValue = ent.raw(modelQuery).toList();
427
+ this[entityField] = modelValue;
428
+ }
429
+ else{
430
+ return "Joining table must declaire joining table names"
431
+ }
432
+ }
433
+ catch(error){
434
+ return error;
435
+ }
436
+ /*
437
+ select * from User
438
+ INNER JOIN Tagging ON User.id = Tagging.user_id
439
+ INNER JOIN Tag ON Tag.id = Tagging.tag_id
440
+ WHERE Tagging.user_id = 13
441
+ */
442
+ break;
443
+ case "hasOne" :
444
+ var entityName = tools.findForeignTable(this.__entity.__name, ent.__entity);
445
+ if(entityName){
446
+ tableName = entityName.foreignKey;
447
+ }
448
+ else{
449
+ return `Error - Entity ${ent.__entity.__name} has no property named ${this.__entity.__name}`;
450
+ }
451
+
452
+ //var jj = ent.raw(`select * from ${entityName} where ${tableName} = ${ this[priKey] }`).single();
453
+ var modelValue = ent.where(`r => r.${tableName} == ${this[priKey]}`).single();
454
+ this[entityField] = modelValue;
455
+ break;
456
+ case "hasMany" :
457
+ var entityName = tools.findForeignTable(this.__entity.__name, ent.__entity);
458
+ if(entityName){
459
+ tableName = entityName.foreignKey;
460
+ }
461
+ else{
462
+ return `Error - Entity ${ent.__entity.__name} has no property named ${this.__entity.__name}`;
463
+ }
464
+ //var modelValue = ent.raw(`select * from ${entityName} where ${tableName} = ${ this[priKey] }`).toList();
465
+ var modelValue = ent.where(`r => r.${tableName} == ${this[priKey]}`).toList();
466
+ this[entityField] = modelValue;
467
+ break;
468
+ }
469
+ }
470
+ else{
471
+ return "Entity name must be defined"
472
+ }
473
+ }
474
+ else{
475
+ return this["__proto__"]["_" + entityField];
476
+ }
477
+ }
478
+
479
+
480
+ return this["__proto__"]["_" + entityField];
481
+ }
482
+ });
483
+
484
+ if(currentEntity[entityField].relationshipType === "belongsTo"){
485
+ // check if entity has a value if so then return that value
486
+ if(currentModel[entityField]){
487
+ modelClass[entityField] = currentModel[entityField];
488
+ }
489
+ }
490
+
491
+ }
492
+
493
+ }
494
+ }
495
+
496
+ }
497
+
498
+ module.exports = EntityTrackerModel
499
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Entity/entityTrackerModel.js **
500
+
501
+ // https://github.com/kriasoft/node-sqlite
502
+ // https://www.learnentityframeworkcore.com/dbset/deleting-data
503
+ // version 1.0.20
504
+ var context = require("./context");
505
+ var schema = require("./Migrations/schema");
506
+
507
+ class masterrecord{
508
+
509
+ constructor(){
510
+ this.context = context;
511
+ this.schema = schema;
512
+ }
513
+ }
514
+
515
+
516
+ module.exports = new masterrecord();
517
+
518
+
519
+
520
+ /*
521
+
522
+ //Create new standard
523
+ var standard = new Standard();
524
+ standard.StandardName = "Standard1";
525
+
526
+ //create three new teachers
527
+ var teacher1 = new Teacher();
528
+ teacher1.TeacherName = "New Teacher1";
529
+
530
+ var teacher2 = new Teacher();
531
+ teacher2.TeacherName = "New Teacher2";
532
+
533
+ var teacher3 = new Teacher();
534
+ teacher3.TeacherName = "New Teacher3";
535
+
536
+ //add teachers for new standard
537
+ standard.Teachers.Add(teacher1);
538
+ standard.Teachers.Add(teacher2);
539
+ standard.Teachers.Add(teacher3);
540
+
541
+ using (var dbCtx = new SchoolDBEntities())
542
+ {
543
+ //add standard entity into standards entitySet
544
+ dbCtx.Standards.Add(standard);
545
+ //Save whole entity graph to the database
546
+ dbCtx.SaveChanges();
547
+ }
548
+ */
549
+
550
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/MasterRecord.js **
551
+ #!/usr/bin/env node
552
+
553
+ // version 0.0.4
554
+ // https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/migrations/
555
+ // how to add environment variables on cli call example - master=development masterrecord add-migration auth authContext
556
+
557
+ const program = require('commander');
558
+ let fs = require('fs');
559
+ let path = require('path');
560
+ var Migration = require('./migrations');
561
+ var globSearch = require("glob");
562
+
563
+
564
+ const [,, ...args] = process.argv
565
+
566
+
567
+
568
+ program
569
+ .version('0.0.3', '-v, --version', '0.0.3')
570
+ .description('A ORM framework that facilitates the creation and use of business objects whose data requires persistent storage to a database');
571
+
572
+ // Instructions : to run command you must go to main project folder is located and run the command using the context file name.
573
+ program
574
+ .command('enable-migrations <contextFileName>')
575
+ .alias('em')
576
+ .description('Enables the migration in your project by creating a configuration class called ContextSnapShot.json')
577
+ .action(function(contextFileName){
578
+
579
+ var migration = new Migration();
580
+ // location of folder where command is being executed..
581
+ var executedLocation = process.cwd();
582
+ // find context file from main folder location
583
+ var contextInstance = migration.findContext(executedLocation, contextFileName);
584
+ var context = new contextInstance.context();
585
+ var snap = {
586
+ file : contextInstance.fileLocation,
587
+ executedLocation : executedLocation,
588
+ context : context,
589
+ contextEntities : [],
590
+ contextFileName: contextFileName.toLowerCase()
591
+ }
592
+
593
+ migration.createSnapShot(snap);
594
+ console.log("Migration enabled")
595
+
596
+ });
597
+
598
+ // program
599
+ // .command('create-database <contextFileName> <dbName>')
600
+ // .alias('cd')
601
+ // .description('allows you to create a database')
602
+ // .action(function(contextFileName, dbName){
603
+ // var executedLocation = process.cwd();
604
+ // contextFileName = contextFileName.toLowerCase();
605
+
606
+ // try{
607
+ // // find context file from main folder location
608
+ // // find context file from main folder location
609
+ // var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
610
+ // var files = globSearch.sync(search, executedLocation);
611
+ // var file = files[0];
612
+
613
+ // if(file){
614
+ // var contextSnapshot = require(file);
615
+ // var context = require(contextSnapshot.contextLocation);
616
+ // var newSchema = new schema(context);
617
+ // newSchema.createDatabase(dbName);
618
+ // }
619
+ // else{
620
+ // console.log("Error - Cannot read or find Context file");
621
+ // }
622
+
623
+
624
+ // }catch (e){
625
+ // console.log("Error - Cannot read or find file ", e);
626
+ // }
627
+ // console.log("Database Created");
628
+
629
+ // });
630
+
631
+
632
+ // Instructions : to run command you must go to folder where migration file is located.
633
+ program
634
+ .command('add-migration <name> <contextFileName>')
635
+ .alias('am')
636
+ .action(function(name, contextFileName){
637
+ var executedLocation = process.cwd();
638
+ contextFileName = contextFileName.toLowerCase();
639
+ var migration = new Migration();
640
+ try{
641
+ // find context file from main folder location
642
+ var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
643
+ var files = globSearch.sync(search, executedLocation);
644
+ var file = files[0];
645
+ if(file){
646
+ var contextSnapshot = require(files[0]);
647
+ var context = require(contextSnapshot.contextLocation);
648
+ var contextInstance = new context();
649
+ var cleanEntities = migration.cleanEntities(contextInstance.__entities);
650
+ var newEntity = migration.template(name, contextSnapshot.schema, cleanEntities);
651
+ var migrationDate = Date.now();
652
+ var file = `${contextSnapshot.migrationFolder}/${migrationDate}_${name}_migration.js`
653
+ fs.writeFile(file, newEntity, 'utf8', function (err) {
654
+ if (err) return console.log("--- Error running cammand, re-run command add-migration ---- ", err);
655
+ });
656
+ console.log(`${name} migration file created`);
657
+ }
658
+ else{
659
+ console.log("Error - Cannot read or find Context file");
660
+ }
661
+ }catch (e){
662
+ console.log("Error - Cannot read or find file ", e);
663
+ }
664
+ });
665
+
666
+ program
667
+ .command('update-database <contextFileName>')
668
+ .alias('ud')
669
+ .description('Apply pending migrations to database - up method call')
670
+ .action(function(contextFileName){
671
+ var executedLocation = process.cwd();
672
+ contextFileName = contextFileName.toLowerCase();
673
+ var migration = new Migration();
674
+ try{
675
+ // find context file from main folder location
676
+ var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
677
+ var files = globSearch.sync(search, executedLocation);
678
+ var file = files[0];
679
+ if(file){
680
+ var contextSnapshot = require(file);
681
+ var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
682
+ var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
683
+ if( migrationFiles){
684
+
685
+ // find newest migration file
686
+ // THIS DOESNT WORK BUG - hack to fix this I just take the last file which I am asuming is the newest one
687
+ var mFiles = migrationFiles.sort(function(x, y){
688
+ return new Date(x.timestamp) < new Date(y.timestamp) ? 1 : -1
689
+ });
690
+ /** ENd of BUG */
691
+
692
+ var mFile = mFiles[mFiles.length -1];
693
+
694
+ var migrationProjectFile = require(mFile);
695
+ var context = require(contextSnapshot.contextLocation);
696
+ var contextInstance = new context();
697
+ var newMigrationProjectInstance = new migrationProjectFile(context);
698
+
699
+ var tableObj = migration.buildUpObject(contextSnapshot.schema, contextInstance.__entities);
700
+
701
+ var cleanEntities = migration.cleanEntities(contextInstance.__entities);
702
+ newMigrationProjectInstance.up(tableObj);
703
+
704
+ var snap = {
705
+ file : contextSnapshot.contextLocation,
706
+ executedLocation : executedLocation,
707
+ context : contextInstance,
708
+ contextEntities : cleanEntities,
709
+ contextFileName: contextFileName
710
+ }
711
+
712
+ migration.createSnapShot(snap);
713
+ console.log("database updated");
714
+ }
715
+ else{
716
+ console.log("Error - Cannot read or find migration file");
717
+ }
718
+
719
+ }
720
+ else{
721
+ console.log("Error - Cannot read or find Context file");
722
+ }
723
+ }catch (e){
724
+ console.log("Error - Cannot read or find file ", e);
725
+ }
726
+ });
727
+
728
+
729
+ program
730
+ .command('update-database-restart <contextFileName>')
731
+ .alias('udr')
732
+ .description('Apply pending migrations to database - up method call')
733
+ .action(function(contextFileName){
734
+ var executedLocation = process.cwd();
735
+ contextFileName = contextFileName.toLowerCase();
736
+ var migration = new Migration();
737
+ try{
738
+ // find context file from main folder location
739
+ var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
740
+ var files = globSearch.sync(search, executedLocation);
741
+ var file = files[0];
742
+ if(file){
743
+ var contextSnapshot = require(file);
744
+ var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
745
+ var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
746
+ if( migrationFiles){
747
+
748
+
749
+ // organize by time
750
+ var mFiles = migrationFiles.sort(function(x, y){
751
+ return new Date(x.timestamp) < new Date(y.timestamp) ? 1 : -1;
752
+ });
753
+ var context = require(contextSnapshot.contextLocation);
754
+
755
+ for (let i = 0; i < mFiles.length; i++) {
756
+ var file = mFiles[i];
757
+ var migrationProjectFile = require(file);
758
+
759
+ var contextInstance = new context();
760
+ var newMigrationProjectInstance = new migrationProjectFile(context);
761
+ var tableObj = migration.buildUpObject(contextSnapshot.schema, contextInstance.__entities);
762
+ var cleanEntities = migration.cleanEntities(contextInstance.__entities);
763
+ newMigrationProjectInstance.up(tableObj);
764
+ }
765
+
766
+
767
+ var snap = {
768
+ file : contextSnapshot.contextLocation,
769
+ executedLocation : executedLocation,
770
+ context : contextInstance,
771
+ contextEntities : cleanEntities,
772
+ contextFileName: contextFileName
773
+ }
774
+
775
+ migration.createSnapShot(snap);
776
+ console.log("database updated");
777
+ }
778
+ else{
779
+ console.log("Error - Cannot read or find migration file");
780
+ }
781
+
782
+ }
783
+ else{
784
+ console.log("Error - Cannot read or find Context file");
785
+ }
786
+ }catch (e){
787
+ console.log("Error - Cannot read or find file ", e);
788
+ }
789
+ });
790
+
791
+
792
+ program
793
+ .command('get-migrations <contextFileName>')
794
+ .alias('gm')
795
+ .description('Get a list of migration file names using the context')
796
+ .action(function(contextFileName){
797
+ var executedLocation = process.cwd();
798
+ contextFileName = contextFileName.toLowerCase();
799
+ var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
800
+ var files = globSearch.sync(search, executedLocation);
801
+ var file = files[0];
802
+ if(file){
803
+ var contextSnapshot = require(file);
804
+ var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
805
+ var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
806
+ if( migrationFiles){
807
+ return migrationFiles;
808
+ }
809
+ }
810
+ else{
811
+ console.log("Error - Cannot read or find Context file");
812
+ }
813
+ });
814
+
815
+ // we will find the migration folder inside the nearest app folder if no migration folder is location is added
816
+ program
817
+ .command('update-database-target <migrationFileName>')
818
+ .alias('udt')
819
+ .description('Apply pending migrations to database - down method call')
820
+ .action(function(migrationFileName){
821
+ // this will call all the down methods until it gets to the one your looking for. First it needs to validate that there is such a file.
822
+ // TODO:
823
+
824
+ });
825
+
826
+
827
+
828
+ program.parse(process.argv);
829
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/cli.js **
830
+
831
+ // verison 0.0.2
832
+ class migrationMySQLQuery {
833
+
834
+ #tempTableName = "_temp_alter_column_update"
835
+
836
+ #getTableColumns(table){
837
+ var columnList = [];
838
+ for (var key in table) {
839
+ if(typeof table[key] === "object"){
840
+ columnList.push(table[key].name);
841
+ }
842
+ }
843
+ return columnList.join(',');;
844
+ }
845
+
846
+ #columnMapping(table){
847
+ /*
848
+ var mapping = {
849
+ "name": "id", // if this changes then call rename column
850
+ "type": "integer", // if this changes then call altercolumn
851
+ "primary": false, // is primary key
852
+ "nullable": false, // is nullable
853
+ "unique": true, // vlaue has to be uniqe
854
+ "auto": true, // sets the value to AUTOINCREMENT
855
+ "cascadeOnDelete": true,
856
+ "lazyLoading": true,
857
+ "isNavigational": false
858
+
859
+ }
860
+ */
861
+ // name TEXT NOT NULL,
862
+
863
+ var auto = table.auto ? " AUTO_INCREMENT":"";
864
+ var primaryKey = table.primary ? " PRIMARY KEY" : "";
865
+ var nullName = table.nullable ? "" : " NOT NULL";
866
+ var unique = table.unique ? " UNIQUE" : "";
867
+ var type = this.typeManager(table.type);
868
+ var tableName = table.name;
869
+ var defaultValue = "";
870
+ if(table.default != null){
871
+
872
+ defaultValue = ` DEFAULT ${this.boolType(table.default)}`
873
+ }
874
+ if(table.relationshipType === 'belongsTo'){
875
+ tableName = table.foreignKey;
876
+ }
877
+
878
+ return `${tableName} ${type}${nullName}${defaultValue}${unique}${primaryKey}${auto}`;
879
+ }
880
+
881
+ boolType(type){
882
+ switch(type) {
883
+ case "true":
884
+ return "1"
885
+ break;
886
+ case "false":
887
+ return "0"
888
+ break;
889
+ case true:
890
+ return "1"
891
+ break;
892
+ case false:
893
+ return "0"
894
+ break;
895
+ default:
896
+ return type;
897
+ }
898
+ }
899
+
900
+ typeManager(type){
901
+ switch(type) {
902
+ case "string":
903
+ return "VARCHAR(255)"
904
+ break;
905
+ case "text":
906
+ return "TEXT"
907
+ break;
908
+ case "float":
909
+ return "fLOAT(24)"
910
+ break;
911
+ case "decimal":
912
+ return "DECIMAL"
913
+ break;
914
+ case "datetime":
915
+ return "DATETIME"
916
+ break;
917
+ case "timestamp":
918
+ return "TIMESTAMP"
919
+ break;
920
+ case "date":
921
+ return "DATE"
922
+ break;
923
+ case "time":
924
+ return "TIME"
925
+ break;
926
+ case "boolean":
927
+ return "TINYINT"
928
+ break;
929
+ case "integer":
930
+ return "INTEGER"
931
+ break;
932
+ case "binary":
933
+ return "BLOB"
934
+ break;
935
+ case "blob":
936
+ return "BLOB"
937
+ break;
938
+ case "json":
939
+ return "JSON"
940
+ break;
941
+ }
942
+ // :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean
943
+ }
944
+
945
+ // table is the altered field
946
+ alterColumn( table){
947
+
948
+ if(table){
949
+
950
+ return `ALTER TABLE ${table.tableName} MODIFY COLUMN ${this.#columnMapping(table.table)} `;
951
+ }
952
+ else{
953
+ console.log("table information is null");
954
+ return null;
955
+ }
956
+ }
957
+
958
+ alterNullable(table){
959
+ // check if has value
960
+ if(typeof table.changes.nullable !== 'undefined'){
961
+ // if it does we want to add that to the alter statment
962
+ }
963
+ }
964
+
965
+
966
+ addColum(table){
967
+ return `ALTER TABLE ${table.tableName}
968
+ ADD ${table.name} ${table.realDataType}`;
969
+
970
+ /*
971
+ column definations
972
+ NULL
973
+ TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
974
+ BLOB. The value is a blob of data, stored exactly as it was input
975
+ INTEGER,
976
+ real
977
+ */
978
+ }
979
+
980
+ dropColumn(table){
981
+ /*
982
+ COLUMNS CANNOT BE DROPPED - RULES
983
+ has unique constraint
984
+ is indexed
985
+ appears in a view
986
+ */
987
+ return `ALTER TABLE ${table.tableName} DROP COLUMN ${table.name}`;
988
+ }
989
+
990
+ insertInto(name, table){
991
+ return `INSERT INTO ${name} (${this.#getTableColumns(table)})
992
+ SELECT ${this.#getTableColumns(table)} FROM ${this.#tempTableName}`;
993
+ }
994
+
995
+ createTable(table){
996
+
997
+ var queryVar = "";
998
+ //console.log("Dsfdsfdsf---------", table)
999
+ for (var key in table) {
1000
+ if(typeof table[key] === "object"){
1001
+
1002
+ if(table[key].type !== "hasOne" && table[key].type !== "hasMany" && table[key].type !== "hasManyThrough"){
1003
+ queryVar += `${this.#columnMapping(table[key])}, `;
1004
+ }
1005
+ }
1006
+ }
1007
+
1008
+ var completeQuery = `CREATE TABLE ${table.__name} (${queryVar.replace(/,\s*$/, "")});`;
1009
+ return completeQuery;
1010
+
1011
+ /*
1012
+ INTEGER PRIMARY KEY AUTOINCREMENT
1013
+ all these are equal to interger
1014
+ INT
1015
+ INTEGER
1016
+ TINYINT
1017
+ SMALLINT
1018
+ MEDIUMINT
1019
+ BIGINT
1020
+ UNSIGNED BIG INT
1021
+ INT2
1022
+ INT8
1023
+ */
1024
+ }
1025
+
1026
+
1027
+ dropTable(name){
1028
+ return `DROP TABLE ${name}`
1029
+ }
1030
+
1031
+ renameTable(table){
1032
+ return `ALTER TABLE ${table.tableName} RENAME TO ${table.newName}`;
1033
+ }
1034
+
1035
+ renameColumn(table){
1036
+ return `ALTER TABLE ${table.tableName} RENAME COLUMN ${table.name} TO ${table.newName}`
1037
+ }
1038
+
1039
+
1040
+ }
1041
+
1042
+
1043
+ module.exports = migrationMySQLQuery;
1044
+
1045
+ /**
1046
+ *
1047
+ *
1048
+ *
1049
+ *
1050
+ MySQL Data Types NATIVE_DATABASE_TYPES = {
1051
+ primary_key: "bigint auto_increment PRIMARY KEY",
1052
+ string: { name: "varchar", limit: 255 },
1053
+ text: { name: "text" },
1054
+ integer: { name: "int", limit: 4 },
1055
+ float: { name: "float", limit: 24 },
1056
+ decimal: { name: "decimal" },
1057
+ datetime: { name: "datetime" },
1058
+ timestamp: { name: "timestamp" },
1059
+ time: { name: "time" },
1060
+ date: { name: "date" },
1061
+ binary: { name: "blob" },
1062
+ blob: { name: "blob" },
1063
+ boolean: { name: "tinyint", limit: 1 },
1064
+ json: { name: "json" },
1065
+ }
1066
+
1067
+
1068
+ PostgreSQL Data Types NATIVE_DATABASE_TYPES = {
1069
+ primary_key: "bigserial primary key",
1070
+ string: { name: "character varying" },
1071
+ text: { name: "text" },
1072
+ integer: { name: "integer", limit: 4 },
1073
+ float: { name: "float" },
1074
+ decimal: { name: "decimal" },
1075
+ datetime: {}, # set dynamically based on datetime_type
1076
+ timestamp: { name: "timestamp" },
1077
+ timestamptz: { name: "timestamptz" },
1078
+ time: { name: "time" },
1079
+ date: { name: "date" },
1080
+ daterange: { name: "daterange" },
1081
+ numrange: { name: "numrange" },
1082
+ tsrange: { name: "tsrange" },
1083
+ tstzrange: { name: "tstzrange" },
1084
+ int4range: { name: "int4range" },
1085
+ int8range: { name: "int8range" },
1086
+ binary: { name: "bytea" },
1087
+ boolean: { name: "boolean" },
1088
+ xml: { name: "xml" },
1089
+ tsvector: { name: "tsvector" },
1090
+ hstore: { name: "hstore" },
1091
+ inet: { name: "inet" },
1092
+ cidr: { name: "cidr" },
1093
+ macaddr: { name: "macaddr" },
1094
+ uuid: { name: "uuid" },
1095
+ json: { name: "json" },
1096
+ jsonb: { name: "jsonb" },
1097
+ ltree: { name: "ltree" },
1098
+ citext: { name: "citext" },
1099
+ point: { name: "point" },
1100
+ line: { name: "line" },
1101
+ lseg: { name: "lseg" },
1102
+ box: { name: "box" },
1103
+ path: { name: "path" },
1104
+ polygon: { name: "polygon" },
1105
+ circle: { name: "circle" },
1106
+ bit: { name: "bit" },
1107
+ bit_varying: { name: "bit varying" },
1108
+ money: { name: "money" },
1109
+ interval: { name: "interval" },
1110
+ oid: { name: "oid" },
1111
+ }
1112
+ */
1113
+
1114
+
1115
+ /****
1116
+ *
1117
+ *
1118
+ * console.log("sdfdsfdsf", this.#tempTableName);
1119
+ return `ALTER TABLE ${table.tableName} MODIFY COLUMN NOT NULL`
1120
+ TODO -- We need to find a way build the alter query based on the data that is changed
1121
+ //ALTER TABLE MyTable MODIFY COLUMN comment BIGINT NOT NULL;
1122
+ if(table){
1123
+ table.newName = this.#tempTableName;
1124
+ //console.log("----------------------", table)
1125
+ return {
1126
+ 1 : this.renameTable(table),
1127
+ 2 : this.createTable(fullTable),
1128
+ 3 : this.insertInto(table.tableName, fullTable),
1129
+ 4 : this.dropTable(this.#tempTableName)
1130
+ }
1131
+ }
1132
+ else{
1133
+ console.log("table information is null")
1134
+ }
1135
+ */
1136
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrationMySQLQuery.js **
1137
+
1138
+ // verison 0.0.5
1139
+ class migrationSQLiteQuery {
1140
+
1141
+ #tempTableName = "_temp_alter_column_update"
1142
+
1143
+ #getTableColumns(table){
1144
+ var columnList = [];
1145
+ for (var key in table) {
1146
+ if(typeof table[key] === "object"){
1147
+ columnList.push(table[key].name);
1148
+ }
1149
+ }
1150
+ return columnList.join(',');;
1151
+ }
1152
+
1153
+ #columnMapping(table){
1154
+ /*
1155
+ var mapping = {
1156
+ "name": "id", // if this chnages then call rename column
1157
+ "type": "integer", // if this changes then call altercolumn
1158
+ "primary": false, // is primary key
1159
+ "nullable": false, // is nullable
1160
+ "unique": true, // vlaue has to be uniqe
1161
+ "auto": true, // sets the value to AUTOINCREMENT
1162
+ "cascadeOnDelete": true,
1163
+ "lazyLoading": true,
1164
+ "isNavigational": false
1165
+
1166
+ }
1167
+ */
1168
+ // name TEXT NOT NULL,
1169
+
1170
+ var auto = table.auto ? " AUTOINCREMENT":"";
1171
+ var primaryKey = table.primary ? " PRIMARY KEY" : "";
1172
+ var nullName = table.nullable ? "" : " NOT NULL";
1173
+ var unique = table.unique ? " UNIQUE" : "";
1174
+ var type = this.#typeManager(table.type);
1175
+
1176
+ return `${table.name} ${type}${nullName}${unique}${primaryKey}${auto}`;
1177
+ }
1178
+
1179
+ #typeManager(type){
1180
+ switch(type) {
1181
+ case "string":
1182
+ return "TEXT"
1183
+ break;
1184
+ case "time":
1185
+ return "TEXT"
1186
+ break;
1187
+ case "boolean":
1188
+ return "INTEGER"
1189
+ break;
1190
+ case "integer":
1191
+ return "INTEGER"
1192
+ break;
1193
+ }
1194
+
1195
+ }
1196
+
1197
+ alterColumn(fullTable, table){
1198
+ if(table){
1199
+ table.newName = this.#tempTableName;
1200
+ return {
1201
+ 1 : this.renameTable(table),
1202
+ 2 : this.createTable(fullTable),
1203
+ 3 : this.insertInto(table.tableName, fullTable),
1204
+ 4 : this.dropTable(this.#tempTableName)
1205
+ }
1206
+ }
1207
+ else{
1208
+ console.log("table information is null")
1209
+ }
1210
+ }
1211
+
1212
+
1213
+ addColum(table){
1214
+ return `ALTER TABLE ${table.tableName}
1215
+ ADD COLUMN ${table.name}`;
1216
+
1217
+ /*
1218
+ column definations
1219
+ NULL
1220
+ TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
1221
+ BLOB. The value is a blob of data, stored exactly as it was input
1222
+ INTEGER,
1223
+ real
1224
+ */
1225
+ }
1226
+
1227
+ dropColumn(table){
1228
+ /*
1229
+ COLUMNS CANNOT BE DROPPED - RULES
1230
+ has unique constraint
1231
+ is indexed
1232
+ appears in a view
1233
+ */
1234
+ return `ALTER TABLE ${table.tableName} DROP COLUMN ${table.name}`
1235
+ }
1236
+
1237
+ insertInto(name, table){
1238
+ return `INSERT INTO ${name} (${this.#getTableColumns(table)})
1239
+ SELECT ${this.#getTableColumns(table)} FROM ${this.#tempTableName}`;
1240
+ }
1241
+
1242
+ createTable(table){
1243
+ var queryVar = "";
1244
+ for (var key in table) {
1245
+ if(typeof table[key] === "object"){
1246
+ queryVar += `${this.#columnMapping(table[key])}, `;
1247
+ }
1248
+ }
1249
+
1250
+ return `CREATE TABLE ${table.__name} (${queryVar.replace(/,\s*$/, "")});`;
1251
+
1252
+ /*
1253
+ INTEGER PRIMARY KEY AUTOINCREMENT
1254
+ all these are equal to interger
1255
+ INT
1256
+ INTEGER
1257
+ TINYINT
1258
+ SMALLINT
1259
+ MEDIUMINT
1260
+ BIGINT
1261
+ UNSIGNED BIG INT
1262
+ INT2
1263
+ INT8
1264
+ */
1265
+ }
1266
+
1267
+
1268
+ dropTable(name){
1269
+ return `DROP TABLE ${name}`
1270
+ }
1271
+
1272
+ renameTable(table){
1273
+ return `ALTER TABLE ${table.tableName} RENAME TO ${table.newName}`;
1274
+ }
1275
+
1276
+ renameColumn(table){
1277
+ return `ALTER TABLE ${table.tableName} RENAME COLUMN ${table.name} TO ${table.newName}`
1278
+ }
1279
+
1280
+
1281
+ }
1282
+
1283
+
1284
+ module.exports = migrationSQLiteQuery;
1285
+
1286
+
1287
+ /*
1288
+ ADDING NEW COLUMN SQLITE
1289
+ There are some restrictions on the new column:
1290
+ The new column cannot have a UNIQUE or PRIMARY KEY constraint.
1291
+ If the new column has a NOT NULL constraint, you must specify a default value for the column other than a NULL value.
1292
+ The new column cannot have a default of CURRENT_TIMESTAMP, CURRENT_DATE, and CURRENT_TIME, or an expression.
1293
+ If the new column is a foreign key and the foreign key constraint check is enabled, the new column must accept a default value NULL.
1294
+
1295
+ */
1296
+
1297
+ /*
1298
+
1299
+ DROPING A COLUMN SQLITE
1300
+ Possible reasons why the DROP COLUMN command can fail include:
1301
+
1302
+ The column is a PRIMARY KEY or part of one.
1303
+ The column has a UNIQUE constraint.
1304
+ The column is indexed.
1305
+ The column is named in the WHERE clause of a partial index.
1306
+ The column is named in a table or column CHECK constraint not associated with the column being dropped.
1307
+ The column is used in a foreign key constraint.
1308
+ The column is used in the expression of a generated column.
1309
+ The column appears in a trigger or view.
1310
+
1311
+ */
1312
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrationSQLiteQuery.js **
1313
+
1314
+
1315
+ // https://channel9.msdn.com/Blogs/EF/Migrations-Under-the-Hood
1316
+ // version 0.0.3
1317
+
1318
+ const os = require('os');
1319
+ class MigrationTemplate {
1320
+
1321
+ constructor(name) {
1322
+ this.name = name;
1323
+ }
1324
+
1325
+ #up = ''
1326
+ #down = ''
1327
+
1328
+ get(){
1329
+ return `
1330
+
1331
+ var masterrecord = require('masterrecord');
1332
+
1333
+ class ${this.name} extends masterrecord.schema {
1334
+ constructor(context){
1335
+ super(context);
1336
+ }
1337
+
1338
+ up(table){
1339
+ this.init(table);
1340
+ ${this.#up}
1341
+ }
1342
+
1343
+ down(table){
1344
+ this.init(table);
1345
+ ${this.#down}
1346
+ }
1347
+ }
1348
+ module.exports = ${this.name};
1349
+ `
1350
+ }
1351
+
1352
+ alterColumn(type, name, parent){
1353
+ if(type === "up"){
1354
+ this.#up += os.EOL + ` this.alterColumn(table.${name});`
1355
+ }
1356
+ else{
1357
+ this.#down += os.EOL + ` this.alterColumn(table.${name});`
1358
+ }
1359
+ }
1360
+ createTable(type, name){
1361
+ if(type === "up"){
1362
+ this.#up += os.EOL + ` this.createTable(table.${name});`
1363
+ }
1364
+ else{
1365
+ this.#down += os.EOL + ` this.createTable(table.${name});`
1366
+ }
1367
+ }
1368
+
1369
+ addColumn(type, name, parent){
1370
+ if(type === "up"){
1371
+ this.#up += os.EOL + ` this.addColumn(table.${name});`
1372
+ }
1373
+ else{
1374
+ this.#down += os.EOL + ` this.addColumn(table.${name});`
1375
+ }
1376
+ }
1377
+ //this.addColumn(table.${parent}.${name});`
1378
+
1379
+ dropTable(type, name){
1380
+ if(type === "up"){
1381
+ this.#down += os.EOL + ` this.droptable(table.${name});`
1382
+ }
1383
+ else{
1384
+ this.#down += os.EOL + ` this.droptable(table.${name});`
1385
+ }
1386
+ }
1387
+
1388
+ dropColumn(type, name, parent){
1389
+ if(type === "up"){
1390
+ this.#up += os.EOL + ` this.dropColumn(table.${name});`
1391
+ }
1392
+ else{
1393
+ this.#down += os.EOL + ` this.dropColumn(table.${name});`
1394
+ }
1395
+ }
1396
+
1397
+ }
1398
+
1399
+ module.exports = MigrationTemplate;
1400
+
1401
+
1402
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrationTemplate.js **
1403
+ // version 0.0.8
1404
+ // learn more about seeding info - https://www.pauric.blog/Database-Updates-and-Migrations-with-Entity-Framework/
1405
+
1406
+ var fs = require('fs');
1407
+ var diff = require("deep-object-diff");
1408
+ var MigrationTemplate = require("./migrationTemplate");
1409
+ var globSearch = require("glob");
1410
+
1411
+ // https://blog.tekspace.io/code-first-multiple-db-context-migration/
1412
+
1413
+ // node masterrecord add-migration josh C:\Users\rbatista\Downloads\kollege\freshmen\app\models\context
1414
+ class Migrations{
1415
+
1416
+ #organizeSchemaByTables(oldSchema, newSchema){
1417
+ var tables = []
1418
+ if(oldSchema.length === 0){
1419
+ newSchema.forEach(function (item, index) {
1420
+ var table = {
1421
+ name: item["__name"],
1422
+ new :item,
1423
+ old : {},
1424
+ newColumns : [],
1425
+ newTables : [],
1426
+ deletedColumns : [],
1427
+ updatedColumns : []
1428
+ }
1429
+ tables.push(table);
1430
+ });
1431
+ }
1432
+ else{
1433
+ newSchema.forEach(function (item, index) {
1434
+ var table = {
1435
+ name: item["__name"],
1436
+ old: null,
1437
+ new :item,
1438
+ newColumns : [],
1439
+ newTables : [],
1440
+ deletedColumns : [],
1441
+ updatedColumns : []
1442
+ }
1443
+
1444
+ oldSchema.forEach(function (oldItem, index) {
1445
+ var oldItemName = oldItem["__name"];
1446
+ if(table.name === oldItemName){
1447
+ table.old = oldItem;
1448
+ tables.push(table);
1449
+ }
1450
+ });
1451
+
1452
+ });
1453
+ }
1454
+
1455
+ return tables;
1456
+ }
1457
+
1458
+ #findDeletedColumns(tables){
1459
+ tables.forEach(function (item, index) {
1460
+ var deletedColumn = null;
1461
+ if(item.new && item.old){
1462
+ Object.keys(item.old).forEach(function (key) {
1463
+ var value = item.old[key].name;
1464
+ deletedColumn = null;
1465
+ Object.keys(item.new).forEach(function (newKey) {
1466
+ var newValue = item.new[newKey].name;
1467
+ if(value === newValue){
1468
+ deletedColumn = value;
1469
+ }
1470
+ });
1471
+ if(deletedColumn === null){
1472
+ item.deletedColumns.push(value);
1473
+ }
1474
+ });
1475
+ }
1476
+ });
1477
+ return tables;
1478
+ }
1479
+
1480
+ #findUpdatedColumns(tables){
1481
+
1482
+
1483
+ tables.forEach(function (item, index) {
1484
+
1485
+ var UD = diff.updatedDiff(item.old, item.new);
1486
+ const isEmpty = Object.keys(UD).length === 0;
1487
+ if(!isEmpty){
1488
+ for (var key in UD) {
1489
+ var tableChanges = {
1490
+ changes : UD[key],
1491
+ table : item.new[key],
1492
+ tableName : item.name
1493
+ };
1494
+ item.updatedColumns.push(tableChanges);
1495
+ }
1496
+ }
1497
+
1498
+ });
1499
+ return tables;
1500
+ }
1501
+
1502
+ #findNewColumns(tables){
1503
+ tables.forEach(function (item, index) {
1504
+ if(item.new && item.old){
1505
+ Object.keys(item.new).forEach(function (key) {
1506
+ if(typeof item.new[key] === "object"){
1507
+ var value = item.new[key].name;
1508
+ var columnNotFound = false;
1509
+ Object.keys(item.old).forEach(function (oldKey) {
1510
+ if(typeof item.old[oldKey] === "object"){
1511
+ var oldValue = item.old[oldKey].name;
1512
+ if(value === oldValue){
1513
+ columnNotFound = true;
1514
+ }
1515
+ }
1516
+ });
1517
+
1518
+ if(columnNotFound === false){
1519
+ // this means it did not find the column
1520
+
1521
+ if(item.new[key].type !== "hasOne" && item.new[key].type !== "hasMany" && item.new[key].type !== "hasManyThrough"){
1522
+ // if you have to create a new table no need to create the columns
1523
+ if(item.newTables.length === 0){
1524
+ item.newColumns.push(value);
1525
+ }
1526
+
1527
+ }
1528
+ }
1529
+ }
1530
+
1531
+ });
1532
+ }
1533
+ else{
1534
+ console.log("Table object has no old or new values");
1535
+ }
1536
+ });
1537
+ return tables;
1538
+ }
1539
+
1540
+ #findNewTables(tables){
1541
+ // find new tables
1542
+ tables.forEach(function (item, index) {
1543
+ if(item.new && item.old){
1544
+ if(Object.keys(item.old).length === 0){
1545
+ item.newTables.push(item);
1546
+ }
1547
+ }else{
1548
+ console.log("Cannot find NEW or and Old Objects");
1549
+ }
1550
+
1551
+ });
1552
+ return tables;
1553
+ }
1554
+
1555
+ // build table to build new migration snapshot
1556
+ #buildMigrationObject(oldSchema, newSchema){
1557
+
1558
+ var tables = this.#organizeSchemaByTables(oldSchema, newSchema);
1559
+
1560
+ tables = this.#findNewTables(tables);
1561
+ tables = this.#findNewColumns(tables);
1562
+ tables = this.#findDeletedColumns(tables);
1563
+ tables = this.#findUpdatedColumns(tables);
1564
+ return tables;
1565
+ }
1566
+
1567
+
1568
+
1569
+ findContext(executedLocation, contextFileName){
1570
+ var search = `${executedLocation}/**/*${contextFileName}.js`
1571
+ var files = globSearch.sync(search, executedLocation);
1572
+ var file = files[0];
1573
+ var context = require(file);
1574
+ return {
1575
+ context : context,
1576
+ fileLocation : file
1577
+ }
1578
+ }
1579
+
1580
+ // remove hasMany and hasOne and hasManyThrough
1581
+ cleanEntities(entities){
1582
+ var newEntity = [];
1583
+ for (let i = 0; i < entities.length; i++) {
1584
+ var entity = entities[i];
1585
+ var newObj = {}
1586
+
1587
+ for (let key in entity) {
1588
+ if (entity.hasOwnProperty(key)) {
1589
+
1590
+ if(entity[key].type !== "hasOne" && entity[key].type !== "hasMany" && entity[key].type !== "hasManyThrough"){
1591
+ // if(entity[key].relationshipType == "belongsTo" ){
1592
+ // entity[key].name = entity[key].foreignKey;
1593
+ // }
1594
+ newObj[key] = entity[key];
1595
+ }
1596
+ }
1597
+ }
1598
+ newEntity.push(newObj);
1599
+ }
1600
+
1601
+ return newEntity;
1602
+ }
1603
+
1604
+ createSnapShot(snap){
1605
+
1606
+ var dbFolder = `${snap.executedLocation}/db`;
1607
+ if (!fs.existsSync(dbFolder)){
1608
+ fs.mkdirSync(dbFolder);
1609
+ }
1610
+
1611
+ var migrationsDirectory = `${snap.executedLocation}/db/migrations`;
1612
+ if (!fs.existsSync(migrationsDirectory)){
1613
+ fs.mkdirSync(migrationsDirectory);
1614
+ }
1615
+
1616
+ var content = {
1617
+ contextLocation: snap.file,
1618
+ migrationFolder: `${snap.executedLocation}/db/migrations`,
1619
+ snapShotLocation: `${snap.executedLocation}/db/migrations/${snap.contextFileName}_contextSnapShot.json`,
1620
+ schema : snap.contextEntities
1621
+ };
1622
+
1623
+ const jsonContent = JSON.stringify(content, null, 2);
1624
+ try{
1625
+ // will replace the whole file if it exist
1626
+ fs.writeFileSync(`${migrationsDirectory}/${snap.contextFileName}_contextSnapShot.json`, jsonContent);
1627
+ }catch (e){
1628
+ console.log("Cannot write file ", e);
1629
+ }
1630
+ }
1631
+
1632
+ // validate if schema has changed based on new and old
1633
+ buildUpObject(oldSchema, newSchema){
1634
+ var tableObj = {}
1635
+ var tables = this.#buildMigrationObject(oldSchema, newSchema);
1636
+
1637
+ tables.forEach(function (item, index) {
1638
+ // add new columns for table
1639
+ var columnInfo = tables[index];
1640
+
1641
+ item.newTables.forEach(function (column, ind) {
1642
+ tableObj[item.name] = columnInfo.new;
1643
+ });
1644
+
1645
+ item.newColumns.forEach(function (column, ind) {
1646
+ columnInfo.new[column].tableName = item.name;
1647
+ tableObj[column] = columnInfo.new[column];
1648
+ });
1649
+
1650
+ item.deletedColumns.forEach(function (column, ind) {
1651
+ columnInfo.old[column].tableName = item.name;
1652
+ tableObj[column] = columnInfo.old[column];
1653
+ });
1654
+
1655
+ item.updatedColumns.forEach(function (column, ind) {
1656
+ tableObj[column.table.name] = column;
1657
+ });
1658
+
1659
+ if(item.new === null){
1660
+ columnInfo.old.tableName = item.name;
1661
+ tableObj["new"] = columnInfo.old;
1662
+ }
1663
+
1664
+ tableObj.___table = item;
1665
+ });
1666
+ return tableObj;
1667
+ }
1668
+
1669
+ template(name, oldSchema, newSchema){
1670
+ var MT = new MigrationTemplate(name);
1671
+ var tables = this.#buildMigrationObject(oldSchema, newSchema);
1672
+
1673
+ tables.forEach(function (item, index) {
1674
+ if(item.old === null){
1675
+ MT.createTable("up", column, item.name);
1676
+ MT.dropTable("down", column, item.name);
1677
+ }
1678
+
1679
+ if(item.new === null){
1680
+ MT.dropTable("up", column, item.name);
1681
+ MT.createTable("down", column, item.name);
1682
+ }
1683
+
1684
+ item.newTables.forEach(function (column, ind) {
1685
+ MT.createTable("up", item.name);
1686
+ MT.dropTable("down", item.name);
1687
+ });
1688
+
1689
+ // add new columns for table
1690
+ item.newColumns.forEach(function (column, index) {
1691
+ MT.addColumn("up", column, item.name);
1692
+ MT.dropColumn("down", column, item.name);
1693
+ });
1694
+
1695
+ item.deletedColumns.forEach(function (column, index) {
1696
+ MT.dropColumn("up", column, item.name);
1697
+ MT.addColumn("down",column, item.name);
1698
+ });
1699
+
1700
+ item.updatedColumns.forEach(function (column, index) {
1701
+ const isEmpty = Object.keys(column).length === 0;
1702
+ if(!isEmpty){
1703
+ MT.alterColumn("up", column.table.name, item.name);
1704
+ MT.alterColumn("down", column.table.name, item.name);
1705
+ }
1706
+ });
1707
+
1708
+ });
1709
+
1710
+ return MT.get();
1711
+ }
1712
+
1713
+
1714
+ }
1715
+
1716
+ module.exports = Migrations;
1717
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrations.js **
1718
+ // version 0.0.4
1719
+ class schema{
1720
+
1721
+ constructor(context){
1722
+ this.context = new context();
1723
+ }
1724
+
1725
+
1726
+ init(table){
1727
+ if(table){
1728
+ this.fullTable = table.___table;
1729
+ }
1730
+ }
1731
+
1732
+ // create obj to convert into create sql
1733
+ addColumn(table){
1734
+ // todo need to work on add column for mysql
1735
+ if(table){
1736
+ if(this.context.isSQLite){
1737
+ var sqliteQuery = require("./migrationSQLiteQuery");
1738
+ var queryBuilder = new sqliteQuery();
1739
+ var queryObj = queryBuilder.alterColumn(this.fullTable.new, table);
1740
+ for (var key in queryObj) {
1741
+ var query = queryObj[key];
1742
+ this.context._execute(query);
1743
+ }
1744
+ }
1745
+
1746
+ if(this.context.isMySQL){
1747
+ var sqlquery = require("./migrationMySQLQuery");
1748
+ var queryBuilder = new sqlquery();
1749
+ table.realDataType = queryBuilder.typeManager(table.type);
1750
+ var query = queryBuilder.addColum(table);
1751
+ this.context._execute(query);
1752
+ }
1753
+ }
1754
+
1755
+ // add column to database
1756
+ }
1757
+
1758
+ dropColumn(table){
1759
+ if(table){
1760
+ if(this.fullTable){
1761
+ // drop column
1762
+ if(this.context.isSQLite){
1763
+ var sqliteQuery = require("./migrationSQLiteQuery");
1764
+ var queryBuilder = new sqliteQuery();
1765
+ var query = queryBuilder.dropColumn(table);
1766
+ this.context._execute(query);
1767
+ }
1768
+
1769
+ if(this.context.isMySQL){
1770
+ var sqlquery = require("./migrationMySQLQuery");
1771
+ var queryBuilder = new sqlquery();
1772
+ var query = queryBuilder.dropColumn(table);
1773
+ this.context._execute(query);
1774
+ }
1775
+
1776
+ }else{
1777
+ console.log("Must call the addTable function.");
1778
+ }
1779
+ }
1780
+ }
1781
+
1782
+ createTable(table){
1783
+
1784
+ if(table){
1785
+ if(this.context.isSQLite){
1786
+ var sqliteQuery = require("./migrationSQLiteQuery");
1787
+ var queryBuilder = new sqliteQuery();
1788
+ var query = queryBuilder.createTable(table);
1789
+ this.context._execute(query);
1790
+ }
1791
+
1792
+ if(this.context.isMySQL){
1793
+ var sqlquery = require("./migrationMySQLQuery");
1794
+ var queryBuilder = new sqlquery();
1795
+ var query = queryBuilder.createTable(table);
1796
+ this.context._execute(query);
1797
+ }
1798
+ }else{
1799
+ console.log("Table that your trying to create is undefined. PLease check if there are any changes that need to be made");
1800
+ }
1801
+ }
1802
+
1803
+
1804
+ dropTable(table){
1805
+ if(table){
1806
+ if(this.context.isSQLite){
1807
+ var sqliteQuery = require("./migrationSQLiteQuery");
1808
+ var queryBuilder = new sqliteQuery();
1809
+ var query = queryBuilder.dropTable(table.__name);
1810
+ this.context._execute(query);
1811
+ }
1812
+
1813
+ if(this.context.isMySQL){
1814
+ var sqlquery = require("./migrationMySQLQuery");
1815
+ var queryBuilder = new sqlquery();
1816
+ var query = queryBuilder.dropTable(table.__name);
1817
+ this.context._execute(query);
1818
+ }
1819
+ }
1820
+ }
1821
+
1822
+
1823
+ //"dbo.People", "Location"
1824
+ alterColumn(table){
1825
+ if(table){
1826
+ if(this.fullTable){
1827
+ if(this.context.isSQLite){
1828
+ var sqliteQuery = require("./migrationSQLiteQuery");
1829
+ var queryBuilder = new sqliteQuery();
1830
+ var queryObj = queryBuilder.alterColumn(this.fullTable.new, table);
1831
+ for (var key in queryObj) {
1832
+ var query = queryObj[key];
1833
+ this.context._execute(query);
1834
+ }
1835
+ }
1836
+
1837
+ if(this.context.isMySQL){
1838
+ var sqlquery = require("./migrationMySQLQuery");
1839
+ var queryBuilder = new sqlquery();
1840
+ var query = queryBuilder.alterColumn(table);
1841
+ this.context._execute(query);
1842
+ }
1843
+
1844
+ }else{
1845
+ console.log("Must call the addTable function.");
1846
+ }
1847
+ }
1848
+ }
1849
+
1850
+ renameColumn(){
1851
+ // TODO
1852
+ }
1853
+
1854
+ seed(){
1855
+ // TODO
1856
+ }
1857
+
1858
+ }
1859
+
1860
+
1861
+ module.exports = schema;
1862
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/schema.js **
1863
+
1864
+ // version 0.0.13
1865
+ var entityTrackerModel = require('masterrecord/Entity/entityTrackerModel');
1866
+ var tools = require('masterrecord/Tools');
1867
+ var queryScript = require('masterrecord/QueryLanguage/queryScript');
1868
+
1869
+ class queryMethods{
1870
+
1871
+ constructor(entity, context) {
1872
+ this.__entity = entity;
1873
+ this.__context = context;
1874
+ this.__queryObject = new queryScript();
1875
+ }
1876
+
1877
+ // build a single entity
1878
+ __singleEntityBuilder(dataModel){
1879
+ var $that = this;
1880
+ if(dataModel){
1881
+ var ent = new entityTrackerModel();
1882
+ var mod = ent.build(dataModel, $that.__entity, $that.__context);
1883
+ mod.__state = "track";
1884
+ $that.__context.__track(mod);
1885
+ return mod;
1886
+ }else{
1887
+ return null;
1888
+ }
1889
+ }
1890
+
1891
+ // build multiple entities
1892
+ __multipleEntityBuilder(entityValue){
1893
+ var $that = this;
1894
+ var listArray = [];
1895
+ if(entityValue){
1896
+ for(let i = 0; i < entityValue.length; i++){
1897
+ listArray.push($that.__singleEntityBuilder(entityValue[i]));
1898
+ }
1899
+ return listArray;
1900
+ }else{
1901
+ return null;
1902
+ }
1903
+ }
1904
+
1905
+ __reset(){
1906
+ this.__queryObject.reset();
1907
+ }
1908
+
1909
+
1910
+ // do join on two tables = inner join
1911
+ join(){
1912
+
1913
+ }
1914
+
1915
+ groupBy(){
1916
+
1917
+ }
1918
+
1919
+ // do join on two tables = inner join
1920
+ _____leftJoin(){
1921
+
1922
+ }
1923
+
1924
+ ______orderByCount(query, ...args){
1925
+ var str = query.toString();
1926
+ if(args){
1927
+ for(let argument in args){
1928
+ var item = args[argument];
1929
+ str = str.replace("$$", item);
1930
+ }
1931
+ }
1932
+ this.__queryObject.orderByCount(str, this.__entity.__name);
1933
+ return this;
1934
+ }
1935
+
1936
+ ______orderByCountDescending(query, ...args){
1937
+ var str = query.toString();
1938
+ if(args){
1939
+ for(let argument in args){
1940
+ var item = args[argument];
1941
+ str = str.replace("$$", item);
1942
+ }
1943
+ }
1944
+ this.__queryObject.orderByCountDesc(str, this.__entity.__name);
1945
+ return this;
1946
+ }
1947
+
1948
+ orderBy(query, ...args){
1949
+ var str = query.toString();
1950
+ if(args){
1951
+ for(let argument in args){
1952
+ var item = args[argument];
1953
+ str = str.replace("$$", item);
1954
+ }
1955
+ }
1956
+ this.__queryObject.orderBy(str, this.__entity.__name);
1957
+ return this;
1958
+ }
1959
+
1960
+ orderByDescending(query, ...args){
1961
+ var str = query.toString();
1962
+ if(args){
1963
+ for(let argument in args){
1964
+ var item = args[argument];
1965
+ str = str.replace("$$", item);
1966
+ }
1967
+ }
1968
+ this.__queryObject.orderByDesc(str, this.__entity.__name);
1969
+ return this;
1970
+ }
1971
+
1972
+ raw(query){
1973
+ this.__queryObject.raw(query);
1974
+ return this;
1975
+ }
1976
+
1977
+ /* WHERE and AND work together its a way to add to the WHERE CLAUSE DYNAMICALLY */
1978
+ and(query, ...args){
1979
+ var str = query.toString();
1980
+ if(args){
1981
+ for(let argument in args){
1982
+ var item = args[argument];
1983
+ str = str.replace("$$", item);
1984
+ }
1985
+ }
1986
+ this.__queryObject.and(str, this.__entity.__name);
1987
+ return this;
1988
+ }
1989
+
1990
+ where(query, ...args){
1991
+ var str = query.toString();
1992
+ if(args){
1993
+ for(let argument in args){
1994
+ var item = args[argument];
1995
+ str = str.replace("$$", item);
1996
+ }
1997
+ }
1998
+
1999
+ this.__queryObject.where(str, this.__entity.__name);
2000
+ return this;
2001
+ }
2002
+
2003
+ // when you dont want to use lazy loading and want it called at that moment
2004
+ //Eagerly loading
2005
+ include(query, ...args){
2006
+ var str = query.toString();
2007
+ if(args){
2008
+ for(let argument in args){
2009
+ var item = args[argument];
2010
+ str = str.replace("$$", item);
2011
+ }
2012
+ }
2013
+ this.__queryObject.include(str, this.__entity.__name);
2014
+ return this;
2015
+ }
2016
+
2017
+ // only takes a array of selected items
2018
+ select(query, ...args){
2019
+ var str = query.toString();
2020
+ if(args){
2021
+ for(let argument in args){
2022
+ var item = args[argument];
2023
+ str = str.replace("$$", item);
2024
+ }
2025
+ }
2026
+ this.__queryObject.select(str, this.__entity.__name);
2027
+ return this;
2028
+ }
2029
+
2030
+ take(number){
2031
+ this.__queryObject.script.take = number;
2032
+ return this;
2033
+ }
2034
+
2035
+ skip(number){
2036
+ this.__queryObject.script.skip = number;
2037
+ return this;
2038
+ }
2039
+
2040
+
2041
+ // ------------------------------- FUNCTIONS THAT MAKE THE SQL CALL START FROM HERE ON -----------------------------------------------------
2042
+ // ---------------------------------------------------------------------------------------------------------------------------------------
2043
+
2044
+ count(query, ...args){
2045
+ if(query){
2046
+ var str = query.toString();
2047
+ if(args){
2048
+ for(let argument in args){
2049
+ var item = args[argument];
2050
+ str = str.replace("$$", item);
2051
+ }
2052
+ }
2053
+ this.__queryObject.count(str, this.__entity.__name);
2054
+ }
2055
+
2056
+ if(this.__context.isSQLite){
2057
+ // trying to match string select and relace with select Count(*);
2058
+ var entityValue = this.__context._SQLEngine.getCount(this.__queryObject, this.__entity, this.__context);
2059
+ var val = entityValue[Object.keys(entityValue)[0]];
2060
+ this.__reset();
2061
+ return val;
2062
+ }
2063
+
2064
+ if(this.__context.isMySQL){
2065
+ // trying to match string select and relace with select Count(*);
2066
+ var entityValue = this.__context._SQLEngine.getCount(this.__queryObject, this.__entity, this.__context);
2067
+ var val = entityValue[Object.keys(entityValue)[0]];
2068
+ this.__reset();
2069
+ return val;
2070
+ }
2071
+ }
2072
+
2073
+ single(){
2074
+ // If no clauses were used before single(), seed defaults so SQL is valid
2075
+ if(this.__queryObject.script.entityMap.length === 0){
2076
+ this.__queryObject.skipClause(this.__entity.__name);
2077
+ this.__queryObject.script.take = 1;
2078
+ }
2079
+
2080
+ if(this.__context.isSQLite){
2081
+ var entityValue = this.__context._SQLEngine.get(this.__queryObject.script, this.__entity, this.__context);
2082
+ var sing = this.__singleEntityBuilder(entityValue);
2083
+ this.__reset();
2084
+ return sing;
2085
+ }
2086
+
2087
+ if(this.__context.isMySQL){
2088
+ var entityValue = this.__context._SQLEngine.get(this.__queryObject.script, this.__entity, this.__context);
2089
+ var sing = this.__singleEntityBuilder(entityValue[0]);
2090
+ this.__reset();
2091
+ return sing;
2092
+ }
2093
+ }
2094
+
2095
+ toList(){
2096
+ if(this.__context.isSQLite){
2097
+ if(this.__queryObject.script.entityMap.length === 0){
2098
+ this.__queryObject.skipClause( this.__entity.__name);
2099
+ if(!this.__queryObject.script.take || this.__queryObject.script.take === 0){
2100
+ this.__queryObject.script.take = 1000;
2101
+ }
2102
+ }
2103
+ var entityValue = this.__context._SQLEngine.all(this.__queryObject.script, this.__entity, this.__context);
2104
+ var toLi = this.__multipleEntityBuilder(entityValue);
2105
+ this.__reset();
2106
+ return toLi;
2107
+ }
2108
+
2109
+ if(this.__context.isMySQL){
2110
+ if(this.__queryObject.script.entityMap.length === 0){
2111
+ this.__queryObject.skipClause( this.__entity.__name);
2112
+ if(!this.__queryObject.script.take || this.__queryObject.script.take === 0){
2113
+ this.__queryObject.script.take = 1000;
2114
+ }
2115
+ }
2116
+ var entityValue = this.__context._SQLEngine.all(this.__queryObject.script, this.__entity, this.__context);
2117
+ var toLi = this.__multipleEntityBuilder(entityValue);
2118
+ this.__reset();
2119
+ return toLi;
2120
+ }
2121
+ }
2122
+
2123
+ // ------------------------------- FUNCTIONS THAT UPDATE SQL START FROM HERE -----------------------------------------------------
2124
+ // ---------------------------------------------------------------------------------------------------------------------------------------
2125
+ add(entityValue){
2126
+ entityValue.__state = "insert";
2127
+ entityValue.__entity = this.__entity;
2128
+ entityValue.__context = this.__context;
2129
+ entityValue.__name = this.__entity.__name;
2130
+ this.__context.__track(entityValue);
2131
+ }
2132
+
2133
+ remove(entityValue){
2134
+ entityValue.__state = "delete";
2135
+ entityValue.__entity = this.__entity;
2136
+ entityValue.__context = this.__context;
2137
+ }
2138
+
2139
+ removeRange(entityValues){
2140
+ for (const property in entityValues) {
2141
+ entityValues[property].__state = "delete";
2142
+ entityValues[property].__entity = this.__entity;
2143
+ entityValues[property].__context = this.__context;
2144
+ }
2145
+ }
2146
+
2147
+ track(entityValue){
2148
+ entityValue.__state = "track";
2149
+ tools.clearAllProto(entityValue);
2150
+ entityValue.__entity = this.__entity;
2151
+ entityValue.__context = this.__context;
2152
+ this.__context.__track(entityValue);
2153
+ }
2154
+ }
2155
+
2156
+ module.exports = queryMethods;
2157
+
2158
+
2159
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/QueryLanguage/queryMethods.js **
2160
+ // version 0.0.5
2161
+
2162
+ const LOG_OPERATORS_REGEX = /(\|\|)|(&&)/;
2163
+ var tools = require('../Tools');
2164
+
2165
+ class queryScript{
2166
+
2167
+ constructor(){ }
2168
+
2169
+ script = {
2170
+ select : false,
2171
+ where: false,
2172
+ and : [],
2173
+ include : [],
2174
+ raw: false,
2175
+ entity : "",
2176
+ entityMap : [],
2177
+ take : 0,
2178
+ skip: 0,
2179
+ orderBy : false,
2180
+ orderByDesc : false,
2181
+ parentName : ""
2182
+ };
2183
+
2184
+
2185
+ reset(){
2186
+ this.script = {
2187
+ select : false,
2188
+ where: false,
2189
+ and : [],
2190
+ include : [],
2191
+ raw: false,
2192
+ entity : "",
2193
+ entityMap : [],
2194
+ take : 0,
2195
+ skip: 0,
2196
+ orderBy : false,
2197
+ orderByDesc : false
2198
+ };
2199
+ }
2200
+
2201
+ getScript(){
2202
+ return this.script;
2203
+ }
2204
+
2205
+ raw(query){
2206
+ this.script.raw = query;
2207
+ return this.script;
2208
+ }
2209
+
2210
+ orderBy(text, entityName){
2211
+ this.buildScript(text, "orderBy", this.script, entityName);
2212
+ return this.script;
2213
+ }
2214
+
2215
+ orderByDesc(text, entityName){
2216
+ this.buildScript(text, "orderByDesc", this.script, entityName);
2217
+ return this.script;
2218
+ }
2219
+
2220
+ and(text, entityName){
2221
+ this.buildScript(text, "and", this.script, entityName);
2222
+ return this.script;
2223
+ }
2224
+
2225
+ where(text, entityName){
2226
+ this.buildScript(text, "where", this.script, entityName);
2227
+ return this.script;
2228
+ }
2229
+
2230
+ // this gets called when you skip a where clause
2231
+ skipClause(entityName){
2232
+ //this.buildScript(text, "skipClause", this.script, entityName);
2233
+ this.script.entityMap.push({
2234
+ name: entityName,
2235
+ entity : "ran"
2236
+ });
2237
+ this.script.parentName = entityName;
2238
+ return this.script;
2239
+ }
2240
+
2241
+ include(text, entityName){
2242
+ this.buildScript(text, "include", this.script, entityName);
2243
+ return this.script;
2244
+ }
2245
+
2246
+ select(text, entityName){
2247
+ this.buildScript(text, "select", this.script, entityName);
2248
+ return this.script;
2249
+ }
2250
+
2251
+ count(text, entityName){
2252
+ this.buildScript(text, "count", this.script, entityName);
2253
+ return this.script;
2254
+ }
2255
+
2256
+ buildScript(text, type, obj, entityName){
2257
+
2258
+ var cachedExpr = {}; // this function will just get the high level
2259
+
2260
+ /// first we find all the groups in the query string
2261
+ var querySections = this.getFunctionsInQuery(text);
2262
+
2263
+ // remove spaces from query and get the Entity
2264
+ cachedExpr.entity = this.getEntity(text);
2265
+
2266
+
2267
+ if(!this.isMapped(entityName, obj.entityMap)){
2268
+
2269
+ obj.entityMap.push({
2270
+ name: entityName,
2271
+ entity : cachedExpr.entity
2272
+ });
2273
+ }
2274
+
2275
+ // attach the entity name to the main Object
2276
+ obj.entity = cachedExpr.entity
2277
+
2278
+ cachedExpr[entityName] = this.splitGroupsByLogicalOperators(querySections.query);
2279
+
2280
+ // lets break the string into a list of functions
2281
+ this.buildFields(text, cachedExpr);
2282
+
2283
+ if(type === "include"){
2284
+ if(cachedExpr.selectFields){
2285
+ if(!this.isMapped(cachedExpr.selectFields[0], obj.entityMap)){
2286
+ obj.entityMap.push({
2287
+ name: tools.capitalizeFirstLetter(cachedExpr.selectFields[0]),
2288
+ entity : tools.getRandomLetter(1, obj.entityMap)
2289
+ });
2290
+ }
2291
+ };
2292
+ }
2293
+
2294
+ this.describeExpressionParts(cachedExpr[entityName]);
2295
+ this.describeExpressionPartsFunctions(cachedExpr[entityName], querySections.functions);
2296
+ if(type === "include" || type === "and"){
2297
+ obj[type].push(cachedExpr);
2298
+ }
2299
+ else{
2300
+ obj[type] = cachedExpr;
2301
+ }
2302
+
2303
+ obj.parentName = entityName;
2304
+ return cachedExpr;
2305
+ }
2306
+
2307
+
2308
+
2309
+ // look and grab all fields
2310
+ buildFields(text, desc){
2311
+ var match = text.match(/^([\w\d$_]+?)\s*=>((?:\{\sreturn\s)?[\s\S]*(?:\})?)/);
2312
+ if(match){
2313
+ const entity = match[1];
2314
+ let exprStr = match[2];
2315
+ const fields = [];
2316
+
2317
+ exprStr.replace(new RegExp(entity + "\\.([\\w_]+)", "g"), function (_, field) {
2318
+ if (!fields.includes(field)) fields.push(field);
2319
+ });
2320
+
2321
+ desc.expr = exprStr.trim();
2322
+ desc.selectFields = fields;
2323
+ return desc;
2324
+ }
2325
+ else{
2326
+ return null;
2327
+
2328
+ }
2329
+ }
2330
+
2331
+ MATCH_ENTITY_REGEXP(entityName) {
2332
+ return new RegExp("(^|[^\\w\\d])" + entityName + "[ \\.\\)]");
2333
+ }
2334
+
2335
+ OPERATORS_REGEX(entityName){
2336
+ return new RegExp("(?:^|[^\\w\\d])" + entityName
2337
+ + "\\.((?:\\.?[\\w\\d_\\$]+)+)(?:\\((.*?)\\))?(?:\\s*(>|<|(?:===)|(?:!==)|(?:==)|(?:!=)|(?:=)|(?:<=)|(?:>=)|(?:in))\\s*(.*))?")
2338
+ }
2339
+
2340
+
2341
+ splitGroupsByLogicalOperators(text) {
2342
+ let parts = {}, tmp;
2343
+ var part = {query : text, name : "query"}
2344
+
2345
+ //tmp = this.splitByLogicalOperators(part.query, entityRegExp)
2346
+ tmp = this.extractInside(part.query, part.name);
2347
+ if(tmp){
2348
+ part.inside = tmp;
2349
+ parts[part.name] = part;
2350
+ }
2351
+ else{
2352
+ part.inside = part.query;
2353
+ parts[part.name] = part;
2354
+ }
2355
+ part.inside = part.inside.replaceAll("&&", "and");
2356
+ part.query = part.query.replaceAll("&&", "and");
2357
+ part.inside = part.inside.replaceAll("||", "or");
2358
+ part.query = part.query.replaceAll("||", "or");
2359
+
2360
+ return parts;
2361
+ }
2362
+
2363
+
2364
+ // find all functions from querySections
2365
+ getFunctionsInQuery(text) {
2366
+
2367
+ const regex = /(?<=\.(?=[A-z]+\())([^(]+)\((.+?)\)(?!\))/g;
2368
+ let m;
2369
+ const items = [];
2370
+
2371
+ while ((m = regex.exec(text)) !== null) {
2372
+ // This is necessary to avoid infinite loops with zero-width matches
2373
+ if (m.index === regex.lastIndex) {
2374
+ regex.lastIndex++;
2375
+ }
2376
+
2377
+ const [, fName, query] = m;
2378
+ items.push(
2379
+ {name: fName, query: query, inside : query, parentFeild : this.getParentField(text, m[0]), input: m.input}
2380
+ );
2381
+
2382
+ text = text.replace(`${fName}(${query})`, `func`);
2383
+
2384
+ }
2385
+
2386
+ //items.push({name: "NOFUNCTIONS", query: str})
2387
+
2388
+ return {
2389
+ functions: items,
2390
+ query : text
2391
+ };
2392
+ }
2393
+
2394
+ getParentField(text, funcName){
2395
+ var split = text.split(".");
2396
+ for (let i = 0; i < split.length; i++) {
2397
+
2398
+ if(split[i].includes(funcName)){
2399
+ return split[i -1];
2400
+ }
2401
+ }
2402
+ return "";
2403
+ }
2404
+
2405
+ findExpression(fieldName, expression){
2406
+ // loop through and find expression
2407
+ for (const key in expression) {
2408
+
2409
+ if(expression[key].field === fieldName){
2410
+ return expression[key]
2411
+ }
2412
+ }
2413
+ }
2414
+
2415
+ describeExpressionPartsFunctions(cachedExpr, functions){
2416
+ cachedExpr.functions = [];
2417
+ if(functions.length > 0){
2418
+ for (let item in functions) {
2419
+
2420
+ var part = functions[item];
2421
+ var partQuery = part.inside;
2422
+ // get entity of inside function
2423
+ var entity = this.getEntity(partQuery);
2424
+
2425
+ part.entity = entity;
2426
+
2427
+ // is the function name white listed
2428
+ if(this.isFunction(part.name)){
2429
+ var scriptInner = {
2430
+ select : false,
2431
+ where: false,
2432
+ and : [],
2433
+ include : [],
2434
+ raw: false,
2435
+ entity : "",
2436
+ entityMap : [],
2437
+ take : 0,
2438
+ skip: 0,
2439
+ orderBy : false,
2440
+ orderByDesc : false
2441
+ };
2442
+
2443
+ if(part.name === "where"){
2444
+ // TODO: we need to Get this working
2445
+ // recall to parse inner query of function that is being called
2446
+ this.buildScript(part.inside, part.name, scriptInner, part.parentFeild);
2447
+ part.parentName = scriptInner.parentName;
2448
+ part.entity = scriptInner.where.entity;
2449
+ part.expr = scriptInner.where.expr;
2450
+ part.selectFields = scriptInner.where.selectFields;
2451
+ part[scriptInner.parentName] = scriptInner.where[scriptInner.parentName];
2452
+ }
2453
+ if(part.name === "include"){
2454
+ // TODO: we need to Get this working
2455
+ // recall to parse inner query of function that is being called
2456
+ this.buildScript(part.inside, part.name, scriptInner, part.parentFeild);
2457
+ part.parentName = scriptInner.parentName;
2458
+ part.entity = scriptInner.include.entity;
2459
+ part.expr = scriptInner.include.expr;
2460
+ part.selectFields = scriptInner.include.selectFields;
2461
+ part[scriptInner.parentName] = scriptInner.include[scriptInner.parentName];
2462
+
2463
+ }
2464
+ if(part.name === "select"){
2465
+ // TODO: we need to Get this working
2466
+ // recall to parse inner query of function that is being called
2467
+ this.buildScript(part.inside, part.name, scriptInner, part.parentFeild);
2468
+ part.parentName = scriptInner.parentName;
2469
+ part.entity = scriptInner.select.entity;
2470
+ part.expr = scriptInner.select.expr;
2471
+ part.selectFields = scriptInner.select.selectFields;
2472
+ part[scriptInner.parentName] = scriptInner.select[scriptInner.parentName];
2473
+ }
2474
+
2475
+ // will search multiple values in a field
2476
+ if(part.name === "any"){
2477
+ // var members = data.memberContext.Member.where(r => r.first_name.any($$), "Rich, james, Oliver" ).toList();
2478
+ var expre = this.findExpression(part.parentFeild, cachedExpr.query.expressions)
2479
+ expre.func = "IN";
2480
+ expre.arg = `(${part.query})`;
2481
+ expre.isFunction = true;
2482
+ }
2483
+
2484
+ if(part.name === "like"){
2485
+ // var members = data.memberContext.Member.where(r => r.space_id == $$ && r.user_id != null && r.first_name.like($$),data.params.query.id, "r" ).toList();
2486
+ var expre = this.findExpression(part.parentFeild, cachedExpr.query.expressions)
2487
+ expre.func = part.name;
2488
+ expre.arg = part.query;
2489
+ expre.isFunction = true;
2490
+ }
2491
+
2492
+ }
2493
+ else{
2494
+ throw "Cannot have inner functions unless its a Where, Include, Select or Like caluse"
2495
+ }
2496
+ }
2497
+ }
2498
+
2499
+ }
2500
+
2501
+ describeExpressionParts(parts) {
2502
+ if(parts.query) {
2503
+ let match, fields, func, arg;
2504
+ var part = {};
2505
+ part.inside = parts.query.query;
2506
+ part.expressions = [];
2507
+ var partQuery = part.inside;
2508
+ var entity = this.getEntity(partQuery);
2509
+ var exprPartRegExp = this.OPERATORS_REGEX(entity);
2510
+ // check if query contains an AND.
2511
+ var trimmedQuery = partQuery.replace(/\s/g, '');
2512
+ var splitByAnd = trimmedQuery.split("and");
2513
+ for (let splitAnds in splitByAnd) {
2514
+
2515
+ if (match = splitByAnd[splitAnds].match(exprPartRegExp)) {
2516
+ fields = match[1].split(".");
2517
+ func = (match[2] ? fields[fields.length - 1] : (match[3] || "exists"));
2518
+
2519
+ if (func == "==" || func == "===") {
2520
+ func = "=";
2521
+ }
2522
+ else if (func == "!==") {
2523
+ func = "!=";
2524
+ }
2525
+
2526
+ arg = match[2] || match[4];
2527
+ if (arg == "true" || arg == "false") {
2528
+ arg = arg == "true";
2529
+ }
2530
+ else if (arg && arg.charAt(0) == arg.charAt(arg.length - 1) && (arg.charAt(0) == "'" || arg.charAt(0) == '"')) {
2531
+ arg = arg.slice(1, -1);
2532
+ }
2533
+
2534
+ part.entity = entity;
2535
+
2536
+ part.expressions.push({
2537
+ field: fields[0],
2538
+ func : func.toLowerCase(),
2539
+ arg : arg
2540
+ });
2541
+ parts.query = part;
2542
+ }
2543
+ }
2544
+ }
2545
+
2546
+ return parts;
2547
+ }
2548
+
2549
+ getEntity(str){
2550
+ var clean = str.replace(/\s/g, '');
2551
+ return clean.substring(0, 1);
2552
+ }
2553
+
2554
+ isMapped(name, maps){
2555
+ for (let item in maps) {
2556
+ var map = maps[item];
2557
+ if(tools.capitalizeFirstLetter(name) === map.name){
2558
+ return true
2559
+ }
2560
+ }
2561
+ return false;
2562
+ }
2563
+
2564
+ //const res1 = extractInside(`User.include(a => a.Profile.where(r => r.name.startWith(i))).single()`, 'where');
2565
+ // const res2 = extractInside(`User.include(a => a.Profile.select(r => r.name === "rick")).single()`, 'select');
2566
+ extractInside(str, fname) {
2567
+ if(this.isFunction(fname)){
2568
+ const startIndex = str.indexOf(fname) + fname.length;
2569
+ const stack = [];
2570
+ for (let i = startIndex; i < str.length; i++) {
2571
+ if(str[i] === '(') {
2572
+ stack.push(str[i]);
2573
+
2574
+ } else if (str[i] === ')') {
2575
+ stack.pop();
2576
+ }
2577
+ if(stack.length === 0) {
2578
+ return str.substring(startIndex+1, i);
2579
+ }
2580
+ }
2581
+ return str;
2582
+ }
2583
+ else{
2584
+ return null;
2585
+ }
2586
+ }
2587
+
2588
+ isFunction(func){
2589
+ var funcList = [ "include", "like", "any"]
2590
+ for(var i =0; i < funcList.length; i++){
2591
+ if(funcList[i] === func){
2592
+ return true
2593
+ }
2594
+ }
2595
+ return false;
2596
+ }
2597
+
2598
+ }
2599
+
2600
+ module.exports = queryScript;
2601
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/QueryLanguage/queryScrip// Version 0.0.19
2602
+ var tools = require('masterrecord/Tools');
2603
+
2604
+ class SQLLiteEngine {
2605
+
2606
+ unsupportedWords = ["order"]
2607
+
2608
+ update(query){
2609
+ var sqlQuery = ` UPDATE [${query.tableName}]
2610
+ SET ${query.arg}
2611
+ WHERE [${query.tableName}].[${query.primaryKey}] = ${query.primaryKeyValue}` // primary key for that table =
2612
+ return this._run(sqlQuery);
2613
+ }
2614
+
2615
+ delete(queryObject){
2616
+ var sqlObject = this._buildDeleteObject(queryObject);
2617
+ var sqlQuery = `DELETE FROM [${sqlObject.tableName}] WHERE [${sqlObject.tableName}].[${sqlObject.primaryKey}] = ${sqlObject.value}`;
2618
+ return this._execute(sqlQuery);
2619
+ }
2620
+
2621
+ insert(queryObject){
2622
+ var sqlObject = this._buildSQLInsertObject(queryObject, queryObject.__entity);
2623
+ var query = `INSERT INTO [${sqlObject.tableName}] (${sqlObject.columns})
2624
+ VALUES (${sqlObject.values})`;
2625
+ var queryObj = this._run(query);
2626
+ var open = {
2627
+ "id": queryObj.lastInsertRowid
2628
+ };
2629
+ return open;
2630
+ }
2631
+
2632
+ get(query, entity, context){
2633
+ var queryString = {};
2634
+ try {
2635
+ if(query.raw){
2636
+ queryString.query = query.raw;
2637
+ }
2638
+ else{
2639
+ if(typeof query === 'string'){
2640
+ queryString.query = query;
2641
+ }
2642
+ else{
2643
+ queryString = this.buildQuery(query, entity, context);
2644
+ }
2645
+ }
2646
+ if(queryString.query){
2647
+ console.log("SQL:", queryString.query);
2648
+ var queryReturn = this.db.prepare(queryString.query).get();
2649
+ return queryReturn;
2650
+ }
2651
+ return null;
2652
+ } catch (err) {
2653
+ console.error(err);
2654
+ return null;
2655
+ }
2656
+ }
2657
+
2658
+ getCount(queryObject, entity, context){
2659
+ var query = queryObject.script;
2660
+ var queryString = {};
2661
+ try {
2662
+ if(query.raw){
2663
+ queryString.query = query.raw;
2664
+ }
2665
+ else{
2666
+ if(query.count === undefined){
2667
+ query.count = "none";
2668
+ }
2669
+ queryString.entity = this.getEntity(entity.__name, query.entityMap);
2670
+ queryString.query = `SELECT ${this.buildCount(query, entity)} ${this.buildFrom(query, entity)} ${this.buildWhere(query, entity)}`
2671
+ }
2672
+ if(queryString.query){
2673
+ var queryCount = queryString.query
2674
+ console.log("SQL:", queryCount );
2675
+ var queryReturn = this.db.prepare(queryCount ).get();
2676
+ return queryReturn;
2677
+ }
2678
+ return null;
2679
+ } catch (err) {
2680
+ console.error(err);
2681
+ return null;
2682
+ }
2683
+ }
2684
+
2685
+ all(query, entity, context){
2686
+ var selectQuery = {};
2687
+ try {
2688
+ if(query.raw){
2689
+ selectQuery.query = query.raw;
2690
+ }
2691
+ else{
2692
+
2693
+ selectQuery = this.buildQuery(query, entity, context);
2694
+ }
2695
+ if(selectQuery.query){
2696
+ console.log("SQL:", selectQuery.query);
2697
+ var queryReturn = this.db.prepare(selectQuery.query).all();
2698
+ return queryReturn;
2699
+ }
2700
+ return null;
2701
+ } catch (err) {
2702
+ console.error(err);
2703
+ return null;
2704
+ }
2705
+ }
2706
+
2707
+ changeNullQuery(query){
2708
+ if(query.where){
2709
+ var whereClaus;
2710
+ whereClaus = query.where.expr.replace("=== null", "is null");
2711
+ if(whereClaus === query.where.expr){
2712
+ whereClaus = query.where.expr.replace("!= null", "is not null");
2713
+ }
2714
+ query.where.expr = whereClaus;
2715
+ }
2716
+
2717
+ }
2718
+
2719
+ buildCount(query, mainQuery){
2720
+ var entity = this.getEntity(query.parentName, query.entityMap);
2721
+ if(query.count){
2722
+ if(query.count !== "none"){
2723
+ return `COUNT(${entity}.${query.count.selectFields[0]})`
2724
+ }
2725
+ else{
2726
+ return `COUNT(*)`
2727
+ }
2728
+ }
2729
+ else{
2730
+ return ""
2731
+ }
2732
+ }
2733
+
2734
+ buildQuery(query, entity, context, limit){
2735
+
2736
+ var queryObject = {};
2737
+ queryObject.entity = this.getEntity(entity.__name, query.entityMap);
2738
+ queryObject.select = this.buildSelect(query, entity);
2739
+ queryObject.count = this.buildCount(query, entity);
2740
+ queryObject.from = this.buildFrom(query, entity);
2741
+ queryObject.include = this.buildInclude(query, entity, context, queryObject);
2742
+ queryObject.where = this.buildWhere(query, entity);
2743
+ queryObject.and = this.buildAnd(query, entity);
2744
+ queryObject.take = this.buildTake(query);
2745
+ queryObject.skip = this.buildSkip(query);
2746
+ queryObject.orderBy = this.buildOrderBy(query);
2747
+
2748
+
2749
+ var queryString = `${queryObject.select} ${queryObject.count} ${queryObject.from} ${queryObject.include} ${queryObject.where} ${queryObject.and} ${queryObject.orderBy} ${queryObject.take} ${queryObject.skip}`;
2750
+ return {
2751
+ query : queryString,
2752
+ entity : this.getEntity(entity.__name, query.entityMap)
2753
+ }
2754
+
2755
+ }
2756
+
2757
+ buildOrderBy(query){
2758
+ // ORDER BY column1, column2, ... ASC|DESC;
2759
+ var $that = this;
2760
+ var orderByType = "ASC";
2761
+ var orderByEntity = query.orderBy;
2762
+ var strQuery = "";
2763
+ if(orderByEntity === false){
2764
+ orderByType = "DESC";
2765
+ orderByEntity = query.orderByDesc;
2766
+ }
2767
+ if(orderByEntity){
2768
+ var entity = this.getEntity(query.parentName, query.entityMap);
2769
+ var fieldList = "";
2770
+ for (const item in orderByEntity.selectFields) {
2771
+ fieldList += `${entity}.${orderByEntity.selectFields[item]}, `;
2772
+ };
2773
+ fieldList = fieldList.replace(/,\s*$/, "");
2774
+ strQuery = "ORDER BY";
2775
+ strQuery += ` ${fieldList} ${orderByType}`;
2776
+ }
2777
+ return strQuery;
2778
+ }
2779
+
2780
+ buildTake(query){
2781
+ if(query.take){
2782
+ return `LIMIT ${query.take}`
2783
+ }
2784
+ else{
2785
+ return "";
2786
+ }
2787
+ }
2788
+
2789
+ buildSkip(query){
2790
+ if(query.skip){
2791
+ return `OFFSET ${query.skip}`
2792
+ }
2793
+ else{
2794
+ return "";
2795
+ }
2796
+ }
2797
+
2798
+ buildAnd(query, mainQuery){
2799
+ // loop through the AND
2800
+ // loop update ther where .expr
2801
+ var andEntity = query.and;
2802
+ var strQuery = "";
2803
+ var $that = this;
2804
+ var str = "";
2805
+
2806
+ if(andEntity){
2807
+ var entity = this.getEntity(query.parentName, query.entityMap);
2808
+ var andList = [];
2809
+ for (let entityPart in andEntity) { // loop through list of and's
2810
+ var itemEntity = andEntity[entityPart]; // get the entityANd
2811
+ for (let table in itemEntity[query.parentName]) { // find the main table
2812
+ var item = itemEntity[query.parentName][table];
2813
+ for (let exp in item.expressions) {
2814
+ var field = tools.capitalizeFirstLetter(item.expressions[exp].field);
2815
+ if(mainQuery[field]){
2816
+ if(mainQuery[field].isNavigational){
2817
+ entity = $that.getEntity(field, query.entityMap);
2818
+ field = item.fields[1];
2819
+ }
2820
+ }
2821
+ if(item.expressions[exp].arg === "null"){
2822
+ if(item.expressions[exp].func === "="){
2823
+ item.expressions[exp].func = "is"
2824
+ }
2825
+ if(item.expressions[exp].func === "!="){
2826
+ item.expressions[exp].func = "is not"
2827
+ }
2828
+ }
2829
+ if(strQuery === ""){
2830
+ if(item.expressions[exp].arg === "null"){
2831
+ strQuery = `${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
2832
+ }else{
2833
+ strQuery = `${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
2834
+ }
2835
+ }
2836
+ else{
2837
+ if(item.expressions[exp].arg === "null"){
2838
+ strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
2839
+ }else{
2840
+ strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
2841
+ }
2842
+
2843
+ }
2844
+ }
2845
+ andList.push(strQuery);
2846
+ }
2847
+ }
2848
+ }
2849
+
2850
+ if(andList.length > 0){
2851
+ str = `and ${andList.join(" and ")}`;
2852
+ }
2853
+ return str
2854
+ }
2855
+
2856
+ buildWhere(query, mainQuery){
2857
+ var whereEntity = query.where;
2858
+
2859
+ var strQuery = "";
2860
+ var $that = this;
2861
+ if(whereEntity){
2862
+ var entity = this.getEntity(query.parentName, query.entityMap);
2863
+
2864
+ var item = whereEntity[query.parentName].query;
2865
+ for (let exp in item.expressions) {
2866
+ var field = item.expressions[exp].field.toLowerCase();
2867
+ // var field = tools.capitalizeFirstLetter(item.expressions[exp].field); removed this because it was causing issues not sure why we added it in the firstplace
2868
+ if(mainQuery[field]){
2869
+ if(mainQuery[field].isNavigational){
2870
+ entity = $that.getEntity(field, query.entityMap);
2871
+ field = item.fields[1];
2872
+ }
2873
+ }
2874
+ if(item.expressions[exp].arg === "null"){
2875
+ if(item.expressions[exp].func === "="){
2876
+ item.expressions[exp].func = "is"
2877
+ }
2878
+ if(item.expressions[exp].func === "!="){
2879
+ item.expressions[exp].func = "is not"
2880
+ }
2881
+ }
2882
+ if(strQuery === ""){
2883
+ if(item.expressions[exp].arg === "null"){
2884
+ strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
2885
+ }else{
2886
+ if(item.expressions[exp].func === "IN"){
2887
+ strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
2888
+ }
2889
+ else{
2890
+ strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
2891
+ }
2892
+ }
2893
+ }
2894
+ else{
2895
+ if(item.expressions[exp].arg === "null"){
2896
+ strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
2897
+ }else{
2898
+ strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
2899
+ }
2900
+
2901
+ }
2902
+ }
2903
+
2904
+
2905
+
2906
+ }
2907
+ return strQuery;
2908
+ }
2909
+
2910
+ buildInclude( query, entity, context){
2911
+ var includeQuery = "";
2912
+ for (let part in query.include) {
2913
+ var includeEntity = query.include[part];
2914
+ var $that = this;
2915
+ if(includeEntity){
2916
+ var parentObj = includeEntity[query.parentName];
2917
+ var currentContext = "";
2918
+ if(includeEntity.selectFields){
2919
+ currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
2920
+ }
2921
+
2922
+ if(parentObj){
2923
+ parentObj.entityMap = query.entityMap;
2924
+ var foreignKey = $that.getForeignKey(entity.__name, currentContext.__entity);
2925
+ var mainPrimaryKey = $that.getPrimarykey(entity);
2926
+ var mainEntity = $that.getEntity(entity.__name, query.entityMap);
2927
+ if(currentContext.__entity[entity.__name].type === "hasManyThrough"){
2928
+ var foreignTable = tools.capitalizeFirstLetter(currentContext.__entity[entity.__name].foreignTable); //to uppercase letter
2929
+ foreignKey = $that.getPrimarykey(currentContext.__entity);
2930
+ mainPrimaryKey = context[foreignTable].__entity[currentContext.__entity.__name].foreignKey;
2931
+ var mainEntity = $that.getEntity(foreignTable,query.entityMap);
2932
+ }
2933
+ // add foreign key to select so that it picks it up
2934
+ if(parentObj.select){
2935
+ parentObj.select.selectFields.push(foreignKey);
2936
+ }else{
2937
+ parentObj.select = {};
2938
+ parentObj.select.selectFields = [];
2939
+ parentObj.select.selectFields.push(foreignKey);
2940
+ }
2941
+
2942
+ var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
2943
+
2944
+ includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
2945
+
2946
+ }
2947
+ }
2948
+ }
2949
+ return includeQuery;
2950
+ }
2951
+
2952
+ buildFrom(query, entity){
2953
+ var entityName = this.getEntity(entity.__name, query.entityMap);
2954
+ if(entityName ){
2955
+ return `FROM ${entity.__name } AS ${entityName}`;
2956
+ }
2957
+ else{ return "" }
2958
+ }
2959
+
2960
+ buildSelect(query, entity){
2961
+ // this means that there is a select statement
2962
+ var select = "SELECT";
2963
+ var arr = "";
2964
+ var $that = this;
2965
+ if(query.select){
2966
+ for (const item in query.select.selectFields) {
2967
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
2968
+ };
2969
+
2970
+ }
2971
+ else{
2972
+ var entityList = this.getEntityList(entity);
2973
+ for (const item in entityList) {
2974
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
2975
+ };
2976
+ }
2977
+ arr = arr.replace(/,\s*$/, "");
2978
+ return `${select} ${arr} `;
2979
+ }
2980
+
2981
+ getForeignKey(name, entity){
2982
+ if(entity && name){
2983
+ return entity[name].foreignKey;
2984
+ }
2985
+ }
2986
+
2987
+ getPrimarykey(entity){
2988
+ for (const item in entity) {
2989
+ if(entity[item].primary){
2990
+ if(entity[item].primary === true){
2991
+ return entity[item].name;
2992
+ }
2993
+ }
2994
+ };
2995
+ }
2996
+
2997
+ getForeignTable(name, entity){
2998
+ if(entity && name){
2999
+ return entity[name].foreignTable;
3000
+ }
3001
+ }
3002
+
3003
+ getInclude(name, query){
3004
+ var include = query.include;
3005
+ if(include){
3006
+ for (let part in include) {
3007
+ if(tools.capitalizeFirstLetter(include[part].selectFields[0]) === name){
3008
+ return include[part];
3009
+ }
3010
+ }
3011
+ }
3012
+ else{
3013
+ return "";
3014
+ }
3015
+ }
3016
+
3017
+ getEntity(name, maps){
3018
+ for (let item in maps) {
3019
+ var map = maps[item];
3020
+ if(tools.capitalizeFirstLetter(name) === tools.capitalizeFirstLetter(map.name)){
3021
+ return map.entity
3022
+ }
3023
+ }
3024
+ return "";
3025
+ }
3026
+
3027
+ // return a list of entity names and skip foreign keys and underscore.
3028
+ getEntityList(entity){
3029
+ var entitiesList = [];
3030
+ var $that = this;
3031
+ for (var ent in entity) {
3032
+ if(!ent.startsWith("_")){
3033
+ if(!entity[ent].foreignKey){
3034
+ if(entity[ent].relationshipTable){
3035
+ if($that.chechUnsupportedWords(entity[ent].relationshipTable)){
3036
+ entitiesList.push(`'${entity[ent].relationshipTable}'`);
3037
+ }
3038
+ else{
3039
+ entitiesList.push(entity[ent].relationshipTable);
3040
+ }
3041
+ }
3042
+ else{
3043
+ if($that.chechUnsupportedWords(ent)){
3044
+ entitiesList.push(`'${ent}'`);
3045
+ }
3046
+ else{
3047
+ entitiesList.push(ent);
3048
+ }
3049
+ }
3050
+ }
3051
+ else{
3052
+
3053
+ if(entity[ent].relationshipType === "belongsTo"){
3054
+ var name = entity[ent].foreignKey;
3055
+ if($that.chechUnsupportedWords(name)){
3056
+ entitiesList.push(`'${name}'`);
3057
+ //entitiesList.push(`'${ent}'`);
3058
+ }
3059
+ else{
3060
+ entitiesList.push(name);
3061
+ //entitiesList.push(ent);
3062
+ }
3063
+ }
3064
+
3065
+ }
3066
+ }
3067
+ }
3068
+ return entitiesList
3069
+ }
3070
+ chechUnsupportedWords(word){
3071
+ for (var item in this.unsupportedWords) {
3072
+ var text = this.unsupportedWords[item];
3073
+ if(text === word){
3074
+ return true
3075
+ }
3076
+ }
3077
+ return false;
3078
+ }
3079
+
3080
+ startTransaction(){
3081
+ this.db.prepare('BEGIN').run();
3082
+ }
3083
+
3084
+ endTransaction(){
3085
+ this.db.prepare('COMMIT').run();
3086
+ }
3087
+
3088
+ errorTransaction(){
3089
+ this.db.prepare('ROLLBACK').run();
3090
+ }
3091
+
3092
+ _buildSQLEqualTo(model){
3093
+ var $that = this;
3094
+ var argument = null;
3095
+ var dirtyFields = model.__dirtyFields;
3096
+
3097
+ for (var column in dirtyFields) {
3098
+
3099
+ var type = model.__entity[dirtyFields[column]].type;
3100
+
3101
+ if(model.__entity[dirtyFields[column]].relationshipType === "belongsTo"){
3102
+ type = "belongsTo";
3103
+ }
3104
+ // TODO Boolean value is a string with a letter
3105
+ switch(type){
3106
+ case "belongsTo" :
3107
+ const foreignKey = model.__entity[dirtyFields[column]].foreignKey;
3108
+ argument = `${foreignKey} = ${model[dirtyFields[column]]},`;
3109
+ break;
3110
+ case "integer" :
3111
+ //model.__entity[dirtyFields[column]].skipGetFunction = true;
3112
+ var columneValue = model[`_${dirtyFields[column]}`];
3113
+ argument = argument === null ? `[${dirtyFields[column]}] = ${model[dirtyFields[column]]},` : `${argument} [${dirtyFields[column]}] = ${columneValue},`;
3114
+ //model.__entity[dirtyFields[column]].skipGetFunction = false;
3115
+ break;
3116
+ case "string" :
3117
+ argument = argument === null ? `[${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',` : `${argument} [${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',`;
3118
+ break;
3119
+ case "boolean" :
3120
+ var bool = "";
3121
+ if(model.__entity[dirtyFields[column]].valueConversion){
3122
+ bool = tools.convertBooleanToNumber(model[dirtyFields[column]]);
3123
+ }
3124
+ else{
3125
+ bool = model[dirtyFields[column]];
3126
+ }
3127
+ argument = argument === null ? `[${dirtyFields[column]}] = '${bool}',` : `${argument} [${dirtyFields[column]}] = ${bool},`;
3128
+ break;
3129
+ case "time" :
3130
+ argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = ${model[dirtyFields[column]]},`;
3131
+ break;
3132
+ case "belongsTo" :
3133
+ var fore = `_${dirtyFields[column]}`;
3134
+ argument = argument === null ? `[${model.__entity[dirtyFields[column]].foreignKey}] = '${model[fore]}',` : `${argument} [${model.__entity[dirtyFields[column]].foreignKey}] = '${model[fore]}',`;
3135
+ break;
3136
+ case "hasMany" :
3137
+ argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
3138
+ break;
3139
+ default:
3140
+ argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
3141
+ }
3142
+ }
3143
+
3144
+ if(argument){
3145
+ return argument.replace(/,\s*$/, "");
3146
+ }
3147
+ else{
3148
+ return -1;
3149
+ }
3150
+
3151
+ }
3152
+
3153
+
3154
+ _buildDeleteObject(currentModel){
3155
+ var primaryKey = currentModel.__Key === undefined ? tools.getPrimaryKeyObject(currentModel.__entity) : currentModel.__Key;
3156
+ var value = currentModel.__value === undefined ? currentModel[primaryKey] : currentModel.__value;
3157
+ var tableName = currentModel.__tableName === undefined ? currentModel.__entity.__name : currentModel.__tableName;
3158
+ return {tableName: tableName, primaryKey : primaryKey, value : value};
3159
+ }
3160
+
3161
+
3162
+ // return columns and value strings
3163
+ _buildSQLInsertObject(fields, modelEntity){
3164
+ var $that = this;
3165
+ var columns = null;
3166
+ var values = null;
3167
+ for (var column in modelEntity) {
3168
+ // column1 = value1, column2 = value2, ...
3169
+ if(column.indexOf("__") === -1 ){
3170
+ // call the get method if avlable
3171
+ var fieldColumn = "";
3172
+ // check if get function is avaliable if so use that
3173
+ fieldColumn = fields[column];
3174
+
3175
+ if((fieldColumn !== undefined && fieldColumn !== null ) && typeof(fieldColumn) !== "object"){
3176
+ switch(modelEntity[column].type){
3177
+ case "string" :
3178
+ fieldColumn = `'${$that._santizeSingleQuotes(fields[column])}'`;
3179
+ break;
3180
+ case "time" :
3181
+ fieldColumn = fields[column];
3182
+ break;
3183
+ }
3184
+
3185
+ var relationship = modelEntity[column].relationshipType
3186
+ if(relationship === "belongsTo"){
3187
+ column = modelEntity[column].foreignKey
3188
+ }
3189
+
3190
+ columns = columns === null ? `'${column}',` : `${columns} '${column}',`;
3191
+ values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
3192
+
3193
+ }
3194
+ else{
3195
+ switch(modelEntity[column].type){
3196
+ case "belongsTo" :
3197
+ var fieldObject = tools.findTrackedObject(fields.__context.__trackedEntities, column );
3198
+ if( Object.keys(fieldObject).length > 0){
3199
+ var primaryKey = tools.getPrimaryKeyObject(fieldObject.__entity);
3200
+ fieldColumn = fieldObject[primaryKey];
3201
+ column = modelEntity[column].foreignKey;
3202
+ columns = columns === null ? `'${column}',` : `${columns} '${column}',`;
3203
+ values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
3204
+ }else{
3205
+ console.log("Cannot find belings to relationship")
3206
+ }
3207
+
3208
+ break;
3209
+ }
3210
+
3211
+ }
3212
+ }
3213
+ }
3214
+ return {tableName: modelEntity.__name, columns: columns.replace(/,\s*$/, ""), values: values.replace(/,\s*$/, "")};
3215
+
3216
+ }
3217
+
3218
+ // will add double single quotes to allow sting to be saved.
3219
+ _santizeSingleQuotes(string){
3220
+ if (typeof string === 'string' || string instanceof String){
3221
+ return string.replace(/'/g, "''");
3222
+ }
3223
+ else{
3224
+ console.log("warning - Field being passed is not a string");
3225
+ throw "error warning - Field being passed is not a string";
3226
+ }
3227
+ }
3228
+
3229
+ // converts any object into SQL parameter select string
3230
+ _convertEntityToSelectParameterString(obj, entityName){
3231
+ // todo: loop throgh object and append string with comma to
3232
+ var mainString = "";
3233
+ const entries = Object.keys(obj);
3234
+
3235
+ for (const [name] of entries) {
3236
+ mainString += `${mainString}, ${entityName}.${name}`;
3237
+ }
3238
+ return mainString;;
3239
+ }
3240
+
3241
+ _execute(query){
3242
+ console.log("SQL:", query);
3243
+ return this.db.exec(query);
3244
+ }
3245
+
3246
+ _run(query){
3247
+ console.log("SQL:", query);
3248
+ return this.db.prepare(query).run();
3249
+ }
3250
+
3251
+ setDB(db, type){
3252
+ this.db = db;
3253
+ this.dbType = type; // this will let us know which type of sqlengine to use.
3254
+ }
3255
+ }
3256
+
3257
+ module.exports = SQLLiteEngine;
3258
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/SQLLiteEngine.js **
3259
+ // Version 0.0.4
3260
+ class Tools{
3261
+
3262
+ static checkIfArrayLike(obj) {
3263
+ if (Array.isArray(obj)) {
3264
+ return true;
3265
+ }
3266
+
3267
+ if (
3268
+ obj &&
3269
+ typeof obj === 'object' &&
3270
+ Object.keys(obj).some(k => !isNaN(k)) &&
3271
+ '0' in obj
3272
+ ) {
3273
+ return true;
3274
+ }
3275
+
3276
+ return -1;
3277
+ }
3278
+
3279
+ static returnEntityList(list, entityList ){
3280
+ var newList = [];
3281
+ for(var max = 0; max < list.length; max++ ){
3282
+ var ent = entityList[list[max]];
3283
+ if(ent){
3284
+ if(ent.relationshipType === "hasMany" || ent.relationshipType === "hasOne"){
3285
+ newList.push(ent.name);
3286
+ }
3287
+ }
3288
+ }
3289
+ return newList;
3290
+ }
3291
+
3292
+ static findEntity(name, entityList){
3293
+ return entityList[name];
3294
+ }
3295
+
3296
+ // this will remove everthing from back slash amount
3297
+ static removeBackwardSlashSection(string, amount, type){
3298
+ type = type === undefined ? "\\" : type;
3299
+ var stringArray = string.split(type);
3300
+ for(var i = 0; i < amount; i++){
3301
+ stringArray.pop();
3302
+ }
3303
+ return stringArray.join(type);
3304
+ }
3305
+
3306
+ static getPrimaryKeyObject(model){
3307
+ for (var key in model) {
3308
+ if (model.hasOwnProperty(key)) {
3309
+ if(model[key].primary){
3310
+ if(model[key].primary === true){
3311
+ return key
3312
+ }
3313
+ }
3314
+ }
3315
+ }
3316
+ }
3317
+
3318
+ static findForeignTable(name, model){
3319
+ for (var key in model) {
3320
+ if (model.hasOwnProperty(key)) {
3321
+ if(model[key].foreignTable){
3322
+ if(model[key].foreignTable === name){
3323
+ return model[key];
3324
+ }
3325
+ }
3326
+ }
3327
+ }
3328
+ return null;
3329
+ }
3330
+
3331
+ static createNewInstance(validModel, type, classModel){
3332
+ return new type(validModel, classModel);
3333
+ }
3334
+
3335
+ static findTrackedObject(obj, name){
3336
+ for (const property in obj) {
3337
+ if(obj[property].__name === name){
3338
+ return obj[property];
3339
+ }
3340
+ }
3341
+ return {};
3342
+ }
3343
+
3344
+ static clearAllProto(proto){
3345
+
3346
+ var newproto = {}
3347
+ if(proto.__proto__ ){
3348
+ for (var key in proto) {
3349
+ if(!key.startsWith("_")){
3350
+ var typeObj = typeof(proto[key]);
3351
+ newproto[key] = proto[key];
3352
+ if(typeObj === "object"){
3353
+ proto[key] = this.clearAllProto(proto[key]);
3354
+ }
3355
+ }
3356
+ }
3357
+ }
3358
+
3359
+ newproto["__name"] = proto["__name"];
3360
+ newproto["__state"] = proto["__state"];
3361
+ newproto["__entity"] = proto["__entity"];
3362
+ newproto["__context"] = proto["__context"];
3363
+ newproto["__dirtyFields"] = proto["__dirtyFields"];
3364
+
3365
+ newproto.__proto__ = null;
3366
+ return newproto;
3367
+
3368
+ }
3369
+
3370
+ static removePrimarykeyandVirtual(currentModel, modelEntity){
3371
+ var newCurrentModel = Object.create(currentModel);
3372
+
3373
+ for(var entity in modelEntity) {
3374
+ var currentEntity = modelEntity[entity];
3375
+ if (modelEntity.hasOwnProperty(entity)) {
3376
+ if(currentEntity.primary === true){
3377
+ delete newCurrentModel[`_${entity}`];
3378
+ }
3379
+ }
3380
+ if(currentEntity.virtual === true){
3381
+ // skip it from the insert
3382
+ delete newCurrentModel[`_${entity}`];
3383
+ }
3384
+
3385
+ }
3386
+ return newCurrentModel;
3387
+ }
3388
+
3389
+ static getEntity(name, modelEntity){
3390
+ for(var entity in modelEntity) {
3391
+ var currentEntity = modelEntity[entity];
3392
+ if (modelEntity.hasOwnProperty(entity)) {
3393
+ if(currentEntity.__name === name){
3394
+ return currentEntity;
3395
+ }
3396
+ }
3397
+ }
3398
+ return false;
3399
+ }
3400
+
3401
+ static capitalize = (s) => {
3402
+ if (typeof s !== 'string') return ''
3403
+ return s.charAt(0).toUpperCase() + s.slice(1)
3404
+ }
3405
+
3406
+ static capitalizeFirstLetter(string) {
3407
+ return string.charAt(0).toUpperCase() + string.slice(1);
3408
+ }
3409
+
3410
+ // return randome letter that is not the skip letter
3411
+ static getRandomLetter(length, skip){
3412
+ var result = '';
3413
+ var characters = 'abcdefghijklmnopqrstuvwxyz';
3414
+ var charactersLength = characters.length;
3415
+
3416
+ for ( var i = 0; i < length; i++ ) {
3417
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
3418
+ if(skip){
3419
+ for ( var b = 0; b < skip.length; b++ ) {
3420
+ if(result === skip[i].entity){
3421
+ result = "";
3422
+ i--;
3423
+ }
3424
+ }
3425
+ }
3426
+ }
3427
+
3428
+ return result;
3429
+ }
3430
+
3431
+ // TODO: this should be removed once we create a SQLIte Manager;
3432
+ // converts any object into SQL parameter select string
3433
+
3434
+ static convertEntityToSelectParameterString(obj){
3435
+ // todo: loop throgh object and append string with comma to
3436
+ var mainString = "";
3437
+ const entries = Object.keys(obj);
3438
+ for (const key of entries) {
3439
+ if(obj[key].type !== 'hasManyThrough' && obj[key].type !== "hasMany" && obj[key].type !== "hasOne"){
3440
+ if(obj[key].name){
3441
+ mainString = mainString === "" ? `${obj.__name}.${obj[key].name}` : `${mainString}, ${obj.__name}.${obj[key].name}`;
3442
+ }
3443
+ }
3444
+ }
3445
+ return mainString;;
3446
+ }
3447
+
3448
+ static convertBooleanToNumber(num) {
3449
+ num = num === 'true' ? true : (num === 'false' ? false : num);
3450
+ return num ? 1 : 0;
3451
+ }
3452
+ }
3453
+
3454
+ module.exports = Tools;
3455
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Tools.js **
3456
+ // Version 0.0.13
3457
+
3458
+ var modelBuilder = require('./Entity/entityModelBuilder');
3459
+ var query = require('masterrecord/QueryLanguage/queryMethods');
3460
+ var tools = require('./Tools');
3461
+ var SQLLiteEngine = require('masterrecord/SQLLiteEngine');
3462
+ var MYSQLEngine = require('masterrecord/mySQLEngine');
3463
+ var insertManager = require('./insertManager');
3464
+ var deleteManager = require('./deleteManager');
3465
+ var globSearch = require("glob");
3466
+ var fs = require('fs');
3467
+ const appRoot = require('app-root-path');
3468
+ const MySQLClient = require('masterrecord/mySQLSyncConnect');
3469
+
3470
+ class context {
3471
+ _isModelValid = {
3472
+ isValid: true,
3473
+ errors: []
3474
+ };
3475
+ __entities = [];
3476
+ __builderEntities = [];
3477
+ __trackedEntities = [];
3478
+ __relationshipModels = [];
3479
+ __environment = "";
3480
+ __name = "";
3481
+ isSQLite = false;
3482
+ isMySQL = false;
3483
+ isPostgres = false;
3484
+
3485
+ constructor(){
3486
+ this. __environment = process.env.master;
3487
+ this.__name = this.constructor.name;
3488
+ this._SQLEngine = "";
3489
+ }
3490
+
3491
+ /*
3492
+ SQLite expected model
3493
+ {
3494
+ "type": "better-sqlite3",
3495
+ "connection" : "/db/",
3496
+ "password": "",
3497
+ "username": ""
3498
+ }
3499
+ */
3500
+ __SQLiteInit(env, sqlName){
3501
+ try{
3502
+
3503
+ const sqlite3 = require(sqlName);
3504
+ let DBAddress = env.completeConnection;
3505
+ var db = new sqlite3(DBAddress, env);
3506
+ db.__name = sqlName;
3507
+ this._SQLEngine = new SQLLiteEngine();
3508
+ return db;
3509
+ }
3510
+ catch (e) {
3511
+ console.log("error SQL", e);
3512
+ throw error(e)
3513
+ }
3514
+ }
3515
+
3516
+ /*
3517
+ mysql expected model
3518
+ {
3519
+ "type": "mysql",
3520
+ host : 'localhost',
3521
+ user : 'me',
3522
+ password : 'secret',
3523
+ database : 'my_db'
3524
+ }
3525
+ */
3526
+ __mysqlInit(env, sqlName){
3527
+ try{
3528
+
3529
+ //const mysql = require(sqlName);
3530
+ const connection = new MySQLClient(env);
3531
+ this._SQLEngine = new MYSQLEngine();
3532
+ this._SQLEngine.__name = sqlName;
3533
+ return connection;
3534
+
3535
+ }
3536
+ catch (e) {
3537
+ console.log("error SQL", e);
3538
+ }
3539
+ }
3540
+
3541
+ __clearErrorHandler(){
3542
+ this._isModelValid = {
3543
+ isValid: true,
3544
+ errors: []
3545
+ };
3546
+ };
3547
+
3548
+ __findSettings(root, rootFolderLocation, envType){
3549
+
3550
+ if(envType === undefined){
3551
+ envType = "development";
3552
+ }
3553
+ var rootFolder = `${root}/${rootFolderLocation}`;
3554
+ var search = `${rootFolder}/**/*env.${envType}.json`;
3555
+ var files = globSearch.sync(search, rootFolder);
3556
+ var file = files[0];
3557
+ if(file === undefined){
3558
+ root = tools.removeBackwardSlashSection(root, 1, "/");
3559
+ rootFolder = `${root}/${rootFolderLocation}`;
3560
+ var search = `${rootFolder}/**/*env.${envType}.json`;
3561
+ var files = globSearch.sync(search,rootFolder);
3562
+ file = files[0];
3563
+ if(file === undefined){
3564
+ root = tools.removeBackwardSlashSection(root, 1, "/");
3565
+ rootFolder = `${root}/${rootFolderLocation}`;
3566
+ var search = `${rootFolder}/**/*env.${envType}.json`;
3567
+ var files = globSearch.sync(search,rootFolder);
3568
+ file = files[0];
3569
+ if(file === undefined){
3570
+ console.log(`could not find file - ${rootFolder}/env.${envType}.json`);
3571
+ throw error(`could not find file - ${rootFolder}/env.${envType}.json`);
3572
+ }
3573
+
3574
+ }
3575
+
3576
+ }
3577
+
3578
+ return {
3579
+ file: file,
3580
+ rootFolder : root
3581
+ };
3582
+ }
3583
+
3584
+ useSqlite(rootFolderLocation){
3585
+ try{
3586
+ this.isSQLite = true;
3587
+ var root = process.cwd();
3588
+ var envType = this.__environment;
3589
+ var contextName = this.__name;
3590
+ var file = this.__findSettings(root, rootFolderLocation, envType);
3591
+ var settings = require(file.file);
3592
+ var options = settings[contextName];
3593
+
3594
+ if(options === undefined){
3595
+ console.log("settings missing context name settings");
3596
+ throw error("settings missing context name settings");
3597
+ }
3598
+
3599
+ this.validateSQLiteOptions(options);
3600
+ options.completeConnection = `${file.rootFolder}${options.connection}`;
3601
+ var dbDirectory = options.completeConnection.substr(0, options.completeConnection.lastIndexOf("\/"));
3602
+
3603
+ if (!fs.existsSync(dbDirectory)){
3604
+ fs.mkdirSync(dbDirectory);
3605
+ }
3606
+
3607
+ this.db = this.__SQLiteInit(options, "better-sqlite3");
3608
+ this._SQLEngine.setDB(this.db, "better-sqlite3");
3609
+ return this;
3610
+ }
3611
+ catch(err){
3612
+ console.log("error:",err );
3613
+ throw error(err);
3614
+ }
3615
+ }
3616
+
3617
+ validateSQLiteOptions(options){
3618
+ if(options.hasOwnProperty('connect') === undefined){
3619
+ console.log("connnect string settings is missing")
3620
+ throw error("connection string settings is missing");
3621
+ }
3622
+
3623
+ }
3624
+
3625
+ useMySql(rootFolderLocation){
3626
+
3627
+ this.isMySQL = true;
3628
+ var envType = this.__environment;
3629
+ var contextName = this.__name;
3630
+ var root = appRoot.path;
3631
+ var file = this.__findSettings(root, rootFolderLocation, envType);
3632
+ var settings = require(file.file);
3633
+ var options = settings[contextName];
3634
+
3635
+ if(options === undefined){
3636
+ console.log("settings missing context name settings");
3637
+ throw error("settings missing context name settings");
3638
+ }
3639
+
3640
+ this.db = this.__mysqlInit(options, "mysql2");
3641
+ this._SQLEngine.setDB(this.db, "mysql");
3642
+ return this;
3643
+
3644
+ }
3645
+
3646
+
3647
+ dbset(model, name){
3648
+ var validModel = modelBuilder.create(model);
3649
+ validModel.__name = name === undefined ? model.name : name;
3650
+ this.__entities.push(validModel); // model object
3651
+ var buildMod = tools.createNewInstance(validModel, query, this);
3652
+ this.__builderEntities.push(buildMod); // query builder entites
3653
+ this[validModel.__name] = buildMod;
3654
+ }
3655
+
3656
+ modelState(){
3657
+ return this._isModelValid;
3658
+ }
3659
+
3660
+ saveChanges(){
3661
+ try{
3662
+ var tracked = this.__trackedEntities;
3663
+
3664
+ if(tracked.length > 0){
3665
+ // start transaction
3666
+ if(this.isSQLite){
3667
+ this._SQLEngine.startTransaction();
3668
+ for (var model in tracked) {
3669
+ var currentModel = tracked[model];
3670
+ switch(currentModel.__state) {
3671
+ case "insert":
3672
+ var insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
3673
+ insert.init(currentModel);
3674
+
3675
+ break;
3676
+ case "modified":
3677
+ if(currentModel.__dirtyFields.length > 0){
3678
+ var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
3679
+ // build columns equal to value string
3680
+ var argu = this._SQLEngine._buildSQLEqualTo(cleanCurrentModel);
3681
+ if(argu !== -1 ){
3682
+ var primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
3683
+ var sqlUpdate = {tableName: cleanCurrentModel.__entity.__name, arg: argu, primaryKey : primaryKey, primaryKeyValue : cleanCurrentModel[primaryKey] };
3684
+ this._SQLEngine.update(sqlUpdate);
3685
+ }
3686
+ else{
3687
+ console.log("Nothing has been tracked, modified, created or added");
3688
+ }
3689
+
3690
+ }
3691
+ else{
3692
+ console.log("Tracked entity modified with no values being changed");
3693
+ }
3694
+
3695
+ // code block
3696
+ break;
3697
+ case "delete":
3698
+ var deleteObject = new deleteManager(this._SQLEngine, this.__entities);
3699
+ deleteObject.init(currentModel);
3700
+
3701
+ break;
3702
+ }
3703
+ }
3704
+ this.__clearErrorHandler();
3705
+ this._SQLEngine.endTransaction();
3706
+ }
3707
+ if(this.isMySQL){
3708
+ //this._SQLEngine.startTransaction();
3709
+ for (var model in tracked) {
3710
+ var currentModel = tracked[model];
3711
+ switch(currentModel.__state) {
3712
+ case "insert":
3713
+ var insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
3714
+ insert.init(currentModel);
3715
+
3716
+ break;
3717
+ case "modified":
3718
+ if(currentModel.__dirtyFields.length > 0){
3719
+ var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
3720
+ // build columns equal to value string
3721
+ var argu = this._SQLEngine._buildSQLEqualTo(cleanCurrentModel);
3722
+ if(argu !== -1 ){
3723
+ var primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
3724
+ var sqlUpdate = {tableName: cleanCurrentModel.__entity.__name, arg: argu, primaryKey : primaryKey, primaryKeyValue : cleanCurrentModel[primaryKey] };
3725
+ this._SQLEngine.update(sqlUpdate);
3726
+ }
3727
+ else{
3728
+ console.log("Nothing has been tracked, modified, created or added");
3729
+ }
3730
+
3731
+ }
3732
+ else{
3733
+ console.log("Tracked entity modified with no values being changed");
3734
+ }
3735
+
3736
+ // code block
3737
+ break;
3738
+ case "delete":
3739
+ var deleteObject = new deleteManager(this._SQLEngine, this.__entities);
3740
+ deleteObject.init(currentModel);
3741
+
3742
+ break;
3743
+ }
3744
+ }
3745
+ this.__clearErrorHandler();
3746
+ //this._SQLEngine.endTransaction();
3747
+ }
3748
+ }
3749
+ else{
3750
+ console.log("save changes has no tracked entities");
3751
+ }
3752
+ }
3753
+
3754
+ catch(error){
3755
+ this.__clearErrorHandler();
3756
+
3757
+ console.log("error", error);
3758
+ if(this.isSQLite){
3759
+ this._SQLEngine.errorTransaction();
3760
+ }
3761
+ this.__clearTracked();
3762
+ throw error;
3763
+ }
3764
+
3765
+ this.__clearTracked();
3766
+ return true;
3767
+ }
3768
+
3769
+
3770
+ _execute(query){
3771
+ this._SQLEngine._execute(query);
3772
+ }
3773
+
3774
+ // __track(model){
3775
+ // this.__trackedEntities.push(model);
3776
+ // return model;
3777
+ // }
3778
+
3779
+ __track(model){
3780
+ var add = true;
3781
+ for (var mod in this.__trackedEntities) {
3782
+ var id = this.__trackedEntities[mod].__ID;
3783
+ if(id === undefined){
3784
+ id = Math.floor((Math.random() * 100000) + 1);
3785
+ }
3786
+ if(id === model.__ID){
3787
+ add = false;
3788
+ }
3789
+ }
3790
+ if(this.__trackedEntities.length === 0){
3791
+ this.__trackedEntities.push(model);
3792
+ }
3793
+ else{
3794
+ if(add){
3795
+ this.__trackedEntities.push(model);
3796
+ }
3797
+ }
3798
+
3799
+ return model;
3800
+ }
3801
+
3802
+ __findTracked(id){
3803
+ if(id){
3804
+ for (var model in this.__trackedEntities) {
3805
+ if(this.__trackedEntities[model].__ID === id){
3806
+ return this.__trackedEntities[model];
3807
+ }
3808
+ }
3809
+ }
3810
+ return null;
3811
+ }
3812
+
3813
+ __clearTracked(){
3814
+ this.__trackedEntities = [];
3815
+ }
3816
+ }
3817
+
3818
+
3819
+ module.exports = context;
3820
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/context.js **
3821
+ // version 0.0.2
3822
+ var tools = require('./Tools');
3823
+ class DeleteManager{
3824
+ constructor(sqlEngine, entities){
3825
+ this._SQLEngine = sqlEngine;
3826
+ this._allEntities = entities;
3827
+ }
3828
+
3829
+ init(currentModel){
3830
+ var $that = this;
3831
+ try{
3832
+ this.cascadeDelete(currentModel);
3833
+ }
3834
+ catch(error){
3835
+ throw error;
3836
+ }
3837
+ }
3838
+
3839
+ cascadeDelete(currentModel){
3840
+ var $that = this;
3841
+ if(!Array.isArray(currentModel)){
3842
+ const entityKeys = Object.keys(currentModel.__entity);
3843
+ // loop though all entity properties
3844
+ for (const property of entityKeys) {
3845
+ // cascade delete
3846
+ if(currentModel.__entity[property].type === "hasOne" || currentModel.__entity[property].type === "hasMany"){
3847
+ var curModel = currentModel[property];
3848
+ if(curModel === null){
3849
+ // check if state is nullable - if so and nothing comes back dont call cascadeDelete
3850
+ var prp = currentModel.__entity[property];
3851
+ if(!prp.nullable){
3852
+ throw "No relationship record found - please set hasOne or hasMany to nullable. "
3853
+ }
3854
+ }
3855
+ else{
3856
+ $that.cascadeDelete( currentModel[property]);
3857
+ }
3858
+ }
3859
+ }
3860
+ this._SQLEngine.delete(currentModel);
3861
+ }
3862
+ else{
3863
+
3864
+ for(let i = 0 ; i < currentModel.length; i++) {
3865
+ const entityKeys = Object.keys(currentModel[i].__entity);
3866
+ // loop though all entity properties
3867
+ for (const property of entityKeys) {
3868
+ // cascade delete
3869
+ if(currentModel[i].__entity[property].type === "hasOne" || currentModel[i].__entity[property].type === "hasMany"){
3870
+ $that.cascadeDelete( currentModel[i][property]);
3871
+ }
3872
+ }
3873
+ this._SQLEngine.delete(currentModel[i]);
3874
+ }
3875
+ }
3876
+
3877
+
3878
+ }
3879
+ }
3880
+
3881
+ module.exports = DeleteManager;
3882
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/deleteManager.js **
3883
+
3884
+ // version 0.0.10
3885
+ var tools = require('./Tools');
3886
+ var queryScript = require('masterrecord/QueryLanguage/queryScript');
3887
+
3888
+ class InsertManager {
3889
+
3890
+ constructor(sqlEngine, errorModel, allEntities ){
3891
+ this._SQLEngine = sqlEngine;
3892
+ this._errorModel = errorModel;
3893
+ this._allEntities = allEntities;
3894
+ this.__queryObject = new queryScript();
3895
+ }
3896
+
3897
+ init(currentModel){
3898
+ this.runQueries(currentModel);
3899
+ }
3900
+
3901
+ runQueries(currentModel){
3902
+ var $that = this;
3903
+ var cleanCurrentModel = tools.clearAllProto(currentModel);
3904
+ this.validateEntity(cleanCurrentModel, currentModel, currentModel.__entity);
3905
+ if(this._errorModel.isValid){
3906
+
3907
+ var modelEntity = currentModel.__entity;
3908
+ // TODO: if you try to add belongs to you must have a tag added first. if you dont throw error
3909
+ currentModel = this.belongsToInsert(currentModel, modelEntity);
3910
+ var SQL = this._SQLEngine.insert(cleanCurrentModel);
3911
+ var primaryKey = tools.getPrimaryKeyObject(currentModel.__entity);
3912
+ // return all fields that have auto and dont have a value to the current model on insert
3913
+ if(currentModel.__entity[primaryKey].auto === true){
3914
+ var query = `select * from ${currentModel.__entity.__name} where ${primaryKey} = ${ SQL.id }`;
3915
+ var jj = this.__queryObject.raw(query);
3916
+ var getQueryModel = this._SQLEngine.get(jj, currentModel.__entity, currentModel.__context );
3917
+ var idVal;
3918
+
3919
+ if(!getQueryModel[0]){
3920
+ idVal = getQueryModel[primaryKey]
3921
+ }
3922
+ else{
3923
+ idVal = getQueryModel[0][primaryKey];
3924
+ }
3925
+
3926
+ currentModel[primaryKey] = idVal;
3927
+ }
3928
+
3929
+ const proto = Object.getPrototypeOf(currentModel);
3930
+ const props = Object.getOwnPropertyNames(proto);
3931
+ const cleanPropList = tools.returnEntityList(props, modelEntity);
3932
+ const modelKeys = Object.keys(currentModel);
3933
+ const mergedArray = [...new Set(modelKeys.concat(cleanPropList))];
3934
+ // loop through model properties
3935
+ for (const property of mergedArray) {
3936
+ var propertyModel = currentModel[property];
3937
+ var entityProperty = modelEntity[property] ? modelEntity[property] : {};
3938
+ if(entityProperty.type === "hasOne"){
3939
+ // make sure property model is an object not a primary data type like number or string
3940
+
3941
+ if(typeof(propertyModel) === "object" || typeof(propertyModel) === "function" ){
3942
+ // check if model has its own entity
3943
+ if(modelEntity){
3944
+ // check if property has a value because we dont want this to run on every insert if nothing was added
3945
+ propertyModel.__entity = tools.getEntity(property, $that._allEntities);
3946
+ propertyModel[currentModel.__entity.__name] = SQL.id;
3947
+ $that.runQueries(propertyModel);
3948
+ }
3949
+ else{
3950
+ 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`
3951
+ }
3952
+ }
3953
+ }
3954
+
3955
+ if(entityProperty.type === "hasMany"){
3956
+ if(tools.checkIfArrayLike(propertyModel)){
3957
+ const propertyKeys = Object.keys(propertyModel);
3958
+ for (const propertykey of propertyKeys) {
3959
+ if(propertyModel[propertykey]){
3960
+ propertyModel[propertykey].__entity = tools.getEntity(property, $that._allEntities);
3961
+ propertyModel[propertykey][currentModel.__entity.__name] = SQL.id;
3962
+ $that.runQueries(propertyModel[propertykey]);
3963
+ }
3964
+ }
3965
+ }
3966
+ else{
3967
+ throw `Relationship "${entityProperty.name}" must be an array`;
3968
+ }
3969
+ }
3970
+
3971
+ }
3972
+ }
3973
+ else{
3974
+ var messages = this._errorModel.errors;
3975
+ const combinedError = messages.join('; and ');
3976
+ throw combinedError;
3977
+
3978
+ }
3979
+ }
3980
+
3981
+
3982
+
3983
+ // will insert belongs to row first and return the id so that next call can be make correctly
3984
+ belongsToInsert(currentModel, modelEntity){
3985
+ var $that = this;
3986
+ for(var entity in modelEntity) {
3987
+ if(modelEntity[entity].relationshipType === "belongsTo"){
3988
+ var foreignKey = modelEntity[entity].foreignKey === undefined ? modelEntity[entity].name : modelEntity[entity].foreignKey;
3989
+ var newPropertyModel = currentModel[foreignKey];
3990
+ // check if model is a an object. If so insert the child first then the parent.
3991
+ if(typeof newPropertyModel === 'object'){
3992
+ newPropertyModel.__entity = tools.getEntity(entity, $that._allEntities);
3993
+ var propertyCleanCurrentModel = tools.clearAllProto(newPropertyModel);
3994
+ this.validateEntity(propertyCleanCurrentModel, newPropertyModel, newPropertyModel.__entity);
3995
+ var propertySQL = this._SQLEngine.insert(newPropertyModel);
3996
+ currentModel[foreignKey] = propertySQL.id;
3997
+ }
3998
+ }
3999
+ }
4000
+ // todo:
4001
+ // loop through all modelEntity and find all the belongs to
4002
+ // if belongs to is true then make sql call to insert
4003
+ // update the currentModel.
4004
+ return currentModel;
4005
+ }
4006
+
4007
+ // validate entity for nullable fields and if the entity has any values at all
4008
+ validateEntity(currentModel, currentRealModel, entityModel){
4009
+ for(var entity in entityModel) {
4010
+ var currentEntity = entityModel[entity];
4011
+ if (entityModel.hasOwnProperty(entity)) {
4012
+ // check if there is a default value
4013
+ if(currentEntity.default){
4014
+ if(currentRealModel[entity] === undefined || currentRealModel[entity] === null){
4015
+ // if its empty add the default value
4016
+ currentRealModel[entity] = currentEntity.default;
4017
+ }
4018
+ }
4019
+
4020
+ // SKIP belongs too ----- // call sets for correct data for DB
4021
+ if(currentEntity.type !== "belongsTo" && currentEntity.type !== "hasMany"){
4022
+ if(currentEntity.relationshipType !== "belongsTo"){
4023
+ // primary is always null in an insert so validation insert must be null
4024
+ if(currentEntity.nullable === false && !currentEntity.primary){
4025
+ // if it doesnt have a get method then call error
4026
+ if(currentEntity.set === undefined){
4027
+ if(currentModel[entity] === undefined || currentModel[entity] === null ){
4028
+ this._errorModel.isValid = false;
4029
+ var errorMessage = `Entity ${currentModel.__entity.__name} column ${entity} is a required Field`;
4030
+ this._errorModel.errors.push(errorMessage);
4031
+ //throw errorMessage;
4032
+ }
4033
+ }
4034
+ else{
4035
+ var realData = currentEntity.set(currentModel[entity]);
4036
+ currentRealModel[entity] = realData;
4037
+ currentModel[entity] = realData;
4038
+ }
4039
+ }
4040
+ }
4041
+
4042
+ }
4043
+ }
4044
+
4045
+ }
4046
+ }
4047
+
4048
+ }
4049
+
4050
+
4051
+ module.exports = InsertManager;
4052
+
4053
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/insertManager.js **
4054
+ // version : 0.0.3
4055
+
4056
+ var tools = require('masterrecord/Tools');
4057
+ var util = require('util');
4058
+
4059
+ class SQLLiteEngine {
4060
+
4061
+ unsupportedWords = ["order"]
4062
+
4063
+ update(query){
4064
+ var sqlQuery = ` UPDATE ${query.tableName} SET ${query.arg} WHERE ${query.tableName}.${query.primaryKey} = ${query.primaryKeyValue}` // primary key for that table =
4065
+ return this._run(sqlQuery);
4066
+ }
4067
+
4068
+ delete(queryObject){
4069
+ var sqlObject = this._buildDeleteObject(queryObject);
4070
+ var sqlQuery = `DELETE FROM ${sqlObject.tableName} WHERE ${sqlObject.tableName}.${sqlObject.primaryKey} = ${sqlObject.value}`;
4071
+ return this._run(sqlQuery);
4072
+ }
4073
+
4074
+ insert(queryObject){
4075
+ var sqlObject = this._buildSQLInsertObject(queryObject, queryObject.__entity);
4076
+ var query = `INSERT INTO ${sqlObject.tableName} (${sqlObject.columns}) VALUES (${sqlObject.values})`;
4077
+ var queryObj = this._run(query);
4078
+ // return
4079
+ var open = {
4080
+ "id": queryObj.insertId
4081
+ };
4082
+ return open;
4083
+ }
4084
+
4085
+ get(query, entity, context){
4086
+ var queryString = {};
4087
+ try {
4088
+ if(query.raw){
4089
+ queryString.query = query.raw;
4090
+ }
4091
+ else{
4092
+ queryString = this.buildQuery(query, entity, context);
4093
+ }
4094
+ if(queryString.query){
4095
+ console.log("SQL:", queryString.query);
4096
+ this.db.connect(this.db);
4097
+ const result = this.db.query(queryString.query);
4098
+ console.log("results:", result);
4099
+ return result;
4100
+ }
4101
+ return null;
4102
+ } catch (err) {
4103
+ console.error(err);
4104
+ return null;
4105
+ }
4106
+ }
4107
+
4108
+ getCount(queryObject, entity, context){
4109
+ var query = queryObject.script;
4110
+ var queryString = {};
4111
+ try {
4112
+ if(query.raw){
4113
+ queryString.query = query.raw;
4114
+ }
4115
+ else{
4116
+ queryString = this.buildQuery(query, entity, context);
4117
+ }
4118
+ if(queryString.query){
4119
+ var queryCount = queryObject.count(queryString.query)
4120
+ console.log("SQL:", queryCount );
4121
+ var queryReturn = this.db.prepare(queryCount ).get();
4122
+ return queryReturn;
4123
+ }
4124
+ return null;
4125
+ } catch (err) {
4126
+ console.error(err);
4127
+ return null;
4128
+ }
4129
+ }
4130
+
4131
+ all(query, entity, context){
4132
+ var queryString = {};
4133
+ try {
4134
+ if(query.raw){
4135
+ queryString.query = query.raw;
4136
+ }
4137
+ else{
4138
+ queryString = this.buildQuery(query, entity, context);
4139
+ }
4140
+ if(queryString.query){
4141
+ console.log("SQL:", queryString.query);
4142
+ this.db.connect(this.db);
4143
+ const result = this.db.query(queryString.query);
4144
+ console.log("results:", result);
4145
+ return result;
4146
+ }
4147
+ return null;
4148
+ } catch (err) {
4149
+ console.error(err);
4150
+ return null;
4151
+ }
4152
+ }
4153
+
4154
+
4155
+ buildQuery(query, entity, context){
4156
+
4157
+ var queryObject = {};
4158
+ if(entity){
4159
+ queryObject.entity = this.getEntity(entity.__name, query.entityMap);
4160
+ queryObject.select = this.buildSelect(query, entity);
4161
+ queryObject.from = this.buildFrom(query, entity);
4162
+ queryObject.include = this.buildInclude(query, entity, context, queryObject);
4163
+ queryObject.where = this.buildWhere(query, entity);
4164
+
4165
+ var queryString = `${queryObject.select} ${queryObject.from} ${queryObject.include} ${queryObject.where}`;
4166
+ return {
4167
+ query : queryString,
4168
+ entity : this.getEntity(entity.__name, query.entityMap)
4169
+ }
4170
+ }
4171
+ else{
4172
+ console.log("Error: Entity object is blank");
4173
+ }
4174
+
4175
+
4176
+ }
4177
+
4178
+ buildWhere(query, mainQuery){
4179
+ var whereEntity = query.where;
4180
+ var strQuery = "";
4181
+ var $that = this;
4182
+ if(whereEntity){
4183
+ var entity = this.getEntity(query.parentName, query.entityMap);
4184
+ for (let part in whereEntity[query.parentName]) {
4185
+ var item = whereEntity[query.parentName][part];
4186
+ for (let exp in item.expressions) {
4187
+ var field = tools.capitalizeFirstLetter(item.expressions[exp].field);
4188
+ if(mainQuery[field]){
4189
+ if(mainQuery[field].isNavigational){
4190
+ entity = $that.getEntity(field, query.entityMap);
4191
+ field = item.fields[1];
4192
+ }
4193
+ }
4194
+ if(strQuery === ""){
4195
+ strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
4196
+ }
4197
+ else{
4198
+ strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
4199
+ }
4200
+ }
4201
+ }
4202
+ }
4203
+ return strQuery;
4204
+ }
4205
+
4206
+ buildInclude( query, entity, context){
4207
+ var includeQuery = "";
4208
+ for (let part in query.include) {
4209
+ var includeEntity = query.include[part];
4210
+ var $that = this;
4211
+ if(includeEntity){
4212
+ var parentObj = includeEntity[query.parentName];
4213
+ var currentContext = "";
4214
+ if(includeEntity.selectFields){
4215
+ currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
4216
+ }
4217
+
4218
+ if(parentObj){
4219
+ parentObj.entityMap = query.entityMap;
4220
+ var foreignKey = $that.getForeignKey(entity.__name, currentContext.__entity);
4221
+ var mainPrimaryKey = $that.getPrimarykey(entity);
4222
+ var mainEntity = $that.getEntity(entity.__name, query.entityMap);
4223
+ if(currentContext.__entity[entity.__name].type === "hasManyThrough"){
4224
+ var foreignTable = tools.capitalizeFirstLetter(currentContext.__entity[entity.__name].foreignTable); //to uppercase letter
4225
+ foreignKey = $that.getPrimarykey(currentContext.__entity);
4226
+ mainPrimaryKey = context[foreignTable].__entity[currentContext.__entity.__name].foreignKey;
4227
+ var mainEntity = $that.getEntity(foreignTable,query.entityMap);
4228
+ }
4229
+ // add foreign key to select so that it picks it up
4230
+ if(parentObj.select){
4231
+ parentObj.select.selectFields.push(foreignKey);
4232
+ }else{
4233
+ parentObj.select = {};
4234
+ parentObj.select.selectFields = [];
4235
+ parentObj.select.selectFields.push(foreignKey);
4236
+ }
4237
+
4238
+ var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
4239
+
4240
+ includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
4241
+
4242
+ }
4243
+ }
4244
+ }
4245
+ return includeQuery;
4246
+ }
4247
+
4248
+ buildFrom(query, entity){
4249
+ var entityName = this.getEntity(entity.__name, query.entityMap);
4250
+ if(entityName ){
4251
+ return `FROM ${entity.__name } AS ${entityName}`;
4252
+ }
4253
+ else{ return "" }
4254
+ }
4255
+
4256
+ buildSelect(query, entity){
4257
+ // this means that there is a select statement
4258
+ var select = "SELECT";
4259
+ var arr = "";
4260
+ var $that = this;
4261
+ if(query.select){
4262
+ for (const item in query.select.selectFields) {
4263
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
4264
+ };
4265
+
4266
+ }
4267
+ else{
4268
+ var entityList = this.getEntityList(entity);
4269
+ for (const item in entityList) {
4270
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
4271
+ };
4272
+ }
4273
+ arr = arr.replace(/,\s*$/, "");
4274
+ return `${select} ${arr} `;
4275
+ }
4276
+
4277
+ getForeignKey(name, entity){
4278
+ if(entity && name){
4279
+ return entity[name].foreignKey;
4280
+ }
4281
+ }
4282
+
4283
+ getPrimarykey(entity){
4284
+ for (const item in entity) {
4285
+ if(entity[item].primary){
4286
+ if(entity[item].primary === true){
4287
+ return entity[item].name;
4288
+ }
4289
+ }
4290
+ };
4291
+ }
4292
+
4293
+ getForeignTable(name, entity){
4294
+ if(entity && name){
4295
+ return entity[name].foreignTable;
4296
+ }
4297
+ }
4298
+
4299
+ getInclude(name, query){
4300
+ var include = query.include;
4301
+ if(include){
4302
+ for (let part in include) {
4303
+ if(tools.capitalizeFirstLetter(include[part].selectFields[0]) === name){
4304
+ return include[part];
4305
+ }
4306
+ }
4307
+ }
4308
+ else{
4309
+ return "";
4310
+ }
4311
+ }
4312
+
4313
+ getEntity(name, maps){
4314
+ for (let item in maps) {
4315
+ var map = maps[item];
4316
+ if(tools.capitalizeFirstLetter(name) === map.name){
4317
+ return map.entity
4318
+ }
4319
+ }
4320
+ return "";
4321
+ }
4322
+
4323
+ // return a list of entity names and skip foreign keys and underscore.
4324
+ getEntityList(entity){
4325
+ var entitiesList = [];
4326
+ var $that = this;
4327
+ for (var ent in entity) {
4328
+ if(!ent.startsWith("_")){
4329
+ if(!entity[ent].foreignKey){
4330
+ if(entity[ent].relationshipTable){
4331
+ if($that.chechUnsupportedWords(entity[ent].relationshipTable)){
4332
+ entitiesList.push(`'${entity[ent].relationshipTable}'`);
4333
+ }
4334
+ else{
4335
+ entitiesList.push(entity[ent].relationshipTable);
4336
+ }
4337
+ }
4338
+ else{
4339
+ if($that.chechUnsupportedWords(ent)){
4340
+ entitiesList.push(`'${ent}'`);
4341
+ }
4342
+ else{
4343
+ entitiesList.push(ent);
4344
+ }
4345
+ }
4346
+ }
4347
+ else{
4348
+
4349
+ if(entity[ent].relationshipType === "belongsTo"){
4350
+ var name = entity[ent].foreignKey;
4351
+ if($that.chechUnsupportedWords(name)){
4352
+ entitiesList.push(`'${name}'`);
4353
+ //entitiesList.push(`'${ent}'`);
4354
+ }
4355
+ else{
4356
+ entitiesList.push(name);
4357
+ //entitiesList.push(ent);
4358
+ }
4359
+ }
4360
+
4361
+ }
4362
+ }
4363
+ }
4364
+ return entitiesList
4365
+ }
4366
+
4367
+ chechUnsupportedWords(word){
4368
+ for (var item in this.unsupportedWords) {
4369
+ var text = this.unsupportedWords[item];
4370
+ if(text === word){
4371
+ return true
4372
+ }
4373
+ }
4374
+ return false;
4375
+ }
4376
+
4377
+ startTransaction(){
4378
+ this.db.prepare('BEGIN').run();
4379
+ }
4380
+
4381
+ endTransaction(){
4382
+ this.db.prepare('COMMIT').run();
4383
+ }
4384
+
4385
+ errorTransaction(){
4386
+ this.db.prepare('ROLLBACK').run();
4387
+ }
4388
+
4389
+ _buildSQLEqualTo(model){
4390
+ var $that = this;
4391
+ var argument = null;
4392
+ var dirtyFields = model.__dirtyFields;
4393
+
4394
+ for (var column in dirtyFields) {
4395
+ // TODO Boolean value is a string with a letter
4396
+ var type = model.__entity[dirtyFields[column]].type;
4397
+
4398
+ if(model.__entity[dirtyFields[column]].relationshipType === "belongsTo"){
4399
+ type = "belongsTo";
4400
+ }
4401
+
4402
+ switch(type){
4403
+ case "belongsTo" :
4404
+ const foreignKey = model.__entity[dirtyFields[column]].foreignKey;
4405
+ argument = `${foreignKey} = ${model[dirtyFields[column]]},`;
4406
+ break;
4407
+ case "integer" :
4408
+ const columneValue = model[`_${dirtyFields[column]}`];
4409
+ argument = argument === null ? `[${dirtyFields[column]}] = ${model[dirtyFields[column]]},` : `${argument} [${dirtyFields[column]}] = ${columneValue},`;
4410
+ break;
4411
+ case "string" :
4412
+ argument = argument === null ? `${dirtyFields[column]} = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',` : `${argument} ${dirtyFields[column]} = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',`;
4413
+ break;
4414
+ case "boolean" :
4415
+ argument = argument === null ? `${dirtyFields[column]} = '${this.boolType(model[dirtyFields[column]])}',` : `${argument} ${dirtyFields[column]} = '${this.boolType(model[dirtyFields[column]])}',`;
4416
+ break;
4417
+ default:
4418
+ argument = argument === null ? `${dirtyFields[column]} = '${model[dirtyFields[column]]}',` : `${argument} ${dirtyFields[column]} = '${model[dirtyFields[column]]}',`;
4419
+ }
4420
+ }
4421
+ if(argument){
4422
+ return argument.replace(/,\s*$/, "");
4423
+ }
4424
+ else{
4425
+ return -1;
4426
+ }
4427
+ }
4428
+
4429
+ boolType(type){
4430
+ var jj = String(type);
4431
+ switch(jj) {
4432
+ case "true":
4433
+ return 1
4434
+ break;
4435
+ case "false":
4436
+ return 0
4437
+ break;
4438
+ default:
4439
+ return type;
4440
+ }
4441
+ }
4442
+
4443
+
4444
+ _buildDeleteObject(currentModel){
4445
+ var primaryKey = currentModel.__Key === undefined ? tools.getPrimaryKeyObject(currentModel.__entity) : currentModel.__Key;
4446
+ var value = currentModel.__value === undefined ? currentModel[primaryKey] : currentModel.__value;
4447
+ var tableName = currentModel.__tableName === undefined ? currentModel.__entity.__name : currentModel.__tableName;
4448
+ return {tableName: tableName, primaryKey : primaryKey, value : value};
4449
+ }
4450
+
4451
+
4452
+ // return columns and value strings
4453
+ _buildSQLInsertObject(fields, modelEntity){
4454
+ var $that = this;
4455
+ var columns = null;
4456
+ var values = null;
4457
+ for (var column in modelEntity) {
4458
+ // column1 = value1, column2 = value2, ...
4459
+ if(column.indexOf("__") === -1 ){
4460
+ // call the get method if avlable
4461
+ var fieldColumn = "";
4462
+ // check if get function is avaliable if so use that
4463
+ fieldColumn = fields[column];
4464
+
4465
+ if((fieldColumn !== undefined && fieldColumn !== null) && typeof(fieldColumn) !== "object"){
4466
+ switch(modelEntity[column].type){
4467
+ case "string" :
4468
+ fieldColumn = `'${$that._santizeSingleQuotes(fields[column])}'`;
4469
+ break;
4470
+ case "time" :
4471
+ fieldColumn = fields[column];
4472
+ break;
4473
+ }
4474
+
4475
+ var relationship = modelEntity[column].relationshipType
4476
+ if(relationship === "belongsTo"){
4477
+ column = modelEntity[column].foreignKey
4478
+ }
4479
+
4480
+
4481
+ columns = columns === null ? `${column},` : `${columns} ${column},`;
4482
+ values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
4483
+
4484
+ }
4485
+ }
4486
+ }
4487
+
4488
+ return {tableName: modelEntity.__name, columns: columns.replace(/,\s*$/, ""), values: values.replace(/,\s*$/, "")};
4489
+
4490
+ }
4491
+
4492
+ // will add double single quotes to allow sting to be saved.
4493
+ _santizeSingleQuotes(string){
4494
+
4495
+ if(typeof string === "string"){
4496
+ return string.replace(/'/g, "''");
4497
+ }else{
4498
+ return `${string}`;
4499
+ }
4500
+ }
4501
+
4502
+ // converts any object into SQL parameter select string
4503
+ _convertEntityToSelectParameterString(obj, entityName){
4504
+ // todo: loop throgh object and append string with comma to
4505
+ var mainString = "";
4506
+ const entries = Object.keys(obj);
4507
+
4508
+ for (const [name] of entries) {
4509
+ mainString += `${mainString}, ${entityName}.${name}`;
4510
+ }
4511
+ return mainString;;
4512
+ }
4513
+
4514
+ _execute(query){
4515
+ console.log("SQL:", query);
4516
+ this.db.connect(this.db);
4517
+ return this.db.query(query);
4518
+ }
4519
+
4520
+ _run(query){
4521
+ try{
4522
+
4523
+ console.log("SQL:", query);
4524
+ this.db.connect(this.db);
4525
+ const result = this.db.query(query);
4526
+
4527
+ return result;}
4528
+ catch (error) {
4529
+ console.error(error);
4530
+ // Expected output: ReferenceError: nonExistentFunction is not defined
4531
+ // (Note: the exact output may be browser-dependent)
4532
+ }
4533
+ }
4534
+
4535
+ setDB(db, type){
4536
+ this.db = db;
4537
+ this.dbType = type; // this will let us know which type of sqlengine to use.
4538
+ }
4539
+ }
4540
+
4541
+ module.exports = SQLLiteEngine;
4542
+
4543
+
4544
+
4545
+
4546
+ /***
4547
+ *
4548
+ *
4549
+ *
4550
+ * const mysql = require('mysql2/promise');
4551
+
4552
+ class MySQLClient {
4553
+ constructor(config) {
4554
+ this.config = config;
4555
+ this.pool = mysql.createPool(config);
4556
+ }
4557
+
4558
+ async query(sql, params = []) {
4559
+ const connection = await this.pool.getConnection();
4560
+ try {
4561
+ const [results] = await connection.execute(sql, params);
4562
+ return results;
4563
+ } finally {
4564
+ connection.release();
4565
+ }
4566
+ }
4567
+
4568
+ async close() {
4569
+ await this.pool.end();
4570
+ }
4571
+ }
4572
+
4573
+ module.exports = MySQLClient;
4574
+
4575
+ */
4576
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/mySQLEngine.js **
4577
+ var MySql = require('sync-mysql2');
4578
+
4579
+
4580
+ class MySQLClient {
4581
+ constructor(config) {
4582
+ this.config = config;
4583
+ this.connection = null;
4584
+ }
4585
+
4586
+ connect() {
4587
+ if (!this.connection) {
4588
+ this.connection = new MySql(this.config);
4589
+ }
4590
+ }
4591
+
4592
+ query(sql, params = []) {
4593
+ try {
4594
+ if (!this.connection) {
4595
+ throw new Error('Database connection not established. Call connect() first.');
4596
+ }
4597
+ var jj = this.connection.query(sql);
4598
+ this.connection.finishAll();
4599
+ return jj;
4600
+ } catch (err) {
4601
+ console.error(err);
4602
+ return null;
4603
+ }
4604
+
4605
+ }
4606
+
4607
+ close() {
4608
+ if (this.connection) {
4609
+ this.connection.end();
4610
+ this.connection = null;
4611
+ }
4612
+ }
4613
+ }
4614
+
4615
+ module.exports = MySQLClient;
4616
+
4617
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/mySQLSyncConnect.js **
4618
+ {
4619
+ "name": "masterrecord",
4620
+ "dependencies": {
4621
+ "commander": "^13.1.0",
4622
+ "glob" : "^11.0.1",
4623
+ "deep-object-diff" : "^1.1.9",
4624
+ "pg" : "^8.14.1",
4625
+ "sync-mysql2" : "^1.0.5",
4626
+ "app-root-path": "^3.1.0"
4627
+ },
4628
+ "version": "0.1.4",
4629
+ "description": "An Object-relational mapping for the Master framework. Master Record connects classes to relational database tables to establish a database with almost zero-configuration ",
4630
+ "homepage": "https://github.com/Tailor/MasterRecord#readme",
4631
+ "repository": {
4632
+ "type": "git",
4633
+ "url": "git+https://github.com/Tailor/Masterrecord.git"
4634
+ },
4635
+ "main": "MasterRecord.js",
4636
+ "scripts": {
4637
+ "test": "echo \"Error: no test specified\" && exit 1"
4638
+ },
4639
+ "author": "Alexander Rich",
4640
+ "license": "ISC",
4641
+ "bin": {
4642
+ "masterrecord": "./migrations/cli.js"
4643
+ }
4644
+ }
4645
+
4646
+ ** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/package.json **