@memberjunction/codegen-lib 2.48.0 → 2.49.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.
Files changed (78) hide show
  1. package/dist/Angular/angular-codegen.d.ts +164 -6
  2. package/dist/Angular/angular-codegen.d.ts.map +1 -1
  3. package/dist/Angular/angular-codegen.js +177 -13
  4. package/dist/Angular/angular-codegen.js.map +1 -1
  5. package/dist/Angular/join-grid-related-entity-component.d.ts +52 -3
  6. package/dist/Angular/join-grid-related-entity-component.d.ts.map +1 -1
  7. package/dist/Angular/join-grid-related-entity-component.js +58 -3
  8. package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
  9. package/dist/Angular/related-entity-components.d.ts +99 -42
  10. package/dist/Angular/related-entity-components.d.ts.map +1 -1
  11. package/dist/Angular/related-entity-components.js +116 -26
  12. package/dist/Angular/related-entity-components.js.map +1 -1
  13. package/dist/Angular/timeline-related-entity-component.d.ts +46 -7
  14. package/dist/Angular/timeline-related-entity-component.d.ts.map +1 -1
  15. package/dist/Angular/timeline-related-entity-component.js +64 -7
  16. package/dist/Angular/timeline-related-entity-component.js.map +1 -1
  17. package/dist/Angular/user-view-grid-related-entity-component.d.ts +33 -1
  18. package/dist/Angular/user-view-grid-related-entity-component.d.ts.map +1 -1
  19. package/dist/Angular/user-view-grid-related-entity-component.js +33 -1
  20. package/dist/Angular/user-view-grid-related-entity-component.js.map +1 -1
  21. package/dist/Config/config.d.ts +369 -45
  22. package/dist/Config/config.d.ts.map +1 -1
  23. package/dist/Config/config.js +136 -2
  24. package/dist/Config/config.js.map +1 -1
  25. package/dist/Config/db-connection.d.ts +17 -3
  26. package/dist/Config/db-connection.d.ts.map +1 -1
  27. package/dist/Config/db-connection.js +31 -19
  28. package/dist/Config/db-connection.js.map +1 -1
  29. package/dist/Database/dbSchema.d.ts +44 -1
  30. package/dist/Database/dbSchema.d.ts.map +1 -1
  31. package/dist/Database/dbSchema.js +44 -1
  32. package/dist/Database/dbSchema.js.map +1 -1
  33. package/dist/Database/manage-metadata.d.ts +52 -46
  34. package/dist/Database/manage-metadata.d.ts.map +1 -1
  35. package/dist/Database/manage-metadata.js +221 -167
  36. package/dist/Database/manage-metadata.js.map +1 -1
  37. package/dist/Database/reorder-columns.d.ts +2 -2
  38. package/dist/Database/reorder-columns.d.ts.map +1 -1
  39. package/dist/Database/reorder-columns.js +23 -17
  40. package/dist/Database/reorder-columns.js.map +1 -1
  41. package/dist/Database/sql.d.ts +4 -4
  42. package/dist/Database/sql.d.ts.map +1 -1
  43. package/dist/Database/sql.js +2 -2
  44. package/dist/Database/sql.js.map +1 -1
  45. package/dist/Database/sql_codegen.d.ts +15 -15
  46. package/dist/Database/sql_codegen.d.ts.map +1 -1
  47. package/dist/Database/sql_codegen.js +184 -112
  48. package/dist/Database/sql_codegen.js.map +1 -1
  49. package/dist/Misc/advanced_generation.js +81 -81
  50. package/dist/Misc/advanced_generation.js.map +1 -1
  51. package/dist/Misc/entity_subclasses_codegen.d.ts +5 -5
  52. package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
  53. package/dist/Misc/entity_subclasses_codegen.js +10 -8
  54. package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
  55. package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
  56. package/dist/Misc/graphql_server_codegen.js +33 -28
  57. package/dist/Misc/graphql_server_codegen.js.map +1 -1
  58. package/dist/Misc/sql_logging.d.ts +2 -2
  59. package/dist/Misc/sql_logging.d.ts.map +1 -1
  60. package/dist/Misc/sql_logging.js +4 -3
  61. package/dist/Misc/sql_logging.js.map +1 -1
  62. package/dist/Misc/status_logging.d.ts +37 -0
  63. package/dist/Misc/status_logging.d.ts.map +1 -1
  64. package/dist/Misc/status_logging.js +145 -3
  65. package/dist/Misc/status_logging.js.map +1 -1
  66. package/dist/Misc/system_integrity.d.ts +9 -9
  67. package/dist/Misc/system_integrity.d.ts.map +1 -1
  68. package/dist/Misc/system_integrity.js +23 -21
  69. package/dist/Misc/system_integrity.js.map +1 -1
  70. package/dist/index.d.ts +45 -7
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +51 -7
  73. package/dist/index.js.map +1 -1
  74. package/dist/runCodeGen.d.ts +84 -6
  75. package/dist/runCodeGen.d.ts.map +1 -1
  76. package/dist/runCodeGen.js +242 -82
  77. package/dist/runCodeGen.js.map +1 -1
  78. package/package.json +14 -14
