masterrecord 0.0.26 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Masterrecord.js CHANGED
@@ -10,6 +10,7 @@ var SQLLiteEngine = require('masterrecord/SQLLiteEngine');
10
10
  var MYSQLEngine = require('masterrecord/MYSQLEngine');
11
11
  var insertManager = require('./InsertManager');
12
12
  var deleteManager = require('./DeleteManager');
13
+ var globSearch = require("glob");
13
14
 
14
15
  class Context {
15
16
  _isModelValid = {
@@ -40,7 +41,7 @@ class Context {
40
41
  __SQLiteInit(env, sqlName){
41
42
  try{
42
43
  const sqlite3 = require(sqlName);
43
- let DBAddress = `${env.completeConnection}${env.env}.sqlite3`;
44
+ let DBAddress = env.connection;
44
45
  var db = new sqlite3(DBAddress, env);
45
46
  db.__name = sqlName;
46
47
  this._SQLEngine = new SQLLiteEngine();
@@ -84,35 +85,43 @@ class Context {
84
85
  errors: []
85
86
  };
86
87
  };
88
+
89
+ useSqlite(rootFolderLocation){
90
+ var contextName = this.constructor.name;
91
+ var envType = process.env.master;
92
+ var search = `${rootFolderLocation}/**/*env.${envType}.json`;
93
+ var files = globSearch.sync(search, rootFolderLocation);
94
+ var file = files[0];
95
+ var settings = require(file);
96
+ var options = settings[contextName][contextName];
97
+ this.db = this.__SQLiteInit(options, "better-sqlite3");
98
+ this._SQLEngine.setDB(this.db, "better-sqlite3");
99
+ return this;
100
+ }
101
+
102
+ useSqlite(options){
103
+ if(options !== undefined){
104
+ this.db = this.__SQLiteInit(options, "better-sqlite3");
105
+ this._SQLEngine.setDB(this.db, "better-sqlite3");
106
+ return this;
107
+ }
108
+ else{
109
+ console.log("database options not defined - Master Record");
110
+ }
111
+ }
87
112
 
88
- setup(options, rootFolderLocation){
89
-
113
+ useMySql(options, rootFolderLocation){
90
114
  if(options !== undefined){
91
- if(options.type !== undefined){
92
- switch(options.type) {
93
- case "better-sqlite3":
94
- options.completeConnection = rootFolderLocation + options.connection;
95
- this.db = this.__SQLiteInit(options, options.type);
96
- this._SQLEngine.setDB(this.db, "better-sqlite3");
97
- return this;
98
- break;
99
- case "mysql":
100
- this.db = this.__mysqlInit(options, options.type);
101
- this._SQLEngine.setDB(this.db, "mysql");
102
- return this;
103
- break;
104
- }
105
- }
106
- else{
107
- console.log("database type not defined - Master Record");
108
- }
115
+ this.db = this.__mysqlInit(options, "mysql");
116
+ this._SQLEngine.setDB(this.db, "mysql");
117
+ return this;
109
118
  }
110
119
  else{
111
- console.log("database information not added - Master Record");
120
+ console.log("database options not defined - Master Record");
112
121
  }
113
-
114
122
  }
115
123
 
124
+
116
125
  dbset(model, name){
117
126
  var validModel = modelBuilder.create(model);
118
127
  validModel.__name = name === undefined ? model.name : name;
package/Migrations/cli.js CHANGED
@@ -4,82 +4,104 @@ const program = require('commander');
4
4
  let fs = require('fs');
5
5
  let path = require('path');
6
6
  var Migration = require('./migrations');
7
+ var globSearch = require("glob");
7
8
 
8
9
  const [,, ...args] = process.argv
9
10
 
11
+
12
+
10
13
  program
11
- .version('0.0.1')
12
- .option('-v, --version', '0.0.1')
14
+ .version('0.0.2')
15
+ .option('-v, --version', '0.0.2')
13
16
  .description('A ORM framework that facilitates the creation and use of business objects whose data requires persistent storage to a database');
14
17
 
15
- // Instructions : to run command you must go to folder where context file is located and run command with context file name.
18
+ // Instructions : to run command you must go to main project folder is located and run the command using the context file name.
16
19
  program
17
20
  .command('enable-migrations <contextFileName>')
18
- .alias('am')
19
- .description('Enables the migration in your project by creating a Configuration class called ContextModelSnapShot.json')
21
+ .alias('em')
22
+ .description('Enables the migration in your project by creating a configuration class called ContextSnapShot.json')
20
23
  .action(function(contextFileName){
24
+ var migration = new Migration();
21
25
  // location of folder where command is being executed..
22
26
  var executedLocation = process.cwd();
23
- // go back 1 folder level
24
- let previousFolder = path.join(executedLocation, '../');
25
- var migrationsDirectory = `${previousFolder}/Migrations`;
26
- if (!fs.existsSync(migrationsDirectory)){
27
- fs.mkdirSync(migrationsDirectory);
28
- }
27
+ // find context file from main folder location
28
+ var search = `${executedLocation}/**/*${contextFileName}.js`
29
29
 
30
- var content = {
31
- contextLocation: `${executedLocation}/${contextFileName}.js`,
32
- migrationLocation: `${executedLocation}/Migrations`,
33
- schema : {}
34
- };
35
- const jsonContent = JSON.stringify(content, null, 2);
36
- try{
37
- // will replace the whole file if it exist
38
- fs.writeFileSync(`${migrationsDirectory}/ContextSnapShot.json`, jsonContent);
39
- }catch (e){
40
- console.log("Cannot write file ", e);
30
+ var files = globSearch.sync(search, executedLocation);
31
+ var file = files[0];
32
+ var contextInstance = require(file);
33
+
34
+ var snap = {
35
+ file : files[0],
36
+ executedLocation : executedLocation,
37
+ context : new contextInstance(),
38
+ contextFileName: contextFileName.toLowerCase()
41
39
  }
40
+
41
+ migration.createSnapShot(snap);
42
+ console.log("Migration enabled")
43
+
42
44
  });
43
45
 
44
46
  // Instructions : to run command you must go to folder where migration file is located.
45
47
  program
46
- .command('add-migration <name>')
48
+ .command('add-migration <name> <contextFileName>')
47
49
  .alias('am')
48
- .description('Creates a new migration class')
49
- .action(function(name){
50
-
50
+ .action(function(name, contextFileName){
51
+ var executedLocation = process.cwd();
52
+ contextFileName = contextFileName.toLowerCase();
51
53
  try{
52
- var contextSnapshot = fs.readFileSync(`./ContextSnapShot.json`, 'utf8');
53
- var migration = new Migration();
54
- var context = require(contextSnapshot.contextLocation);
55
- var newEntity = migration.EDMModelDiffer(contextSnapshot.schema, context);
56
- if(newEntity.length > 0){
57
- var migrationDate = Date.now();
58
- migration.migrationCodeGenerator(name, newEntity, migrationDate);
59
- console.log(`migration ${name}_${migrationDate} created`);
60
- }
61
- }catch (e){
62
- console.log("Cannot read or find file ", e);
54
+ // find context file from main folder location
55
+ var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
56
+
57
+ var files = globSearch.sync(search, executedLocation);
58
+ var contextSnapshot = require(files[0]);
59
+ var migration = new Migration();
60
+ var context = require(contextSnapshot.contextLocation);
61
+ var newEntity = migration.buildMigrationTemplate(name, contextSnapshot.schema, context.__entities);
62
+ var migrationDate = Date.now();
63
+ var file = `${contextSnapshot.migrationFolder}/${migrationDate}_${name}_migration.js`
64
+ fs.writeFile(file, newEntity, 'utf8', function (err) {
65
+ if (err) return console.log("--- Error running cammand, rlease run command add-migration ---- ", err);
66
+
67
+ });
68
+ }catch (e){
69
+ console.log("Cannot read or find file ", e);
63
70
  }
64
71
 
72
+ console.log(`${name} migration file created`);
65
73
  });
66
74
 
67
- // will use the database settings to call and get the schema_migration table
68
- // we will get the list of all migrations that have been ran
69
- // we will compare that list with the folder of migrations to see if we ran everything
70
- // the migrations we missed we will call the up method
71
- // the up method will run the create database functions
72
- // then update the database.js schema
73
-
74
75
  program
75
- .command('update-database <databaseSettingLocation> <migrationFolderLocation> ')
76
+ .command('update-database <contextFileName>')
76
77
  .alias('ud')
77
78
  .description('Apply pending migrations to database')
78
- .action(function(cmd){
79
- var dir = process.cwd();
80
- console.log("starting server");
81
- require(dir + '/server.js');
82
- //return "node c:\node\server.js"
79
+ .action(function(contextFileName){
80
+ var executedLocation = process.cwd();
81
+ contextFileName = contextFileName.toLowerCase();
82
+
83
+ try{
84
+ // find context file from main folder location
85
+ var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
86
+ var files = globSearch.sync(search, executedLocation);
87
+ var file = files[0];
88
+ var contextSnapshot = require(file);
89
+ var searchMigration = `**/*_migration.js`
90
+ var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
91
+ if( migrationFiles){
92
+ var mFiles = migrationFiles.sort(function(x, y){
93
+ return new Date(x.timestamp) < new Date(y.timestamp) ? 1 : -1
94
+ });
95
+ mFiles = mFiles[0];
96
+ var migration = require(mFiles);
97
+ var settings = Migration.getSettings(executedLocation);
98
+ var newMigrationInstance = new migration(settings);
99
+ newMigrationInstance.up();
100
+ }
101
+ }catch (e){
102
+ console.log("Cannot read or find file ", e);
103
+ }
104
+ console.log("database updated");
83
105
  });
84
106
 
85
107
  // we will find the migration folder inside the nearest app folder if no migration folder is location is added
@@ -88,6 +110,7 @@ program
88
110
  .alias('rm')
89
111
  .description('Removes the last migration that has not been applied')
90
112
  .action(function(name){
113
+ // remove migrations call the down method of a migration newMigrationInstance.down();
91
114
  // find migration file using name and delete it.
92
115
  });
93
116
 
@@ -1,64 +1,79 @@
1
- var Schema = require('./schema');
2
-
1
+ const os = require('os');
3
2
 
4
3
  // https://channel9.msdn.com/Blogs/EF/Migrations-Under-the-Hood
5
4
 
6
-
7
- // migration Logic
8
- // using the command line run the command - "add migration 'name' 'context file location' ";
9
- // this will call the context which will return on object of entities
10
- // then call the previous migration and pass that model object and the context object to the EDMModelDiffer
11
- // the EDMModelDiffer function to calculate database changes
12
- // EDMModelDiffer will return only database changes model that have not been implemented already
13
- // we then create the miration using function 'migrationCodeGenerator' based on the model that was provided by EDMModelDiffer
14
- // js date stamp Date.now()
15
-
16
- class Migration extends Schema {
17
-
18
- constructor() {
19
- super();
5
+ class MigrationTemplate {
6
+ up = ''
7
+ down = ''
8
+ constructor(name) {
9
+ this.name = name;
20
10
  }
21
11
 
22
- static up(){
23
-
24
- this.createTable("user", {
25
- id : {
26
- type : "integer",
27
- primary : true,
28
- unique : true,
29
- nullable : false
30
- },
31
- user_id : {
32
- type : "integer",
33
- required : true, // SQL Data Constraints
34
- foreignKey : "user" // SQL Data Constraints
35
- },
36
- website_url : {
37
- type : "string",
38
- required : true,
39
- maxLength : 60
40
- },
41
- website_type : {
42
- type : "string",
43
- required : true
44
- }
45
- });
12
+ get(){
13
+ return `
14
+ class ${this.name} extends Schema {
15
+ constructor(settings){
16
+ super(settings);
17
+ }
46
18
 
47
- this.addColumn("blog", "url", {
48
- type : "string",
49
- required : true
50
- });
19
+ up(table){
20
+ ${this.up}
21
+ }
51
22
 
52
- this.done();
23
+ down(table){
24
+ ${this.down}
25
+ }
26
+ }
27
+ module.exports = ${this.name};
28
+ `
53
29
  }
54
30
 
55
- static down(){
56
- this.dropTable("user");
31
+ alterColumn(){
32
+ if(type === "up"){
33
+ this.up += os.EOL + ` this.alterColumn(table.name, table.column);`
34
+ }
35
+ else{
36
+ this.down += os.EOL + ` this.alterColumn(table.name, table.column);`
37
+ }
38
+ }
39
+ createTable(){
40
+ if(type === "up"){
41
+ this.up += os.EOL + ` this.createTable(table.name);`
42
+ }
43
+ else{
44
+ this.down += os.EOL + ` this.createTable(table.name);`
45
+ }
46
+ }
57
47
 
58
- this.dropColumn("blog", "url");
48
+ addColumn(type){
49
+ if(type === "up"){
50
+ this.up += os.EOL + ` this.addColumn(table.name, table.column);`
51
+ }
52
+ else{
53
+ this.down += os.EOL + ` this.addColumn(table.name, table.column);`
54
+ }
55
+ }
56
+
57
+
58
+ dropTable(type){
59
+ if(type === "up"){
60
+ this.down += os.EOL + ` this.droptable(table.name);`
61
+ }
62
+ else{
63
+ this.down += os.EOL + ` this.droptable(table.name);`
64
+ }
65
+ }
59
66
 
60
- this.done();
67
+ dropColumn(type){
68
+ if(type === "up"){
69
+ this.up += os.EOL + ` this.dropColumn(table.name, table.column);`
70
+ }
71
+ else{
72
+ this.down += os.EOL + ` this.dropColumn(table.name, table.column);`
73
+ }
61
74
  }
75
+
62
76
  }
63
77
 
64
- module.exports = Migration;
78
+ module.exports = MigrationTemplate;
79
+
@@ -1,57 +1,176 @@
1
- // version 1
1
+ // version 0.0.2
2
+ // learn more about seeding info - https://www.pauric.blog/Database-Updates-and-Migrations-with-Entity-Framework/
3
+
2
4
  var fs = require('fs');
5
+ var diff = require("deep-object-diff");
6
+ var MigrationTemplate = require("./migrationTemplate");
7
+
8
+
3
9
  // https://blog.tekspace.io/code-first-multiple-db-context-migration/
4
10
 
5
11
  // node masterrecord add-migration josh C:\Users\rbatista\Downloads\kollege\freshmen\app\models\context
6
12
  class Migrations{
7
13
 
8
- EDMModelDiffer(snapShots, tableList){
9
- new schemaList = []
10
- const tableKeyList = Object.keys(tableList);
11
- // loop through tables
12
- for (const tableKey of tableKeyList) {
13
- // check if table has already been migrated
14
- var tableSnapShot = snapShots[tableKey];
15
- if(tableSnapShot){
16
- // check if we have the column in the sceama
17
- const columnKeyList = Object.keys(tableKey);
18
- // loop through tables
19
- for (const columnKey of columnKeyList) {
20
- var newTable = {};
21
- if(tableSnapShot[columnKey]){
22
-
23
- /*
24
- id : {
25
- nullabale : true,
26
- type : string
27
- }
14
+ getSettings(rootLocation){
15
+ var envType = process.env.master;
16
+ var search = `${rootFolderLocation}/**/*env.${envType}.json`;
17
+ var files = globSearch.sync(search, rootFolderLocation);
18
+ var file = files[0];
19
+ var settings = require(file);
20
+ options = settings[contextName];
21
+ this.db = this.__SQLiteInit(options, "better-sqlite3");
22
+ this._SQLEngine.setDB(this.db, "better-sqlite3");
23
+ return this;
24
+ }
28
25
 
29
- id : {
30
- nullabale : true,
31
- type : int,
32
- unique : true
33
- }
34
-
35
- */
36
- // if we then check if we have all the same types
37
- // loop through schema list
38
- // check if we have the same in the context
39
- // if the same then check if if value is diffrent
40
- // then delete from context
41
- // after loop complete wahtever left in conext just push to object
26
+ createSnapShot(snap){
27
+ var migrationsDirectory = `${snap.executedLocation}/db/migrations`;
28
+ if (!fs.existsSync(migrationsDirectory)){
29
+ fs.mkdirSync(migrationsDirectory);
30
+ }
31
+
32
+ var content = {
33
+ seed : function(seed){
34
+ this.seed(this);
35
+ },
36
+ database: {},
37
+ contextLocation: snap.file,
38
+ migrationFolder: `${snap.executedLocation}/db/migrations`,
39
+ snapShotLocation: `${snap.executedLocation}/db/migrations/${snap.contextFileName}_contextSnapShot.json`,
40
+ schema : snap.context.__entities
41
+ };
42
+
43
+ const jsonContent = JSON.stringify(content, null, 2);
44
+ try{
45
+ // will replace the whole file if it exist
46
+ fs.writeFileSync(`${migrationsDirectory}/${snap.contextFileName}_contextSnapShot.json`, jsonContent);
47
+ }catch (e){
48
+ console.log("Cannot write file ", e);
49
+ }
50
+ }
51
+
52
+ organizeSchemaByTables(oldSchema, newSchema){
53
+ var tables = []
54
+
55
+ newSchema.forEach(function (item, index) {
56
+
57
+ var table = {
58
+ name: item["__name"],
59
+ old: null,
60
+ new :item,
61
+ newColumns : [],
62
+ deletedColumns : [],
63
+ updatedColumns : []
64
+ }
65
+
66
+ oldSchema.forEach(function (oldItem, index) {
67
+ var oldItemName = oldItem["__name"];
68
+ if(table.name === oldItemName){
69
+ table.old = oldItem;
70
+ tables.push(table);
42
71
  }
43
- else{
44
- // if we dont have it then add it to the
45
- newTable[columnKey] = tableKey[columnKey];
46
- schemaList.push(newTable);
47
- }
72
+ });
73
+ });
74
+
75
+ return tables;
76
+ }
77
+
78
+ findDeletedColumnsInTables(tables){
79
+ tables.forEach(function (item, index) {
80
+ var deletedColumn = null;
81
+ if(item.new && item.old){
82
+ Object.keys(item.old).forEach(function (key) {
83
+ var value = item.old[key].name;
84
+ deletedColumn = null;
85
+ Object.keys(item.new).forEach(function (newKey) {
86
+ var newValue = item.new[newKey].name;
87
+ if(value === newValue){
88
+ deletedColumn = value;
89
+ }
90
+ });
91
+ if(deletedColumn === null){
92
+ item.deletedColumns.push(value);
93
+ }
94
+ });
48
95
  }
49
- }else{
50
- schemaList.push(contextKeys[key]);
96
+ });
97
+ return tables;
98
+ }
99
+
100
+ findUpdatedColumns(tables){
101
+ tables.forEach(function (item, index) {
102
+ var UD = diff.updatedDiff(item.old, item.new);
103
+ const isEmpty = Object.keys(UD).length === 0;
104
+ if(!isEmpty){
105
+ item.updatedColumns.push(diff.updatedDiff(item.old, item.new));
51
106
  }
52
-
53
107
 
54
- }
108
+ });
109
+ return tables;
110
+ }
111
+
112
+ findNewColumnsInTables(tables){
113
+ tables.forEach(function (item, index) {
114
+ var newColumn = null;
115
+ if(item.new && item.old){
116
+ Object.keys(item.new).forEach(function (key) {
117
+ var value = item.new[key].name;
118
+ newColumn = null;
119
+ Object.keys(item.old).forEach(function (oldKey) {
120
+ var oldValue = item.old[oldKey].name;
121
+ if(value === oldValue){
122
+ newColumn = value;
123
+ }
124
+ });
125
+ if(newColumn === null){
126
+ item.newColumns.push(value);
127
+ }
128
+ });
129
+ }
130
+ });
131
+ return tables;
132
+ }
133
+
134
+
135
+ buildMigrationTemplate(name, oldSchema, newSchema){
136
+
137
+ var MT = new MigrationTemplate(name);
138
+ var tables = this.organizeSchemaByTables(oldSchema, newSchema);
139
+ tables = this.findNewColumnsInTables(tables);
140
+ tables = this.findDeletedColumnsInTables(tables);
141
+ tables = this.findUpdatedColumns(tables);
142
+ tables.forEach(function (item, index) {
143
+ // add new columns for table
144
+ item.newColumns.forEach(function (column, index) {
145
+ MT.addColumn();
146
+ MT.dropColumn("down");
147
+ });
148
+
149
+ item.deletedColumns.forEach(function (column, index) {
150
+ MT.dropColumn("up");
151
+ MT.addColumn("down");
152
+ });
153
+
154
+ item.updatedColumns.forEach(function (column, index) {
155
+ MT.alterColumn();
156
+ MT.alterColumn("down")
157
+ });
158
+
159
+ if(item.old === null){
160
+ MT.createTable();
161
+ MT.dropTable("down");
162
+
163
+ }
164
+ if(item.new === null){
165
+ MT.dropTable("up");
166
+ MT.createTable("down");
167
+ }
168
+
169
+ });
170
+
171
+ return MT.get();
172
+
173
+
55
174
  }
56
175
 
57
176
  migrationCodeGenerator(name, column, migrationDate){
@@ -2,23 +2,43 @@
2
2
  var fs = require('fs');
3
3
 
4
4
  class Schema{
5
-
5
+
6
+ constructor(settings){
7
+ this.settings = settings;
8
+ }
9
+
6
10
  // create obj to convert into create sql
7
11
  addColumn(tableName, columnName, ){
8
12
 
9
13
  // add column to database
10
14
  }
11
15
 
16
+ createTable(name, columns){
17
+
18
+ }
19
+
12
20
  dropColumn(tableName, columnName){
13
21
  // drop column
14
22
 
15
23
  }
16
24
 
17
- createTable(name, columns){
25
+ dropTable(name){
18
26
 
19
27
  }
20
28
 
21
- dropTable(name){
29
+ dropIndex(){
30
+
31
+ }
32
+ //"dbo.People", "Location"
33
+ alterColumn(){
34
+
35
+ }
36
+
37
+ renameColumn(){
38
+
39
+ }
40
+
41
+ seed(){
22
42
 
23
43
  }
24
44
 
@@ -27,6 +47,7 @@ class Schema{
27
47
 
28
48
 
29
49
  }
50
+
30
51
  }
31
52
 
32
53
 
package/package.json CHANGED
@@ -2,22 +2,24 @@
2
2
  "name": "masterrecord",
3
3
  "dependencies": {
4
4
  "better-sqlite3": "^7.6.2",
5
- "commander": "^9.4.0"
5
+ "commander": "^9.4.0",
6
+ "glob" : "^8.0.3",
7
+ "deep-object-diff" : "1.1.7"
6
8
  },
7
- "version": "0.0.26",
9
+ "version": "0.0.27",
8
10
  "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 ",
9
11
  "homepage": "https://github.com/Tailor/MasterRecord#readme",
10
12
  "repository": {
11
13
  "type": "git",
12
14
  "url": "git+https://github.com/Tailor/Masterrecord.git"
13
15
  },
14
- "main": "./MasterRecord.js",
16
+ "main": "./migrations/cli.js",
15
17
  "scripts": {
16
18
  "test": "echo \"Error: no test specified\" && exit 1"
17
19
  },
18
20
  "author": "Alexander Rich",
19
21
  "license": "ISC",
20
22
  "bin": {
21
- "masterrecord": "migrations/cli.js"
23
+ "masterrecord": "./migrations/cli.js"
22
24
  }
23
25
  }
package/readme.md ADDED
@@ -0,0 +1 @@
1
+ local install of CLI package run code in terminal - "npm install -g ./"