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.
- package/DeleteManager.js +51 -0
- package/Entity/EntityModel.js +192 -120
- package/Entity/EntityModelBuilder.js +41 -63
- package/Entity/EntityTrackerModel.js +222 -42
- package/InsertManager.js +138 -0
- package/MYSQLEngine.js +409 -0
- package/Masterrecord.js +233 -179
- package/Migrations/cli.js +106 -105
- package/Migrations/migrationTemplate.js +63 -63
- package/Migrations/migrations.js +65 -22
- package/Migrations/schema.js +42 -42
- package/QueryLanguage/queryManager.js +66 -0
- package/QueryLanguage/queryMethods.js +171 -0
- package/QueryLanguage/queryScript.js +331 -0
- package/SQLLiteEngine.js +409 -0
- package/Tools.js +118 -55
- package/package.json +23 -27
- package/QueryLanguage/_Expression.js +0 -322
- package/QueryLanguage/_LogicalQuery.js +0 -23
- package/QueryLanguage/_OperatorList.js +0 -88
- package/QueryLanguage/_QueryModel.js +0 -442
- package/QueryLanguage/_Tokenization.js +0 -173
- package/QueryLanguage/__Query.js +0 -386
- package/QueryLanguage/_simpleQuery.js +0 -184
- package/QueryLanguage/queryBuilder.js +0 -52
- package/SQLEngine.js +0 -52
|
@@ -1,43 +1,223 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// set the value dynamiclly
|
|
22
|
-
if(currentEntity[modelField]){
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
modelClass.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
var tools = require('../Tools');
|
|
2
|
+
class EntityTrackerModel {
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// entity states https://docs.microsoft.com/en-us/dotnet/api/system.data.entitystate?view=netframework-4.7.2
|
|
6
|
+
|
|
7
|
+
// start tracking model
|
|
8
|
+
build(dataModel, currentEntity, context){
|
|
9
|
+
var $that = this;
|
|
10
|
+
var modelClass = this.buildObject(); // build entity with models
|
|
11
|
+
modelClass.__proto__ = {};
|
|
12
|
+
const modelFields = Object.entries(dataModel); /// return array of objects
|
|
13
|
+
modelClass.__entity = currentEntity;
|
|
14
|
+
modelClass.__name = currentEntity.__name;
|
|
15
|
+
modelClass.__context = context;
|
|
16
|
+
this.buildRelationshipModels(modelClass, currentEntity, dataModel);
|
|
17
|
+
|
|
18
|
+
// loop through data model fields
|
|
19
|
+
for (const [modelField, modelFieldValue] of modelFields) {
|
|
20
|
+
|
|
21
|
+
// set the value dynamiclly
|
|
22
|
+
if(!$that._isRelationship(currentEntity[modelField])){
|
|
23
|
+
// current entity has a value then add
|
|
24
|
+
modelClass["__proto__"]["_" + modelField] = modelFieldValue;
|
|
25
|
+
|
|
26
|
+
// Setter
|
|
27
|
+
modelClass.__defineSetter__(modelField, function(value){
|
|
28
|
+
modelClass.__state = "modified";
|
|
29
|
+
modelClass.__dirtyFields.push(modelField);
|
|
30
|
+
if(typeof currentEntity[modelField].set === "function"){
|
|
31
|
+
this["__proto__"]["_" + modelField] = currentEntity[modelField].set(value);
|
|
32
|
+
}else{
|
|
33
|
+
// Then it will add name to dirty fields
|
|
34
|
+
this["__proto__"]["_" + modelField] = value;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Getter
|
|
39
|
+
modelClass.__defineGetter__(modelField, function(){
|
|
40
|
+
if(typeof currentEntity[modelField].get === "function"){
|
|
41
|
+
return currentEntity[modelField].get(this["__proto__"]["_" + modelField]);
|
|
42
|
+
}else{
|
|
43
|
+
return this["__proto__"]["_" + modelField];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
return modelClass;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
buildObject(){
|
|
55
|
+
return {
|
|
56
|
+
__ID : Math.floor((Math.random() * 100000) + 1),
|
|
57
|
+
__dirtyFields : [],
|
|
58
|
+
__state : "track",
|
|
59
|
+
__entity : null,
|
|
60
|
+
__context : null,
|
|
61
|
+
__name : null
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_isRelationship(entity){
|
|
66
|
+
if(entity){
|
|
67
|
+
if(entity.type === "hasOne" || entity.type === "hasMany" || entity.type === "belongsTo" || entity.type === "hasManyThrough"){
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
else{
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}else{
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
buildRelationshipModels(modelClass, currentEntity, currentModel){
|
|
79
|
+
var $that = this;
|
|
80
|
+
// loop though current entity and add only relationship models to this list
|
|
81
|
+
const entityFields = Object.entries(currentEntity);
|
|
82
|
+
for (const [entityField, entityFieldValue] of entityFields) { // loop through entity values
|
|
83
|
+
|
|
84
|
+
if($that._isRelationship(currentEntity[entityField])){
|
|
85
|
+
|
|
86
|
+
// Setter
|
|
87
|
+
modelClass.__defineSetter__(entityField, function(value){
|
|
88
|
+
this["__proto__"]["_" + entityField] = value;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Getter
|
|
92
|
+
modelClass.__defineGetter__(entityField, function(){
|
|
93
|
+
|
|
94
|
+
var ent = tools.findEntity(entityField, this.__context);
|
|
95
|
+
if(!ent){
|
|
96
|
+
var parentEntity = tools.findEntity(this.__name, this.__context);
|
|
97
|
+
if(parentEntity){
|
|
98
|
+
ent = tools.findEntity(parentEntity.__entity[entityField].foreignKey, this.__context);
|
|
99
|
+
if(!ent){
|
|
100
|
+
return `Error - Entity ${parentEntity.__entity[entityField].foreignTable} not found. Please check your context for proper name.`
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else{
|
|
104
|
+
return `Error - Entity ${parentEntity} not found. Please check your context for proper name.`
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if(currentEntity[entityField].type === "belongsTo"){
|
|
110
|
+
if(currentEntity[entityField].lazyLoading){
|
|
111
|
+
var priKey = tools.getPrimaryKeyObject(this.__entity);
|
|
112
|
+
var currentValue = this.__proto__[`_${entityField}`];
|
|
113
|
+
if(currentValue){
|
|
114
|
+
// CHECK to see if you got the info already
|
|
115
|
+
if(typeof currentValue === 'object'){
|
|
116
|
+
currentValue = currentValue[priKey];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else{
|
|
120
|
+
var idValue = currentEntity[entityField].foreignTable;
|
|
121
|
+
currentValue = this.__proto__[`_${idValue}`];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//var modelValue = ent.raw(`select * from ${ent.__entity.__name} where ${priKey} = ${ currentValue }`).single();
|
|
125
|
+
var modelValue = ent.where(`r => r. ${priKey} == ${ currentValue }`)
|
|
126
|
+
this[entityField] = modelValue;
|
|
127
|
+
}
|
|
128
|
+
else{
|
|
129
|
+
return this["__proto__"]["_" + entityField];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else{
|
|
133
|
+
// user.tags = gets all tags related to user
|
|
134
|
+
// tag.users = get all users related to tags
|
|
135
|
+
if(currentEntity[entityField].lazyLoading){
|
|
136
|
+
var priKey = tools.getPrimaryKeyObject(this.__entity);
|
|
137
|
+
var entityName = currentEntity[entityField].foreignTable === undefined ? entityField : currentEntity[entityField].foreignTable;
|
|
138
|
+
var tableName = "";
|
|
139
|
+
if(entityName){
|
|
140
|
+
switch(currentEntity[entityField].type){
|
|
141
|
+
// TODO: move the SQL generation part to the SQL builder so that we can later on use many diffrent types of SQL databases.
|
|
142
|
+
case "hasManyThrough" :
|
|
143
|
+
try{
|
|
144
|
+
var joiningEntity = this.__context[tools.capitalize(entityName)];
|
|
145
|
+
var entityFieldJoinName = currentEntity[entityField].foreignTable === undefined? entityField : currentEntity[entityField].foreignTable;
|
|
146
|
+
var thirdEntity = this.__context[tools.capitalize(entityFieldJoinName)];
|
|
147
|
+
var firstJoiningID = joiningEntity.__entity[this.__entity.__name].foreignTable;
|
|
148
|
+
var secondJoiningID = joiningEntity.__entity[entityField].foreignTable;
|
|
149
|
+
if(firstJoiningID && secondJoiningID )
|
|
150
|
+
{
|
|
151
|
+
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();
|
|
152
|
+
// 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]}`;
|
|
153
|
+
// var modelValue = ent.raw(modelQuery).toList();
|
|
154
|
+
this[entityField] = modelValue;
|
|
155
|
+
}
|
|
156
|
+
else{
|
|
157
|
+
return "Joining table must declaire joining table names"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch(error){
|
|
161
|
+
return error;
|
|
162
|
+
}
|
|
163
|
+
/*
|
|
164
|
+
select * from User
|
|
165
|
+
INNER JOIN Tagging ON User.id = Tagging.user_id
|
|
166
|
+
INNER JOIN Tag ON Tag.id = Tagging.tag_id
|
|
167
|
+
WHERE Tagging.user_id = 13
|
|
168
|
+
*/
|
|
169
|
+
break;
|
|
170
|
+
case "hasOne" :
|
|
171
|
+
if(ent.__entity[this.__entity.__name]){
|
|
172
|
+
tableName = ent.__entity[this.__entity.__name].foreignKey;
|
|
173
|
+
}
|
|
174
|
+
else{
|
|
175
|
+
return `Error - Entity ${ent.__entity.__name} has no property named ${this.__entity.__name}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//var jj = ent.raw(`select * from ${entityName} where ${tableName} = ${ this[priKey] }`).single();
|
|
179
|
+
var modelValue = ent.where(`r => r.${tableName} == ${this[priKey]}`).single();
|
|
180
|
+
this[entityField] = modelValue;
|
|
181
|
+
break;
|
|
182
|
+
case "hasMany" :
|
|
183
|
+
if(ent.__entity[this.__entity.__name]){
|
|
184
|
+
tableName = ent.__entity[this.__entity.__name].foreignKey;
|
|
185
|
+
}
|
|
186
|
+
else{
|
|
187
|
+
return `Error - Entity ${ent.__entity.__name} has no property named ${this.__entity.__name}`;
|
|
188
|
+
}
|
|
189
|
+
//var modelValue = ent.raw(`select * from ${entityName} where ${tableName} = ${ this[priKey] }`).toList();
|
|
190
|
+
var modelValue = ent.where(`r => r.${tableName} == ${this[priKey]}`).toList();
|
|
191
|
+
this[entityField] = modelValue;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else{
|
|
196
|
+
return "Entity name must be defined"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else{
|
|
200
|
+
return this["__proto__"]["_" + entityField];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
return this["__proto__"]["_" + entityField];
|
|
206
|
+
//return console.log("make db call to get value", entityField);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
if(currentEntity[entityField].type === "belongsTo"){
|
|
210
|
+
// check if entity has a value if so then return that value
|
|
211
|
+
if(currentModel[entityField]){
|
|
212
|
+
modelClass[entityField] = currentModel[entityField];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
}
|
|
222
|
+
|
|
43
223
|
module.exports = EntityTrackerModel
|
package/InsertManager.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
|
|
2
|
+
var tools = require('./Tools');
|
|
3
|
+
class InsertManager {
|
|
4
|
+
// TODO: WE are using the relationshipTable and the object name stick to one so that hasMany doesnt have to give a name
|
|
5
|
+
constructor(sqlEngine, errorModel, allEntities ){
|
|
6
|
+
this._SQLEngine = sqlEngine;
|
|
7
|
+
this._errorModel = errorModel;
|
|
8
|
+
this._allEntities = allEntities;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
init(currentModel){
|
|
12
|
+
this.runQueries(currentModel);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
runQueries(currentModel){
|
|
16
|
+
var $that = this;
|
|
17
|
+
var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel.__entity);
|
|
18
|
+
this.validateEntity(cleanCurrentModel, currentModel, currentModel.__entity);
|
|
19
|
+
if(this._errorModel.isValid){
|
|
20
|
+
|
|
21
|
+
var modelEntity = currentModel.__entity;
|
|
22
|
+
// TODO: if you try to add belongs to you must have a tag added first. if you dont throw error
|
|
23
|
+
currentModel = this.belongsToInsert(currentModel, modelEntity);
|
|
24
|
+
var SQL = this._SQLEngine.insert(currentModel);
|
|
25
|
+
var primaryKey = tools.getPrimaryKeyObject(currentModel.__entity);
|
|
26
|
+
// return all fields that have auto and dont have a value to the current model on insert
|
|
27
|
+
if(currentModel.__entity[primaryKey].auto === true){
|
|
28
|
+
var getQueryModel = this._SQLEngine.get(`select * from ${currentModel.__entity.__name} where ${primaryKey} = ${ SQL.id }`);
|
|
29
|
+
currentModel[primaryKey] = getQueryModel[primaryKey];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const modelKeys = Object.keys(currentModel);
|
|
33
|
+
// loop through model properties
|
|
34
|
+
for (const property of modelKeys) {
|
|
35
|
+
var propertyModel = currentModel[property];
|
|
36
|
+
var entityProperty = modelEntity[property] ? modelEntity[property] : {};
|
|
37
|
+
if(entityProperty.type === "hasOne"){
|
|
38
|
+
// make sure property model is an object not a primary data type like number or string
|
|
39
|
+
if(typeof(propertyModel) === "object"){
|
|
40
|
+
// check if model has its own entity
|
|
41
|
+
if(modelEntity){
|
|
42
|
+
propertyModel.__entity = tools.getEntity(property, $that._allEntities);
|
|
43
|
+
propertyModel[currentModel.__entity.__name] = SQL.id;
|
|
44
|
+
$that.runQueries(propertyModel);
|
|
45
|
+
}
|
|
46
|
+
else{
|
|
47
|
+
throw `Relationship "${entityProperty.name}" could not be found please check if object has correct spelling or if it has been added to the context class`
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if(entityProperty.type === "hasMany"){
|
|
53
|
+
if(Array.isArray(propertyModel)){
|
|
54
|
+
const propertyKeys = Object.keys(propertyModel);
|
|
55
|
+
for (const propertykey of propertyKeys) {
|
|
56
|
+
if(propertyModel[propertykey]){
|
|
57
|
+
propertyModel[propertykey].__entity = tools.getEntity(property, $that._allEntities);
|
|
58
|
+
propertyModel[propertykey][currentModel.__entity.__name] = SQL.id;
|
|
59
|
+
$that.runQueries(propertyModel[propertykey]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else{
|
|
64
|
+
throw `Relationship "${entityProperty.name}" must be an array`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else{
|
|
71
|
+
var name = currentModel.__entity.__name;
|
|
72
|
+
console.log(`entity ${name} not valid`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// will insert belongs to row first and return the id so that next call can be make correctly
|
|
77
|
+
belongsToInsert(currentModel, modelEntity){
|
|
78
|
+
var $that = this;
|
|
79
|
+
for(var entity in modelEntity) {
|
|
80
|
+
if(modelEntity[entity].type === "belongsTo"){
|
|
81
|
+
var foreignKey = modelEntity[entity].foreignKey === undefined ? modelEntity[entity].name : modelEntity[entity].foreignKey;
|
|
82
|
+
var newPropertyModel = currentModel[foreignKey];
|
|
83
|
+
// check if model is a value because if so then skip we dont need to make an ajax call
|
|
84
|
+
if(typeof newPropertyModel === 'object'){
|
|
85
|
+
newPropertyModel.__entity = tools.getEntity(entity, $that._allEntities);
|
|
86
|
+
var propertyCleanCurrentModel = tools.removePrimarykeyandVirtual(newPropertyModel, newPropertyModel.__entity);
|
|
87
|
+
this.validateEntity(propertyCleanCurrentModel, newPropertyModel, newPropertyModel.__entity);
|
|
88
|
+
var propertySQL = this._SQLEngine.insert(newPropertyModel);
|
|
89
|
+
currentModel[foreignKey] = propertySQL.id;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// todo:
|
|
94
|
+
// loop through all modelEntity and find all the belongs to
|
|
95
|
+
// if belongs to is true then make sql call to insert
|
|
96
|
+
// update the currentModel.
|
|
97
|
+
return currentModel;
|
|
98
|
+
}
|
|
99
|
+
// validate entity for nullable fields
|
|
100
|
+
validateEntity(currentModel, currentRealModel, entityModel){
|
|
101
|
+
for(var entity in entityModel) {
|
|
102
|
+
var currentEntity = entityModel[entity];
|
|
103
|
+
if (entityModel.hasOwnProperty(entity)) {
|
|
104
|
+
// check if there is a default value
|
|
105
|
+
if(currentEntity.default){
|
|
106
|
+
if(!currentRealModel[entity]){
|
|
107
|
+
// if its empty add the default value
|
|
108
|
+
currentRealModel[entity] = currentEntity.default;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// SKIP belongs too
|
|
113
|
+
if(currentEntity.type !== "belongsTo"){
|
|
114
|
+
// primary is always null in an insert so validation insert must be null
|
|
115
|
+
if(currentEntity.nullable === false && !currentEntity.primary){
|
|
116
|
+
// if it doesnt have a get method then call error
|
|
117
|
+
if(currentEntity.get === undefined){
|
|
118
|
+
if(currentModel[entity] === undefined || currentModel[entity] === null || currentModel[entity] === "" ){
|
|
119
|
+
this._errorModel.isValid = false;
|
|
120
|
+
var errorMessage = `Entity ${currentModel.__entity.__name} column ${entity} is a required Field`;
|
|
121
|
+
this._errorModel.errors.push(errorMessage);
|
|
122
|
+
throw errorMessage;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else{
|
|
126
|
+
currentRealModel[entity] = currentEntity.get(currentModel[entity]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
module.exports = InsertManager;
|