@@ -49,33 +49,36 @@ exports.SPType = {
49
49
  * of that abstract base class and other databases will be sub-classes of the abstract base class as well.
50
50
  */
51
51
  class SQLCodeGenBase {
52
- constructor() {
53
- this._sqlUtilityObject = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
54
- this.__specialUUIDValue = '00000000-0000-0000-0000-000000000000';
55
- }
52
+ _sqlUtilityObject = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
56
53
  get SQLUtilityObject() {
57
54
  return this._sqlUtilityObject;
58
55
  }
59
- async manageSQLScriptsAndExecution(ds, entities, directory, currentUser) {
56
+ async manageSQLScriptsAndExecution(pool, entities, directory, currentUser) {
60
57
  try {
61
58
  // STEP 1 - execute any custom SQL scripts for object creation that need to happen first - for example, if
62
59
  // we have custom base views, need to have them defined before we do
63
60
  // the rest as the generated stuff might use custom base views in compiled
64
61
  // objects like spCreate for a given entity might reference the vw for that entity
62
+ (0, status_logging_1.startSpinner)('Running custom SQL scripts...');
65
63
  const startTime = new Date();
66
- if (!await this.runCustomSQLScripts(ds, 'before-sql'))
64
+ if (!await this.runCustomSQLScripts(pool, 'before-sql')) {
65
+ (0, status_logging_1.failSpinner)('Failed to run custom SQL scripts');
67
66
  return false;
68
- (0, status_logging_1.logStatus)(` Time to run custom SQL scripts: ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
67
+ }
68
+ (0, status_logging_1.succeedSpinner)(`Custom SQL scripts completed (${(new Date().getTime() - startTime.getTime()) / 1000}s)`);
69
69
  // ALWAYS use the first filter where we only include entities that have IncludeInAPI = 1
70
70
  const baselineEntities = entities.filter(e => e.IncludeInAPI);
71
71
  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
72
72
  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
73
73
  // STEP 2(a) - clean out all *.generated.sql and *.permissions.generated.sql files from the directory
74
+ (0, status_logging_1.startSpinner)('Cleaning generated files...');
74
75
  this.deleteGeneratedEntityFiles(directory, baselineEntities);
76
+ (0, status_logging_1.succeedSpinner)('Cleaned generated files');
75
77
  // STEP 2(b) - generate all the SQL files and execute them
78
+ (0, status_logging_1.startSpinner)(`Generating SQL for ${includedEntities.length} entities...`);
76
79
  const step2StartTime = new Date();
77
80
  const genResult = await this.generateAndExecuteEntitySQLToSeparateFiles({
78
- ds,
81
+ pool,
79
82
  entities: includedEntities,
80
83
  directory,
81
84
  onlyPermissions: false,
@@ -85,12 +88,13 @@ class SQLCodeGenBase {
85
88
  enableSQLLoggingForNewOrModifiedEntities: true
86
89
  }); // enable sql logging for NEW entities....
87
90
  if (!genResult.Success) {
88
- (0, status_logging_1.logError)('Error generating all entities SQL to separate files');
91
+ (0, status_logging_1.failSpinner)('Failed to generate entity SQL files');
89
92
  return false;
90
93
  }
91
94
  // STEP 2(c) - for the excludedEntities, while we don't want to generate SQL, we do want to generate the permissions files for them
95
+ (0, status_logging_1.updateSpinner)(`Generating permissions for ${excludedEntities.length} excluded entities...`);
92
96
  const genResult2 = await this.generateAndExecuteEntitySQLToSeparateFiles({
93
- ds,
97
+ pool,
94
98
  entities: excludedEntities,
95
99
  directory,
96
100
  onlyPermissions: true,
@@ -100,41 +104,50 @@ class SQLCodeGenBase {
100
104
  enableSQLLoggingForNewOrModifiedEntities: false /*don't log this stuff, it is just permissions for excluded entities*/
101
105
  });
102
106
  if (!genResult2.Success) {
103
- (0, status_logging_1.logError)('Error generating permissions SQL for excluded entities to separate files');
107
+ (0, status_logging_1.failSpinner)('Failed to generate permissions for excluded entities');
104
108
  return false;
105
109
  }
106
- (0, status_logging_1.logStatus)(` Time to generate entity SQL: ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
110
+ (0, status_logging_1.succeedSpinner)(`Entity generation completed (${(new Date().getTime() - step2StartTime.getTime()) / 1000}s)`);
107
111
  // STEP 2(d) now that we've generated the SQL, let's create a combined file in each schema sub-directory for convenience for a DBA
112
+ (0, status_logging_1.startSpinner)('Creating combined SQL files...');
108
113
  const allEntityFiles = this.createCombinedEntitySQLFiles(directory, baselineEntities);
114
+ (0, status_logging_1.succeedSpinner)(`Created combined SQL files for ${allEntityFiles.length} schemas`);
109
115
  // STEP 2(e) ---- FINALLY, we now execute all the combined files by schema;
116
+ (0, status_logging_1.startSpinner)('Executing combined entity SQL files...');
110
117
  const step2eStartTime = new Date();
111
- if (!await this.SQLUtilityObject.executeSQLFiles(allEntityFiles, true)) {
112
- (0, status_logging_1.logError)('Error executing combined entity SQL files');
118
+ if (!await this.SQLUtilityObject.executeSQLFiles(allEntityFiles, config_1.configInfo?.verboseOutput ?? false)) {
119
+ (0, status_logging_1.failSpinner)('Failed to execute combined entity SQL files');
113
120
  return false;
114
121
  }
115
122
  const step2eEndTime = new Date();
116
- (0, status_logging_1.logStatus)(` Time to Execute Combined Entity SQL: ${(step2eEndTime.getTime() - step2eStartTime.getTime()) / 1000} seconds`);
123
+ (0, status_logging_1.succeedSpinner)(`SQL execution completed (${(step2eEndTime.getTime() - step2eStartTime.getTime()) / 1000}s)`);
117
124
  const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
118
125
  // 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
119
126
  // we CAN skip the entity field values part because that wouldn't change from the first time we ran it
120
- if (!await manageMD.manageEntityFields(ds, config_1.configInfo.excludeSchemas, true, true, currentUser)) {
121
- (0, status_logging_1.logError)('Error managing entity fields');
127
+ (0, status_logging_1.startSpinner)('Managing entity fields metadata...');
128
+ if (!await manageMD.manageEntityFields(pool, config_1.configInfo.excludeSchemas, true, true, currentUser)) {
129
+ (0, status_logging_1.failSpinner)('Failed to manage entity fields');
122
130
  return false;
123
131
  }
132
+ (0, status_logging_1.succeedSpinner)('Entity fields metadata updated');
124
133
  // no logStatus/timer for this because manageEntityFields() has its own internal logging for this including the total, so it is redundant to log it here
125
134
  // STEP 4- Apply permissions, executing all .permissions files
135
+ (0, status_logging_1.startSpinner)('Applying permissions...');
126
136
  const step4StartTime = new Date();
127
- if (!await this.applyPermissions(ds, directory, baselineEntities)) {
128
- (0, status_logging_1.logError)('Error applying permissions');
137
+ if (!await this.applyPermissions(pool, directory, baselineEntities)) {
138
+ (0, status_logging_1.failSpinner)('Failed to apply permissions');
129
139
  return false;
130
140
  }
131
- (0, status_logging_1.logStatus)(` Time to Apply Permissions: ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
141
+ (0, status_logging_1.succeedSpinner)(`Permissions applied (${(new Date().getTime() - step4StartTime.getTime()) / 1000}s)`);
132
142
  // STEP 5 - execute any custom SQL scripts that should run afterwards
143
+ (0, status_logging_1.startSpinner)('Running post-generation SQL scripts...');
133
144
  const step5StartTime = new Date();
134
- if (!await this.runCustomSQLScripts(ds, 'after-sql'))
145
+ if (!await this.runCustomSQLScripts(pool, 'after-sql')) {
146
+ (0, status_logging_1.failSpinner)('Failed to run post-generation SQL scripts');
135
147
  return false;
136
- (0, status_logging_1.logStatus)(` Time to run custom SQL scripts: ${(new Date().getTime() - step5StartTime.getTime()) / 1000} seconds`);
137
- (0, status_logging_1.logStatus)(' Total time to run generate and execute SQL scripts: ' + ((new Date().getTime() - startTime.getTime()) / 1000) + ' seconds');
148
+ }
149
+ (0, status_logging_1.succeedSpinner)(`Post-generation scripts completed (${(new Date().getTime() - step5StartTime.getTime()) / 1000}s)`);
150
+ (0, status_logging_1.succeedSpinner)(`CodeGen completed successfully (${((new Date().getTime() - startTime.getTime()) / 1000)}s total)`);
138
151
  // now - we need to tell our metadata object to refresh itself
139
152
  const md = new core_1.Metadata();
140
153
  await md.Refresh();
@@ -145,7 +158,7 @@ class SQLCodeGenBase {
145
158
  return false;
146
159
  }
147
160
  }
148
- async runCustomSQLScripts(ds, when) {
161
+ async runCustomSQLScripts(pool, when) {
149
162
  try {
150
163
  const scripts = (0, config_1.customSqlScripts)(when);
151
164
  let bSuccess = true;
@@ -165,7 +178,7 @@ class SQLCodeGenBase {
165
178
  return false;
166
179
  }
167
180
  }
168
- async applyPermissions(ds, directory, entities, batchSize = 5) {
181
+ async applyPermissions(pool, directory, entities, batchSize = 5) {
169
182
  try {
170
183
  let bSuccess = true;
171
184
  for (let i = 0; i < entities.length; i += batchSize) {
@@ -180,7 +193,7 @@ class SQLCodeGenBase {
180
193
  const fileBuffer = fs.readFileSync(fullPath);
181
194
  const fileContents = fileBuffer.toString();
182
195
  try {
183
- await ds.query(fileContents);
196
+ await pool.request().query(fileContents);
184
197
  }
185
198
  catch (e) {
186
199
  (0, status_logging_1.logError)(`Error executing permissions file ${fullPath} for entity ${e.Name}: ${e}`);
@@ -231,7 +244,7 @@ class SQLCodeGenBase {
231
244
  return { Success: false, Files: [] };
232
245
  }
233
246
  return this.generateAndExecuteSingleEntitySQLToSeparateFiles({
234
- ds: options.ds,
247
+ pool: options.pool,
235
248
  entity: e,
236
249
  directory: options.directory,
237
250
  onlyPermissions: options.onlyPermissions,
@@ -304,7 +317,7 @@ class SQLCodeGenBase {
304
317
  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
305
318
  if (!options.skipExecution) {
306
319
  return {
307
- Success: await this.SQLUtilityObject.executeSQLScript(options.ds, sql + "\n\nGO\n\n" + permissionsSQL, true), // combine the SQL and permissions and execute it,
320
+ Success: await this.SQLUtilityObject.executeSQLScript(options.pool, sql + "\n\nGO\n\n" + permissionsSQL, true), // combine the SQL and permissions and execute it,
308
321
  Files: files
309
322
  };
310
323
  }
@@ -317,9 +330,26 @@ class SQLCodeGenBase {
317
330
  }
318
331
  }
319
332
  logSQLForNewOrModifiedEntity(entity, sql, description, logSql = false) {
320
- if (logSql &&
321
- (manage_metadata_1.ManageMetadataBase.newEntityList.find(e => e === entity.Name) || manage_metadata_1.ManageMetadataBase.modifiedEntityList.find(e => e === entity.Name))) {
322
- // per above only do this if (a) logSql is true and (b) the entity is in the newEntityList OR modifiedEntityList
333
+ // Check if we should log this SQL
334
+ let shouldLog = false;
335
+ if (logSql) {
336
+ // Check if entity is in new or modified lists
337
+ const isNewOrModified = !!manage_metadata_1.ManageMetadataBase.newEntityList.find(e => e === entity.Name) ||
338
+ !!manage_metadata_1.ManageMetadataBase.modifiedEntityList.find(e => e === entity.Name);
339
+ // Check if force regeneration is enabled for relevant SQL types
340
+ const isForceRegeneration = config_1.configInfo.forceRegeneration?.enabled && ((description.toLowerCase().includes('base view') && config_1.configInfo.forceRegeneration.baseViews) ||
341
+ (description.toLowerCase().includes('spcreate') && config_1.configInfo.forceRegeneration.spCreate) ||
342
+ (description.toLowerCase().includes('spupdate') && config_1.configInfo.forceRegeneration.spUpdate) ||
343
+ (description.toLowerCase().includes('spdelete') && config_1.configInfo.forceRegeneration.spDelete) ||
344
+ (description.toLowerCase().includes('index') && config_1.configInfo.forceRegeneration.indexes) ||
345
+ (description.toLowerCase().includes('full text search') && config_1.configInfo.forceRegeneration.fullTextSearch) ||
346
+ (config_1.configInfo.forceRegeneration.allStoredProcedures &&
347
+ (description.toLowerCase().includes('spcreate') ||
348
+ description.toLowerCase().includes('spupdate') ||
349
+ description.toLowerCase().includes('spdelete'))));
350
+ shouldLog = isNewOrModified || isForceRegeneration;
351
+ }
352
+ if (shouldLog) {
323
353
  sql_logging_1.SQLLogging.appendToSQLLogFile(sql, description);
324
354
  }
325
355
  (0, util_1.logIf)(config_1.configInfo.verboseOutput, `SQL Generated for ${entity.Name}: ${description}`);
@@ -338,7 +368,8 @@ class SQLCodeGenBase {
338
368
  let permissionsSQL = '';
339
369
  // Indexes for Fkeys for the table
340
370
  if (!options.onlyPermissions) {
341
- const indexSQL = (0, config_1.autoIndexForeignKeys)() ? this.generateIndexesForForeignKeys(options.ds, options.entity) : ''; // if autoIndexForeignKeys is true, generate the indexes, otherwise, just add a blank string to the header
371
+ const shouldGenerateIndexes = (0, config_1.autoIndexForeignKeys)() || (config_1.configInfo.forceRegeneration?.enabled && config_1.configInfo.forceRegeneration?.indexes);
372
+ const indexSQL = shouldGenerateIndexes ? this.generateIndexesForForeignKeys(options.pool, options.entity) : ''; // generate indexes if auto-indexing is on OR force regeneration is enabled
342
373
  const s = this.generateSingleEntitySQLFileHeader(options.entity, 'Index for Foreign Keys') + indexSQL;
343
374
  if (options.writeFiles) {
344
375
  const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('index', options.entity.SchemaName, options.entity.BaseTable, false, true));
@@ -349,9 +380,11 @@ class SQLCodeGenBase {
349
380
  sRet += s + '\nGO\n';
350
381
  }
351
382
  // BASE VIEW
352
- if (!options.onlyPermissions && options.entity.BaseViewGenerated && !options.entity.VirtualEntity) {
383
+ if (!options.onlyPermissions &&
384
+ (options.entity.BaseViewGenerated || (config_1.configInfo.forceRegeneration?.enabled && config_1.configInfo.forceRegeneration?.baseViews)) &&
385
+ !options.entity.VirtualEntity) {
353
386
  // generate the base view
354
- const s = this.generateSingleEntitySQLFileHeader(options.entity, options.entity.BaseView) + await this.generateBaseView(options.ds, options.entity);
387
+ const s = this.generateSingleEntitySQLFileHeader(options.entity, options.entity.BaseView) + await this.generateBaseView(options.pool, options.entity);
355
388
  const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('view', options.entity.SchemaName, options.entity.BaseView, false, true));
356
389
  if (options.writeFiles) {
357
390
  this.logSQLForNewOrModifiedEntity(options.entity, s, `Base View SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
@@ -377,7 +410,9 @@ class SQLCodeGenBase {
377
410
  // CREATE SP
378
411
  if (options.entity.AllowCreateAPI && !options.entity.VirtualEntity) {
379
412
  const spName = this.getSPName(options.entity, exports.SPType.Create);
380
- if (!options.onlyPermissions && options.entity.spCreateGenerated) {
413
+ if (!options.onlyPermissions &&
414
+ (options.entity.spCreateGenerated ||
415
+ (config_1.configInfo.forceRegeneration?.enabled && (config_1.configInfo.forceRegeneration?.spCreate || config_1.configInfo.forceRegeneration?.allStoredProcedures)))) {
381
416
  // generate the create SP
382
417
  const s = this.generateSingleEntitySQLFileHeader(options.entity, spName) + this.generateSPCreate(options.entity);
383
418
  if (options.writeFiles) {
@@ -405,7 +440,9 @@ class SQLCodeGenBase {
405
440
  // UPDATE SP
406
441
  if (options.entity.AllowUpdateAPI && !options.entity.VirtualEntity) {
407
442
  const spName = this.getSPName(options.entity, exports.SPType.Update);
408
- if (!options.onlyPermissions && options.entity.spUpdateGenerated) {
443
+ if (!options.onlyPermissions &&
444
+ (options.entity.spUpdateGenerated ||
445
+ (config_1.configInfo.forceRegeneration?.enabled && (config_1.configInfo.forceRegeneration?.spUpdate || config_1.configInfo.forceRegeneration?.allStoredProcedures)))) {
409
446
  // generate the update SP
410
447
  const s = this.generateSingleEntitySQLFileHeader(options.entity, spName) + this.generateSPUpdate(options.entity);
411
448
  if (options.writeFiles) {
@@ -433,7 +470,9 @@ class SQLCodeGenBase {
433
470
  // DELETE SP
434
471
  if (options.entity.AllowDeleteAPI && !options.entity.VirtualEntity) {
435
472
  const spName = this.getSPName(options.entity, exports.SPType.Delete);
436
- if (!options.onlyPermissions && options.entity.spDeleteGenerated) {
473
+ if (!options.onlyPermissions &&
474
+ (options.entity.spDeleteGenerated ||
475
+ (config_1.configInfo.forceRegeneration?.enabled && (config_1.configInfo.forceRegeneration?.spDelete || config_1.configInfo.forceRegeneration?.allStoredProcedures)))) {
437
476
  // generate the delete SP
438
477
  const s = this.generateSingleEntitySQLFileHeader(options.entity, spName) + this.generateSPDelete(options.entity);
439
478
  if (options.writeFiles) {
@@ -459,9 +498,9 @@ class SQLCodeGenBase {
459
498
  sRet += s + '\nGO\n';
460
499
  }
461
500
  // check to see if the options.entity supports full text search or not
462
- if (options.entity.FullTextSearchEnabled) {
501
+ if (options.entity.FullTextSearchEnabled || (config_1.configInfo.forceRegeneration?.enabled && config_1.configInfo.forceRegeneration?.fullTextSearch)) {
463
502
  // always generate the code so we can get the function name from the below function call
464
- const ft = await this.generateEntityFullTextSearchSQL(options.ds, options.entity);
503
+ const ft = await this.generateEntityFullTextSearchSQL(options.pool, options.entity);
465
504
  if (!options.onlyPermissions) {
466
505
  // only write the actual sql out if we're not only generating permissions
467
506
  const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', options.entity.SchemaName, options.entity.BaseTable, false, true));
@@ -528,11 +567,11 @@ class SQLCodeGenBase {
528
567
  * @param entity
529
568
  * @returns
530
569
  */
531
- async generateEntitySQL(ds, entity) {
570
+ async generateEntitySQL(pool, entity) {
532
571
  let sOutput = '';
533
572
  if (entity.BaseViewGenerated && !entity.VirtualEntity)
534
573
  // generated the base view (will include permissions)
535
- sOutput += await this.generateBaseView(ds, entity) + '\n\n';
574
+ sOutput += await this.generateBaseView(pool, entity) + '\n\n';
536
575
  else
537
576
  // still generate the permissions for the view even if a custom view
538
577
  sOutput += this.generateViewPermissions(entity) + '\n\n';
@@ -562,11 +601,11 @@ class SQLCodeGenBase {
562
601
  }
563
602
  // check to see if the entity supports full text search or not
564
603
  if (entity.FullTextSearchEnabled) {
565
- sOutput += await this.generateEntityFullTextSearchSQL(ds, entity) + '\n\n';
604
+ sOutput += await this.generateEntityFullTextSearchSQL(pool, entity) + '\n\n';
566
605
  }
567
606
  return sOutput;
568
607
  }
569
- async generateEntityFullTextSearchSQL(ds, entity) {
608
+ async generateEntityFullTextSearchSQL(pool, entity) {
570
609
  let sql = '';
571
610
  const catalogName = entity.FullTextCatalog && entity.FullTextCatalog.length > 0 ? entity.FullTextCatalog : config_1.dbDatabase + '_FullTextCatalog';
572
611
  if (entity.FullTextCatalogGenerated) {
@@ -586,7 +625,7 @@ class SQLCodeGenBase {
586
625
  if (fullTextFields.length === 0)
587
626
  throw new Error(`FullTextIndexGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
588
627
  // drop and recreate the full text index
589
- const entity_pk_name = await this.getEntityPrimaryKeyIndexName(ds, entity);
628
+ const entity_pk_name = await this.getEntityPrimaryKeyIndexName(pool, entity);
590
629
  sql += ` -- DROP AND RECREATE THE FULL TEXT INDEX
591
630
  IF EXISTS (
592
631
  SELECT *
@@ -653,7 +692,7 @@ class SQLCodeGenBase {
653
692
  }
654
693
  return { sql, functionName };
655
694
  }
656
- async getEntityPrimaryKeyIndexName(ds, entity) {
695
+ async getEntityPrimaryKeyIndexName(pool, entity) {
657
696
  const sSQL = ` SELECT
658
697
  i.name AS IndexName
659
698
  FROM
@@ -668,7 +707,8 @@ class SQLCodeGenBase {
668
707
  o.schema_id = SCHEMA_ID('${entity.SchemaName}') AND
669
708
  kc.type = 'PK';
670
709
  `;
671
- const result = await ds.query(sSQL);
710
+ const resultResult = await pool.request().query(sSQL);
711
+ const result = resultResult.recordset;
672
712
  if (result && result.length > 0)
673
713
  return result[0].IndexName;
674
714
  else
@@ -700,10 +740,10 @@ class SQLCodeGenBase {
700
740
  }
701
741
  /**
702
742
  * Generates the SQL for creating indexes for the foreign keys in the entity
703
- * @param ds
743
+ * @param pool
704
744
  * @param entity
705
745
  */
706
- generateIndexesForForeignKeys(ds, entity) {
746
+ generateIndexesForForeignKeys(pool, entity) {
707
747
  // iterate through all of the fields in the entity that are foreign keys and generate an index for each one
708
748
  let sOutput = '';
709
749
  for (const f of entity.Fields) {
@@ -726,10 +766,10 @@ CREATE INDEX ${indexName} ON [${entity.SchemaName}].[${entity.BaseTable}] ([${f.
726
766
  }
727
767
  return sOutput;
728
768
  }
729
- async generateBaseView(ds, entity) {
769
+ async generateBaseView(pool, entity) {
730
770
  const viewName = entity.BaseView ? entity.BaseView : `vw${entity.CodeName}`;
731
771
  const classNameFirstChar = entity.ClassName.charAt(0).toLowerCase();
732
- const relatedFieldsString = await this.generateBaseViewRelatedFieldsString(ds, entity.Fields);
772
+ const relatedFieldsString = await this.generateBaseViewRelatedFieldsString(pool, entity.Fields);
733
773
  const relatedFieldsJoinString = this.generateBaseViewJoins(entity, entity.Fields);
734
774
  const permissions = this.generateViewPermissions(entity);
735
775
  const whereClause = entity.DeleteType === 'Soft' ? `WHERE
@@ -776,7 +816,7 @@ ${whereClause}GO${permissions}
776
816
  }
777
817
  return sOutput;
778
818
  }
779
- async generateBaseViewRelatedFieldsString(ds, entityFields) {
819
+ async generateBaseViewRelatedFieldsString(pool, entityFields) {
780
820
  let sOutput = '';
781
821
  let fieldCount = 0;
782
822
  const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
@@ -808,7 +848,7 @@ ${whereClause}GO${permissions}
808
848
  // and it also reflects what the DB will hold
809
849
  ef.RelatedEntityNameFieldMap = ef._RelatedEntityNameFieldMap;
810
850
  // then update the database itself
811
- await manageMD.updateEntityFieldRelatedEntityNameFieldMap(ds, ef.ID, ef.RelatedEntityNameFieldMap);
851
+ await manageMD.updateEntityFieldRelatedEntityNameFieldMap(pool, ef.ID, ef.RelatedEntityNameFieldMap);
812
852
  }
813
853
  fieldCount++;
814
854
  }
@@ -863,8 +903,8 @@ ${whereClause}GO${permissions}
863
903
  const spName = entity.spCreate ? entity.spCreate : `spCreate${entity.ClassName}`;
864
904
  const firstKey = entity.FirstPrimaryKey;
865
905
  //double exclamations used on the firstKey.DefaultValue property otherwise the type of this variable is 'number | ""';
866
- const primaryKeyAutomatic = firstKey.AutoIncrement || (firstKey.Type.toLowerCase().trim() === 'uniqueidentifier') && (!!firstKey.DefaultValue && firstKey.DefaultValue.trim().length > 0);
867
- const efString = this.createEntityFieldsParamString(entity.Fields, !primaryKeyAutomatic);
906
+ const primaryKeyAutomatic = firstKey.AutoIncrement; // Only exclude auto-increment fields, allow manual override for all other PKs including UUIDs with defaults
907
+ const efString = this.createEntityFieldsParamString(entity.Fields, false); // Always pass false for isUpdate since this is generateSPCreate
868
908
  const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Create);
869
909
  let preInsertCode = '';
870
910
  let outputCode = '';
@@ -874,29 +914,58 @@ ${whereClause}GO${permissions}
874
914
  if (entity.FirstPrimaryKey.AutoIncrement) {
875
915
  selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = SCOPE_IDENTITY()`;
876
916
  }
877
- else if (entity.FirstPrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier') {
878
- // our primary key is a uniqueidentifier. Two scenarios exist for this:
879
- // 1) The default value is specified in the database (usually either NEWID() or NEWSEQUENTIALID()).
880
- // 2) No default value is specified, so we need to generate a new GUID in the stored procedure using NEWID() --- NewSequentialID() is not allowed in a stored procedure, only usable as a default value.
881
- // so, first check to see if there is a default value for the field or not
917
+ else if (entity.FirstPrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier' && entity.PrimaryKeys.length === 1) {
918
+ // our primary key is a uniqueidentifier. Now we support optional override:
919
+ // - If PKEY is provided (not NULL), use it
920
+ // - If PKEY is NULL and there's a default value, let the database use it
921
+ // - If PKEY is NULL and no default value, generate NEWID()
882
922
  const hasDefaultValue = entity.FirstPrimaryKey.DefaultValue && entity.FirstPrimaryKey.DefaultValue.trim().length > 0;
883
- // if we have a default value, then we do NOT want to insert a new value, let the database use the default
884
923
  if (hasDefaultValue) {
885
- // in this situation, we DO have a default value, so we do NOT insert a new value, we let the database use the default
886
- // however, we need to do some extra preprocessing to use the OUTPUT from the INSERT statement and return the record that was
887
- // just created, that is how we get the newly created GUID that was the default value
888
- preInsertCode = `DECLARE @InsertedRow TABLE ([${entity.FirstPrimaryKey.Name}] UNIQUEIDENTIFIER)`;
889
- outputCode = `OUTPUT INSERTED.[${entity.FirstPrimaryKey.Name}] INTO @InsertedRow
890
- `;
924
+ // Has default value - use conditional logic to either include the field or let DB use default
925
+ preInsertCode = `DECLARE @InsertedRow TABLE ([${entity.FirstPrimaryKey.Name}] UNIQUEIDENTIFIER)
926
+
927
+ IF @${entity.FirstPrimaryKey.Name} IS NOT NULL
928
+ BEGIN
929
+ -- User provided a value, use it
930
+ INSERT INTO [${entity.SchemaName}].[${entity.BaseTable}]
931
+ (
932
+ [${entity.FirstPrimaryKey.Name}],
933
+ ${this.createEntityFieldsInsertString(entity, entity.Fields, '', true)}
934
+ )
935
+ OUTPUT INSERTED.[${entity.FirstPrimaryKey.Name}] INTO @InsertedRow
936
+ VALUES
937
+ (
938
+ @${entity.FirstPrimaryKey.Name},
939
+ ${this.createEntityFieldsInsertString(entity, entity.Fields, '@', true)}
940
+ )
941
+ END
942
+ ELSE
943
+ BEGIN
944
+ -- No value provided, let database use its default (e.g., NEWSEQUENTIALID())
945
+ INSERT INTO [${entity.SchemaName}].[${entity.BaseTable}]
946
+ (
947
+ ${this.createEntityFieldsInsertString(entity, entity.Fields, '', true)}
948
+ )
949
+ OUTPUT INSERTED.[${entity.FirstPrimaryKey.Name}] INTO @InsertedRow
950
+ VALUES
951
+ (
952
+ ${this.createEntityFieldsInsertString(entity, entity.Fields, '@', true)}
953
+ )
954
+ END`;
955
+ // Clear these as we're handling the INSERT in preInsertCode
956
+ additionalFieldList = '';
957
+ additionalValueList = '';
958
+ outputCode = '';
891
959
  selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = (SELECT [${entity.FirstPrimaryKey.Name}] FROM @InsertedRow)`;
892
960
  }
893
961
  else {
894
- // we have no default value, so we use NEWID() to generate a new GUID and we manually insert it into the table
895
- // as part of the sproc
896
- preInsertCode = `DECLARE @newId UNIQUEIDENTIFIER = NEWID();\n SET @${entity.FirstPrimaryKey.Name} = @newId;\n`;
897
- additionalFieldList = ',\n [' + entity.FirstPrimaryKey.Name + ']';
898
- additionalValueList = ',\n @newId';
899
- selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = @newId`;
962
+ // No default value - we calculate the ID upfront, so no need for OUTPUT clause
963
+ preInsertCode = `DECLARE @ActualID UNIQUEIDENTIFIER = ISNULL(@${entity.FirstPrimaryKey.Name}, NEWID())`;
964
+ additionalFieldList = ',\n [' + entity.FirstPrimaryKey.Name + ']';
965
+ additionalValueList = ',\n @ActualID';
966
+ outputCode = ''; // No OUTPUT clause needed
967
+ // We already know the ID, so just select using it directly
968
+ selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = @ActualID`;
900
969
  }
901
970
  }
902
971
  else {
@@ -921,7 +990,7 @@ CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
921
990
  AS
922
991
  BEGIN
923
992
  SET NOCOUNT ON;
924
- ${preInsertCode}
993
+ ${preInsertCode}${preInsertCode.includes('INSERT INTO') ? '' : `
925
994
  INSERT INTO
926
995
  [${entity.SchemaName}].[${entity.BaseTable}]
927
996
  (
@@ -930,7 +999,7 @@ BEGIN
930
999
  ${outputCode}VALUES
931
1000
  (
932
1001
  ${this.createEntityFieldsInsertString(entity, entity.Fields, '@')}${additionalValueList}
933
- )
1002
+ )`}
934
1003
  -- return the new record from the base view, which might have some calculated fields
935
1004
  ${selectInsertedRecord}
936
1005
  END
@@ -1007,12 +1076,13 @@ GO
1007
1076
  ${updatedAtTrigger}
1008
1077
  `;
1009
1078
  }
1079
+ __specialUUIDValue = '00000000-0000-0000-0000-000000000000';
1010
1080
  createEntityFieldsParamString(entityFields, isUpdate) {
1011
1081
  let sOutput = '', isFirst = true;
1012
1082
  for (let i = 0; i < entityFields.length; ++i) {
1013
1083
  const ef = entityFields[i];
1014
- const autoGeneratedPrimaryKey = ef.AutoIncrement || ef.Type.toLowerCase().trim() === 'uniqueidentifier';
1015
- if ((ef.AllowUpdateAPI || (ef.IsPrimaryKey && isUpdate)) &&
1084
+ const autoGeneratedPrimaryKey = ef.AutoIncrement; // Only exclude auto-increment fields from params
1085
+ if ((ef.AllowUpdateAPI || (ef.IsPrimaryKey && isUpdate) || (ef.IsPrimaryKey && !autoGeneratedPrimaryKey && !isUpdate)) &&
1016
1086
  !ef.IsVirtual &&
1017
1087
  (!ef.IsPrimaryKey || !autoGeneratedPrimaryKey || isUpdate) &&
1018
1088
  !ef.IsSpecialDateField) {
@@ -1020,58 +1090,60 @@ ${updatedAtTrigger}
1020
1090
  sOutput += ',\n ';
1021
1091
  else
1022
1092
  isFirst = false;
1023
- // for unique identifiers, only for spCreate procs, IF there is a default value specified then we append a default parameter value
1024
- // to the sproc param with '00000000-0000-0000-0000-000000000000' so that the sproc can be called without passing in a value
1025
- // within the sproc body for spCreate, we will look for this special value and substitute it with the actual default value for the column (typically newid() or newsequentialid())
1093
+ // Check if we need a default value
1026
1094
  let defaultParamValue = '';
1027
- if (!isUpdate && ef.IsUniqueIdentifier && ef.HasDefaultValue) {
1028
- defaultParamValue = ` = '${this.__specialUUIDValue}'`;
1095
+ if (!isUpdate && ef.IsPrimaryKey && !ef.AutoIncrement) {
1096
+ // For primary keys (non-auto-increment), make them optional with NULL default
1097
+ // This allows callers to omit the PK and let the DB/sproc handle it
1098
+ defaultParamValue = ' = NULL';
1029
1099
  }
1030
1100
  sOutput += `@${ef.CodeName} ${ef.SQLFullType}${defaultParamValue}`;
1031
1101
  }
1032
1102
  }
1033
1103
  return sOutput;
1034
1104
  }
1035
- createEntityFieldsInsertString(entity, entityFields, prefix) {
1036
- const autoGeneratedPrimaryKey = entity.FirstPrimaryKey.AutoIncrement || entity.FirstPrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier';
1105
+ createEntityFieldsInsertString(entity, entityFields, prefix, excludePrimaryKey = false) {
1106
+ const autoGeneratedPrimaryKey = entity.FirstPrimaryKey.AutoIncrement; // Only exclude auto-increment PKs from insert
1037
1107
  let sOutput = '', isFirst = true;
1038
1108
  const filteredFields = entityFields.filter(f => f.AllowUpdateAPI);
1039
1109
  for (let i = 0; i < entityFields.length; ++i) {
1040
1110
  const ef = entityFields[i];
1041
1111
  const quotes = ef.NeedsQuotes ? "'" : "";
1042
1112
  // 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)
1043
- if ((!ef.IsPrimaryKey || !autoGeneratedPrimaryKey) && !ef.IsVirtual && ef.AllowUpdateAPI && !ef.AutoIncrement) {
1044
- if (!isFirst)
1045
- sOutput += ',\n ';
1113
+ // ALSO: if excludePrimaryKey is true, skip all primary key fields
1114
+ if ((excludePrimaryKey && ef.IsPrimaryKey) || (ef.IsPrimaryKey && autoGeneratedPrimaryKey) || ef.IsVirtual || !ef.AllowUpdateAPI || ef.AutoIncrement) {
1115
+ continue; // skip this field
1116
+ }
1117
+ if (!isFirst)
1118
+ sOutput += ',\n ';
1119
+ else
1120
+ isFirst = false;
1121
+ if (prefix !== '' && ef.IsSpecialDateField) {
1122
+ if (ef.IsCreatedAtField || ef.IsUpdatedAtField)
1123
+ sOutput += `GETUTCDATE()`; // we set the inserted row value to the current date for created and updated at fields
1046
1124
  else
1047
- isFirst = false;
1048
- if (prefix !== '' && ef.IsSpecialDateField) {
1049
- if (ef.IsCreatedAtField || ef.IsUpdatedAtField)
1050
- sOutput += `GETUTCDATE()`; // we set the inserted row value to the current date for created and updated at fields
1051
- else
1052
- sOutput += `NULL`; // we don't set the deleted at field on an insert, only on a delete
1053
- }
1054
- else if ((prefix && prefix !== '') && !ef.IsPrimaryKey && ef.IsUniqueIdentifier && ef.HasDefaultValue) {
1055
- // this is the VALUE side (prefix not null/blank), is NOT a primary key, and is a uniqueidentifier column, and has a default value specified
1056
- // in this situation we need to check if the value being passed in is the special value '00000000-0000-0000-0000-000000000000' (which is in __specialUUIDValue) if it is, we substitute it with the actual default value
1057
- // otherwise we use the value passed in
1058
- // next check to make sure ef.DefaultValue does not contain quotes around the value if it is a string type, if it does, we need to remove them
1059
- let defValue = ef.DefaultValue;
1060
- if (ef.TSType === core_1.EntityFieldTSType.String) {
1061
- if (defValue.startsWith("'") && defValue.endsWith("'")) {
1062
- defValue = defValue.substring(1, defValue.length - 1).trim(); // remove the quotes
1063
- }
1125
+ sOutput += `NULL`; // we don't set the deleted at field on an insert, only on a delete
1126
+ }
1127
+ else if ((prefix && prefix !== '') && !ef.IsPrimaryKey && ef.IsUniqueIdentifier && ef.HasDefaultValue) {
1128
+ // this is the VALUE side (prefix not null/blank), is NOT a primary key, and is a uniqueidentifier column, and has a default value specified
1129
+ // in this situation we need to check if the value being passed in is the special value '00000000-0000-0000-0000-000000000000' (which is in __specialUUIDValue) if it is, we substitute it with the actual default value
1130
+ // otherwise we use the value passed in
1131
+ // next check to make sure ef.DefaultValue does not contain quotes around the value if it is a string type, if it does, we need to remove them
1132
+ let defValue = ef.DefaultValue;
1133
+ if (ef.TSType === core_1.EntityFieldTSType.String) {
1134
+ if (defValue.startsWith("'") && defValue.endsWith("'")) {
1135
+ defValue = defValue.substring(1, defValue.length - 1).trim(); // remove the quotes
1064
1136
  }
1065
- sOutput += `CASE @${ef.CodeName} WHEN '${this.__specialUUIDValue}' THEN ${quotes}${defValue}${quotes} ELSE @${ef.CodeName} END`;
1066
- }
1067
- else {
1068
- let sVal = '';
1069
- if (!prefix || prefix.length === 0)
1070
- sVal = '[' + ef.Name + ']'; // always put field names in brackets so that if reserved words are being used for field names in a table like "USER" and so on, they still work
1071
- else
1072
- sVal = prefix + ef.CodeName;
1073
- sOutput += sVal;
1074
1137
  }
1138
+ sOutput += `CASE @${ef.CodeName} WHEN '${this.__specialUUIDValue}' THEN ${quotes}${defValue}${quotes} ELSE @${ef.CodeName} END`;
1139
+ }
1140
+ else {
1141
+ let sVal = '';
1142
+ if (!prefix || prefix.length === 0)
1143
+ sVal = '[' + ef.Name + ']'; // always put field names in brackets so that if reserved words are being used for field names in a table like "USER" and so on, they still work
1144
+ else
1145
+ sVal = prefix + ef.CodeName;
1146
+ sOutput += sVal;
1075
1147
  }
1076
1148
  }
1077
1149
  return sOutput;