@memberjunction/codegen-lib 2.48.0 → 2.50.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.
- package/dist/Angular/angular-codegen.d.ts +164 -6
- package/dist/Angular/angular-codegen.d.ts.map +1 -1
- package/dist/Angular/angular-codegen.js +177 -13
- package/dist/Angular/angular-codegen.js.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts +52 -3
- package/dist/Angular/join-grid-related-entity-component.d.ts.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.js +58 -3
- package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/related-entity-components.d.ts +99 -42
- package/dist/Angular/related-entity-components.d.ts.map +1 -1
- package/dist/Angular/related-entity-components.js +116 -26
- package/dist/Angular/related-entity-components.js.map +1 -1
- package/dist/Angular/timeline-related-entity-component.d.ts +46 -7
- package/dist/Angular/timeline-related-entity-component.d.ts.map +1 -1
- package/dist/Angular/timeline-related-entity-component.js +64 -7
- package/dist/Angular/timeline-related-entity-component.js.map +1 -1
- package/dist/Angular/user-view-grid-related-entity-component.d.ts +33 -1
- package/dist/Angular/user-view-grid-related-entity-component.d.ts.map +1 -1
- package/dist/Angular/user-view-grid-related-entity-component.js +33 -1
- package/dist/Angular/user-view-grid-related-entity-component.js.map +1 -1
- package/dist/Config/config.d.ts +369 -45
- package/dist/Config/config.d.ts.map +1 -1
- package/dist/Config/config.js +136 -2
- package/dist/Config/config.js.map +1 -1
- package/dist/Config/db-connection.d.ts +17 -3
- package/dist/Config/db-connection.d.ts.map +1 -1
- package/dist/Config/db-connection.js +31 -19
- package/dist/Config/db-connection.js.map +1 -1
- package/dist/Database/dbSchema.d.ts +44 -1
- package/dist/Database/dbSchema.d.ts.map +1 -1
- package/dist/Database/dbSchema.js +44 -1
- package/dist/Database/dbSchema.js.map +1 -1
- package/dist/Database/manage-metadata.d.ts +52 -46
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +221 -167
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/reorder-columns.d.ts +2 -2
- package/dist/Database/reorder-columns.d.ts.map +1 -1
- package/dist/Database/reorder-columns.js +23 -17
- package/dist/Database/reorder-columns.js.map +1 -1
- package/dist/Database/sql.d.ts +4 -4
- package/dist/Database/sql.d.ts.map +1 -1
- package/dist/Database/sql.js +2 -2
- package/dist/Database/sql.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +15 -15
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +184 -112
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Misc/advanced_generation.js +81 -81
- package/dist/Misc/advanced_generation.js.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.d.ts +5 -5
- package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.js +10 -8
- package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
- package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
- package/dist/Misc/graphql_server_codegen.js +33 -28
- package/dist/Misc/graphql_server_codegen.js.map +1 -1
- package/dist/Misc/sql_logging.d.ts +2 -2
- package/dist/Misc/sql_logging.d.ts.map +1 -1
- package/dist/Misc/sql_logging.js +4 -3
- package/dist/Misc/sql_logging.js.map +1 -1
- package/dist/Misc/status_logging.d.ts +37 -0
- package/dist/Misc/status_logging.d.ts.map +1 -1
- package/dist/Misc/status_logging.js +145 -3
- package/dist/Misc/status_logging.js.map +1 -1
- package/dist/Misc/system_integrity.d.ts +9 -9
- package/dist/Misc/system_integrity.d.ts.map +1 -1
- package/dist/Misc/system_integrity.js +23 -21
- package/dist/Misc/system_integrity.js.map +1 -1
- package/dist/index.d.ts +45 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -7
- package/dist/index.js.map +1 -1
- package/dist/runCodeGen.d.ts +84 -6
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +242 -82
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +14 -14
|
@@ -27,6 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.ManageMetadataBase = exports.ValidatorResult = void 0;
|
|
30
|
+
const sql = __importStar(require("mssql"));
|
|
30
31
|
const config_1 = require("../Config/config");
|
|
31
32
|
const core_1 = require("@memberjunction/core");
|
|
32
33
|
const status_logging_1 = require("../Misc/status_logging");
|
|
@@ -39,23 +40,22 @@ const path_1 = __importDefault(require("path"));
|
|
|
39
40
|
const sql_logging_1 = require("../Misc/sql_logging");
|
|
40
41
|
const aiengine_1 = require("@memberjunction/aiengine");
|
|
41
42
|
class ValidatorResult {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
43
|
+
entityName = "";
|
|
44
|
+
fieldName;
|
|
45
|
+
sourceCheckConstraint = "";
|
|
46
|
+
functionText = "";
|
|
47
|
+
functionName = "";
|
|
48
|
+
functionDescription = "";
|
|
49
|
+
/**
|
|
50
|
+
* The ID value in the Generated Codes entity that was created for this validator.
|
|
51
|
+
*/
|
|
52
|
+
generatedCodeId = "";
|
|
53
|
+
/**
|
|
54
|
+
* The ID for the AI Model that was used to generate the code
|
|
55
|
+
*/
|
|
56
|
+
aiModelID = "";
|
|
57
|
+
wasGenerated = true;
|
|
58
|
+
success = false;
|
|
59
59
|
}
|
|
60
60
|
exports.ValidatorResult = ValidatorResult;
|
|
61
61
|
/**
|
|
@@ -63,24 +63,25 @@ exports.ValidatorResult = ValidatorResult;
|
|
|
63
63
|
* to properly register your subclass with a priority of 1+ to ensure it gets instantiated.
|
|
64
64
|
*/
|
|
65
65
|
class ManageMetadataBase {
|
|
66
|
-
|
|
67
|
-
this._sqlUtilityObject = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
|
|
68
|
-
}
|
|
66
|
+
_sqlUtilityObject = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
|
|
69
67
|
get SQLUtilityObject() {
|
|
70
68
|
return this._sqlUtilityObject;
|
|
71
69
|
}
|
|
70
|
+
static _newEntityList = [];
|
|
72
71
|
/**
|
|
73
72
|
* Globally scoped list of entities that have been created during the metadata management process.
|
|
74
73
|
*/
|
|
75
74
|
static get newEntityList() {
|
|
76
75
|
return this._newEntityList;
|
|
77
76
|
}
|
|
77
|
+
static _modifiedEntityList = [];
|
|
78
78
|
/**
|
|
79
79
|
* Globally scoped list of entities that have been modified during the metadata management process.
|
|
80
80
|
*/
|
|
81
81
|
static get modifiedEntityList() {
|
|
82
82
|
return this._modifiedEntityList;
|
|
83
83
|
}
|
|
84
|
+
static _generatedValidators = [];
|
|
84
85
|
/**
|
|
85
86
|
* Globally scoped list of validators that have been generated during the metadata management process.
|
|
86
87
|
*/
|
|
@@ -89,30 +90,30 @@ class ManageMetadataBase {
|
|
|
89
90
|
}
|
|
90
91
|
/**
|
|
91
92
|
* Primary function to manage metadata within the CodeGen system. This function will call a series of sub-functions to manage the metadata.
|
|
92
|
-
* @param
|
|
93
|
+
* @param pool - the ConnectionPool object to use for querying and updating the database
|
|
93
94
|
* @returns
|
|
94
95
|
*/
|
|
95
|
-
async manageMetadata(
|
|
96
|
+
async manageMetadata(pool, currentUser) {
|
|
96
97
|
const md = new core_1.Metadata();
|
|
97
98
|
const excludeSchemas = config_1.configInfo.excludeSchemas ? config_1.configInfo.excludeSchemas : [];
|
|
98
99
|
let bSuccess = true;
|
|
99
100
|
let start = new Date();
|
|
100
101
|
(0, status_logging_1.logStatus)(' Creating new entities...');
|
|
101
|
-
if (!await this.createNewEntities(
|
|
102
|
+
if (!await this.createNewEntities(pool)) {
|
|
102
103
|
(0, status_logging_1.logError)(' Error creating new entities');
|
|
103
104
|
bSuccess = false;
|
|
104
105
|
}
|
|
105
106
|
(0, status_logging_1.logStatus)(` > Created new entities in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
106
107
|
start = new Date();
|
|
107
108
|
(0, status_logging_1.logStatus)(' Updating existing entities...');
|
|
108
|
-
if (!await this.updateExistingEntitiesFromSchema(
|
|
109
|
+
if (!await this.updateExistingEntitiesFromSchema(pool, excludeSchemas)) {
|
|
109
110
|
(0, status_logging_1.logError)(' Error updating existing entities');
|
|
110
111
|
bSuccess = false;
|
|
111
112
|
}
|
|
112
113
|
(0, status_logging_1.logStatus)(` > Updated existing entities in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
113
114
|
start = new Date();
|
|
114
115
|
(0, status_logging_1.logStatus)(' Scanning for tables that were deleted where entity metadata still exists...');
|
|
115
|
-
if (!await this.checkAndRemoveMetadataForDeletedTables(
|
|
116
|
+
if (!await this.checkAndRemoveMetadataForDeletedTables(pool, excludeSchemas)) {
|
|
116
117
|
(0, status_logging_1.logError)(' Error removing metadata for tables that were removed');
|
|
117
118
|
bSuccess = false;
|
|
118
119
|
}
|
|
@@ -124,7 +125,7 @@ class ManageMetadataBase {
|
|
|
124
125
|
const schemasToExclude = (0, config_1.getSettingValue)('recompile_mj_views', true)
|
|
125
126
|
? excludeSchemas.filter((s) => s !== adminSchema)
|
|
126
127
|
: excludeSchemas;
|
|
127
|
-
if (!await sqlUtility.recompileAllBaseViews(
|
|
128
|
+
if (!await sqlUtility.recompileAllBaseViews(pool, schemasToExclude, true, ManageMetadataBase._newEntityList /*exclude the newly created entities from the above step the first time we run as those views don't exist yet*/)) {
|
|
128
129
|
(0, status_logging_1.logMessage)(' Warning: Non-Fatal error recompiling base views', core_1.SeverityType.Warning, false);
|
|
129
130
|
// many times the former versions of base views will NOT succesfully recompile, so don't consider that scenario to be a
|
|
130
131
|
// failure for this entire function
|
|
@@ -132,40 +133,41 @@ class ManageMetadataBase {
|
|
|
132
133
|
(0, status_logging_1.logStatus)(` > Recompiled base views in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
133
134
|
start = new Date();
|
|
134
135
|
(0, status_logging_1.logStatus)(' Managing entity fields...');
|
|
135
|
-
if (!await this.manageEntityFields(
|
|
136
|
+
if (!await this.manageEntityFields(pool, excludeSchemas, false, false, currentUser)) {
|
|
136
137
|
(0, status_logging_1.logError)(' Error managing entity fields');
|
|
137
138
|
bSuccess = false;
|
|
138
139
|
}
|
|
139
140
|
(0, status_logging_1.logStatus)(` > Managed entity fields in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
140
141
|
start = new Date();
|
|
141
142
|
(0, status_logging_1.logStatus)(' Managing entity relationships...');
|
|
142
|
-
if (!await this.manageEntityRelationships(
|
|
143
|
+
if (!await this.manageEntityRelationships(pool, excludeSchemas, md)) {
|
|
143
144
|
(0, status_logging_1.logError)(' Error managing entity relationships');
|
|
144
145
|
bSuccess = false;
|
|
145
146
|
}
|
|
146
147
|
(0, status_logging_1.logStatus)(` > Managed entity relationships in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
147
148
|
if (ManageMetadataBase.newEntityList.length > 0) {
|
|
148
|
-
await this.generateNewEntityDescriptions(
|
|
149
|
+
await this.generateNewEntityDescriptions(pool, md); // don't pass excludeSchemas becuase by definition this is the NEW entities we created
|
|
149
150
|
}
|
|
150
|
-
const veResult = await this.manageVirtualEntities(
|
|
151
|
+
const veResult = await this.manageVirtualEntities(pool);
|
|
151
152
|
if (!veResult.success) {
|
|
152
153
|
(0, status_logging_1.logError)(' Error managing virtual entities');
|
|
153
154
|
bSuccess = false;
|
|
154
155
|
}
|
|
155
156
|
return bSuccess;
|
|
156
157
|
}
|
|
157
|
-
async manageVirtualEntities(
|
|
158
|
+
async manageVirtualEntities(pool) {
|
|
158
159
|
let bSuccess = true;
|
|
159
160
|
// virtual entities are records defined in the entity metadata and do NOT define a distinct base table
|
|
160
161
|
// but they do specify a base view. We DO NOT generate a base view for a virtual entity, we simply use it to figure
|
|
161
162
|
// out the fields that should be in the entity definition and add/update/delete the entity definition to match what's in the view when this runs
|
|
162
163
|
const sql = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntities WHERE VirtualEntity = 1`;
|
|
163
|
-
const
|
|
164
|
+
const virtualEntitiesResult = await pool.request().query(sql);
|
|
165
|
+
const virtualEntities = virtualEntitiesResult.recordset;
|
|
164
166
|
let anyUpdates = false;
|
|
165
167
|
if (virtualEntities && virtualEntities.length > 0) {
|
|
166
168
|
// we have 1+ virtual entities, now loop through them and process each one
|
|
167
169
|
for (const ve of virtualEntities) {
|
|
168
|
-
const { success, updatedEntity } = await this.manageSingleVirtualEntity(
|
|
170
|
+
const { success, updatedEntity } = await this.manageSingleVirtualEntity(pool, ve);
|
|
169
171
|
anyUpdates = anyUpdates || updatedEntity;
|
|
170
172
|
if (!success) {
|
|
171
173
|
(0, status_logging_1.logError)(` Error managing virtual entity ${ve.Name}`);
|
|
@@ -175,7 +177,7 @@ class ManageMetadataBase {
|
|
|
175
177
|
}
|
|
176
178
|
return { success: bSuccess, anyUpdates: anyUpdates };
|
|
177
179
|
}
|
|
178
|
-
async manageSingleVirtualEntity(
|
|
180
|
+
async manageSingleVirtualEntity(pool, virtualEntity) {
|
|
179
181
|
let bSuccess = true;
|
|
180
182
|
let bUpdated = false;
|
|
181
183
|
try {
|
|
@@ -194,7 +196,8 @@ class ManageMetadataBase {
|
|
|
194
196
|
SCHEMA_NAME(v.schema_id) = '${virtualEntity.SchemaName}'
|
|
195
197
|
ORDER BY
|
|
196
198
|
c.column_id`;
|
|
197
|
-
const
|
|
199
|
+
const veFieldsResult = await pool.request().query(sql);
|
|
200
|
+
const veFields = veFieldsResult.recordset;
|
|
198
201
|
if (veFields && veFields.length > 0) {
|
|
199
202
|
// we have 1+ fields, now loop through them and process each one
|
|
200
203
|
// first though, remove any fields that are no longer in the view
|
|
@@ -209,7 +212,7 @@ class ManageMetadataBase {
|
|
|
209
212
|
if (removeList.length > 0) {
|
|
210
213
|
const sqlRemove = `DELETE FROM [${(0, config_1.mj_core_schema)()}].EntityField WHERE ID IN (${removeList.map(removeId => `'${removeId}'`).join(',')})`;
|
|
211
214
|
// this removes the fields that shouldn't be there anymore
|
|
212
|
-
await this.LogSQLAndExecute(
|
|
215
|
+
await this.LogSQLAndExecute(pool, sqlRemove, `SQL text to remove fields from entity ${virtualEntity.Name}`);
|
|
213
216
|
bUpdated = true;
|
|
214
217
|
}
|
|
215
218
|
// check to see if any of the fields in the virtual entity have Pkey attribute set. If not, we will default to the first field
|
|
@@ -218,7 +221,7 @@ class ManageMetadataBase {
|
|
|
218
221
|
// now create/update the fields that are in the view
|
|
219
222
|
for (let i = 0; i < veFields.length; i++) {
|
|
220
223
|
const vef = veFields[i];
|
|
221
|
-
const { success, updatedField } = await this.manageSingleVirtualEntityField(
|
|
224
|
+
const { success, updatedField } = await this.manageSingleVirtualEntityField(pool, virtualEntity, vef, i + 1, !hasPkey && i === 0);
|
|
222
225
|
bUpdated = bUpdated || updatedField;
|
|
223
226
|
if (!success) {
|
|
224
227
|
(0, status_logging_1.logError)(`Error managing virtual entity field ${vef.FieldName} for virtual entity ${virtualEntity.Name}`);
|
|
@@ -230,7 +233,7 @@ class ManageMetadataBase {
|
|
|
230
233
|
if (bUpdated) {
|
|
231
234
|
// finally make sure we update the UpdatedAt field for the entity if we made changes to its fields
|
|
232
235
|
const sqlUpdate = `UPDATE [${(0, config_1.mj_core_schema)()}].Entity SET [${core_1.EntityInfo.UpdatedAtFieldName}]=GETUTCDATE() WHERE ID='${virtualEntity.ID}'`;
|
|
233
|
-
await this.LogSQLAndExecute(
|
|
236
|
+
await this.LogSQLAndExecute(pool, sqlUpdate, `SQL text to update virtual entity updated date for ${virtualEntity.Name}`);
|
|
234
237
|
}
|
|
235
238
|
return { success: bSuccess, updatedEntity: bUpdated };
|
|
236
239
|
}
|
|
@@ -239,7 +242,7 @@ class ManageMetadataBase {
|
|
|
239
242
|
return { success: false, updatedEntity: bUpdated };
|
|
240
243
|
}
|
|
241
244
|
}
|
|
242
|
-
async manageSingleVirtualEntityField(
|
|
245
|
+
async manageSingleVirtualEntityField(pool, virtualEntity, veField, fieldSequence, makePrimaryKey) {
|
|
243
246
|
// this protected checks to see if the field exists in the entity definition, and if not, adds it
|
|
244
247
|
// if it exist it updates the entity field to match the view's data type and nullability attributes
|
|
245
248
|
// first, get the entity definition
|
|
@@ -271,7 +274,7 @@ class ManageMetadataBase {
|
|
|
271
274
|
Scale=${veField.Scale}
|
|
272
275
|
WHERE
|
|
273
276
|
ID = '${field.ID}'`; // don't need to update the __mj_UpdatedAt field here, that happens automatically via the trigger
|
|
274
|
-
await this.LogSQLAndExecute(
|
|
277
|
+
await this.LogSQLAndExecute(pool, sqlUpdate, `SQL text to update virtual entity field ${veField.FieldName} for entity ${virtualEntity.Name}`);
|
|
275
278
|
didUpdate = true;
|
|
276
279
|
}
|
|
277
280
|
}
|
|
@@ -286,7 +289,7 @@ class ManageMetadataBase {
|
|
|
286
289
|
${veField.Length}, ${veField.Precision}, ${veField.Scale},
|
|
287
290
|
${fieldSequence}, ${makePrimaryKey ? 1 : 0}, ${makePrimaryKey ? 1 : 0}
|
|
288
291
|
)`;
|
|
289
|
-
await this.LogSQLAndExecute(
|
|
292
|
+
await this.LogSQLAndExecute(pool, sqlAdd, `SQL text to add virtual entity field ${veField.FieldName} for entity ${virtualEntity.Name}`);
|
|
290
293
|
didUpdate = true;
|
|
291
294
|
}
|
|
292
295
|
}
|
|
@@ -294,25 +297,25 @@ class ManageMetadataBase {
|
|
|
294
297
|
}
|
|
295
298
|
/**
|
|
296
299
|
* This method creates and updates relationships in the metadata based on foreign key relationships in the database.
|
|
297
|
-
* @param
|
|
300
|
+
* @param pool
|
|
298
301
|
* @param excludeSchemas - specify any schemas to exclude here and any relationships to/from the specified schemas will be ignored
|
|
299
302
|
* @param md
|
|
300
303
|
* @returns
|
|
301
304
|
*/
|
|
302
|
-
async manageEntityRelationships(
|
|
305
|
+
async manageEntityRelationships(pool, excludeSchemas, md, batchItems = 5) {
|
|
303
306
|
let bResult = true;
|
|
304
|
-
bResult = bResult && await this.manageManyToManyEntityRelationships(
|
|
305
|
-
bResult = bResult && await this.manageOneToManyEntityRelationships(
|
|
307
|
+
bResult = bResult && await this.manageManyToManyEntityRelationships(pool, excludeSchemas, batchItems);
|
|
308
|
+
bResult = bResult && await this.manageOneToManyEntityRelationships(pool, excludeSchemas, md, batchItems);
|
|
306
309
|
return bResult;
|
|
307
310
|
}
|
|
308
311
|
/**
|
|
309
312
|
* Manages 1->M relationships between entities in the metadata based on foreign key relationships in the database.
|
|
310
|
-
* @param
|
|
313
|
+
* @param pool
|
|
311
314
|
* @param excludeSchemas - specify any schemas to exclude here and any relationships to/from the specified schemas will be ignored
|
|
312
315
|
* @param md
|
|
313
316
|
* @returns
|
|
314
317
|
*/
|
|
315
|
-
async manageOneToManyEntityRelationships(
|
|
318
|
+
async manageOneToManyEntityRelationships(pool, excludeSchemas, md, batchItems = 5) {
|
|
316
319
|
// the way this works is that we look for entities in our catalog and we look for
|
|
317
320
|
// foreign keys in those entities. For example, if we saw an entity called Persons and that entity
|
|
318
321
|
// had a foreign key linking to an entity called Organizations via a field called OrganizationID, then we would create a relationship
|
|
@@ -334,17 +337,20 @@ class ManageMetadataBase {
|
|
|
334
337
|
IsVirtual = 0 AND
|
|
335
338
|
EntityID NOT IN (SELECT ID FROM ${(0, config_1.mj_core_schema)()}.Entity WHERE SchemaName IN (${excludeSchemas.map(s => `'${s}'`).join(',')}))
|
|
336
339
|
ORDER BY RelatedEntityID`;
|
|
337
|
-
const
|
|
340
|
+
const entityFieldsResult = await pool.request().query(sSQL);
|
|
341
|
+
const entityFields = entityFieldsResult.recordset;
|
|
338
342
|
// Get the relationship counts for each entity
|
|
339
343
|
const sSQLRelationshipCount = `SELECT EntityID, COUNT(*) AS Count FROM ${(0, config_1.mj_core_schema)()}.EntityRelationship GROUP BY EntityID`;
|
|
340
|
-
const
|
|
344
|
+
const relationshipCountsResult = await pool.request().query(sSQLRelationshipCount);
|
|
345
|
+
const relationshipCounts = relationshipCountsResult.recordset;
|
|
341
346
|
const relationshipCountMap = new Map();
|
|
342
347
|
for (const rc of relationshipCounts) {
|
|
343
348
|
relationshipCountMap.set(rc.EntityID, rc.Count);
|
|
344
349
|
}
|
|
345
350
|
// get all relationships in one query for performance improvement
|
|
346
351
|
const sSQLRelationship = `SELECT * FROM ${(0, config_1.mj_core_schema)()}.EntityRelationship`;
|
|
347
|
-
const
|
|
352
|
+
const allRelationshipsResult = await pool.request().query(sSQLRelationship);
|
|
353
|
+
const allRelationships = allRelationshipsResult.recordset;
|
|
348
354
|
// Function to process a batch of entity fields
|
|
349
355
|
const processBatch = async (batch) => {
|
|
350
356
|
let batchSQL = '';
|
|
@@ -374,7 +380,7 @@ class ManageMetadataBase {
|
|
|
374
380
|
}
|
|
375
381
|
});
|
|
376
382
|
if (batchSQL.length > 0) {
|
|
377
|
-
await this.LogSQLAndExecute(
|
|
383
|
+
await this.LogSQLAndExecute(pool, batchSQL, `SQL text to create Entitiy Relationships`);
|
|
378
384
|
}
|
|
379
385
|
};
|
|
380
386
|
// Split entityFields into batches and process each batch
|
|
@@ -395,10 +401,11 @@ class ManageMetadataBase {
|
|
|
395
401
|
* @param ds
|
|
396
402
|
* @param excludeSchemas
|
|
397
403
|
*/
|
|
398
|
-
async checkAndRemoveMetadataForDeletedTables(
|
|
404
|
+
async checkAndRemoveMetadataForDeletedTables(pool, excludeSchemas) {
|
|
399
405
|
try {
|
|
400
406
|
const sql = `SELECT * FROM ${(0, config_1.mj_core_schema)()}.vwEntitiesWithMissingBaseTables WHERE VirtualEntity=0`;
|
|
401
|
-
const
|
|
407
|
+
const entitiesResult = await pool.request().query(sql);
|
|
408
|
+
const entities = entitiesResult.recordset;
|
|
402
409
|
if (entities && entities.length > 0) {
|
|
403
410
|
for (const e of entities) {
|
|
404
411
|
// for the given entity, wipe out the entity metadata and its core deps.
|
|
@@ -406,15 +413,15 @@ class ManageMetadataBase {
|
|
|
406
413
|
// for the admin to handle manually
|
|
407
414
|
try {
|
|
408
415
|
const sqlDelete = `__mj.spDeleteEntityWithCoreDependencies @EntityID='${e.ID}'`;
|
|
409
|
-
await this.LogSQLAndExecute(
|
|
416
|
+
await this.LogSQLAndExecute(pool, sqlDelete, `SQL text to remove entity ${e.Name}`);
|
|
410
417
|
(0, status_logging_1.logStatus)(` > Removed metadata for table ${e.SchemaName}.${e.BaseTable}`);
|
|
411
418
|
// next up we need to remove the spCreate, spDelete, spUpdate, BaseView, and FullTextSearchFunction, if provided.
|
|
412
419
|
// We only remoe these artifcacts when they are generated which is info we have in the BaseViewGenerated, spCreateGenerated, etc. fields
|
|
413
|
-
await this.checkDropSQLObject(
|
|
414
|
-
await this.checkDropSQLObject(
|
|
415
|
-
await this.checkDropSQLObject(
|
|
416
|
-
await this.checkDropSQLObject(
|
|
417
|
-
await this.checkDropSQLObject(
|
|
420
|
+
await this.checkDropSQLObject(pool, e.BaseViewGenerated, 'view', e.SchemaName, e.BaseView);
|
|
421
|
+
await this.checkDropSQLObject(pool, e.spCreateGenerated, 'procedure', e.SchemaName, e.spCreate ? e.spCreate : `spCreate${e.ClassName}`);
|
|
422
|
+
await this.checkDropSQLObject(pool, e.spDeleteGenerated, 'procedure', e.SchemaName, e.spDelete ? e.spDelete : `spDelete${e.ClassName}`);
|
|
423
|
+
await this.checkDropSQLObject(pool, e.spUpdateGenerated, 'procedure', e.SchemaName, e.spUpdate ? e.spUpdate : `spUpdate${e.ClassName}`);
|
|
424
|
+
await this.checkDropSQLObject(pool, e.FullTextSearchFunctionGenerated, 'function', e.SchemaName, e.FullTextSearchFunction);
|
|
418
425
|
}
|
|
419
426
|
catch (ex) {
|
|
420
427
|
(0, status_logging_1.logError)(`Error removing metadata for entity ${ex.Name}, error: ${ex}`);
|
|
@@ -431,11 +438,11 @@ class ManageMetadataBase {
|
|
|
431
438
|
return false;
|
|
432
439
|
}
|
|
433
440
|
}
|
|
434
|
-
async checkDropSQLObject(
|
|
441
|
+
async checkDropSQLObject(pool, proceed, type, schemaName, name) {
|
|
435
442
|
try {
|
|
436
443
|
if (proceed && schemaName && name && schemaName.trim().length > 0 && name.trim().length > 0) {
|
|
437
444
|
const sqlDelete = `DROP ${type} IF EXISTS [${schemaName}].[${name}]`;
|
|
438
|
-
await this.LogSQLAndExecute(
|
|
445
|
+
await this.LogSQLAndExecute(pool, sqlDelete, `SQL text to remove ${type} ${schemaName}.${name}`);
|
|
439
446
|
// next up, we need to clean up the cache of saved DB objects that may exist for this entity in the appropriate sub-directory.
|
|
440
447
|
const sqlOutputDir = (0, config_1.outputDir)('SQL', true);
|
|
441
448
|
if (sqlOutputDir) {
|
|
@@ -464,52 +471,58 @@ class ManageMetadataBase {
|
|
|
464
471
|
* @param excludeSchemas
|
|
465
472
|
* @returns
|
|
466
473
|
*/
|
|
467
|
-
async manageManyToManyEntityRelationships(
|
|
474
|
+
async manageManyToManyEntityRelationships(pool, excludeSchemas, batchItems = 5) {
|
|
468
475
|
return true; // not implemented for now, require the admin to manually create these relationships
|
|
469
476
|
}
|
|
470
477
|
/**
|
|
471
478
|
* Manages the creation, updating and deletion of entity field records in the metadata based on the database schema.
|
|
472
|
-
* @param
|
|
479
|
+
* @param pool
|
|
473
480
|
* @param excludeSchemas
|
|
474
481
|
* @returns
|
|
475
482
|
*/
|
|
476
|
-
async manageEntityFields(
|
|
483
|
+
async manageEntityFields(pool, excludeSchemas, skipCreatedAtUpdatedAtDeletedAtFieldValidation, skipEntityFieldValues, currentUser) {
|
|
477
484
|
let bSuccess = true;
|
|
478
485
|
const startTime = new Date();
|
|
479
486
|
if (!skipCreatedAtUpdatedAtDeletedAtFieldValidation) {
|
|
480
|
-
if (!await this.ensureCreatedAtUpdatedAtFieldsExist(
|
|
481
|
-
!await this.ensureDeletedAtFieldsExist(
|
|
487
|
+
if (!await this.ensureCreatedAtUpdatedAtFieldsExist(pool, excludeSchemas) ||
|
|
488
|
+
!await this.ensureDeletedAtFieldsExist(pool, excludeSchemas)) {
|
|
482
489
|
(0, status_logging_1.logError)(`Error ensuring ${core_1.EntityInfo.CreatedAtFieldName}, ${core_1.EntityInfo.UpdatedAtFieldName} and ${core_1.EntityInfo.DeletedAtFieldName} fields exist`);
|
|
483
490
|
bSuccess = false;
|
|
484
491
|
}
|
|
485
492
|
(0, status_logging_1.logStatus)(` Ensured ${core_1.EntityInfo.CreatedAtFieldName}/${core_1.EntityInfo.UpdatedAtFieldName}/${core_1.EntityInfo.DeletedAtFieldName} fields exist in ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
|
|
486
493
|
}
|
|
487
494
|
const step1StartTime = new Date();
|
|
488
|
-
if (!await this.deleteUnneededEntityFields(
|
|
495
|
+
if (!await this.deleteUnneededEntityFields(pool, excludeSchemas)) {
|
|
489
496
|
(0, status_logging_1.logError)('Error deleting unneeded entity fields');
|
|
490
497
|
bSuccess = false;
|
|
491
498
|
}
|
|
492
499
|
(0, status_logging_1.logStatus)(` Deleted unneeded entity fields in ${(new Date().getTime() - step1StartTime.getTime()) / 1000} seconds`);
|
|
500
|
+
// AN: 14-June-2025 - See note below about the new order of these steps, this must
|
|
501
|
+
// happen before we update existing entity fields from schema.
|
|
493
502
|
const step2StartTime = new Date();
|
|
494
|
-
if (!await this.
|
|
495
|
-
(0, status_logging_1.logError)('Error
|
|
503
|
+
if (!await this.createNewEntityFieldsFromSchema(pool)) { // has its own internal filtering for exclude schema/table so don't pass in
|
|
504
|
+
(0, status_logging_1.logError)('Error creating new entity fields from schema');
|
|
496
505
|
bSuccess = false;
|
|
497
506
|
}
|
|
498
|
-
(0, status_logging_1.logStatus)(`
|
|
507
|
+
(0, status_logging_1.logStatus)(` Created new entity fields from schema in ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
|
|
508
|
+
// AN: 14-June-2025 - we are now running this AFTER we create new entity fields from schema
|
|
509
|
+
// which results in the same pattern of behavior as migrations where we first create new fields
|
|
510
|
+
// with VERY HIGH sequence numbers (e.g. 100,000 above what they will be approx) and then
|
|
511
|
+
// we align them properly in sequential order from 1+ via this method below.
|
|
499
512
|
const step3StartTime = new Date();
|
|
500
|
-
if (!await this.
|
|
501
|
-
(0, status_logging_1.logError)('Error
|
|
513
|
+
if (!await this.updateExistingEntityFieldsFromSchema(pool, excludeSchemas)) {
|
|
514
|
+
(0, status_logging_1.logError)('Error updating existing entity fields from schema');
|
|
502
515
|
bSuccess = false;
|
|
503
516
|
}
|
|
504
|
-
(0, status_logging_1.logStatus)(`
|
|
517
|
+
(0, status_logging_1.logStatus)(` Updated existing entity fields from schema in ${(new Date().getTime() - step3StartTime.getTime()) / 1000} seconds`);
|
|
505
518
|
const step4StartTime = new Date();
|
|
506
|
-
if (!await this.setDefaultColumnWidthWhereNeeded(
|
|
519
|
+
if (!await this.setDefaultColumnWidthWhereNeeded(pool, excludeSchemas)) {
|
|
507
520
|
(0, status_logging_1.logError)('Error setting default column width where needed');
|
|
508
521
|
bSuccess = false;
|
|
509
522
|
}
|
|
510
523
|
(0, status_logging_1.logStatus)(` Set default column width where needed in ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
|
|
511
524
|
const step5StartTime = new Date();
|
|
512
|
-
if (!await this.updateEntityFieldDisplayNameWhereNull(
|
|
525
|
+
if (!await this.updateEntityFieldDisplayNameWhereNull(pool, excludeSchemas)) {
|
|
513
526
|
(0, status_logging_1.logError)('Error updating entity field display name where null');
|
|
514
527
|
bSuccess = false;
|
|
515
528
|
}
|
|
@@ -517,7 +530,7 @@ class ManageMetadataBase {
|
|
|
517
530
|
if (!skipEntityFieldValues) {
|
|
518
531
|
const step6StartTime = new Date();
|
|
519
532
|
(0, status_logging_1.logStatus)(` Starting to manage entity field values...`);
|
|
520
|
-
if (!await this.manageEntityFieldValuesAndValidatorFunctions(
|
|
533
|
+
if (!await this.manageEntityFieldValuesAndValidatorFunctions(pool, excludeSchemas, currentUser, false)) {
|
|
521
534
|
(0, status_logging_1.logError)('Error managing entity field values');
|
|
522
535
|
bSuccess = false;
|
|
523
536
|
}
|
|
@@ -529,7 +542,7 @@ class ManageMetadataBase {
|
|
|
529
542
|
/**
|
|
530
543
|
* This method ensures that the __mj_DeletedAt field exists in each entity that has DeleteType=Soft. If the field does not exist, it is created.
|
|
531
544
|
*/
|
|
532
|
-
async ensureDeletedAtFieldsExist(
|
|
545
|
+
async ensureDeletedAtFieldsExist(pool, excludeSchemas) {
|
|
533
546
|
try {
|
|
534
547
|
const sqlEntities = `SELECT
|
|
535
548
|
*
|
|
@@ -539,7 +552,8 @@ class ManageMetadataBase {
|
|
|
539
552
|
VirtualEntity=0 AND
|
|
540
553
|
DeleteType='Soft' AND
|
|
541
554
|
SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})`;
|
|
542
|
-
const
|
|
555
|
+
const entitiesResult = await pool.request().query(sqlEntities);
|
|
556
|
+
const entities = entitiesResult.recordset;
|
|
543
557
|
let overallResult = true;
|
|
544
558
|
if (entities.length > 0) {
|
|
545
559
|
// we have 1+ entities that need the special fields, so loop through them and ensure the fields exist
|
|
@@ -549,12 +563,13 @@ class ManageMetadataBase {
|
|
|
549
563
|
WHERE
|
|
550
564
|
${entities.map((e) => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
|
|
551
565
|
AND COLUMN_NAME='${core_1.EntityInfo.DeletedAtFieldName}'`;
|
|
552
|
-
const
|
|
566
|
+
const resultResult = await pool.request().query(sql);
|
|
567
|
+
const result = resultResult.recordset;
|
|
553
568
|
for (const e of entities) {
|
|
554
569
|
const eResult = result.filter((r) => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
|
|
555
570
|
const deletedAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.DeletedAtFieldName.trim().toLowerCase());
|
|
556
571
|
// now, if we have the fields, we need to check the default value and update if necessary
|
|
557
|
-
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(
|
|
572
|
+
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e, core_1.EntityInfo.DeletedAtFieldName, deletedAt, true);
|
|
558
573
|
overallResult = overallResult && fieldResult;
|
|
559
574
|
}
|
|
560
575
|
}
|
|
@@ -569,9 +584,9 @@ class ManageMetadataBase {
|
|
|
569
584
|
* This method ensures that the __mj_CreatedAt and __mj_UpdatedAt fields exist in each entity that has TrackRecordChanges set to true. If the fields do not exist, they are created.
|
|
570
585
|
* If the fields exist but have incorrect default values, the default values are updated. The default value that is to be used for these special fields is GETUTCDATE() which is the
|
|
571
586
|
* UTC date and time. This method is called as part of the manageEntityFields method and is not intended to be called directly.
|
|
572
|
-
* @param
|
|
587
|
+
* @param pool
|
|
573
588
|
*/
|
|
574
|
-
async ensureCreatedAtUpdatedAtFieldsExist(
|
|
589
|
+
async ensureCreatedAtUpdatedAtFieldsExist(pool, excludeSchemas) {
|
|
575
590
|
try {
|
|
576
591
|
const sqlEntities = `SELECT
|
|
577
592
|
*
|
|
@@ -581,7 +596,8 @@ class ManageMetadataBase {
|
|
|
581
596
|
VirtualEntity = 0 AND
|
|
582
597
|
TrackRecordChanges = 1 AND
|
|
583
598
|
SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})`;
|
|
584
|
-
const
|
|
599
|
+
const entitiesResult = await pool.request().query(sqlEntities);
|
|
600
|
+
const entities = entitiesResult.recordset;
|
|
585
601
|
let overallResult = true;
|
|
586
602
|
if (entities.length > 0) {
|
|
587
603
|
// we have 1+ entities that need the special fields, so loop through them and ensure the fields exist
|
|
@@ -592,15 +608,16 @@ class ManageMetadataBase {
|
|
|
592
608
|
WHERE
|
|
593
609
|
${entities.map((e) => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
|
|
594
610
|
AND COLUMN_NAME IN ('${core_1.EntityInfo.CreatedAtFieldName}','${core_1.EntityInfo.UpdatedAtFieldName}')`;
|
|
595
|
-
const
|
|
611
|
+
const resultResult = await pool.request().query(sqlCreatedUpdated);
|
|
612
|
+
const result = resultResult.recordset;
|
|
596
613
|
for (const e of entities) {
|
|
597
614
|
// result has both created at and updated at fields, so filter on the result for each and do what we need to based on that
|
|
598
615
|
const eResult = result.filter((r) => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
|
|
599
616
|
const createdAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.CreatedAtFieldName.trim().toLowerCase());
|
|
600
617
|
const updatedAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase());
|
|
601
618
|
// now, if we have the fields, we need to check the default value and update if necessary
|
|
602
|
-
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(
|
|
603
|
-
await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(
|
|
619
|
+
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e, core_1.EntityInfo.CreatedAtFieldName, createdAt, false) &&
|
|
620
|
+
await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e, core_1.EntityInfo.UpdatedAtFieldName, updatedAt, false);
|
|
604
621
|
overallResult = overallResult && fieldResult;
|
|
605
622
|
}
|
|
606
623
|
}
|
|
@@ -618,12 +635,12 @@ class ManageMetadataBase {
|
|
|
618
635
|
* @param fieldName
|
|
619
636
|
* @param currentFieldData
|
|
620
637
|
*/
|
|
621
|
-
async ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(
|
|
638
|
+
async ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, entity, fieldName, currentFieldData, allowNull) {
|
|
622
639
|
try {
|
|
623
640
|
if (!currentFieldData) {
|
|
624
641
|
// field doesn't exist, let's create it
|
|
625
642
|
const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD ${fieldName} DATETIMEOFFSET ${allowNull ? 'NULL' : 'NOT NULL DEFAULT GETUTCDATE()'}`;
|
|
626
|
-
await this.LogSQLAndExecute(
|
|
643
|
+
await this.LogSQLAndExecute(pool, sql, `SQL text to add special date field ${fieldName} to entity ${entity.SchemaName}.${entity.BaseTable}`);
|
|
627
644
|
}
|
|
628
645
|
else {
|
|
629
646
|
// field does exist, let's first check the data type/nullability
|
|
@@ -632,11 +649,11 @@ class ManageMetadataBase {
|
|
|
632
649
|
(currentFieldData.IS_NULLABLE.trim().toLowerCase() === 'no' && allowNull)) {
|
|
633
650
|
// the column is the wrong type, or has wrong nullability attribute, so let's update it, first removing the default constraint, then
|
|
634
651
|
// modifying the column, and finally adding the default constraint back in.
|
|
635
|
-
await this.dropExistingDefaultConstraint(
|
|
652
|
+
await this.dropExistingDefaultConstraint(pool, entity, fieldName);
|
|
636
653
|
const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ALTER COLUMN ${fieldName} DATETIMEOFFSET ${allowNull ? 'NULL' : 'NOT NULL'}`;
|
|
637
|
-
await this.LogSQLAndExecute(
|
|
654
|
+
await this.LogSQLAndExecute(pool, sql, `SQL text to update special date field ${fieldName} in entity ${entity.SchemaName}.${entity.BaseTable}`);
|
|
638
655
|
if (!allowNull)
|
|
639
|
-
await this.createDefaultConstraintForSpecialDateField(
|
|
656
|
+
await this.createDefaultConstraintForSpecialDateField(pool, entity, fieldName);
|
|
640
657
|
}
|
|
641
658
|
else {
|
|
642
659
|
// if we get here that means the column is the correct type and nullability, so now let's check the default value, but we only do that if we are dealing with a
|
|
@@ -645,7 +662,7 @@ class ManageMetadataBase {
|
|
|
645
662
|
const defaultValue = currentFieldData.COLUMN_DEFAULT;
|
|
646
663
|
const realDefaultValue = (0, core_1.ExtractActualDefaultValue)(defaultValue);
|
|
647
664
|
if (!realDefaultValue || realDefaultValue.trim().toLowerCase() !== 'getutcdate()') {
|
|
648
|
-
await this.dropAndCreateDefaultConstraintForSpecialDateField(
|
|
665
|
+
await this.dropAndCreateDefaultConstraintForSpecialDateField(pool, entity, fieldName);
|
|
649
666
|
}
|
|
650
667
|
}
|
|
651
668
|
}
|
|
@@ -661,10 +678,10 @@ class ManageMetadataBase {
|
|
|
661
678
|
/**
|
|
662
679
|
* Creates the default constraint for a special date field. This method is called as part of the ensureSpecialDateFieldExistsAndHasCorrectDefaultValue method and is not intended to be called directly.
|
|
663
680
|
*/
|
|
664
|
-
async createDefaultConstraintForSpecialDateField(
|
|
681
|
+
async createDefaultConstraintForSpecialDateField(pool, entity, fieldName) {
|
|
665
682
|
try {
|
|
666
683
|
const sqlAddDefaultConstraint = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD CONSTRAINT DF_${entity.SchemaName}_${(0, core_1.CodeNameFromString)(entity.BaseTable)}_${fieldName} DEFAULT GETUTCDATE() FOR [${fieldName}]`;
|
|
667
|
-
await this.LogSQLAndExecute(
|
|
684
|
+
await this.LogSQLAndExecute(pool, sqlAddDefaultConstraint, `SQL text to add default constraint for special date field ${fieldName} in entity ${entity.SchemaName}.${entity.BaseTable}`);
|
|
668
685
|
}
|
|
669
686
|
catch (e) {
|
|
670
687
|
(0, status_logging_1.logError)(e);
|
|
@@ -676,18 +693,18 @@ class ManageMetadataBase {
|
|
|
676
693
|
* @param entity
|
|
677
694
|
* @param fieldName
|
|
678
695
|
*/
|
|
679
|
-
async dropAndCreateDefaultConstraintForSpecialDateField(
|
|
696
|
+
async dropAndCreateDefaultConstraintForSpecialDateField(pool, entity, fieldName) {
|
|
680
697
|
// default value is not correct, so let's update it
|
|
681
|
-
await this.dropExistingDefaultConstraint(
|
|
682
|
-
await this.createDefaultConstraintForSpecialDateField(
|
|
698
|
+
await this.dropExistingDefaultConstraint(pool, entity, fieldName);
|
|
699
|
+
await this.createDefaultConstraintForSpecialDateField(pool, entity, fieldName);
|
|
683
700
|
}
|
|
684
701
|
/**
|
|
685
702
|
* Drops an existing default constraint from a given column within a given entity, if it exists
|
|
686
|
-
* @param
|
|
703
|
+
* @param pool
|
|
687
704
|
* @param entity
|
|
688
705
|
* @param fieldName
|
|
689
706
|
*/
|
|
690
|
-
async dropExistingDefaultConstraint(
|
|
707
|
+
async dropExistingDefaultConstraint(pool, entity, fieldName) {
|
|
691
708
|
try {
|
|
692
709
|
const sqlDropDefaultConstraint = `
|
|
693
710
|
DECLARE @constraintName NVARCHAR(255);
|
|
@@ -708,7 +725,7 @@ class ManageMetadataBase {
|
|
|
708
725
|
EXEC('ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] DROP CONSTRAINT ' + @constraintName);
|
|
709
726
|
END
|
|
710
727
|
`;
|
|
711
|
-
await this.LogSQLAndExecute(
|
|
728
|
+
await this.LogSQLAndExecute(pool, sqlDropDefaultConstraint, `SQL text to drop default existing default constraints in entity ${entity.SchemaName}.${entity.BaseTable}`);
|
|
712
729
|
}
|
|
713
730
|
catch (e) {
|
|
714
731
|
(0, status_logging_1.logError)(e);
|
|
@@ -717,10 +734,10 @@ class ManageMetadataBase {
|
|
|
717
734
|
/**
|
|
718
735
|
* This method generates descriptions for entities in teh system where there is no existing description. This is an experimental feature and is done using AI. In order for it
|
|
719
736
|
* to be invoked, the EntityDescriptions feature must be enabled in the Advanced Generation configuration.
|
|
720
|
-
* @param
|
|
737
|
+
* @param pool
|
|
721
738
|
* @param md
|
|
722
739
|
*/
|
|
723
|
-
async generateNewEntityDescriptions(
|
|
740
|
+
async generateNewEntityDescriptions(pool, md) {
|
|
724
741
|
// for the list of new entities, go through and attempt to generate new entity descriptions
|
|
725
742
|
const ag = new advanced_generation_1.AdvancedGeneration();
|
|
726
743
|
if (ag.featureEnabled('EntityDescriptions')) {
|
|
@@ -731,8 +748,10 @@ class ManageMetadataBase {
|
|
|
731
748
|
const userMessage = prompt.userMessage + '\n\n';
|
|
732
749
|
// now loop through the new entities and generate descriptions for them
|
|
733
750
|
for (let e of ManageMetadataBase.newEntityList) {
|
|
734
|
-
const
|
|
735
|
-
const
|
|
751
|
+
const dataResult = await pool.request().query(`SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntities WHERE Name = '${e}'`);
|
|
752
|
+
const data = dataResult.recordset;
|
|
753
|
+
const fieldsResult = await pool.request().query(`SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFields WHERE EntityID='${data[0].ID}'`);
|
|
754
|
+
const fields = fieldsResult.recordset;
|
|
736
755
|
const entityUserMessage = userMessage + `Entity Name: ${e},
|
|
737
756
|
Base Table: ${data[0].BaseTable},
|
|
738
757
|
Schema: ${data[0].SchemaName}.
|
|
@@ -757,7 +776,7 @@ class ManageMetadataBase {
|
|
|
757
776
|
const structuredResult = JSON.parse(resultText);
|
|
758
777
|
if (structuredResult?.entityDescription && structuredResult.entityDescription.length > 0) {
|
|
759
778
|
const sSQL = `UPDATE [${(0, config_1.mj_core_schema)()}].Entity SET Description = '${structuredResult.entityDescription}' WHERE Name = '${e}'`;
|
|
760
|
-
await this.LogSQLAndExecute(
|
|
779
|
+
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update entity description for entity ${e}`);
|
|
761
780
|
}
|
|
762
781
|
else {
|
|
763
782
|
console.warn(' >>> Advanced Generation Error: LLM returned a blank entity description, skipping entity description for entity ' + e);
|
|
@@ -781,7 +800,7 @@ class ManageMetadataBase {
|
|
|
781
800
|
* @param excludeSchemas
|
|
782
801
|
* @returns
|
|
783
802
|
*/
|
|
784
|
-
async updateEntityFieldDisplayNameWhereNull(
|
|
803
|
+
async updateEntityFieldDisplayNameWhereNull(pool, excludeSchemas) {
|
|
785
804
|
try {
|
|
786
805
|
const sql = `SELECT
|
|
787
806
|
ef.ID, ef.Name
|
|
@@ -797,13 +816,14 @@ class ManageMetadataBase {
|
|
|
797
816
|
ef.Name <> \'ID\' AND
|
|
798
817
|
e.SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})
|
|
799
818
|
`;
|
|
800
|
-
const
|
|
819
|
+
const fieldsResult = await pool.request().query(sql);
|
|
820
|
+
const fields = fieldsResult.recordset;
|
|
801
821
|
if (fields && fields.length > 0)
|
|
802
822
|
for (const field of fields) {
|
|
803
823
|
const sDisplayName = (0, global_1.stripTrailingChars)((0, global_1.convertCamelCaseToHaveSpaces)(field.Name), 'ID', true).trim();
|
|
804
824
|
if (sDisplayName.length > 0 && sDisplayName.toLowerCase().trim() !== field.Name.toLowerCase().trim()) {
|
|
805
825
|
const sSQL = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ${core_1.EntityInfo.UpdatedAtFieldName}=GETUTCDATE(), DisplayName = '${sDisplayName}' WHERE ID = '${field.ID}'`;
|
|
806
|
-
await this.LogSQLAndExecute(
|
|
826
|
+
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update display name for field ${field.Name}`);
|
|
807
827
|
}
|
|
808
828
|
}
|
|
809
829
|
return true;
|
|
@@ -817,14 +837,14 @@ class ManageMetadataBase {
|
|
|
817
837
|
* This method updates the DefaultColumnWidth field in the EntityField metadata. The default logic uses a stored procedure called spSetDefaultColumnWidthWhereNeeded
|
|
818
838
|
* which is part of the MJ Core Schema. You can override this method to implement custom logic for setting default column widths. It is NOT recommended to
|
|
819
839
|
* modify the stored procedure in the MJ Core Schema because your changes will be overriden during a future upgrade.
|
|
820
|
-
* @param
|
|
840
|
+
* @param pool
|
|
821
841
|
* @param excludeSchemas
|
|
822
842
|
* @returns
|
|
823
843
|
*/
|
|
824
|
-
async setDefaultColumnWidthWhereNeeded(
|
|
844
|
+
async setDefaultColumnWidthWhereNeeded(pool, excludeSchemas) {
|
|
825
845
|
try {
|
|
826
846
|
const sSQL = `EXEC ${(0, config_1.mj_core_schema)()}.spSetDefaultColumnWidthWhereNeeded @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
827
|
-
await this.LogSQLAndExecute(
|
|
847
|
+
await this.LogSQLAndExecute(pool, sSQL, `SQL text to set default column width where needed`, true);
|
|
828
848
|
return true;
|
|
829
849
|
}
|
|
830
850
|
catch (e) {
|
|
@@ -835,12 +855,18 @@ class ManageMetadataBase {
|
|
|
835
855
|
/**
|
|
836
856
|
* Creates a SQL statement to retrieve all of the pending entity fields that need to be created in the metadata. This method looks for fields that exist in the underlying
|
|
837
857
|
* database but are NOT in the metadata.
|
|
858
|
+
*
|
|
859
|
+
* IMPORTANT: The sequence shown below has a 100,000 added to it to ensure that there is no collision with existing sequences. The spUpdateExistingEntityFieldsFromSchema
|
|
860
|
+
* stored procedure runs AFTER this method and will correct the sequences to ensure they are in the correct order. In a migration, the spUpdateExistingEntityFieldsFromSchema
|
|
861
|
+
* runs afterwards as well so this behavior ensures CodeGen works consistently.
|
|
862
|
+
*
|
|
863
|
+
* @returns {string} - The SQL statement to retrieve pending entity fields.
|
|
838
864
|
*/
|
|
839
865
|
getPendingEntityFieldsSELECTSQL() {
|
|
840
866
|
const sSQL = `WITH NumberedRows AS (
|
|
841
867
|
SELECT
|
|
842
868
|
sf.EntityID,
|
|
843
|
-
sf.Sequence,
|
|
869
|
+
sf.Sequence + 100000 Sequence, -- add a large number to the sequence to ensure no collision with existing sequences - spUpdateExistingEntityFieldsFromSchema runs AFTER this and will correct them.
|
|
844
870
|
sf.FieldName,
|
|
845
871
|
sf.Description,
|
|
846
872
|
sf.Type,
|
|
@@ -1044,11 +1070,14 @@ class ManageMetadataBase {
|
|
|
1044
1070
|
}
|
|
1045
1071
|
return sResult;
|
|
1046
1072
|
}
|
|
1047
|
-
async createNewEntityFieldsFromSchema(
|
|
1073
|
+
async createNewEntityFieldsFromSchema(pool) {
|
|
1048
1074
|
try {
|
|
1049
1075
|
const sSQL = this.getPendingEntityFieldsSELECTSQL();
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1076
|
+
const newEntityFieldsResult = await pool.request().query(sSQL);
|
|
1077
|
+
const newEntityFields = newEntityFieldsResult.recordset;
|
|
1078
|
+
const transaction = new sql.Transaction(pool);
|
|
1079
|
+
await transaction.begin();
|
|
1080
|
+
try {
|
|
1052
1081
|
// wrap in a transaction so we get all of it or none of it
|
|
1053
1082
|
for (let i = 0; i < newEntityFields.length; ++i) {
|
|
1054
1083
|
const n = newEntityFields[i];
|
|
@@ -1058,7 +1087,7 @@ class ManageMetadataBase {
|
|
|
1058
1087
|
const newEntityFieldUUID = this.createNewUUID();
|
|
1059
1088
|
const sSQLInsert = this.getPendingEntityFieldINSERTSQL(newEntityFieldUUID, n);
|
|
1060
1089
|
try {
|
|
1061
|
-
await this.LogSQLAndExecute(
|
|
1090
|
+
await this.LogSQLAndExecute(pool, sSQLInsert, `SQL text to insert new entity field`);
|
|
1062
1091
|
// if we get here, we're okay, otherwise we have an exception, which we want as it blows up transaction
|
|
1063
1092
|
}
|
|
1064
1093
|
catch (e) {
|
|
@@ -1068,7 +1097,12 @@ class ManageMetadataBase {
|
|
|
1068
1097
|
}
|
|
1069
1098
|
}
|
|
1070
1099
|
}
|
|
1071
|
-
|
|
1100
|
+
await transaction.commit();
|
|
1101
|
+
}
|
|
1102
|
+
catch (e) {
|
|
1103
|
+
await transaction.rollback();
|
|
1104
|
+
throw e;
|
|
1105
|
+
}
|
|
1072
1106
|
// if we get here now send a distinct list of the entities that had new fields to the modified entity list
|
|
1073
1107
|
// column in the resultset is called EntityName, we dont have to dedupe them here because the method below
|
|
1074
1108
|
// will do that for us
|
|
@@ -1087,12 +1121,12 @@ class ManageMetadataBase {
|
|
|
1087
1121
|
* @param relatedEntityNameFieldMap
|
|
1088
1122
|
* @returns
|
|
1089
1123
|
*/
|
|
1090
|
-
async updateEntityFieldRelatedEntityNameFieldMap(
|
|
1124
|
+
async updateEntityFieldRelatedEntityNameFieldMap(pool, entityFieldID, relatedEntityNameFieldMap) {
|
|
1091
1125
|
try {
|
|
1092
1126
|
const sSQL = `EXEC [${(0, config_1.mj_core_schema)()}].spUpdateEntityFieldRelatedEntityNameFieldMap
|
|
1093
1127
|
@EntityFieldID='${entityFieldID}',
|
|
1094
1128
|
@RelatedEntityNameFieldMap='${relatedEntityNameFieldMap}'`;
|
|
1095
|
-
await this.LogSQLAndExecute(
|
|
1129
|
+
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update entity field related entity name field map for entity field ID ${entityFieldID}`);
|
|
1096
1130
|
return true;
|
|
1097
1131
|
}
|
|
1098
1132
|
catch (e) {
|
|
@@ -1100,10 +1134,10 @@ class ManageMetadataBase {
|
|
|
1100
1134
|
return false;
|
|
1101
1135
|
}
|
|
1102
1136
|
}
|
|
1103
|
-
async updateExistingEntitiesFromSchema(
|
|
1137
|
+
async updateExistingEntitiesFromSchema(pool, excludeSchemas) {
|
|
1104
1138
|
try {
|
|
1105
1139
|
const sSQL = `EXEC [${(0, config_1.mj_core_schema)()}].spUpdateExistingEntitiesFromSchema @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
1106
|
-
const result = await this.LogSQLAndExecute(
|
|
1140
|
+
const result = await this.LogSQLAndExecute(pool, sSQL, `SQL text to update existing entities from schema`, true);
|
|
1107
1141
|
// result contains the updated entities, and there is a property of each row called Name which has the entity name that was modified
|
|
1108
1142
|
// add these to the modified entity list if they're not already in there
|
|
1109
1143
|
if (result && result.length > 0) {
|
|
@@ -1125,10 +1159,10 @@ class ManageMetadataBase {
|
|
|
1125
1159
|
// now make sure that each of these entity names is in the modified entity list
|
|
1126
1160
|
ManageMetadataBase._modifiedEntityList = ManageMetadataBase._modifiedEntityList.concat(newlyModifiedEntityNames);
|
|
1127
1161
|
}
|
|
1128
|
-
async updateExistingEntityFieldsFromSchema(
|
|
1162
|
+
async updateExistingEntityFieldsFromSchema(pool, excludeSchemas) {
|
|
1129
1163
|
try {
|
|
1130
1164
|
const sSQL = `EXEC [${(0, config_1.mj_core_schema)()}].spUpdateExistingEntityFieldsFromSchema @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
1131
|
-
const result = await this.LogSQLAndExecute(
|
|
1165
|
+
const result = await this.LogSQLAndExecute(pool, sSQL, `SQL text to update existing entity fields from schema`, true);
|
|
1132
1166
|
// result contains the updated entity fields
|
|
1133
1167
|
// there is a field in there called EntityName. Get a distinct list of entity names from this and add them
|
|
1134
1168
|
// to the modified entity list if they're not already in there
|
|
@@ -1142,10 +1176,10 @@ class ManageMetadataBase {
|
|
|
1142
1176
|
return false;
|
|
1143
1177
|
}
|
|
1144
1178
|
}
|
|
1145
|
-
async deleteUnneededEntityFields(
|
|
1179
|
+
async deleteUnneededEntityFields(pool, excludeSchemas) {
|
|
1146
1180
|
try {
|
|
1147
1181
|
const sSQL = `EXEC [${(0, config_1.mj_core_schema)()}].spDeleteUnneededEntityFields @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
1148
|
-
const result = await this.LogSQLAndExecute(
|
|
1182
|
+
const result = await this.LogSQLAndExecute(pool, sSQL, `SQL text to delete unneeded entity fields`, true);
|
|
1149
1183
|
// result contains the DELETED entity fields
|
|
1150
1184
|
// there is a field in there called Entity. Get a distinct list of entity names from this and add them
|
|
1151
1185
|
// to the modified entity list if they're not already in there
|
|
@@ -1159,7 +1193,7 @@ class ManageMetadataBase {
|
|
|
1159
1193
|
return false;
|
|
1160
1194
|
}
|
|
1161
1195
|
}
|
|
1162
|
-
async manageEntityFieldValuesAndValidatorFunctions(
|
|
1196
|
+
async manageEntityFieldValuesAndValidatorFunctions(pool, excludeSchemas, currentUser, skipDBUpdate) {
|
|
1163
1197
|
try {
|
|
1164
1198
|
// here we want to get all of the entity fields that have check constraints attached to them. For each field that has a check constraint, we want to
|
|
1165
1199
|
// evaluate it to see if it is a simple series of OR statements or not, if it is a simple series of OR statements, we can parse the possible values
|
|
@@ -1167,11 +1201,14 @@ class ManageMetadataBase {
|
|
|
1167
1201
|
// just ignore it.
|
|
1168
1202
|
const filter = excludeSchemas && excludeSchemas.length > 0 ? ` WHERE SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})` : '';
|
|
1169
1203
|
const sSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFieldsWithCheckConstraints${filter}`;
|
|
1170
|
-
const
|
|
1204
|
+
const resultResult = await pool.request().query(sSQL);
|
|
1205
|
+
const result = resultResult.recordset;
|
|
1171
1206
|
const efvSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue`;
|
|
1172
|
-
const
|
|
1207
|
+
const allEntityFieldValuesResult = await pool.request().query(efvSQL);
|
|
1208
|
+
const allEntityFieldValues = allEntityFieldValuesResult.recordset;
|
|
1173
1209
|
const efSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFields ORDER BY EntityID, Sequence`;
|
|
1174
|
-
const
|
|
1210
|
+
const allEntityFieldsResult = await pool.request().query(efSQL);
|
|
1211
|
+
const allEntityFields = allEntityFieldsResult.recordset;
|
|
1175
1212
|
const generationPromises = [];
|
|
1176
1213
|
const columnLevelResults = result.filter((r) => r.EntityFieldID); // get the column level constraints
|
|
1177
1214
|
const tableLevelResults = result.filter((r) => !r.EntityFieldID); // get the table level constraints
|
|
@@ -1186,14 +1223,15 @@ class ManageMetadataBase {
|
|
|
1186
1223
|
// 1st, flip the order of parsedValues because they come out in reverse order from SQL Server
|
|
1187
1224
|
parsedValues.reverse();
|
|
1188
1225
|
// we have parsed values from the check constraint, so sync them with the entity field values
|
|
1189
|
-
await this.syncEntityFieldValues(
|
|
1226
|
+
await this.syncEntityFieldValues(pool, r.EntityFieldID, parsedValues, allEntityFieldValues);
|
|
1190
1227
|
// finally, make sure the ValueListType column within the EntityField table is set to "List" because for check constraints we only allow the values specified in the list.
|
|
1191
1228
|
// check to see if the ValueListType is already set to "List", if not, update it
|
|
1192
1229
|
const sSQLCheck = `SELECT ValueListType FROM [${(0, config_1.mj_core_schema)()}].EntityField WHERE ID='${r.EntityFieldID}'`;
|
|
1193
|
-
const
|
|
1230
|
+
const checkResultResult = await pool.request().query(sSQLCheck);
|
|
1231
|
+
const checkResult = checkResultResult.recordset;
|
|
1194
1232
|
if (checkResult && checkResult.length > 0 && checkResult[0].ValueListType.trim().toLowerCase() !== 'list') {
|
|
1195
1233
|
const sSQL = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ValueListType='List' WHERE ID='${r.EntityFieldID}'`;
|
|
1196
|
-
await this.LogSQLAndExecute(
|
|
1234
|
+
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update ValueListType for entity field ID ${r.EntityFieldID}`);
|
|
1197
1235
|
}
|
|
1198
1236
|
}
|
|
1199
1237
|
else {
|
|
@@ -1232,13 +1270,13 @@ class ManageMetadataBase {
|
|
|
1232
1270
|
}
|
|
1233
1271
|
/**
|
|
1234
1272
|
* This method will load all generated code from the database - this is intended to be used when you are bypassing managing the metadata.
|
|
1235
|
-
* @param
|
|
1273
|
+
* @param pool
|
|
1236
1274
|
* @param currentUser
|
|
1237
1275
|
*/
|
|
1238
|
-
async loadGeneratedCode(
|
|
1276
|
+
async loadGeneratedCode(pool, currentUser) {
|
|
1239
1277
|
try {
|
|
1240
1278
|
// right now we're just doing validator functions which are handled here
|
|
1241
|
-
return await this.manageEntityFieldValuesAndValidatorFunctions(
|
|
1279
|
+
return await this.manageEntityFieldValuesAndValidatorFunctions(pool, [], currentUser, true);
|
|
1242
1280
|
}
|
|
1243
1281
|
catch (e) {
|
|
1244
1282
|
(0, status_logging_1.logError)(e);
|
|
@@ -1356,7 +1394,9 @@ class ManageMetadataBase {
|
|
|
1356
1394
|
// now, loop through the possible values and add any that are not already in the database
|
|
1357
1395
|
// Step 1: for any existing value that is NOT in the list of possible Values, delete it
|
|
1358
1396
|
let numRemoved = 0;
|
|
1359
|
-
|
|
1397
|
+
const transaction = new sql.Transaction(ds);
|
|
1398
|
+
await transaction.begin();
|
|
1399
|
+
try {
|
|
1360
1400
|
for (const ev of existingValues) {
|
|
1361
1401
|
if (!possibleValues.find(v => v === ev.Value)) {
|
|
1362
1402
|
// delete the value from the database
|
|
@@ -1389,7 +1429,12 @@ class ManageMetadataBase {
|
|
|
1389
1429
|
numUpdated++;
|
|
1390
1430
|
}
|
|
1391
1431
|
}
|
|
1392
|
-
|
|
1432
|
+
await transaction.commit();
|
|
1433
|
+
}
|
|
1434
|
+
catch (e) {
|
|
1435
|
+
await transaction.rollback();
|
|
1436
|
+
throw e;
|
|
1437
|
+
}
|
|
1393
1438
|
return true;
|
|
1394
1439
|
}
|
|
1395
1440
|
catch (e) {
|
|
@@ -1454,19 +1499,27 @@ class ManageMetadataBase {
|
|
|
1454
1499
|
(sExcludeSchemas.length > 0 ? (sExcludeTables.length > 0 ? ` AND ` : ``) + '(' + sExcludeSchemas + ')' : '');
|
|
1455
1500
|
return sWhere;
|
|
1456
1501
|
}
|
|
1457
|
-
async createNewEntities(
|
|
1502
|
+
async createNewEntities(pool) {
|
|
1458
1503
|
try {
|
|
1459
1504
|
const sSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwSQLTablesAndEntities WHERE EntityID IS NULL ` + this.createExcludeTablesAndSchemasFilter('');
|
|
1460
|
-
const
|
|
1505
|
+
const newEntitiesResult = await pool.request().query(sSQL);
|
|
1506
|
+
const newEntities = newEntitiesResult.recordset;
|
|
1461
1507
|
if (newEntities && newEntities.length > 0) {
|
|
1462
1508
|
const md = new core_1.Metadata();
|
|
1463
|
-
|
|
1509
|
+
const transaction = new sql.Transaction(pool);
|
|
1510
|
+
await transaction.begin();
|
|
1511
|
+
try {
|
|
1464
1512
|
// wrap in a transaction so we get all of it or none of it
|
|
1465
1513
|
for (let i = 0; i < newEntities.length; ++i) {
|
|
1466
1514
|
// process each of the new entities
|
|
1467
|
-
await this.createNewEntity(
|
|
1515
|
+
await this.createNewEntity(pool, newEntities[i], md);
|
|
1468
1516
|
}
|
|
1469
|
-
|
|
1517
|
+
await transaction.commit();
|
|
1518
|
+
}
|
|
1519
|
+
catch (e) {
|
|
1520
|
+
await transaction.rollback();
|
|
1521
|
+
throw e;
|
|
1522
|
+
}
|
|
1470
1523
|
if (ManageMetadataBase.newEntityList.length > 0) {
|
|
1471
1524
|
// only do this if we actually created new entities
|
|
1472
1525
|
(0, core_1.LogStatus)(` Done creating entities, refreshing metadata to reflect new entities...`);
|
|
@@ -1487,7 +1540,8 @@ class ManageMetadataBase {
|
|
|
1487
1540
|
// validate all of these factors by getting the sql from SQL Server and check the result, if failure, shouldCreate=false and generate validation message, otherwise return empty validation message and true for shouldCreate.
|
|
1488
1541
|
const query = `EXEC ${core_1.Metadata.Provider.ConfigData.MJCoreSchemaName}.spGetPrimaryKeyForTable @TableName='${newEntity.TableName}', @SchemaName='${newEntity.SchemaName}'`;
|
|
1489
1542
|
try {
|
|
1490
|
-
const
|
|
1543
|
+
const resultResult = await ds.request().query(query);
|
|
1544
|
+
const result = resultResult.recordset;
|
|
1491
1545
|
if (result.length === 0) {
|
|
1492
1546
|
return { shouldCreate: false, validationMessage: "No primary key found" };
|
|
1493
1547
|
}
|
|
@@ -1580,9 +1634,9 @@ class ManageMetadataBase {
|
|
|
1580
1634
|
createNewUUID() {
|
|
1581
1635
|
return (0, uuid_1.v4)();
|
|
1582
1636
|
}
|
|
1583
|
-
async createNewEntity(
|
|
1637
|
+
async createNewEntity(pool, newEntity, md) {
|
|
1584
1638
|
try {
|
|
1585
|
-
const { shouldCreate, validationMessage } = await this.shouldCreateNewEntity(
|
|
1639
|
+
const { shouldCreate, validationMessage } = await this.shouldCreateNewEntity(pool, newEntity);
|
|
1586
1640
|
if (shouldCreate) {
|
|
1587
1641
|
// process a single new entity
|
|
1588
1642
|
let newEntityName = await this.createNewEntityName(newEntity);
|
|
@@ -1596,10 +1650,10 @@ class ManageMetadataBase {
|
|
|
1596
1650
|
newEntityName = newEntityName + suffix;
|
|
1597
1651
|
(0, core_1.LogError)(` >>>> WARNING: Entity name already exists, so using ${newEntityName} instead. If you did not intend for this, please rename the ${newEntity.SchemaName}.${newEntity.TableName} table in the database.`);
|
|
1598
1652
|
}
|
|
1599
|
-
const isNewSchema = await this.isSchemaNew(
|
|
1653
|
+
const isNewSchema = await this.isSchemaNew(pool, newEntity.SchemaName);
|
|
1600
1654
|
const newEntityID = this.createNewUUID();
|
|
1601
1655
|
const sSQLInsert = this.createNewEntityInsertSQL(newEntityID, newEntityName, newEntity, suffix);
|
|
1602
|
-
await this.LogSQLAndExecute(
|
|
1656
|
+
await this.LogSQLAndExecute(pool, sSQLInsert, `SQL generated to create new entity ${newEntityName}`);
|
|
1603
1657
|
// if we get here we created a new entity safely, otherwise we get exception
|
|
1604
1658
|
// add it to the new entity list
|
|
1605
1659
|
ManageMetadataBase.newEntityList.push(newEntityName);
|
|
@@ -1609,11 +1663,11 @@ class ManageMetadataBase {
|
|
|
1609
1663
|
if (isNewSchema && config_1.configInfo.newSchemaDefaults.CreateNewApplicationWithSchemaName) {
|
|
1610
1664
|
// new schema and config option is to create a new application from the schema name so do that
|
|
1611
1665
|
// check to see if the app already exists
|
|
1612
|
-
apps = await this.getApplicationIDForSchema(
|
|
1666
|
+
apps = await this.getApplicationIDForSchema(pool, newEntity.SchemaName);
|
|
1613
1667
|
if (!apps || apps.length === 0) {
|
|
1614
1668
|
// doesn't already exist, so create it
|
|
1615
1669
|
const appUUID = this.createNewUUID();
|
|
1616
|
-
const newAppID = await this.createNewApplication(
|
|
1670
|
+
const newAppID = await this.createNewApplication(pool, appUUID, newEntity.SchemaName, newEntity.SchemaName);
|
|
1617
1671
|
if (newAppID) {
|
|
1618
1672
|
apps = [newAppID];
|
|
1619
1673
|
}
|
|
@@ -1626,7 +1680,7 @@ class ManageMetadataBase {
|
|
|
1626
1680
|
}
|
|
1627
1681
|
else {
|
|
1628
1682
|
// not a new schema, attempt to look up the application for this schema
|
|
1629
|
-
apps = await this.getApplicationIDForSchema(
|
|
1683
|
+
apps = await this.getApplicationIDForSchema(pool, newEntity.SchemaName);
|
|
1630
1684
|
}
|
|
1631
1685
|
if (apps && apps.length > 0) {
|
|
1632
1686
|
if (config_1.configInfo.newEntityDefaults.AddToApplicationWithSchemaName) {
|
|
@@ -1635,7 +1689,7 @@ class ManageMetadataBase {
|
|
|
1635
1689
|
const sSQLInsertApplicationEntity = `INSERT INTO ${(0, config_1.mj_core_schema)()}.ApplicationEntity
|
|
1636
1690
|
(ApplicationID, EntityID, Sequence) VALUES
|
|
1637
1691
|
('${appUUID}', '${newEntityID}', (SELECT ISNULL(MAX(Sequence),0)+1 FROM ${(0, config_1.mj_core_schema)()}.ApplicationEntity WHERE ApplicationID = '${appUUID}'))`;
|
|
1638
|
-
await this.LogSQLAndExecute(
|
|
1692
|
+
await this.LogSQLAndExecute(pool, sSQLInsertApplicationEntity, `SQL generated to add new entity ${newEntityName} to application ID: '${appUUID}'`);
|
|
1639
1693
|
}
|
|
1640
1694
|
}
|
|
1641
1695
|
else {
|
|
@@ -1656,7 +1710,7 @@ class ManageMetadataBase {
|
|
|
1656
1710
|
const sSQLInsertPermission = `INSERT INTO ${(0, config_1.mj_core_schema)()}.EntityPermission
|
|
1657
1711
|
(EntityID, RoleID, CanRead, CanCreate, CanUpdate, CanDelete) VALUES
|
|
1658
1712
|
('${newEntityID}', '${RoleID}', ${p.CanRead ? 1 : 0}, ${p.CanCreate ? 1 : 0}, ${p.CanUpdate ? 1 : 0}, ${p.CanDelete ? 1 : 0})`;
|
|
1659
|
-
await this.LogSQLAndExecute(
|
|
1713
|
+
await this.LogSQLAndExecute(pool, sSQLInsertPermission, `SQL generated to add new permission for entity ${newEntityName} for role ${p.RoleName}`);
|
|
1660
1714
|
}
|
|
1661
1715
|
else
|
|
1662
1716
|
(0, core_1.LogError)(` >>>> ERROR: Unable to find Role ID for role ${p.RoleName} to add permissions for new entity ${newEntityName}`);
|
|
@@ -1673,16 +1727,17 @@ class ManageMetadataBase {
|
|
|
1673
1727
|
(0, core_1.LogError)(`Failed to create new entity ${newEntity?.TableName}`);
|
|
1674
1728
|
}
|
|
1675
1729
|
}
|
|
1676
|
-
async isSchemaNew(
|
|
1730
|
+
async isSchemaNew(pool, schemaName) {
|
|
1677
1731
|
// check to see if there are any entities in the db with this schema name
|
|
1678
1732
|
const sSQL = `SELECT COUNT(*) AS Count FROM [${(0, config_1.mj_core_schema)()}].Entity WHERE SchemaName = '${schemaName}'`;
|
|
1679
|
-
const
|
|
1733
|
+
const resultResult = await pool.request().query(sSQL);
|
|
1734
|
+
const result = resultResult.recordset;
|
|
1680
1735
|
return result && result.length > 0 ? result[0].Count === 0 : true;
|
|
1681
1736
|
}
|
|
1682
|
-
async createNewApplication(
|
|
1737
|
+
async createNewApplication(pool, appID, appName, schemaName) {
|
|
1683
1738
|
try {
|
|
1684
1739
|
const sSQL = "INSERT INTO [" + (0, config_1.mj_core_schema)() + "].Application (ID, Name, Description, SchemaAutoAddNewEntities) VALUES ('" + appID + "', '" + appName + "', 'Generated for schema', '" + schemaName + "')";
|
|
1685
|
-
await this.LogSQLAndExecute(
|
|
1740
|
+
await this.LogSQLAndExecute(pool, sSQL, `SQL generated to create new application ${appName}`);
|
|
1686
1741
|
return appID; // if we get here, we successfully created the application, so return the ID
|
|
1687
1742
|
}
|
|
1688
1743
|
catch (e) {
|
|
@@ -1690,15 +1745,17 @@ class ManageMetadataBase {
|
|
|
1690
1745
|
return null; // if we get here, we failed to create the application
|
|
1691
1746
|
}
|
|
1692
1747
|
}
|
|
1693
|
-
async applicationExists(
|
|
1748
|
+
async applicationExists(pool, applicationName) {
|
|
1694
1749
|
const sSQL = `SELECT ID FROM [${(0, config_1.mj_core_schema)()}].Application WHERE Name = '${applicationName}'`;
|
|
1695
|
-
const
|
|
1750
|
+
const resultResult = await pool.request().query(sSQL);
|
|
1751
|
+
const result = resultResult.recordset;
|
|
1696
1752
|
return result && result.length > 0 ? result[0].ID.length > 0 : false;
|
|
1697
1753
|
}
|
|
1698
|
-
async getApplicationIDForSchema(
|
|
1754
|
+
async getApplicationIDForSchema(pool, schemaName) {
|
|
1699
1755
|
// get all the apps each time from DB as we might be adding, don't use Metadata here for that reason
|
|
1700
1756
|
const sSQL = `SELECT ID, Name, SchemaAutoAddNewEntities FROM [${(0, config_1.mj_core_schema)()}].vwApplications`;
|
|
1701
|
-
const
|
|
1757
|
+
const resultResult = await pool.request().query(sSQL);
|
|
1758
|
+
const result = resultResult.recordset;
|
|
1702
1759
|
if (!result || result.length === 0) {
|
|
1703
1760
|
// no applications found, return null
|
|
1704
1761
|
return null;
|
|
@@ -1761,21 +1818,18 @@ class ManageMetadataBase {
|
|
|
1761
1818
|
return sSQLInsert;
|
|
1762
1819
|
}
|
|
1763
1820
|
/**
|
|
1764
|
-
* Executes the given SQL query using the given
|
|
1821
|
+
* Executes the given SQL query using the given ConnectionPool object.
|
|
1765
1822
|
* If the appendToLogFile parameter is true, the query will also be appended to the log file.
|
|
1766
1823
|
* Note that in order to append to the log file, ManageMetadataBase.manageMetaDataLogging must be called first.
|
|
1767
|
-
* @param
|
|
1824
|
+
* @param pool - The ConnectionPool object to use to execute the query.
|
|
1768
1825
|
* @param query - The SQL query to execute.
|
|
1769
1826
|
* @param description - A description of the query to append to the log file.
|
|
1770
1827
|
* @param isRecurringScript - if set to true tells the logger that the provided SQL represents a recurring script meaning it is something that is executed, generally, for all CodeGen runs. In these cases, the Config settings can result in omitting these recurring scripts from being logged because the configuration environment may have those recurring scripts already set to run after all run-specific migrations get run.
|
|
1771
1828
|
* @returns - The result of the query execution.
|
|
1772
1829
|
*/
|
|
1773
|
-
async LogSQLAndExecute(
|
|
1774
|
-
return await sql_logging_1.SQLLogging.LogSQLAndExecute(
|
|
1830
|
+
async LogSQLAndExecute(pool, query, description, isRecurringScript = false) {
|
|
1831
|
+
return await sql_logging_1.SQLLogging.LogSQLAndExecute(pool, query, description, isRecurringScript);
|
|
1775
1832
|
}
|
|
1776
1833
|
}
|
|
1777
1834
|
exports.ManageMetadataBase = ManageMetadataBase;
|
|
1778
|
-
ManageMetadataBase._newEntityList = [];
|
|
1779
|
-
ManageMetadataBase._modifiedEntityList = [];
|
|
1780
|
-
ManageMetadataBase._generatedValidators = [];
|
|
1781
1835
|
//# sourceMappingURL=manage-metadata.js.map
|