masterrecord 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,409 @@
1
+ var tools = require('masterrecord/Tools');
2
+
3
+ class SQLLiteEngine {
4
+
5
+ unsupportedWords = ["order"]
6
+
7
+ update(query){
8
+ var sqlQuery = ` UPDATE [${query.tableName}]
9
+ SET ${query.arg}
10
+ WHERE [${query.tableName}].[${query.primaryKey}] = ${query.primaryKeyValue}` // primary key for that table =
11
+ return this._run(sqlQuery);
12
+ }
13
+
14
+ delete(queryObject){
15
+ var sqlObject = this._buildDeleteObject(queryObject);
16
+ var sqlQuery = `DELETE FROM [${sqlObject.tableName}] WHERE [${sqlObject.tableName}].[${sqlObject.primaryKey}] = ${sqlObject.value}`;
17
+ return this._execute(sqlQuery);
18
+ }
19
+
20
+ insert(queryObject){
21
+ var sqlObject = this._buildSQLInsertObject(queryObject, queryObject.__entity);
22
+ var query = `INSERT INTO [${sqlObject.tableName}] (${sqlObject.columns})
23
+ VALUES (${sqlObject.values})`;
24
+ var queryObj = this._run(query);
25
+ var open = {
26
+ "id": queryObj.lastInsertRowid
27
+ };
28
+ return open;
29
+ }
30
+
31
+ get(query, entity, context){
32
+ var queryString = {};
33
+ try {
34
+ if(query.raw){
35
+ queryString.query = query.raw;
36
+ }
37
+ else{
38
+ queryString = this.buildQuery(query, entity, context);
39
+ }
40
+ if(queryString.query){
41
+ console.log("SQL:", queryString.query);
42
+ var queryReturn = this.db.prepare(queryString.query).get();
43
+ return queryReturn;
44
+ }
45
+ return null;
46
+ } catch (err) {
47
+ console.error(err);
48
+ return null;
49
+ }
50
+ }
51
+
52
+ getCount(queryObject, entity, context){
53
+ var query = queryObject.script;
54
+ var queryString = {};
55
+ try {
56
+ if(query.raw){
57
+ queryString.query = query.raw;
58
+ }
59
+ else{
60
+ queryString = this.buildQuery(query, entity, context);
61
+ }
62
+ if(queryString.query){
63
+ var queryCount = queryObject.count(queryString.query)
64
+ console.log("SQL:", queryCount );
65
+ var queryReturn = this.db.prepare(queryCount ).get();
66
+ return queryReturn;
67
+ }
68
+ return null;
69
+ } catch (err) {
70
+ console.error(err);
71
+ return null;
72
+ }
73
+ }
74
+
75
+ all(query, entity, context){
76
+ var selectQuery = {};
77
+ try {
78
+ if(query.raw){
79
+ selectQuery.query = query.raw;
80
+ }
81
+ else{
82
+ selectQuery = this.buildQuery(query, entity, context);
83
+ }
84
+ if(selectQuery.query){
85
+ console.log("SQL:", selectQuery.query);
86
+ var queryReturn = this.db.prepare(selectQuery.query).all();
87
+ return queryReturn;
88
+ }
89
+ return null;
90
+ } catch (err) {
91
+ console.error(err);
92
+ return null;
93
+ }
94
+ }
95
+
96
+
97
+ buildQuery(query, entity, context){
98
+
99
+ var queryObject = {};
100
+ queryObject.entity = this.getEntity(entity.__name, query.entityMap);
101
+ queryObject.select = this.buildSelect(query, entity);
102
+ queryObject.from = this.buildFrom(query, entity);
103
+ queryObject.include = this.buildInclude(query, entity, context, queryObject);
104
+ queryObject.where = this.buildWhere(query, entity);
105
+
106
+ var queryString = `${queryObject.select} ${queryObject.from} ${queryObject.include} ${queryObject.where}`;
107
+ return {
108
+ query : queryString,
109
+ entity : this.getEntity(entity.__name, query.entityMap)
110
+ }
111
+
112
+ }
113
+
114
+ buildWhere(query, mainQuery){
115
+ var whereEntity = query.where;
116
+ var strQuery = "";
117
+ var $that = this;
118
+ if(whereEntity){
119
+ var entity = this.getEntity(query.parentName, query.entityMap);
120
+ for (let part in whereEntity[query.parentName]) {
121
+ var item = whereEntity[query.parentName][part];
122
+ for (let exp in item.expressions) {
123
+ var field = tools.capitalizeFirstLetter(item.expressions[exp].field);
124
+ if(mainQuery[field]){
125
+ if(mainQuery[field].isNavigational){
126
+ entity = $that.getEntity(field, query.entityMap);
127
+ field = item.fields[1];
128
+ }
129
+ }
130
+ if(strQuery === ""){
131
+ strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
132
+ }
133
+ else{
134
+ strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return strQuery;
140
+ }
141
+
142
+ buildInclude( query, entity, context){
143
+ var includeQuery = "";
144
+ for (let part in query.include) {
145
+ var includeEntity = query.include[part];
146
+ var $that = this;
147
+ if(includeEntity){
148
+ var parentObj = includeEntity[query.parentName];
149
+ var currentContext = "";
150
+ if(includeEntity.selectFields){
151
+ currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
152
+ }
153
+
154
+ if(parentObj){
155
+ parentObj.entityMap = query.entityMap;
156
+ var foreignKey = $that.getForeignKey(entity.__name, currentContext.__entity);
157
+ var mainPrimaryKey = $that.getPrimarykey(entity);
158
+ var mainEntity = $that.getEntity(entity.__name, query.entityMap);
159
+ if(currentContext.__entity[entity.__name].type === "hasManyThrough"){
160
+ var foreignTable = tools.capitalizeFirstLetter(currentContext.__entity[entity.__name].foreignTable); //to uppercase letter
161
+ foreignKey = $that.getPrimarykey(currentContext.__entity);
162
+ mainPrimaryKey = context[foreignTable].__entity[currentContext.__entity.__name].foreignKey;
163
+ var mainEntity = $that.getEntity(foreignTable,query.entityMap);
164
+ }
165
+ // add foreign key to select so that it picks it up
166
+ if(parentObj.select){
167
+ parentObj.select.selectFields.push(foreignKey);
168
+ }else{
169
+ parentObj.select = {};
170
+ parentObj.select.selectFields = [];
171
+ parentObj.select.selectFields.push(foreignKey);
172
+ }
173
+
174
+ var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
175
+
176
+ includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
177
+
178
+ }
179
+ }
180
+ }
181
+ return includeQuery;
182
+ }
183
+
184
+ buildFrom(query, entity){
185
+ var entityName = this.getEntity(entity.__name, query.entityMap);
186
+ if(entityName ){
187
+ return `FROM ${entity.__name } AS ${entityName}`;
188
+ }
189
+ else{ return "" }
190
+ }
191
+
192
+ buildSelect(query, entity){
193
+ // this means that there is a select statement
194
+ var select = "SELECT";
195
+ var arr = "";
196
+ var $that = this;
197
+ if(query.select){
198
+ for (const item in query.select.selectFields) {
199
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
200
+ };
201
+
202
+ }
203
+ else{
204
+ var entityList = this.getEntityList(entity);
205
+ for (const item in entityList) {
206
+ arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
207
+ };
208
+ }
209
+ arr = arr.replace(/,\s*$/, "");
210
+ return `${select} ${arr} `;
211
+ }
212
+
213
+ getForeignKey(name, entity){
214
+ if(entity && name){
215
+ return entity[name].foreignKey;
216
+ }
217
+ }
218
+
219
+ getPrimarykey(entity){
220
+ for (const item in entity) {
221
+ if(entity[item].primary){
222
+ if(entity[item].primary === true){
223
+ return entity[item].name;
224
+ }
225
+ }
226
+ };
227
+ }
228
+
229
+ getForeignTable(name, entity){
230
+ if(entity && name){
231
+ return entity[name].foreignTable;
232
+ }
233
+ }
234
+
235
+ getInclude(name, query){
236
+ var include = query.include;
237
+ if(include){
238
+ for (let part in include) {
239
+ if(tools.capitalizeFirstLetter(include[part].selectFields[0]) === name){
240
+ return include[part];
241
+ }
242
+ }
243
+ }
244
+ else{
245
+ return "";
246
+ }
247
+ }
248
+
249
+ getEntity(name, maps){
250
+ for (let item in maps) {
251
+ var map = maps[item];
252
+ if(tools.capitalizeFirstLetter(name) === map.name){
253
+ return map.entity
254
+ }
255
+ }
256
+ return "";
257
+ }
258
+
259
+ // return a list of entity names and skip foreign keys and underscore.
260
+ getEntityList(entity){
261
+ var entitiesList = [];
262
+ var $that = this;
263
+ for (var ent in entity) {
264
+ if(!ent.startsWith("_")){
265
+ if(!entity[ent].foreignKey){
266
+ if(entity[ent].relationshipTable){
267
+ if($that.chechUnsupportedWords(entity[ent].relationshipTable)){
268
+ entitiesList.push(`'${entity[ent].relationshipTable}'`);
269
+ }
270
+ else{
271
+ entitiesList.push(entity[ent].relationshipTable);
272
+ }
273
+ }
274
+ else{
275
+ if($that.chechUnsupportedWords(ent)){
276
+ entitiesList.push(`'${ent}'`);
277
+ }
278
+ else{
279
+ entitiesList.push(ent);
280
+ }
281
+ }
282
+ }
283
+ }
284
+ }
285
+ return entitiesList
286
+ }
287
+
288
+ chechUnsupportedWords(word){
289
+ for (var item in this.unsupportedWords) {
290
+ var text = this.unsupportedWords[item];
291
+ if(text === word){
292
+ return true
293
+ }
294
+ }
295
+ return false;
296
+ }
297
+
298
+ startTransaction(){
299
+ this.db.prepare('BEGIN').run();
300
+ }
301
+
302
+ endTransaction(){
303
+ this.db.prepare('COMMIT').run();
304
+ }
305
+
306
+ errorTransaction(){
307
+ this.db.prepare('ROLLBACK').run();
308
+ }
309
+
310
+ _buildSQLEqualTo(model){
311
+ var $that = this;
312
+ var argument = null;
313
+ var dirtyFields = model.__dirtyFields;
314
+
315
+ for (var column in dirtyFields) {
316
+ // TODO Boolean value is a string with a letter
317
+ switch(model.__entity[dirtyFields[column]].type){
318
+ case "integer" :
319
+ argument = argument === null ? `[${dirtyFields[column]}] = ${model[dirtyFields[column]]},` : `${argument} [${dirtyFields[column]}] = ${model[dirtyFields[column]]},`;
320
+ break;
321
+ case "string" :
322
+ argument = argument === null ? `[${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',` : `${argument} [${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',`;
323
+ break;
324
+ default:
325
+ argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
326
+ }
327
+ }
328
+ return argument.replace(/,\s*$/, "");
329
+ }
330
+
331
+
332
+ _buildDeleteObject(currentModel){
333
+ var primaryKey = currentModel.__Key === undefined ? tools.getPrimaryKeyObject(currentModel.__entity) : currentModel.__Key;
334
+ var value = currentModel.__value === undefined ? currentModel[primaryKey] : currentModel.__value;
335
+ var tableName = currentModel.__tableName === undefined ? currentModel.__entity.__name : currentModel.__tableName;
336
+ return {tableName: tableName, primaryKey : primaryKey, value : value};
337
+ }
338
+
339
+
340
+ // return columns and value strings
341
+ _buildSQLInsertObject(fields, modelEntity){
342
+ var $that = this;
343
+ var columns = null;
344
+ var values = null;
345
+ for (var column in modelEntity) {
346
+ // column1 = value1, column2 = value2, ...
347
+ if(column.indexOf("__") === -1 ){
348
+ // call the get method if avlable
349
+ var fieldColumn = "";
350
+ // check if get function is avaliable if so use that
351
+ fieldColumn = fields[column];
352
+
353
+ if((fieldColumn !== undefined && fieldColumn !== null && fieldColumn !== "" ) && typeof(fieldColumn) !== "object"){
354
+ switch(modelEntity[column].type){
355
+ case "belongsTo" :
356
+ column = modelEntity[column].relationshipTable === undefined ? column : modelEntity[column].relationshipTable;
357
+ break;
358
+ case "string" :
359
+ fieldColumn = `'${$that._santizeSingleQuotes(fields[column])}'`;
360
+ break;
361
+ case "time" :
362
+ fieldColumn = fields[column];
363
+ break;
364
+ }
365
+
366
+ columns = columns === null ? `'${column}',` : `${columns} '${column}',`;
367
+ values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
368
+
369
+ }
370
+ }
371
+ }
372
+ return {tableName: modelEntity.__name, columns: columns.replace(/,\s*$/, ""), values: values.replace(/,\s*$/, "")};
373
+
374
+ }
375
+
376
+ // will add double single quotes to allow sting to be saved.
377
+ _santizeSingleQuotes(string){
378
+ return string.replace(/'/g, "''");
379
+ }
380
+
381
+ // converts any object into SQL parameter select string
382
+ _convertEntityToSelectParameterString(obj, entityName){
383
+ // todo: loop throgh object and append string with comma to
384
+ var mainString = "";
385
+ const entries = Object.keys(obj);
386
+
387
+ for (const [name] of entries) {
388
+ mainString += `${mainString}, ${entityName}.${name}`;
389
+ }
390
+ return mainString;;
391
+ }
392
+
393
+ _execute(query){
394
+ console.log("SQL:", query);
395
+ return this.db.exec(query);
396
+ }
397
+
398
+ _run(query){
399
+ console.log("SQL:", query);
400
+ return this.db.prepare(query).run();
401
+ }
402
+
403
+ setDB(db, type){
404
+ this.db = db;
405
+ this.dbType = type; // this will let us know which type of sqlengine to use.
406
+ }
407
+ }
408
+
409
+ module.exports = SQLLiteEngine;
package/Tools.js CHANGED
@@ -1,56 +1,119 @@
1
- class Tools{
2
-
3
- static buildSQLEqualTo(model){
4
- var argument = null;
5
- var dirtyFields = model.__dirtyFields;
6
- for (var column in dirtyFields) {
7
- // column1 = value1, column2 = value2, ...
8
- var value = model[dirtyFields[column]];
9
- // TODO Boolean value is a string with a letter
10
- if(typeof value === "number"){
11
- argument = argument === null ? `${dirtyFields[column]} = ${model[dirtyFields[column]]},` : `${argument} ${dirtyFields[column]} = ${model[dirtyFields[column]]},`;
12
- }else{
13
- argument = argument === null ? `${dirtyFields[column]} = '${model[dirtyFields[column]]}',` : `${argument} ${dirtyFields[column]} = '${model[dirtyFields[column]]}',`;
14
- }
15
- }
16
- return argument.replace(/,\s*$/, "");
17
- }
18
-
19
- // return columns and value strings
20
- static getInsertObj(fields){
21
- var columns = null;
22
- var values = null;
23
- for (var column in fields.__entity) {
24
- // column1 = value1, column2 = value2, ...
25
- if(column.indexOf("__") === -1 ){
26
- if(fields[column] !== undefined){
27
- columns = columns === null ? `${column},` : `${columns} ${column},`;
28
- var fieldColumn = fields[column];
29
- if(fields.__entity[column].type.name === "String"){
30
- fieldColumn = `"${fields[column]}"`;
31
- }
32
- values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
33
- }
34
- }
35
- }
36
-
37
- return{
38
- columns :columns.replace(/,\s*$/, ""),
39
- values : values.replace(/,\s*$/, "")
40
- }
41
- }
42
-
43
- static getPrimaryKeyObject(model){
44
- for (var key in model) {
45
- if (model.hasOwnProperty(key)) {
46
- if(model[key].primary){
47
- if(model[key].primary === true){
48
- return key
49
- }
50
- }
51
- }
52
- }
53
- }
54
- }
55
-
1
+ class Tools{
2
+
3
+ static findEntity(name, entityList){
4
+ return entityList[name];
5
+ }
6
+
7
+ static removePrimarykeyandVirtual(currentModel, modelEntity){
8
+ var newCurrentModel = Object.create(currentModel);
9
+
10
+ for(var entity in modelEntity) {
11
+ var currentEntity = modelEntity[entity];
12
+ if (modelEntity.hasOwnProperty(entity)) {
13
+ if(currentEntity.primary === true){
14
+ delete newCurrentModel[`_${entity}`];
15
+ }
16
+ }
17
+ if(currentEntity.virtual === true){
18
+ // skip it from the insert
19
+ delete newCurrentModel[`_${entity}`];
20
+ }
21
+
22
+ }
23
+ return newCurrentModel;
24
+ }
25
+
26
+ static getPrimaryKeyObject(model){
27
+ for (var key in model) {
28
+ if (model.hasOwnProperty(key)) {
29
+ if(model[key].primary){
30
+ if(model[key].primary === true){
31
+ return key
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ static createNewInstance(validModel, type, classModel){
39
+ return new type(validModel, classModel);
40
+ }
41
+
42
+ static clearAllProto(proto){
43
+ if(proto.__proto__ ){
44
+ proto.__proto__ = null;
45
+ for (var key in proto) {
46
+ if(!key.startsWith("_")){
47
+ var typeObj = typeof(proto[key]);
48
+ if(typeObj === "object"){
49
+ this.clearAllProto(proto[key]);
50
+ }
51
+ }else{
52
+ throw "Cannot add relationship entity model only basic models"
53
+ }
54
+ }
55
+ }
56
+
57
+ }
58
+
59
+ static getEntity(name, modelEntity){
60
+ for(var entity in modelEntity) {
61
+ var currentEntity = modelEntity[entity];
62
+ if (modelEntity.hasOwnProperty(entity)) {
63
+ if(currentEntity.__name === name){
64
+ return currentEntity;
65
+ }
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+
71
+ static capitalize = (s) => {
72
+ if (typeof s !== 'string') return ''
73
+ return s.charAt(0).toUpperCase() + s.slice(1)
74
+ }
75
+
76
+ static capitalizeFirstLetter(string) {
77
+ return string.charAt(0).toUpperCase() + string.slice(1);
78
+ }
79
+
80
+ // return randome letter that is not the skip letter
81
+ static getRandomLetter(length, skip){
82
+ var result = '';
83
+ var characters = 'abcdefghijklmnopqrstuvwxyz';
84
+ var charactersLength = characters.length;
85
+
86
+ for ( var i = 0; i < length; i++ ) {
87
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
88
+ if(skip){
89
+ for ( var b = 0; b < skip.length; b++ ) {
90
+ if(result === skip[i].entity){
91
+ result = "";
92
+ i--;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ return result;
99
+ }
100
+
101
+ // TODO: this should be removed once we create a SQLIte Manager;
102
+ // converts any object into SQL parameter select string
103
+
104
+ static convertEntityToSelectParameterString(obj){
105
+ // todo: loop throgh object and append string with comma to
106
+ var mainString = "";
107
+ const entries = Object.keys(obj);
108
+ for (const key of entries) {
109
+ if(obj[key].type !== 'hasManyThrough' && obj[key].type !== "hasMany" && obj[key].type !== "hasOne"){
110
+ if(obj[key].name){
111
+ mainString = mainString === "" ? `${obj.__name}.${obj[key].name}` : `${mainString}, ${obj.__name}.${obj[key].name}`;
112
+ }
113
+ }
114
+ }
115
+ return mainString;;
116
+ }
117
+ }
118
+
56
119
  module.exports = Tools;
package/package.json CHANGED
@@ -1,27 +1,23 @@
1
- {
2
- "name": "masterrecord",
3
- "version": "0.0.23",
4
- "author": "Alexander Batista",
5
- "main": "./MasterRecord.js",
6
- "bugs": {
7
- "url": "https://github.com/Tailor/Masterrecord/issues"
8
- },
9
- "dependencies": {
10
- "better-sqlite3": "^5.4.3",
11
- "commander": "^4.1.0"
12
- },
13
- "bin": {
14
- "masterrecord": "./migrations/cli.js"
15
- },
16
- "preferGlobal": true,
17
- "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 ",
18
- "homepage": "https://github.com/Tailor/MasterRecord#readme",
19
- "license": "ISC",
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/Tailor/Masterrecord.git"
23
- },
24
- "scripts": {
25
- "test": "echo \"Error: no test specified\" && exit 1"
26
- }
27
- }
1
+ {
2
+ "name": "masterrecord",
3
+ "dependencies": {
4
+ "better-sqlite3": "^7.6.2",
5
+ "commander": "^9.4.0"
6
+ },
7
+ "version": "0.0.25",
8
+ "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
+ "homepage": "https://github.com/Tailor/MasterRecord#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/Tailor/Masterrecord.git"
13
+ },
14
+ "main": "./MasterRecord.js",
15
+ "scripts": {
16
+ "test": "echo \"Error: no test specified\" && exit 1"
17
+ },
18
+ "author": "Alexander Rich",
19
+ "license": "ISC",
20
+ "bin": {
21
+ "masterrecord": "migrations/cli.js"
22
+ }
23
+ }