@memberjunction/codegen-lib 2.111.0 → 2.112.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 +1 -1
- package/dist/Angular/angular-codegen.d.ts.map +1 -1
- package/dist/Angular/angular-codegen.js +119 -80
- package/dist/Angular/angular-codegen.js.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts +1 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.js +12 -12
- package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/related-entity-components.d.ts +1 -1
- package/dist/Angular/related-entity-components.d.ts.map +1 -1
- package/dist/Angular/related-entity-components.js +14 -14
- package/dist/Angular/related-entity-components.js.map +1 -1
- package/dist/Config/config.d.ts.map +1 -1
- package/dist/Config/config.js +3 -3
- package/dist/Config/config.js.map +1 -1
- package/dist/Database/dbSchema.d.ts +1 -1
- package/dist/Database/dbSchema.d.ts.map +1 -1
- package/dist/Database/dbSchema.js +14 -10
- package/dist/Database/dbSchema.js.map +1 -1
- package/dist/Database/manage-metadata.d.ts +3 -3
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +167 -139
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/sql.d.ts +1 -1
- package/dist/Database/sql.d.ts.map +1 -1
- package/dist/Database/sql.js +17 -17
- package/dist/Database/sql.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +2 -2
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +128 -119
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Misc/action_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/action_subclasses_codegen.js +17 -17
- package/dist/Misc/action_subclasses_codegen.js.map +1 -1
- package/dist/Misc/advanced_generation.d.ts +2 -2
- package/dist/Misc/advanced_generation.d.ts.map +1 -1
- package/dist/Misc/advanced_generation.js +29 -29
- package/dist/Misc/advanced_generation.js.map +1 -1
- package/dist/Misc/createNewUser.d.ts +1 -1
- package/dist/Misc/createNewUser.d.ts.map +1 -1
- package/dist/Misc/createNewUser.js +30 -30
- package/dist/Misc/createNewUser.js.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.d.ts +1 -1
- package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.js +56 -37
- package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
- package/dist/Misc/graphql_server_codegen.d.ts +1 -1
- package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
- package/dist/Misc/graphql_server_codegen.js +15 -17
- package/dist/Misc/graphql_server_codegen.js.map +1 -1
- package/dist/Misc/status_logging.d.ts +2 -2
- package/dist/Misc/status_logging.d.ts.map +1 -1
- package/dist/Misc/status_logging.js +22 -22
- package/dist/Misc/status_logging.js.map +1 -1
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +4 -4
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +9 -10
|
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.SQLCodeGenBase = exports.SPType = void 0;
|
|
30
|
-
const
|
|
30
|
+
const global_1 = require("@memberjunction/global");
|
|
31
31
|
const status_logging_1 = require("../Misc/status_logging");
|
|
32
32
|
const fs = __importStar(require("fs"));
|
|
33
33
|
const path_1 = __importDefault(require("path"));
|
|
@@ -36,7 +36,7 @@ const config_1 = require("../Config/config");
|
|
|
36
36
|
const manage_metadata_1 = require("./manage-metadata");
|
|
37
37
|
const sqlserver_dataprovider_1 = require("@memberjunction/sqlserver-dataprovider");
|
|
38
38
|
const util_1 = require("../Misc/util");
|
|
39
|
-
const
|
|
39
|
+
const global_2 = require("@memberjunction/global");
|
|
40
40
|
const sql_logging_1 = require("../Misc/sql_logging");
|
|
41
41
|
const temp_batch_file_1 = require("../Misc/temp_batch_file");
|
|
42
42
|
exports.SPType = {
|
|
@@ -50,7 +50,7 @@ exports.SPType = {
|
|
|
50
50
|
* of that abstract base class and other databases will be sub-classes of the abstract base class as well.
|
|
51
51
|
*/
|
|
52
52
|
class SQLCodeGenBase {
|
|
53
|
-
_sqlUtilityObject =
|
|
53
|
+
_sqlUtilityObject = global_2.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
|
|
54
54
|
get SQLUtilityObject() {
|
|
55
55
|
return this._sqlUtilityObject;
|
|
56
56
|
}
|
|
@@ -105,7 +105,7 @@ class SQLCodeGenBase {
|
|
|
105
105
|
// objects like spCreate for a given entity might reference the vw for that entity
|
|
106
106
|
(0, status_logging_1.startSpinner)('Running custom SQL scripts...');
|
|
107
107
|
const startTime = new Date();
|
|
108
|
-
if (!await this.runCustomSQLScripts(pool, 'before-sql')) {
|
|
108
|
+
if (!(await this.runCustomSQLScripts(pool, 'before-sql'))) {
|
|
109
109
|
(0, status_logging_1.failSpinner)('Failed to run custom SQL scripts');
|
|
110
110
|
return false;
|
|
111
111
|
}
|
|
@@ -113,12 +113,12 @@ class SQLCodeGenBase {
|
|
|
113
113
|
// ALWAYS use the first filter where we only include entities that have IncludeInAPI = 1
|
|
114
114
|
// Sort entities by name for deterministic processing order (workaround until MJCore fix in issue #1436)
|
|
115
115
|
const sortedEntities = entities.sort((a, b) => a.Name.localeCompare(b.Name));
|
|
116
|
-
const baselineEntities = sortedEntities.filter(e => e.IncludeInAPI);
|
|
117
|
-
const includedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) === undefined); //only include entities that are NOT in the excludeSchemas list
|
|
118
|
-
const excludedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) !== undefined); //only include entities that ARE in the excludeSchemas list in this array
|
|
116
|
+
const baselineEntities = sortedEntities.filter((e) => e.IncludeInAPI);
|
|
117
|
+
const includedEntities = baselineEntities.filter((e) => config_1.configInfo.excludeSchemas.find((s) => s.toLowerCase() === e.SchemaName.toLowerCase()) === undefined); //only include entities that are NOT in the excludeSchemas list
|
|
118
|
+
const excludedEntities = baselineEntities.filter((e) => config_1.configInfo.excludeSchemas.find((s) => s.toLowerCase() === e.SchemaName.toLowerCase()) !== undefined); //only include entities that ARE in the excludeSchemas list in this array
|
|
119
119
|
// Initialize temp batch files for each schema
|
|
120
120
|
// These will be populated as SQL is generated and will be used for actual execution
|
|
121
|
-
const schemas = Array.from(new Set(baselineEntities.map(e => e.SchemaName)));
|
|
121
|
+
const schemas = Array.from(new Set(baselineEntities.map((e) => e.SchemaName)));
|
|
122
122
|
temp_batch_file_1.TempBatchFile.initialize(directory, schemas);
|
|
123
123
|
// STEP 1.5 - Check for cascade delete dependencies that require regeneration
|
|
124
124
|
(0, status_logging_1.startSpinner)('Analyzing cascade delete dependencies...');
|
|
@@ -132,10 +132,10 @@ class SQLCodeGenBase {
|
|
|
132
132
|
(0, status_logging_1.startSpinner)(`Generating SQL for ${includedEntities.length} entities...`);
|
|
133
133
|
const step2StartTime = new Date();
|
|
134
134
|
// First, separate entities that need cascade delete regeneration from others
|
|
135
|
-
const entitiesWithoutCascadeRegeneration = includedEntities.filter(e => !this.entitiesNeedingDeleteSPRegeneration.has(e.ID));
|
|
135
|
+
const entitiesWithoutCascadeRegeneration = includedEntities.filter((e) => !this.entitiesNeedingDeleteSPRegeneration.has(e.ID));
|
|
136
136
|
const entitiesForCascadeRegeneration = this.orderedEntitiesForDeleteSPRegeneration
|
|
137
|
-
.map(id => includedEntities.find(e => e.ID === id))
|
|
138
|
-
.filter(e => e !== undefined);
|
|
137
|
+
.map((id) => includedEntities.find((e) => e.ID === id))
|
|
138
|
+
.filter((e) => e !== undefined);
|
|
139
139
|
// Generate SQL for entities that don't need cascade delete regeneration
|
|
140
140
|
const genResult = await this.generateAndExecuteEntitySQLToSeparateFiles({
|
|
141
141
|
pool,
|
|
@@ -145,7 +145,7 @@ class SQLCodeGenBase {
|
|
|
145
145
|
skipExecution: true, // skip execution because we execute it all in a giant batch below
|
|
146
146
|
writeFiles: true,
|
|
147
147
|
batchSize: 5,
|
|
148
|
-
enableSQLLoggingForNewOrModifiedEntities: true
|
|
148
|
+
enableSQLLoggingForNewOrModifiedEntities: true,
|
|
149
149
|
}); // enable sql logging for NEW entities....
|
|
150
150
|
if (!genResult.Success) {
|
|
151
151
|
(0, status_logging_1.failSpinner)('Failed to generate entity SQL files');
|
|
@@ -162,7 +162,7 @@ class SQLCodeGenBase {
|
|
|
162
162
|
skipExecution: true,
|
|
163
163
|
writeFiles: true,
|
|
164
164
|
batchSize: 1, // Process sequentially to maintain dependency order
|
|
165
|
-
enableSQLLoggingForNewOrModifiedEntities: true
|
|
165
|
+
enableSQLLoggingForNewOrModifiedEntities: true,
|
|
166
166
|
});
|
|
167
167
|
if (!cascadeGenResult.Success) {
|
|
168
168
|
(0, status_logging_1.failSpinner)('Failed to regenerate cascade delete SPs');
|
|
@@ -180,7 +180,7 @@ class SQLCodeGenBase {
|
|
|
180
180
|
skipExecution: true, // skip execution because we execute it all in a giant batch below
|
|
181
181
|
batchSize: 5,
|
|
182
182
|
writeFiles: true,
|
|
183
|
-
enableSQLLoggingForNewOrModifiedEntities: false /*don't log this stuff, it is just permissions for excluded entities
|
|
183
|
+
enableSQLLoggingForNewOrModifiedEntities: false /*don't log this stuff, it is just permissions for excluded entities*/,
|
|
184
184
|
});
|
|
185
185
|
if (!genResult2.Success) {
|
|
186
186
|
(0, status_logging_1.failSpinner)('Failed to generate permissions for excluded entities');
|
|
@@ -216,11 +216,11 @@ class SQLCodeGenBase {
|
|
|
216
216
|
}
|
|
217
217
|
const step2eEndTime = new Date();
|
|
218
218
|
(0, status_logging_1.succeedSpinner)(`SQL execution completed (${(step2eEndTime.getTime() - step2eStartTime.getTime()) / 1000}s)`);
|
|
219
|
-
const manageMD =
|
|
219
|
+
const manageMD = global_2.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
|
|
220
220
|
// STEP 3 - re-run the process to manage entity fields since the Step 1 and 2 above might have resulted in differences in base view columns compared to what we had at first
|
|
221
221
|
// we CAN skip the entity field values part because that wouldn't change from the first time we ran it
|
|
222
222
|
(0, status_logging_1.startSpinner)('Managing entity fields metadata...');
|
|
223
|
-
if (!await manageMD.manageEntityFields(pool, config_1.configInfo.excludeSchemas, true, true, currentUser)) {
|
|
223
|
+
if (!(await manageMD.manageEntityFields(pool, config_1.configInfo.excludeSchemas, true, true, currentUser))) {
|
|
224
224
|
(0, status_logging_1.failSpinner)('Failed to manage entity fields');
|
|
225
225
|
return false;
|
|
226
226
|
}
|
|
@@ -229,7 +229,7 @@ class SQLCodeGenBase {
|
|
|
229
229
|
// STEP 4- Apply permissions, executing all .permissions files
|
|
230
230
|
(0, status_logging_1.startSpinner)('Applying permissions...');
|
|
231
231
|
const step4StartTime = new Date();
|
|
232
|
-
if (!await this.applyPermissions(pool, directory, baselineEntities)) {
|
|
232
|
+
if (!(await this.applyPermissions(pool, directory, baselineEntities))) {
|
|
233
233
|
(0, status_logging_1.failSpinner)('Failed to apply permissions');
|
|
234
234
|
return false;
|
|
235
235
|
}
|
|
@@ -237,14 +237,14 @@ class SQLCodeGenBase {
|
|
|
237
237
|
// STEP 5 - execute any custom SQL scripts that should run afterwards
|
|
238
238
|
(0, status_logging_1.startSpinner)('Running post-generation SQL scripts...');
|
|
239
239
|
const step5StartTime = new Date();
|
|
240
|
-
if (!await this.runCustomSQLScripts(pool, 'after-sql')) {
|
|
240
|
+
if (!(await this.runCustomSQLScripts(pool, 'after-sql'))) {
|
|
241
241
|
(0, status_logging_1.failSpinner)('Failed to run post-generation SQL scripts');
|
|
242
242
|
return false;
|
|
243
243
|
}
|
|
244
244
|
(0, status_logging_1.succeedSpinner)(`Post-generation scripts completed (${(new Date().getTime() - step5StartTime.getTime()) / 1000}s)`);
|
|
245
|
-
(0, status_logging_1.succeedSpinner)(`CodeGen completed successfully (${(
|
|
245
|
+
(0, status_logging_1.succeedSpinner)(`CodeGen completed successfully (${(new Date().getTime() - startTime.getTime()) / 1000}s total)`);
|
|
246
246
|
// now - we need to tell our metadata object to refresh itself
|
|
247
|
-
const md = new
|
|
247
|
+
const md = new global_1.Metadata();
|
|
248
248
|
await md.Refresh();
|
|
249
249
|
return true;
|
|
250
250
|
}
|
|
@@ -262,7 +262,7 @@ class SQLCodeGenBase {
|
|
|
262
262
|
if (scripts) {
|
|
263
263
|
for (let i = 0; i < scripts.length; ++i) {
|
|
264
264
|
const s = scripts[i];
|
|
265
|
-
if (!await this.SQLUtilityObject.executeSQLFile(s.scriptFile)) {
|
|
265
|
+
if (!(await this.SQLUtilityObject.executeSQLFile(s.scriptFile))) {
|
|
266
266
|
(0, status_logging_1.logError)(`Error executing custom '${when}' SQL script ${s.scriptFile}`);
|
|
267
267
|
bSuccess = false; // keep going if we have more scripts, but make sure we return false
|
|
268
268
|
}
|
|
@@ -335,7 +335,7 @@ class SQLCodeGenBase {
|
|
|
335
335
|
for (let i = 0; i < totalEntities; i += options.batchSize) {
|
|
336
336
|
const batch = options.entities.slice(i, i + options.batchSize);
|
|
337
337
|
const promises = batch.map(async (e) => {
|
|
338
|
-
const pkeyField = e.Fields.find(f => f.IsPrimaryKey);
|
|
338
|
+
const pkeyField = e.Fields.find((f) => f.IsPrimaryKey);
|
|
339
339
|
if (!pkeyField) {
|
|
340
340
|
(0, status_logging_1.logError)(`SKIPPING ENTITY: Entity ${e.Name}, because it does not have a primary key field defined. A table must have a primary key defined to quality to be a MemberJunction entity`);
|
|
341
341
|
return { Success: false, Files: [] };
|
|
@@ -347,11 +347,11 @@ class SQLCodeGenBase {
|
|
|
347
347
|
onlyPermissions: options.onlyPermissions,
|
|
348
348
|
writeFiles: options.writeFiles,
|
|
349
349
|
skipExecution: options.skipExecution,
|
|
350
|
-
enableSQLLoggingForNewOrModifiedEntities: options.enableSQLLoggingForNewOrModifiedEntities
|
|
350
|
+
enableSQLLoggingForNewOrModifiedEntities: options.enableSQLLoggingForNewOrModifiedEntities,
|
|
351
351
|
});
|
|
352
352
|
});
|
|
353
353
|
const results = await Promise.all(promises);
|
|
354
|
-
results.forEach(r => {
|
|
354
|
+
results.forEach((r) => {
|
|
355
355
|
if (!r.Success)
|
|
356
356
|
bFail = true; // keep going, but will return false at the end
|
|
357
357
|
files.push(...r.Files); // add the files to the main files array
|
|
@@ -367,7 +367,7 @@ class SQLCodeGenBase {
|
|
|
367
367
|
deleteGeneratedEntityFiles(directory, entities) {
|
|
368
368
|
try {
|
|
369
369
|
// for the schemas associated with the specified entities, clean out all the generated files
|
|
370
|
-
const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
|
|
370
|
+
const schemaNames = entities.map((e) => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
|
|
371
371
|
for (const s of schemaNames) {
|
|
372
372
|
const fullPath = path_1.default.join(directory, s);
|
|
373
373
|
// now, within each schema directory, clean out all the generated files
|
|
@@ -381,7 +381,7 @@ class SQLCodeGenBase {
|
|
|
381
381
|
//logMessage(` Directory '${fullPath}' does not exist so no need to delete previously generated SQL`, 'Info');
|
|
382
382
|
}
|
|
383
383
|
if (stats?.isDirectory()) {
|
|
384
|
-
const files = fs.readdirSync(fullPath).filter(f => f.endsWith('.generated.sql') || f.endsWith('.permissions.generated.sql'));
|
|
384
|
+
const files = fs.readdirSync(fullPath).filter((f) => f.endsWith('.generated.sql') || f.endsWith('.permissions.generated.sql'));
|
|
385
385
|
for (const f of files) {
|
|
386
386
|
const filePath = path_1.default.join(fullPath, f);
|
|
387
387
|
fs.unlinkSync(filePath);
|
|
@@ -396,7 +396,7 @@ class SQLCodeGenBase {
|
|
|
396
396
|
createCombinedEntitySQLFiles(directory, entities) {
|
|
397
397
|
// first, get a disinct list of schemanames from the entities
|
|
398
398
|
const files = [];
|
|
399
|
-
const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
|
|
399
|
+
const schemaNames = entities.map((e) => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
|
|
400
400
|
for (const s of schemaNames) {
|
|
401
401
|
// generate the all-entities.sql file and all-entities.permissions.sql file in each schema folder
|
|
402
402
|
const fullPath = path_1.default.join(directory, s);
|
|
@@ -414,8 +414,8 @@ class SQLCodeGenBase {
|
|
|
414
414
|
const { sql, permissionsSQL, files } = await this.generateSingleEntitySQLToSeparateFiles(options); // this creates the files and returns a single string with all the SQL we can then execute
|
|
415
415
|
if (!options.skipExecution) {
|
|
416
416
|
return {
|
|
417
|
-
Success: await this.SQLUtilityObject.executeSQLScript(options.pool, sql +
|
|
418
|
-
Files: files
|
|
417
|
+
Success: await this.SQLUtilityObject.executeSQLScript(options.pool, sql + '\n\nGO\n\n' + permissionsSQL, true), // combine the SQL and permissions and execute it,
|
|
418
|
+
Files: files,
|
|
419
419
|
};
|
|
420
420
|
}
|
|
421
421
|
else
|
|
@@ -431,23 +431,23 @@ class SQLCodeGenBase {
|
|
|
431
431
|
let shouldLog = false;
|
|
432
432
|
if (logSql) {
|
|
433
433
|
// Check if entity is in new or modified lists
|
|
434
|
-
const isNewOrModified = !!manage_metadata_1.ManageMetadataBase.newEntityList.find(e => e === entity.Name) ||
|
|
435
|
-
!!manage_metadata_1.ManageMetadataBase.modifiedEntityList.find(e => e === entity.Name);
|
|
434
|
+
const isNewOrModified = !!manage_metadata_1.ManageMetadataBase.newEntityList.find((e) => e === entity.Name) ||
|
|
435
|
+
!!manage_metadata_1.ManageMetadataBase.modifiedEntityList.find((e) => e === entity.Name);
|
|
436
436
|
// Check if entity is being regenerated due to cascade dependencies
|
|
437
|
-
const isCascadeDependencyRegeneration = description.toLowerCase().includes('spdelete') &&
|
|
438
|
-
this.entitiesNeedingDeleteSPRegeneration.has(entity.ID);
|
|
437
|
+
const isCascadeDependencyRegeneration = description.toLowerCase().includes('spdelete') && this.entitiesNeedingDeleteSPRegeneration.has(entity.ID);
|
|
439
438
|
// Check if force regeneration is enabled for relevant SQL types
|
|
440
|
-
const isForceRegeneration = config_1.configInfo.forceRegeneration?.enabled &&
|
|
441
|
-
(description.toLowerCase().includes('
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
(
|
|
449
|
-
description.toLowerCase().includes('
|
|
450
|
-
|
|
439
|
+
const isForceRegeneration = config_1.configInfo.forceRegeneration?.enabled &&
|
|
440
|
+
((description.toLowerCase().includes('base view') && config_1.configInfo.forceRegeneration.baseViews) ||
|
|
441
|
+
(description.toLowerCase().includes('root id function') && config_1.configInfo.forceRegeneration.baseViews) || // TVFs are part of base view infrastructure
|
|
442
|
+
(description.toLowerCase().includes('spcreate') && config_1.configInfo.forceRegeneration.spCreate) ||
|
|
443
|
+
(description.toLowerCase().includes('spupdate') && config_1.configInfo.forceRegeneration.spUpdate) ||
|
|
444
|
+
(description.toLowerCase().includes('spdelete') && config_1.configInfo.forceRegeneration.spDelete) ||
|
|
445
|
+
(description.toLowerCase().includes('index') && config_1.configInfo.forceRegeneration.indexes) ||
|
|
446
|
+
(description.toLowerCase().includes('full text search') && config_1.configInfo.forceRegeneration.fullTextSearch) ||
|
|
447
|
+
(config_1.configInfo.forceRegeneration.allStoredProcedures &&
|
|
448
|
+
(description.toLowerCase().includes('spcreate') ||
|
|
449
|
+
description.toLowerCase().includes('spupdate') ||
|
|
450
|
+
description.toLowerCase().includes('spdelete'))));
|
|
451
451
|
// Determine if we should log based on entity state and force regeneration settings
|
|
452
452
|
if (isNewOrModified) {
|
|
453
453
|
// Always log new or modified entities
|
|
@@ -458,7 +458,7 @@ class SQLCodeGenBase {
|
|
|
458
458
|
shouldLog = true;
|
|
459
459
|
}
|
|
460
460
|
else if (isForceRegeneration) {
|
|
461
|
-
// For force regeneration, the specific type flags (spCreate, baseViews, etc.)
|
|
461
|
+
// For force regeneration, the specific type flags (spCreate, baseViews, etc.)
|
|
462
462
|
// already filtered this - now we just need to check entity filtering
|
|
463
463
|
if (this.filterEntitiesQualifiedForRegeneration) {
|
|
464
464
|
// Only log if entity is in the qualified list
|
|
@@ -505,9 +505,7 @@ class SQLCodeGenBase {
|
|
|
505
505
|
// BASE VIEW AND RELATED TVFs
|
|
506
506
|
// Only generate if BaseViewGenerated is true (respects custom views where it's false)
|
|
507
507
|
// forceRegeneration.baseViews only forces regeneration of views where BaseViewGenerated=true
|
|
508
|
-
if (!options.onlyPermissions &&
|
|
509
|
-
options.entity.BaseViewGenerated &&
|
|
510
|
-
!options.entity.VirtualEntity) {
|
|
508
|
+
if (!options.onlyPermissions && options.entity.BaseViewGenerated && !options.entity.VirtualEntity) {
|
|
511
509
|
// ROOT ID FUNCTIONS (TVFs)
|
|
512
510
|
// Generate inline Table Value Functions for recursive foreign keys
|
|
513
511
|
// These must be created BEFORE the view that references them
|
|
@@ -515,8 +513,7 @@ class SQLCodeGenBase {
|
|
|
515
513
|
if (recursiveFKs.length > 0) {
|
|
516
514
|
for (const field of recursiveFKs) {
|
|
517
515
|
const functionName = `fn${options.entity.BaseTable}${field.Name}_GetRootID`;
|
|
518
|
-
const s = this.generateSingleEntitySQLFileHeader(options.entity, functionName) +
|
|
519
|
-
this.generateRootIDFunction(options.entity, field);
|
|
516
|
+
const s = this.generateSingleEntitySQLFileHeader(options.entity, functionName) + this.generateRootIDFunction(options.entity, field);
|
|
520
517
|
const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('function', options.entity.SchemaName, functionName, false, true));
|
|
521
518
|
if (options.writeFiles) {
|
|
522
519
|
this.logSQLForNewOrModifiedEntity(options.entity, s, `Root ID Function SQL for ${options.entity.Name}.${field.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
|
|
@@ -528,7 +525,8 @@ class SQLCodeGenBase {
|
|
|
528
525
|
}
|
|
529
526
|
}
|
|
530
527
|
// Generate the base view (which may reference the TVFs created above)
|
|
531
|
-
const s = this.generateSingleEntitySQLFileHeader(options.entity, options.entity.BaseView) +
|
|
528
|
+
const s = this.generateSingleEntitySQLFileHeader(options.entity, options.entity.BaseView) +
|
|
529
|
+
(await this.generateBaseView(options.pool, options.entity));
|
|
532
530
|
const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('view', options.entity.SchemaName, options.entity.BaseView, false, true));
|
|
533
531
|
if (options.writeFiles) {
|
|
534
532
|
this.logSQLForNewOrModifiedEntity(options.entity, s, `Base View SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
|
|
@@ -538,7 +536,8 @@ class SQLCodeGenBase {
|
|
|
538
536
|
sRet += s + '\nGO\n';
|
|
539
537
|
}
|
|
540
538
|
// always generate permissions for the base view
|
|
541
|
-
const s = this.generateSingleEntitySQLFileHeader(options.entity, 'Permissions for ' + options.entity.BaseView) +
|
|
539
|
+
const s = this.generateSingleEntitySQLFileHeader(options.entity, 'Permissions for ' + options.entity.BaseView) +
|
|
540
|
+
this.generateViewPermissions(options.entity);
|
|
542
541
|
if (s.length > 0)
|
|
543
542
|
permissionsSQL += s + '\nGO\n';
|
|
544
543
|
if (options.writeFiles) {
|
|
@@ -618,8 +617,7 @@ class SQLCodeGenBase {
|
|
|
618
617
|
// OR if this entity has cascade delete dependencies that require regeneration
|
|
619
618
|
// forceRegeneration only forces regeneration of SPs where spDeleteGenerated=true
|
|
620
619
|
if (!options.onlyPermissions &&
|
|
621
|
-
(options.entity.spDeleteGenerated ||
|
|
622
|
-
this.entitiesNeedingDeleteSPRegeneration.has(options.entity.ID))) {
|
|
620
|
+
(options.entity.spDeleteGenerated || this.entitiesNeedingDeleteSPRegeneration.has(options.entity.ID))) {
|
|
623
621
|
// generate the delete SP
|
|
624
622
|
if (this.entitiesNeedingDeleteSPRegeneration.has(options.entity.ID)) {
|
|
625
623
|
(0, status_logging_1.logStatus)(` Regenerating ${spName} due to cascade dependency changes`);
|
|
@@ -721,37 +719,37 @@ class SQLCodeGenBase {
|
|
|
721
719
|
let sOutput = '';
|
|
722
720
|
if (entity.BaseViewGenerated && !entity.VirtualEntity)
|
|
723
721
|
// generated the base view (will include permissions)
|
|
724
|
-
sOutput += await this.generateBaseView(pool, entity) + '\n\n';
|
|
722
|
+
sOutput += (await this.generateBaseView(pool, entity)) + '\n\n';
|
|
723
|
+
// still generate the permissions for the view even if a custom view
|
|
725
724
|
else
|
|
726
|
-
// still generate the permissions for the view even if a custom view
|
|
727
725
|
sOutput += this.generateViewPermissions(entity) + '\n\n';
|
|
728
726
|
if (entity.AllowCreateAPI && !entity.VirtualEntity) {
|
|
729
727
|
if (entity.spCreateGenerated)
|
|
730
728
|
// generated SP, will include permissions
|
|
731
729
|
sOutput += this.generateSPCreate(entity) + '\n\n';
|
|
730
|
+
// custom SP, still generate the permissions
|
|
732
731
|
else
|
|
733
|
-
// custom SP, still generate the permissions
|
|
734
732
|
sOutput += this.generateSPPermissions(entity, entity.spCreate, exports.SPType.Create) + '\n\n';
|
|
735
733
|
}
|
|
736
734
|
if (entity.AllowUpdateAPI && !entity.VirtualEntity) {
|
|
737
735
|
if (entity.spUpdateGenerated)
|
|
738
736
|
// generated SP, will include permissions
|
|
739
737
|
sOutput += this.generateSPUpdate(entity) + '\n\n';
|
|
738
|
+
// custom SP, still generate the permissions
|
|
740
739
|
else
|
|
741
|
-
// custom SP, still generate the permissions
|
|
742
740
|
sOutput += this.generateSPPermissions(entity, entity.spUpdate, exports.SPType.Update) + '\n\n';
|
|
743
741
|
}
|
|
744
742
|
if (entity.AllowDeleteAPI && !entity.VirtualEntity) {
|
|
745
743
|
if (entity.spDeleteGenerated)
|
|
746
744
|
// generated SP, will include permissions
|
|
747
745
|
sOutput += this.generateSPDelete(entity) + '\n\n';
|
|
746
|
+
// custom SP, still generate the permissions
|
|
748
747
|
else
|
|
749
|
-
// custom SP, still generate the permissions
|
|
750
748
|
sOutput += this.generateSPPermissions(entity, entity.spDelete, exports.SPType.Delete) + '\n\n';
|
|
751
749
|
}
|
|
752
750
|
// check to see if the entity supports full text search or not
|
|
753
751
|
if (entity.FullTextSearchEnabled) {
|
|
754
|
-
sOutput += await this.generateEntityFullTextSearchSQL(pool, entity) + '\n\n';
|
|
752
|
+
sOutput += (await this.generateEntityFullTextSearchSQL(pool, entity)) + '\n\n';
|
|
755
753
|
}
|
|
756
754
|
return sOutput;
|
|
757
755
|
}
|
|
@@ -771,7 +769,9 @@ class SQLCodeGenBase {
|
|
|
771
769
|
`;
|
|
772
770
|
}
|
|
773
771
|
if (entity.FullTextIndexGenerated) {
|
|
774
|
-
const fullTextFields = entity.Fields.filter(f => f.FullTextSearchEnabled)
|
|
772
|
+
const fullTextFields = entity.Fields.filter((f) => f.FullTextSearchEnabled)
|
|
773
|
+
.map((f) => `${f.Name} LANGUAGE 'English'`)
|
|
774
|
+
.join(', ');
|
|
775
775
|
if (fullTextFields.length === 0)
|
|
776
776
|
throw new Error(`FullTextIndexGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
|
|
777
777
|
// drop and recreate the full text index
|
|
@@ -803,24 +803,28 @@ class SQLCodeGenBase {
|
|
|
803
803
|
GO
|
|
804
804
|
`;
|
|
805
805
|
}
|
|
806
|
-
const functionName = entity.FullTextSearchFunction && entity.FullTextSearchFunction.length > 0
|
|
806
|
+
const functionName = entity.FullTextSearchFunction && entity.FullTextSearchFunction.length > 0
|
|
807
|
+
? entity.FullTextSearchFunction
|
|
808
|
+
: `fnSearch${entity.CodeName}`;
|
|
807
809
|
if (entity.FullTextSearchFunctionGenerated) {
|
|
808
|
-
const fullTextFieldsSimple = entity.Fields.filter(f => f.FullTextSearchEnabled)
|
|
810
|
+
const fullTextFieldsSimple = entity.Fields.filter((f) => f.FullTextSearchEnabled)
|
|
811
|
+
.map((f) => '[' + f.Name + ']')
|
|
812
|
+
.join(', ');
|
|
809
813
|
if (fullTextFieldsSimple.length === 0)
|
|
810
814
|
throw new Error(`FullTextSearchFunctionGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
|
|
811
815
|
if (!entity.FullTextSearchFunction || entity.FullTextSearchFunction.length === 0) {
|
|
812
816
|
// update this in the DB
|
|
813
|
-
const md = new
|
|
817
|
+
const md = new global_1.Metadata();
|
|
814
818
|
const u = sqlserver_dataprovider_1.UserCache.Instance.Users[0];
|
|
815
819
|
if (!u)
|
|
816
820
|
throw new Error('Could not find the first user in the cache, cant generate the full text search function without a user');
|
|
817
821
|
const e = await md.GetEntityObject('Entities', u);
|
|
818
822
|
await e.Load(entity.ID);
|
|
819
823
|
e.FullTextSearchFunction = functionName;
|
|
820
|
-
if (!await e.Save())
|
|
824
|
+
if (!(await e.Save()))
|
|
821
825
|
throw new Error(`Could not update the FullTextSearchFunction for entity ${entity.Name}`);
|
|
822
826
|
}
|
|
823
|
-
const pkeyList = entity.PrimaryKeys.map(pk => '[' + pk.Name + ']').join(', ');
|
|
827
|
+
const pkeyList = entity.PrimaryKeys.map((pk) => '[' + pk.Name + ']').join(', ');
|
|
824
828
|
// drop and recreate the full text search function
|
|
825
829
|
sql += ` -- DROP AND RECREATE THE FULL TEXT SEARCH FUNCTION
|
|
826
830
|
-- Create an inline table-valued function to perform full-text search
|
|
@@ -921,8 +925,7 @@ CREATE INDEX ${indexName} ON [${entity.SchemaName}].[${entity.BaseTable}] ([${f.
|
|
|
921
925
|
* Returns array of field info objects representing recursive relationships
|
|
922
926
|
*/
|
|
923
927
|
detectRecursiveForeignKeys(entity) {
|
|
924
|
-
return entity.Fields.filter(field => field.RelatedEntityID != null &&
|
|
925
|
-
field.RelatedEntityID === entity.ID);
|
|
928
|
+
return entity.Fields.filter((field) => field.RelatedEntityID != null && field.RelatedEntityID === entity.ID);
|
|
926
929
|
}
|
|
927
930
|
/**
|
|
928
931
|
* Generates an inline Table Value Function for calculating root ID for a recursive FK field
|
|
@@ -1000,20 +1003,20 @@ GO
|
|
|
1000
1003
|
if (recursiveFKs.length === 0) {
|
|
1001
1004
|
return '';
|
|
1002
1005
|
}
|
|
1003
|
-
return recursiveFKs
|
|
1004
|
-
.map(field => this.generateRootIDFunction(entity, field))
|
|
1005
|
-
.join('\n');
|
|
1006
|
+
return recursiveFKs.map((field) => this.generateRootIDFunction(entity, field)).join('\n');
|
|
1006
1007
|
}
|
|
1007
1008
|
/**
|
|
1008
1009
|
* Generates the SELECT clause additions for root fields from TVFs
|
|
1009
1010
|
* Example: , root_ParentID.RootID AS [RootParentID]
|
|
1010
1011
|
*/
|
|
1011
1012
|
generateRootFieldSelects(recursiveFKs, classNameFirstChar) {
|
|
1012
|
-
return recursiveFKs
|
|
1013
|
+
return recursiveFKs
|
|
1014
|
+
.map((field) => {
|
|
1013
1015
|
const alias = `root_${field.Name}`;
|
|
1014
1016
|
const columnName = `Root${field.Name}`;
|
|
1015
1017
|
return `,\n ${alias}.RootID AS [${columnName}]`;
|
|
1016
|
-
})
|
|
1018
|
+
})
|
|
1019
|
+
.join('');
|
|
1017
1020
|
}
|
|
1018
1021
|
/**
|
|
1019
1022
|
* Generates OUTER APPLY joins to inline Table Value Functions for root ID calculation
|
|
@@ -1026,11 +1029,13 @@ GO
|
|
|
1026
1029
|
const primaryKey = entity.FirstPrimaryKey.Name;
|
|
1027
1030
|
const schemaName = entity.SchemaName;
|
|
1028
1031
|
const tableName = entity.BaseTable;
|
|
1029
|
-
const joins = recursiveFKs
|
|
1032
|
+
const joins = recursiveFKs
|
|
1033
|
+
.map((field) => {
|
|
1030
1034
|
const functionName = `fn${tableName}${field.Name}_GetRootID`;
|
|
1031
1035
|
const alias = `root_${field.Name}`;
|
|
1032
1036
|
return `OUTER APPLY\n [${schemaName}].[${functionName}]([${classNameFirstChar}].[${primaryKey}], [${classNameFirstChar}].[${field.Name}]) AS ${alias}`;
|
|
1033
|
-
})
|
|
1037
|
+
})
|
|
1038
|
+
.join('\n');
|
|
1034
1039
|
return '\n' + joins;
|
|
1035
1040
|
}
|
|
1036
1041
|
/**
|
|
@@ -1046,9 +1051,11 @@ GO
|
|
|
1046
1051
|
const relatedFieldsString = await this.generateBaseViewRelatedFieldsString(pool, entity.Fields);
|
|
1047
1052
|
const relatedFieldsJoinString = this.generateBaseViewJoins(entity, entity.Fields);
|
|
1048
1053
|
const permissions = this.generateViewPermissions(entity);
|
|
1049
|
-
const whereClause = entity.DeleteType === 'Soft'
|
|
1050
|
-
|
|
1051
|
-
|
|
1054
|
+
const whereClause = entity.DeleteType === 'Soft'
|
|
1055
|
+
? `WHERE
|
|
1056
|
+
${classNameFirstChar}.[${global_1.EntityInfo.DeletedAtFieldName}] IS NULL
|
|
1057
|
+
`
|
|
1058
|
+
: '';
|
|
1052
1059
|
// Detect recursive foreign keys and generate TVF joins and root field selects
|
|
1053
1060
|
const recursiveFKs = this.detectRecursiveForeignKeys(entity);
|
|
1054
1061
|
const rootFields = recursiveFKs.length > 0 ? this.generateRootFieldSelects(recursiveFKs, classNameFirstChar) : '';
|
|
@@ -1058,7 +1065,7 @@ GO
|
|
|
1058
1065
|
----- BASE VIEW FOR ENTITY: ${entity.Name}
|
|
1059
1066
|
----- SCHEMA: ${entity.SchemaName}
|
|
1060
1067
|
----- BASE TABLE: ${entity.BaseTable}
|
|
1061
|
-
----- PRIMARY KEY: ${entity.PrimaryKeys.map(pk => pk.Name).join(', ')}
|
|
1068
|
+
----- PRIMARY KEY: ${entity.PrimaryKeys.map((pk) => pk.Name).join(', ')}
|
|
1062
1069
|
------------------------------------------------------------
|
|
1063
1070
|
IF OBJECT_ID('[${entity.SchemaName}].[${viewName}]', 'V') IS NOT NULL
|
|
1064
1071
|
DROP VIEW [${entity.SchemaName}].[${viewName}];
|
|
@@ -1098,9 +1105,9 @@ ${whereClause}GO${permissions}
|
|
|
1098
1105
|
async generateBaseViewRelatedFieldsString(pool, entityFields) {
|
|
1099
1106
|
let sOutput = '';
|
|
1100
1107
|
let fieldCount = 0;
|
|
1101
|
-
const manageMD =
|
|
1108
|
+
const manageMD = global_2.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
|
|
1102
1109
|
// next get the fields that are related entities and have the IncludeRelatedEntityNameFieldInBaseView flag set to true
|
|
1103
|
-
const qualifyingFields = entityFields.filter(f => f.RelatedEntityID && f.IncludeRelatedEntityNameFieldInBaseView);
|
|
1110
|
+
const qualifyingFields = entityFields.filter((f) => f.RelatedEntityID && f.IncludeRelatedEntityNameFieldInBaseView);
|
|
1104
1111
|
for (const ef of qualifyingFields) {
|
|
1105
1112
|
const { nameField, nameFieldIsVirtual } = this.getIsNameFieldForSingleEntity(ef.RelatedEntity);
|
|
1106
1113
|
if (nameField !== '') {
|
|
@@ -1111,7 +1118,8 @@ ${whereClause}GO${permissions}
|
|
|
1111
1118
|
const candidateName = this.stripID(ef.Name);
|
|
1112
1119
|
// check to make sure candidateName is not already a field name in the base table (other than a virtual field of course, as that is what we're creating)
|
|
1113
1120
|
// because if it is, we need to change it to something else
|
|
1114
|
-
const bFound = entityFields.find(f => f.IsVirtual === false && f.Name.trim().toLowerCase() === candidateName.trim().toLowerCase()) !==
|
|
1121
|
+
const bFound = entityFields.find((f) => f.IsVirtual === false && f.Name.trim().toLowerCase() === candidateName.trim().toLowerCase()) !==
|
|
1122
|
+
undefined;
|
|
1115
1123
|
if (bFound)
|
|
1116
1124
|
ef._RelatedEntityNameFieldMap = candidateName + '_Virtual';
|
|
1117
1125
|
else
|
|
@@ -1135,8 +1143,8 @@ ${whereClause}GO${permissions}
|
|
|
1135
1143
|
return sOutput;
|
|
1136
1144
|
}
|
|
1137
1145
|
getIsNameFieldForSingleEntity(entityName) {
|
|
1138
|
-
const md = new
|
|
1139
|
-
const e = md.Entities.find(e => e.Name === entityName);
|
|
1146
|
+
const md = new global_1.Metadata(); // use the full metadata entity list, not the filtered version that we receive
|
|
1147
|
+
const e = md.Entities.find((e) => e.Name === entityName);
|
|
1140
1148
|
if (e) {
|
|
1141
1149
|
const ef = e.NameField;
|
|
1142
1150
|
if (e.NameField)
|
|
@@ -1156,9 +1164,7 @@ ${whereClause}GO${permissions}
|
|
|
1156
1164
|
let sOutput = '';
|
|
1157
1165
|
for (let i = 0; i < entity.Permissions.length; i++) {
|
|
1158
1166
|
const ep = entity.Permissions[i];
|
|
1159
|
-
if ((type == exports.SPType.Create && ep.CanCreate) ||
|
|
1160
|
-
(type == exports.SPType.Update && ep.CanUpdate) ||
|
|
1161
|
-
(type == exports.SPType.Delete && ep.CanDelete)) {
|
|
1167
|
+
if ((type == exports.SPType.Create && ep.CanCreate) || (type == exports.SPType.Update && ep.CanUpdate) || (type == exports.SPType.Delete && ep.CanDelete)) {
|
|
1162
1168
|
if (ep.RoleSQLName && ep.RoleSQLName.length > 0) {
|
|
1163
1169
|
sOutput += (sOutput === '' ? `GRANT EXECUTE ON [${entity.SchemaName}].[${spName}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
|
|
1164
1170
|
}
|
|
@@ -1270,7 +1276,9 @@ CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
|
|
|
1270
1276
|
AS
|
|
1271
1277
|
BEGIN
|
|
1272
1278
|
SET NOCOUNT ON;
|
|
1273
|
-
${preInsertCode}${preInsertCode.includes('INSERT INTO')
|
|
1279
|
+
${preInsertCode}${preInsertCode.includes('INSERT INTO')
|
|
1280
|
+
? ''
|
|
1281
|
+
: `
|
|
1274
1282
|
INSERT INTO
|
|
1275
1283
|
[${entity.SchemaName}].[${entity.BaseTable}]
|
|
1276
1284
|
(
|
|
@@ -1287,12 +1295,12 @@ GO${permissions}
|
|
|
1287
1295
|
`;
|
|
1288
1296
|
}
|
|
1289
1297
|
generateUpdatedAtTrigger(entity) {
|
|
1290
|
-
const updatedAtField = entity.Fields.find(f => f.Name.toLowerCase().trim() ===
|
|
1298
|
+
const updatedAtField = entity.Fields.find((f) => f.Name.toLowerCase().trim() === global_1.EntityInfo.UpdatedAtFieldName.toLowerCase().trim());
|
|
1291
1299
|
if (!updatedAtField)
|
|
1292
1300
|
return '';
|
|
1293
1301
|
const triggerStatement = `
|
|
1294
1302
|
------------------------------------------------------------
|
|
1295
|
-
----- TRIGGER FOR ${
|
|
1303
|
+
----- TRIGGER FOR ${global_1.EntityInfo.UpdatedAtFieldName} field for the ${entity.BaseTable} table
|
|
1296
1304
|
------------------------------------------------------------
|
|
1297
1305
|
IF OBJECT_ID('[${entity.SchemaName}].[trgUpdate${entity.ClassName}]', 'TR') IS NOT NULL
|
|
1298
1306
|
DROP TRIGGER [${entity.SchemaName}].[trgUpdate${entity.ClassName}];
|
|
@@ -1306,12 +1314,12 @@ BEGIN
|
|
|
1306
1314
|
UPDATE
|
|
1307
1315
|
[${entity.SchemaName}].[${entity.BaseTable}]
|
|
1308
1316
|
SET
|
|
1309
|
-
${
|
|
1317
|
+
${global_1.EntityInfo.UpdatedAtFieldName} = GETUTCDATE()
|
|
1310
1318
|
FROM
|
|
1311
1319
|
[${entity.SchemaName}].[${entity.BaseTable}] AS _organicTable
|
|
1312
1320
|
INNER JOIN
|
|
1313
1321
|
INSERTED AS I ON
|
|
1314
|
-
${entity.PrimaryKeys.map(k => `_organicTable.[${k.Name}] = I.[${k.Name}]`).join(' AND ')};
|
|
1322
|
+
${entity.PrimaryKeys.map((k) => `_organicTable.[${k.Name}] = I.[${k.Name}]`).join(' AND ')};
|
|
1315
1323
|
END;
|
|
1316
1324
|
GO`;
|
|
1317
1325
|
return triggerStatement;
|
|
@@ -1320,14 +1328,14 @@ GO`;
|
|
|
1320
1328
|
const spName = entity.spUpdate ? entity.spUpdate : `spUpdate${entity.BaseTableCodeName}`;
|
|
1321
1329
|
const efParamString = this.createEntityFieldsParamString(entity.Fields, true);
|
|
1322
1330
|
const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Update);
|
|
1323
|
-
const hasUpdatedAtField = entity.Fields.find(f => f.Name.toLowerCase().trim() ===
|
|
1331
|
+
const hasUpdatedAtField = entity.Fields.find((f) => f.Name.toLowerCase().trim() === global_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase()) !== undefined;
|
|
1324
1332
|
const updatedAtTrigger = hasUpdatedAtField ? this.generateUpdatedAtTrigger(entity) : '';
|
|
1325
1333
|
let selectUpdatedRecord = `SELECT
|
|
1326
1334
|
*
|
|
1327
1335
|
FROM
|
|
1328
1336
|
[${entity.SchemaName}].[${entity.BaseView}]
|
|
1329
1337
|
WHERE
|
|
1330
|
-
${entity.PrimaryKeys.map(k => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
|
|
1338
|
+
${entity.PrimaryKeys.map((k) => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
|
|
1331
1339
|
`;
|
|
1332
1340
|
return `
|
|
1333
1341
|
------------------------------------------------------------
|
|
@@ -1347,7 +1355,7 @@ BEGIN
|
|
|
1347
1355
|
SET
|
|
1348
1356
|
${this.createEntityFieldsUpdateString(entity.Fields)}
|
|
1349
1357
|
WHERE
|
|
1350
|
-
${entity.PrimaryKeys.map(k => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
|
|
1358
|
+
${entity.PrimaryKeys.map((k) => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
|
|
1351
1359
|
|
|
1352
1360
|
-- Check if the update was successful
|
|
1353
1361
|
IF @@ROWCOUNT = 0
|
|
@@ -1386,7 +1394,7 @@ ${updatedAtTrigger}
|
|
|
1386
1394
|
'current_timestamp',
|
|
1387
1395
|
'user_name()',
|
|
1388
1396
|
'suser_name()',
|
|
1389
|
-
'system_user'
|
|
1397
|
+
'system_user',
|
|
1390
1398
|
];
|
|
1391
1399
|
// Check if this is a SQL function
|
|
1392
1400
|
for (const func of sqlFunctions) {
|
|
@@ -1446,7 +1454,11 @@ ${updatedAtTrigger}
|
|
|
1446
1454
|
const ef = entityFields[i];
|
|
1447
1455
|
// we only want fields that are (a) not primary keys, or if a pkey, not an auto-increment pkey and (b) not virtual fields and (c) updateable fields and (d) not auto-increment fields if they're not pkeys)
|
|
1448
1456
|
// ALSO: if excludePrimaryKey is true, skip all primary key fields
|
|
1449
|
-
if ((excludePrimaryKey && ef.IsPrimaryKey) ||
|
|
1457
|
+
if ((excludePrimaryKey && ef.IsPrimaryKey) ||
|
|
1458
|
+
(ef.IsPrimaryKey && autoGeneratedPrimaryKey) ||
|
|
1459
|
+
ef.IsVirtual ||
|
|
1460
|
+
!ef.AllowUpdateAPI ||
|
|
1461
|
+
ef.AutoIncrement) {
|
|
1450
1462
|
continue; // skip this field
|
|
1451
1463
|
}
|
|
1452
1464
|
if (!isFirst)
|
|
@@ -1459,7 +1471,7 @@ ${updatedAtTrigger}
|
|
|
1459
1471
|
else
|
|
1460
1472
|
sOutput += `NULL`; // we don't set the deleted at field on an insert, only on a delete
|
|
1461
1473
|
}
|
|
1462
|
-
else if (
|
|
1474
|
+
else if (prefix && prefix !== '' && !ef.IsPrimaryKey && ef.IsUniqueIdentifier && ef.HasDefaultValue && !ef.AllowsNull) {
|
|
1463
1475
|
// this is the VALUE side (prefix not null/blank), is NOT a primary key, and is a uniqueidentifier column with a default value and does NOT allow NULL
|
|
1464
1476
|
// We need to handle both NULL and the special value '00000000-0000-0000-0000-000000000000' for backward compatibility
|
|
1465
1477
|
// Existing code uses the special value to indicate "use the default", so we preserve that behavior
|
|
@@ -1497,11 +1509,7 @@ ${updatedAtTrigger}
|
|
|
1497
1509
|
let sOutput = '', isFirst = true;
|
|
1498
1510
|
for (let i = 0; i < entityFields.length; ++i) {
|
|
1499
1511
|
const ef = entityFields[i];
|
|
1500
|
-
if (!ef.IsPrimaryKey &&
|
|
1501
|
-
!ef.IsVirtual &&
|
|
1502
|
-
ef.AllowUpdateAPI &&
|
|
1503
|
-
!ef.AutoIncrement &&
|
|
1504
|
-
!ef.IsSpecialDateField) {
|
|
1512
|
+
if (!ef.IsPrimaryKey && !ef.IsVirtual && ef.AllowUpdateAPI && !ef.AutoIncrement && !ef.IsSpecialDateField) {
|
|
1505
1513
|
if (!isFirst)
|
|
1506
1514
|
sOutput += ',\n ';
|
|
1507
1515
|
else
|
|
@@ -1528,7 +1536,7 @@ ${updatedAtTrigger}
|
|
|
1528
1536
|
// next up, create the delete code which is based on the type of delete the entity is set to
|
|
1529
1537
|
// start off by creating the where clause first and then prepend the delete or update statement to it
|
|
1530
1538
|
let deleteCode = ` WHERE
|
|
1531
|
-
${entity.PrimaryKeys.map(k => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
|
|
1539
|
+
${entity.PrimaryKeys.map((k) => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
|
|
1532
1540
|
`;
|
|
1533
1541
|
if (entity.DeleteType === 'Hard') {
|
|
1534
1542
|
deleteCode = ` DELETE FROM
|
|
@@ -1539,8 +1547,8 @@ ${deleteCode}`;
|
|
|
1539
1547
|
deleteCode = ` UPDATE
|
|
1540
1548
|
[${entity.SchemaName}].[${entity.BaseTable}]
|
|
1541
1549
|
SET
|
|
1542
|
-
${
|
|
1543
|
-
${deleteCode} AND ${
|
|
1550
|
+
${global_1.EntityInfo.DeletedAtFieldName} = GETUTCDATE()
|
|
1551
|
+
${deleteCode} AND ${global_1.EntityInfo.DeletedAtFieldName} IS NULL -- don't update the record if it's already been deleted via a soft delete`;
|
|
1544
1552
|
}
|
|
1545
1553
|
// Build the NULL select statement for when no rows are affected
|
|
1546
1554
|
let sNullSelect = '';
|
|
@@ -1577,7 +1585,7 @@ GO${permissions}
|
|
|
1577
1585
|
generateCascadeDeletes(entity) {
|
|
1578
1586
|
let sOutput = '';
|
|
1579
1587
|
if (entity.CascadeDeletes) {
|
|
1580
|
-
const md = new
|
|
1588
|
+
const md = new global_1.Metadata();
|
|
1581
1589
|
// Find all fields in other entities that are foreign keys to this entity
|
|
1582
1590
|
for (const e of md.Entities) {
|
|
1583
1591
|
for (const ef of e.Fields) {
|
|
@@ -1719,7 +1727,10 @@ GO${permissions}
|
|
|
1719
1727
|
const pkComponents = this.buildPrimaryKeyComponents(entity, varPrefix);
|
|
1720
1728
|
// Add primary key declarations to the declarations string
|
|
1721
1729
|
// Need to add DECLARE keyword since buildPrimaryKeyComponents doesn't include it
|
|
1722
|
-
declarations = pkComponents.varDeclarations
|
|
1730
|
+
declarations = pkComponents.varDeclarations
|
|
1731
|
+
.split(', ')
|
|
1732
|
+
.map((decl) => `DECLARE ${decl}`)
|
|
1733
|
+
.join('\n ');
|
|
1723
1734
|
selectFields = pkComponents.selectFields;
|
|
1724
1735
|
fetchInto = pkComponents.fetchInto;
|
|
1725
1736
|
allParams = pkComponents.spParams;
|
|
@@ -1750,7 +1761,7 @@ GO${permissions}
|
|
|
1750
1761
|
*/
|
|
1751
1762
|
analyzeCascadeDeleteDependencies(entity) {
|
|
1752
1763
|
if (entity.CascadeDeletes) {
|
|
1753
|
-
const md = new
|
|
1764
|
+
const md = new global_1.Metadata();
|
|
1754
1765
|
// Find all fields in other entities that are foreign keys to this entity
|
|
1755
1766
|
for (const e of md.Entities) {
|
|
1756
1767
|
for (const ef of e.Fields) {
|
|
@@ -1812,9 +1823,9 @@ GO${permissions}
|
|
|
1812
1823
|
// Log the dependency map
|
|
1813
1824
|
(0, status_logging_1.logStatus)(`Cascade delete dependency map built:`);
|
|
1814
1825
|
for (const [dependedOnEntityId, dependentEntityIds] of this.cascadeDeleteDependencies) {
|
|
1815
|
-
const dependedOnEntity = entities.find(e => e.ID === dependedOnEntityId);
|
|
1826
|
+
const dependedOnEntity = entities.find((e) => e.ID === dependedOnEntityId);
|
|
1816
1827
|
const dependentNames = Array.from(dependentEntityIds)
|
|
1817
|
-
.map(id => entities.find(e => e.ID === id)?.Name || id)
|
|
1828
|
+
.map((id) => entities.find((e) => e.ID === id)?.Name || id)
|
|
1818
1829
|
.join(', ');
|
|
1819
1830
|
(0, status_logging_1.logStatus)(` ${dependedOnEntity?.Name || dependedOnEntityId} is depended on by: ${dependentNames}`);
|
|
1820
1831
|
}
|
|
@@ -1830,15 +1841,13 @@ GO${permissions}
|
|
|
1830
1841
|
(0, status_logging_1.logStatus)(`Modified entities from metadata phase: ${modifiedEntityNames.join(', ')}`);
|
|
1831
1842
|
// Convert entity names to IDs and filter for those with update API
|
|
1832
1843
|
for (const entityName of modifiedEntityNames) {
|
|
1833
|
-
const entity = entities.find(e => e.Name === entityName &&
|
|
1834
|
-
e.AllowUpdateAPI &&
|
|
1835
|
-
e.spUpdateGenerated);
|
|
1844
|
+
const entity = entities.find((e) => e.Name === entityName && e.AllowUpdateAPI && e.spUpdateGenerated);
|
|
1836
1845
|
if (entity) {
|
|
1837
1846
|
modifiedEntitiesMap.set(entity.Name, entity.ID);
|
|
1838
1847
|
(0, status_logging_1.logStatus)(` - ${entity.Name} (${entity.ID}) has update API and will be tracked`);
|
|
1839
1848
|
}
|
|
1840
1849
|
else {
|
|
1841
|
-
const nonUpdateEntity = entities.find(e => e.Name === entityName);
|
|
1850
|
+
const nonUpdateEntity = entities.find((e) => e.Name === entityName);
|
|
1842
1851
|
if (nonUpdateEntity) {
|
|
1843
1852
|
(0, status_logging_1.logStatus)(` - ${entityName} found but AllowUpdateAPI=${nonUpdateEntity.AllowUpdateAPI}, spUpdateGenerated=${nonUpdateEntity.spUpdateGenerated}`);
|
|
1844
1853
|
}
|
|
@@ -1886,7 +1895,7 @@ GO${permissions}
|
|
|
1886
1895
|
(0, status_logging_1.logStatus)(`Identified ${entitiesNeedingRegeneration.size} entities requiring delete SP regeneration due to cascade dependencies`);
|
|
1887
1896
|
// Store the entity IDs that need regeneration (only if spDeleteGenerated=true)
|
|
1888
1897
|
for (const entityId of entitiesNeedingRegeneration) {
|
|
1889
|
-
const entity = entities.find(e => e.ID === entityId);
|
|
1898
|
+
const entity = entities.find((e) => e.ID === entityId);
|
|
1890
1899
|
if (entity && entity.spDeleteGenerated) {
|
|
1891
1900
|
this.entitiesNeedingDeleteSPRegeneration.add(entityId);
|
|
1892
1901
|
(0, status_logging_1.logStatus)(` - Marked ${entity.Name} for delete SP regeneration (cascade dependency)`);
|
|
@@ -1900,7 +1909,7 @@ GO${permissions}
|
|
|
1900
1909
|
if (this.orderedEntitiesForDeleteSPRegeneration.length > 0) {
|
|
1901
1910
|
(0, status_logging_1.logStatus)(`Ordered entities for delete SP regeneration:`);
|
|
1902
1911
|
this.orderedEntitiesForDeleteSPRegeneration.forEach((entityId, index) => {
|
|
1903
|
-
const entity = entities.find(e => e.ID === entityId);
|
|
1912
|
+
const entity = entities.find((e) => e.ID === entityId);
|
|
1904
1913
|
(0, status_logging_1.logStatus)(` ${index + 1}. ${entity?.Name || entityId}`);
|
|
1905
1914
|
});
|
|
1906
1915
|
}
|
|
@@ -1948,7 +1957,7 @@ GO${permissions}
|
|
|
1948
1957
|
}
|
|
1949
1958
|
if (visiting.has(entityId)) {
|
|
1950
1959
|
// Circular dependency detected - mark it but don't fail
|
|
1951
|
-
const entity = entities.find(e => e.ID === entityId);
|
|
1960
|
+
const entity = entities.find((e) => e.ID === entityId);
|
|
1952
1961
|
(0, status_logging_1.logStatus)(`Warning: Circular cascade delete dependency detected involving ${entity?.Name || entityId}`);
|
|
1953
1962
|
circularDeps.add(entityId);
|
|
1954
1963
|
return false; // Signal circular dependency but continue processing
|
|
@@ -1975,7 +1984,7 @@ GO${permissions}
|
|
|
1975
1984
|
if (!success && circularDeps.has(entityId)) {
|
|
1976
1985
|
// Entity is part of circular dependency - add it anyway in arbitrary order
|
|
1977
1986
|
// The SQL will still be generated, just not in perfect dependency order
|
|
1978
|
-
(0, status_logging_1.logStatus)(` - Adding ${entities.find(e => e.ID === entityId)?.Name || entityId} despite circular dependency`);
|
|
1987
|
+
(0, status_logging_1.logStatus)(` - Adding ${entities.find((e) => e.ID === entityId)?.Name || entityId} despite circular dependency`);
|
|
1979
1988
|
visited.add(entityId);
|
|
1980
1989
|
ordered.push(entityId);
|
|
1981
1990
|
}
|