@memberjunction/codegen-lib 1.8.1 → 2.1.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 (63) hide show
  1. package/dist/Angular/angular-codegen.js +1 -1
  2. package/dist/Angular/angular-codegen.js.map +1 -1
  3. package/dist/Angular/join-grid-related-entity-component.d.ts +0 -1
  4. package/dist/Angular/join-grid-related-entity-component.d.ts.map +1 -1
  5. package/dist/Angular/join-grid-related-entity-component.js +0 -13
  6. package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
  7. package/dist/Angular/related-entity-components.d.ts +11 -1
  8. package/dist/Angular/related-entity-components.d.ts.map +1 -1
  9. package/dist/Angular/related-entity-components.js +26 -1
  10. package/dist/Angular/related-entity-components.js.map +1 -1
  11. package/dist/Angular/timeline-related-entity-component.d.ts +36 -0
  12. package/dist/Angular/timeline-related-entity-component.d.ts.map +1 -0
  13. package/dist/Angular/timeline-related-entity-component.js +67 -0
  14. package/dist/Angular/timeline-related-entity-component.js.map +1 -0
  15. package/dist/Config/db-connection.d.ts +3 -0
  16. package/dist/Config/db-connection.d.ts.map +1 -1
  17. package/dist/Config/db-connection.js +46 -1
  18. package/dist/Config/db-connection.js.map +1 -1
  19. package/dist/Database/manage-metadata.d.ts +23 -8
  20. package/dist/Database/manage-metadata.d.ts.map +1 -1
  21. package/dist/Database/manage-metadata.js +362 -155
  22. package/dist/Database/manage-metadata.js.map +1 -1
  23. package/dist/Database/sql.d.ts +12 -1
  24. package/dist/Database/sql.d.ts.map +1 -1
  25. package/dist/Database/sql.js +196 -36
  26. package/dist/Database/sql.js.map +1 -1
  27. package/dist/Database/sql_codegen.d.ts +12 -4
  28. package/dist/Database/sql_codegen.d.ts.map +1 -1
  29. package/dist/Database/sql_codegen.js +210 -103
  30. package/dist/Database/sql_codegen.js.map +1 -1
  31. package/dist/Misc/createNewUser.d.ts.map +1 -1
  32. package/dist/Misc/createNewUser.js +2 -1
  33. package/dist/Misc/createNewUser.js.map +1 -1
  34. package/dist/Misc/util.d.ts +1 -0
  35. package/dist/Misc/util.d.ts.map +1 -1
  36. package/dist/Misc/util.js +24 -2
  37. package/dist/Misc/util.js.map +1 -1
  38. package/dist/action_subclasses_codegen.d.ts +1 -1
  39. package/dist/action_subclasses_codegen.js +1 -1
  40. package/dist/action_subclasses_codegen.js.map +1 -1
  41. package/dist/entity_subclasses_codegen.d.ts.map +1 -1
  42. package/dist/entity_subclasses_codegen.js +72 -75
  43. package/dist/entity_subclasses_codegen.js.map +1 -1
  44. package/dist/entity_types_codegen.d.ts +15 -0
  45. package/dist/entity_types_codegen.d.ts.map +1 -0
  46. package/dist/entity_types_codegen.js +106 -0
  47. package/dist/entity_types_codegen.js.map +1 -0
  48. package/dist/graphql_server_codegen.d.ts +4 -4
  49. package/dist/graphql_server_codegen.d.ts.map +1 -1
  50. package/dist/graphql_server_codegen.js +65 -58
  51. package/dist/graphql_server_codegen.js.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/runCodeGen.d.ts.map +1 -1
  57. package/dist/runCodeGen.js +27 -4
  58. package/dist/runCodeGen.js.map +1 -1
  59. package/package.json +8 -7
  60. package/dist/createNewUser.d.ts +0 -12
  61. package/dist/createNewUser.d.ts.map +0 -1
  62. package/dist/createNewUser.js +0 -113
  63. package/dist/createNewUser.js.map +0 -1
@@ -74,34 +74,39 @@ let SQLCodeGenBase = class SQLCodeGenBase {
74
74
  const baselineEntities = entities.filter(e => e.IncludeInAPI);
75
75
  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
76
76
  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
77
- // STEP 2(a) - generate all the SQL files and execute them
77
+ // STEP 2(a) - clean out all *.generated.sql and *.permissions.generated.sql files from the directory
78
+ this.deleteGeneratedEntityFiles(directory, baselineEntities);
79
+ // STEP 2(b) - generate all the SQL files and execute them
78
80
  const step2StartTime = new Date();
79
- if (!await this.generateAndExecuteEntitySQLToSeparateFiles(ds, includedEntities, directory, false)) {
80
- (0, logging_1.logError)('Error generating and executing all entities SQL to separate files');
81
+ const genResult = await this.generateAndExecuteEntitySQLToSeparateFiles(ds, includedEntities, directory, false, true);
82
+ if (!genResult.Success) {
83
+ (0, logging_1.logError)('Error generating all entities SQL to separate files');
81
84
  return false;
82
85
  }
83
- // STEP 2(b) - for the excludedEntities, while we don't want to generate SQL, we do want to generate the permissions files for them
84
- if (!await this.generateAndExecuteEntitySQLToSeparateFiles(ds, excludedEntities, directory, true)) {
85
- (0, logging_1.logError)('Error generating and executing permissions SQL for excluded entities to separate files');
86
+ // STEP 2(c) - for the excludedEntities, while we don't want to generate SQL, we do want to generate the permissions files for them
87
+ const genResult2 = await this.generateAndExecuteEntitySQLToSeparateFiles(ds, excludedEntities, directory, true, true);
88
+ if (!genResult2.Success) {
89
+ (0, logging_1.logError)('Error generating permissions SQL for excluded entities to separate files');
86
90
  return false;
87
91
  }
88
- (0, logging_1.logStatus)(` Time to Generate/Execute Entity SQL: ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
89
- // now that we've generated the SQL, let's create a combined file in each schema sub-directory for convenience for a DBA
90
- this.createCombinedEntitySQLFiles(directory, baselineEntities);
92
+ (0, logging_1.logStatus)(` Time to generate entity SQL: ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
93
+ // 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
94
+ const allEntityFiles = this.createCombinedEntitySQLFiles(directory, baselineEntities);
95
+ // STEP 2(e) ---- FINALLY, we now execute all the combined files by schema;
96
+ const step2eStartTime = new Date();
97
+ if (!await this.SQLUtilityObject.executeSQLFiles(allEntityFiles, true)) {
98
+ (0, logging_1.logError)('Error executing combined entity SQL files');
99
+ return false;
100
+ }
101
+ const step2eEndTime = new Date();
102
+ (0, logging_1.logStatus)(` Time to Execute Combined Entity SQL: ${(step2eEndTime.getTime() - step2eStartTime.getTime()) / 1000} seconds`);
91
103
  const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
92
104
  // 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
93
- if (!await manageMD.manageEntityFields(ds, config_1.configInfo.excludeSchemas, true)) {
105
+ // we CAN skip the entity field values part because that wouldn't change from the first time we ran it
106
+ if (!await manageMD.manageEntityFields(ds, config_1.configInfo.excludeSchemas, true, true)) {
94
107
  (0, logging_1.logError)('Error managing entity fields');
95
108
  return false;
96
109
  }
97
- // 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
98
- // STEP 4- Apply permissions, executing all .permissions files
99
- const step4StartTime = new Date();
100
- if (!await this.applyPermissions(ds, directory, baselineEntities)) {
101
- (0, logging_1.logError)('Error applying permissions');
102
- return false;
103
- }
104
- (0, logging_1.logStatus)(` Time to Apply Permissions: ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
105
110
  // STEP 5 - execute any custom SQL scripts that should run afterwards
106
111
  const step5StartTime = new Date();
107
112
  if (!await this.runCustomSQLScripts(ds, 'after-sql'))
@@ -125,7 +130,7 @@ let SQLCodeGenBase = class SQLCodeGenBase {
125
130
  if (scripts) {
126
131
  for (let i = 0; i < scripts.length; ++i) {
127
132
  const s = scripts[i];
128
- if (!await this.SQLUtilityObject.executeSQLFile(ds, s.scriptFile, true)) {
133
+ if (!await this.SQLUtilityObject.executeSQLFile(s.scriptFile)) {
129
134
  (0, logging_1.logError)(`Error executing custom '${when}' SQL script ${s.scriptFile}`);
130
135
  bSuccess = false; // keep going if we have more scripts, but make sure we return false
131
136
  }
@@ -150,7 +155,7 @@ let SQLCodeGenBase = class SQLCodeGenBase {
150
155
  for (const f of files) {
151
156
  const fullPath = path_1.default.join(directory, f);
152
157
  if (fs.existsSync(fullPath)) {
153
- if (!await this.SQLUtilityObject.executeSQLFile(ds, fullPath, true))
158
+ if (!await this.SQLUtilityObject.executeSQLFile(fullPath))
154
159
  innerSuccess = false; // we keep going, just note that something failed
155
160
  }
156
161
  else {
@@ -181,7 +186,8 @@ let SQLCodeGenBase = class SQLCodeGenBase {
181
186
  * @param directory The directory to save the generated SQL files to
182
187
  * @param onlyPermissions If true, only the permissions files will be generated and executed, not the actual SQL files. Use this if you are simply setting permission changes but no actual changes to the entities have occured.
183
188
  */
184
- async generateAndExecuteEntitySQLToSeparateFiles(ds, entities, directory, onlyPermissions, writeFiles = true, batchSize = 5) {
189
+ async generateAndExecuteEntitySQLToSeparateFiles(ds, entities, directory, onlyPermissions, skipExecution = false, writeFiles = true, batchSize = 5) {
190
+ const files = [];
185
191
  try {
186
192
  let bFail = false;
187
193
  const totalEntities = entities.length;
@@ -191,46 +197,80 @@ let SQLCodeGenBase = class SQLCodeGenBase {
191
197
  const pkeyField = e.Fields.find(f => f.IsPrimaryKey);
192
198
  if (!pkeyField) {
193
199
  (0, 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`);
194
- return false;
200
+ return { Success: false, Files: [] };
195
201
  }
196
- return this.generateAndExecuteSingleEntitySQLToSeparateFiles(ds, e, directory, onlyPermissions, writeFiles);
202
+ return this.generateAndExecuteSingleEntitySQLToSeparateFiles(ds, e, directory, onlyPermissions, skipExecution, writeFiles);
197
203
  });
198
204
  const results = await Promise.all(promises);
199
- if (results.includes(false)) {
200
- bFail = true; // keep going, but will return false at the end
201
- }
205
+ results.forEach(r => {
206
+ if (!r.Success)
207
+ bFail = true; // keep going, but will return false at the end
208
+ files.push(...r.Files); // add the files to the main files array
209
+ });
202
210
  }
203
- return !bFail;
211
+ return { Success: !bFail, Files: files };
204
212
  }
205
213
  catch (err) {
206
214
  (0, logging_1.logError)(err);
207
- return false;
215
+ return { Success: false, Files: files };
208
216
  }
209
217
  }
210
- async createCombinedEntitySQLFiles(directory, entities) {
218
+ deleteGeneratedEntityFiles(directory, entities) {
219
+ try {
220
+ // for the schemas associated with the specified entities, clean out all the generated files
221
+ const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
222
+ for (const s of schemaNames) {
223
+ const fullPath = path_1.default.join(directory, s);
224
+ // now, within each schema directory, clean out all the generated files
225
+ // the generated files map this pattern: *.generated.sql or *.permissions.generated.sql
226
+ if (fs.statSync(fullPath).isDirectory()) {
227
+ const files = fs.readdirSync(fullPath).filter(f => f.endsWith('.generated.sql') || f.endsWith('.permissions.generated.sql'));
228
+ for (const f of files) {
229
+ const filePath = path_1.default.join(fullPath, f);
230
+ fs.unlinkSync(filePath);
231
+ }
232
+ }
233
+ }
234
+ }
235
+ catch (e) {
236
+ (0, logging_1.logError)(e);
237
+ }
238
+ }
239
+ createCombinedEntitySQLFiles(directory, entities) {
211
240
  // first, get a disinct list of schemanames from the entities
241
+ const files = [];
212
242
  const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
213
243
  for (const s of schemaNames) {
214
244
  // generate the all-entities.sql file and all-entities.permissions.sql file in each schema folder
215
245
  const fullPath = path_1.default.join(directory, s);
216
246
  if (fs.statSync(fullPath).isDirectory()) {
217
247
  (0, util_1.combineFiles)(fullPath, '_all_entities.sql', '*.generated.sql', true);
248
+ files.push(path_1.default.join(fullPath, '_all_entities.sql'));
218
249
  (0, util_1.combineFiles)(fullPath, '_all_entities.permissions.sql', '*.permissions.generated.sql', true);
250
+ files.push(path_1.default.join(fullPath, '_all_entities.permissions.sql'));
219
251
  }
220
252
  }
253
+ return files;
221
254
  }
222
- async generateAndExecuteSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, writeFiles = true) {
255
+ async generateAndExecuteSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, skipExecution = false, writeFiles = true) {
223
256
  try {
224
- const { sql, permissionsSQL } = await this.generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, writeFiles); // this creates the files and returns a single string with all the SQL we can then execute
225
- return await this.SQLUtilityObject.executeSQLScript(ds, sql, true) &&
226
- await this.SQLUtilityObject.executeSQLScript(ds, permissionsSQL, true);
257
+ const { sql, permissionsSQL, files } = await this.generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, skipExecution, writeFiles); // this creates the files and returns a single string with all the SQL we can then execute
258
+ if (!skipExecution) {
259
+ return {
260
+ Success: await this.SQLUtilityObject.executeSQLScript(ds, sql + "\n\nGO\n\n" + permissionsSQL, true), // combine the SQL and permissions and execute it,
261
+ Files: files
262
+ };
263
+ }
264
+ else
265
+ return { Success: true, Files: files };
227
266
  }
228
267
  catch (err) {
229
268
  (0, logging_1.logError)(err);
230
- return false;
269
+ return { Success: false, Files: [] };
231
270
  }
232
271
  }
233
- async generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, writeFiles = true) {
272
+ async generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, skipExecution = false, writeFiles = true) {
273
+ const files = [];
234
274
  try {
235
275
  // create the directory if it doesn't exist
236
276
  if (writeFiles && !fs.existsSync(directory))
@@ -246,16 +286,21 @@ let SQLCodeGenBase = class SQLCodeGenBase {
246
286
  // generate the base view
247
287
  const s = this.generateSingleEntitySQLFileHeader(entity, entity.BaseView) + await this.generateBaseView(ds, entity);
248
288
  const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('view', entity.SchemaName, entity.BaseView, false, true));
249
- if (writeFiles)
289
+ if (writeFiles) {
250
290
  fs.writeFileSync(filePath, s);
291
+ files.push(filePath);
292
+ }
251
293
  sRet += s + '\nGO\n';
252
294
  }
253
295
  // always generate permissions for the base view
254
296
  const s = this.generateSingleEntitySQLFileHeader(entity, 'Permissions for ' + entity.BaseView) + this.generateViewPermissions(entity);
255
297
  if (s.length > 0)
256
298
  permissionsSQL += s + '\nGO\n';
257
- if (writeFiles)
258
- fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('view', entity.SchemaName, entity.BaseView, true, true)), s);
299
+ if (writeFiles) {
300
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('view', entity.SchemaName, entity.BaseView, true, true));
301
+ fs.writeFileSync(filePath, s);
302
+ files.push(filePath);
303
+ }
259
304
  // now, append the permissions to the return string IF we did NOT generate the base view - because if we generated the base view, that
260
305
  // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
261
306
  if (!entity.BaseViewGenerated)
@@ -266,15 +311,21 @@ let SQLCodeGenBase = class SQLCodeGenBase {
266
311
  if (!onlyPermissions && entity.spCreateGenerated) {
267
312
  // generate the create SP
268
313
  const s = this.generateSingleEntitySQLFileHeader(entity, spName) + this.generateSPCreate(entity);
269
- if (writeFiles)
270
- fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true)), s);
314
+ if (writeFiles) {
315
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true));
316
+ fs.writeFileSync(filePath, s);
317
+ files.push(filePath);
318
+ }
271
319
  sRet += s + '\nGO\n';
272
320
  }
273
321
  const s = this.generateSPPermissions(entity, spName, exports.SPType.Create) + '\n\n';
274
322
  if (s.length > 0)
275
323
  permissionsSQL += s + '\nGO\n';
276
- if (writeFiles)
277
- fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true)), s);
324
+ if (writeFiles) {
325
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true));
326
+ fs.writeFileSync(filePath, s);
327
+ files.push(filePath);
328
+ }
278
329
  // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
279
330
  // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
280
331
  if (!entity.spCreateGenerated)
@@ -286,15 +337,21 @@ let SQLCodeGenBase = class SQLCodeGenBase {
286
337
  if (!onlyPermissions && entity.spUpdateGenerated) {
287
338
  // generate the update SP
288
339
  const s = this.generateSingleEntitySQLFileHeader(entity, spName) + this.generateSPUpdate(entity);
289
- if (writeFiles)
290
- fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true)), s);
340
+ if (writeFiles) {
341
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true));
342
+ fs.writeFileSync(filePath, s);
343
+ files.push(filePath);
344
+ }
291
345
  sRet += s + '\nGO\n';
292
346
  }
293
347
  const s = this.generateSPPermissions(entity, spName, exports.SPType.Update) + '\n\n';
294
348
  if (s.length > 0)
295
349
  permissionsSQL += s + '\nGO\n';
296
- if (writeFiles)
297
- fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true)), s);
350
+ if (writeFiles) {
351
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true));
352
+ fs.writeFileSync(filePath, s);
353
+ files.push(filePath);
354
+ }
298
355
  // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
299
356
  // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
300
357
  if (!entity.spUpdateGenerated)
@@ -306,15 +363,21 @@ let SQLCodeGenBase = class SQLCodeGenBase {
306
363
  if (!onlyPermissions && entity.spDeleteGenerated) {
307
364
  // generate the delete SP
308
365
  const s = this.generateSingleEntitySQLFileHeader(entity, spName) + this.generateSPDelete(entity);
309
- if (writeFiles)
310
- fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true)), s);
366
+ if (writeFiles) {
367
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true));
368
+ fs.writeFileSync(filePath, s);
369
+ files.push(filePath);
370
+ }
311
371
  sRet += s + '\nGO\n';
312
372
  }
313
373
  const s = this.generateSPPermissions(entity, spName, exports.SPType.Delete) + '\n\n';
314
374
  if (s.length > 0)
315
375
  permissionsSQL += s + '\nGO\n';
316
- if (writeFiles)
317
- fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true)), s);
376
+ if (writeFiles) {
377
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true));
378
+ fs.writeFileSync(filePath, s);
379
+ files.push(filePath);
380
+ }
318
381
  // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
319
382
  // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
320
383
  if (!entity.spDeleteGenerated)
@@ -327,22 +390,26 @@ let SQLCodeGenBase = class SQLCodeGenBase {
327
390
  if (!onlyPermissions) {
328
391
  // only write the actual sql out if we're not only generating permissions
329
392
  const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', entity.SchemaName, entity.BaseTable, false, true));
330
- if (writeFiles)
393
+ if (writeFiles) {
331
394
  fs.writeFileSync(filePath, ft.sql);
395
+ files.push(filePath);
396
+ }
332
397
  sRet += ft.sql + '\nGO\n';
333
398
  }
334
399
  const sP = this.generateFullTextSearchFunctionPermissions(entity, ft.functionName) + '\n\n';
335
400
  if (sP.length > 0)
336
401
  permissionsSQL += sP + '\nGO\n';
337
402
  const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', entity.SchemaName, entity.BaseTable, true, true));
338
- if (writeFiles)
403
+ if (writeFiles) {
339
404
  fs.writeFileSync(filePath, sP);
405
+ files.push(filePath);
406
+ }
340
407
  // now, append the permissions to the return string IF we did NOT generate the function - because if we generated the function, that
341
408
  // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
342
409
  if (!entity.FullTextSearchFunctionGenerated)
343
410
  sRet += sP + '\nGO\n';
344
411
  }
345
- return { sql: sRet, permissionsSQL: permissionsSQL };
412
+ return { sql: sRet, permissionsSQL: permissionsSQL, files: files };
346
413
  }
347
414
  catch (err) {
348
415
  (0, logging_1.logError)(err);
@@ -555,6 +622,9 @@ let SQLCodeGenBase = class SQLCodeGenBase {
555
622
  const relatedFieldsString = await this.generateBaseViewRelatedFieldsString(ds, entity.Fields);
556
623
  const relatedFieldsJoinString = this.generateBaseViewJoins(entity.Fields);
557
624
  const permissions = this.generateViewPermissions(entity);
625
+ const whereClause = entity.DeleteType === 'Soft' ? `WHERE
626
+ ${baseTableFirstChar}.[${core_1.EntityInfo.DeletedAtFieldName}] IS NULL
627
+ ` : '';
558
628
  return `
559
629
  ------------------------------------------------------------
560
630
  ----- BASE VIEW FOR ENTITY: ${entity.Name}
@@ -571,7 +641,7 @@ SELECT
571
641
  ${baseTableFirstChar}.*${relatedFieldsString.length > 0 ? ',' : ''}${relatedFieldsString}
572
642
  FROM
573
643
  [${entity.SchemaName}].[${entity.BaseTable}] AS ${baseTableFirstChar}${relatedFieldsJoinString ? '\n' + relatedFieldsJoinString : ''}
574
- GO${permissions}
644
+ ${whereClause}GO${permissions}
575
645
  `;
576
646
  }
577
647
  generateViewPermissions(entity) {
@@ -597,38 +667,37 @@ GO${permissions}
597
667
  let sOutput = '';
598
668
  let fieldCount = 0;
599
669
  const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
600
- for (let i = 0; i < entityFields.length; i++) {
601
- const ef = entityFields[i];
602
- if (ef.RelatedEntityID && ef.IncludeRelatedEntityNameFieldInBaseView) {
603
- const { nameField, nameFieldIsVirtual } = this.getIsNameFieldForSingleEntity(ef.RelatedEntity);
604
- if (nameField !== '') {
605
- // only add to the output, if we found a name field for the related entity.
606
- ef._RelatedEntityTableAlias = ef.RelatedEntityClassName + '_' + ef.Name;
607
- ef._RelatedEntityNameFieldIsVirtual = nameFieldIsVirtual;
608
- // This next section generates a field name for the new virtual field and makes sure it doesn't collide with a field in the base table
609
- const candidateName = this.stripID(ef.Name);
610
- // 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)
611
- // because if it is, we need to change it to something else
612
- const bFound = entityFields.find(f => f.IsVirtual === false && f.Name.trim().toLowerCase() === candidateName.trim().toLowerCase()) !== undefined;
613
- if (bFound)
614
- ef._RelatedEntityNameFieldMap = candidateName + '_Virtual';
615
- else
616
- ef._RelatedEntityNameFieldMap = candidateName;
617
- // now we have a safe field name alias for the new virtual field in the _RelatedEntityNameFieldMap property, so use it...
618
- sOutput += `${fieldCount == 0 ? '' : ','}\n ${ef._RelatedEntityTableAlias}.[${nameField}] AS [${ef._RelatedEntityNameFieldMap}]`;
619
- // check to see if the database already knows about the RelatedEntityNameFieldMap or not
620
- if (ef.RelatedEntityNameFieldMap === null ||
621
- ef.RelatedEntityNameFieldMap === undefined ||
622
- ef.RelatedEntityNameFieldMap.trim().length === 0) {
623
- // the database doesn't yet know about this RelatedEntityNameFieldMap, so we need to update it
624
- // first update the actul field in the metadata object so it can be used from this point forward
625
- // and it also reflects what the DB will hold
626
- ef.RelatedEntityNameFieldMap = ef._RelatedEntityNameFieldMap;
627
- // then update the database itself
628
- await manageMD.updateEntityFieldRelatedEntityNameFieldMap(ds, ef.ID, ef.RelatedEntityNameFieldMap);
629
- }
630
- fieldCount++;
670
+ // next get the fields that are related entities and have the IncludeRelatedEntityNameFieldInBaseView flag set to true
671
+ const qualifyingFields = entityFields.filter(f => f.RelatedEntityID && f.IncludeRelatedEntityNameFieldInBaseView);
672
+ for (const ef of qualifyingFields) {
673
+ const { nameField, nameFieldIsVirtual } = this.getIsNameFieldForSingleEntity(ef.RelatedEntity);
674
+ if (nameField !== '') {
675
+ // only add to the output, if we found a name field for the related entity.
676
+ ef._RelatedEntityTableAlias = ef.RelatedEntityClassName + '_' + ef.Name;
677
+ ef._RelatedEntityNameFieldIsVirtual = nameFieldIsVirtual;
678
+ // This next section generates a field name for the new virtual field and makes sure it doesn't collide with a field in the base table
679
+ const candidateName = this.stripID(ef.Name);
680
+ // 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)
681
+ // because if it is, we need to change it to something else
682
+ const bFound = entityFields.find(f => f.IsVirtual === false && f.Name.trim().toLowerCase() === candidateName.trim().toLowerCase()) !== undefined;
683
+ if (bFound)
684
+ ef._RelatedEntityNameFieldMap = candidateName + '_Virtual';
685
+ else
686
+ ef._RelatedEntityNameFieldMap = candidateName;
687
+ // now we have a safe field name alias for the new virtual field in the _RelatedEntityNameFieldMap property, so use it...
688
+ sOutput += `${fieldCount === 0 ? '' : ','}\n ${ef._RelatedEntityTableAlias}.[${nameField}] AS [${ef._RelatedEntityNameFieldMap}]`;
689
+ // check to see if the database already knows about the RelatedEntityNameFieldMap or not
690
+ if (ef.RelatedEntityNameFieldMap === null ||
691
+ ef.RelatedEntityNameFieldMap === undefined ||
692
+ ef.RelatedEntityNameFieldMap.trim().length === 0) {
693
+ // the database doesn't yet know about this RelatedEntityNameFieldMap, so we need to update it
694
+ // first update the actul field in the metadata object so it can be used from this point forward
695
+ // and it also reflects what the DB will hold
696
+ ef.RelatedEntityNameFieldMap = ef._RelatedEntityNameFieldMap;
697
+ // then update the database itself
698
+ await manageMD.updateEntityFieldRelatedEntityNameFieldMap(ds, ef.ID, ef.RelatedEntityNameFieldMap);
631
699
  }
700
+ fieldCount++;
632
701
  }
633
702
  }
634
703
  return sOutput;
@@ -673,9 +742,12 @@ GO${permissions}
673
742
  }
674
743
  generateSPCreate(entity) {
675
744
  const spName = entity.spCreate ? entity.spCreate : `spCreate${entity.ClassName}`;
676
- const efString = this.createEntityFieldsParamString(entity.Fields, !entity.FirstPrimaryKey.AutoIncrement);
745
+ const firstKey = entity.FirstPrimaryKey;
746
+ const primaryKeyAutomatic = firstKey.AutoIncrement || (firstKey.Type.toLowerCase().trim() === 'uniqueidentifier' && firstKey.DefaultValue && firstKey.DefaultValue.trim().length > 0);
747
+ const efString = this.createEntityFieldsParamString(entity.Fields, !primaryKeyAutomatic);
677
748
  const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Create);
678
- let primaryKeyInsertValue = '';
749
+ let preInsertCode = '';
750
+ let outputCode = '';
679
751
  let selectInsertedRecord = '';
680
752
  let additionalFieldList = '';
681
753
  let additionalValueList = '';
@@ -683,10 +755,29 @@ GO${permissions}
683
755
  selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = SCOPE_IDENTITY()`;
684
756
  }
685
757
  else if (entity.FirstPrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier') {
686
- primaryKeyInsertValue = `DECLARE @newId UNIQUEIDENTIFIER = NEWID();\n SET @${entity.FirstPrimaryKey.Name} = @newId;\n`;
687
- additionalFieldList = ',\n [' + entity.FirstPrimaryKey.CodeName + ']';
688
- additionalValueList = ',\n @newId';
689
- selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = @newId`;
758
+ // our primary key is a uniqueidentifier. Two scenarios exist for this:
759
+ // 1) The default value is specified in the database (usually either NEWID() or NEWSEQUENTIALID()).
760
+ // 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.
761
+ // so, first check to see if there is a default value for the field or not
762
+ const hasDefaultValue = entity.FirstPrimaryKey.DefaultValue && entity.FirstPrimaryKey.DefaultValue.trim().length > 0;
763
+ // if we have a default value, then we do NOT want to insert a new value, let the database use the default
764
+ if (hasDefaultValue) {
765
+ // in this situation, we DO have a default value, so we do NOT insert a new value, we let the database use the default
766
+ // however, we need to do some extra preprocessing to use the OUTPUT from the INSERT statement and return the record that was
767
+ // just created, that is how we get the newly created GUID that was the default value
768
+ preInsertCode = `DECLARE @InsertedRow TABLE ([${entity.FirstPrimaryKey.Name}] UNIQUEIDENTIFIER)`;
769
+ outputCode = `OUTPUT INSERTED.[${entity.FirstPrimaryKey.Name}] INTO @InsertedRow
770
+ `;
771
+ selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = (SELECT [${entity.FirstPrimaryKey.Name}] FROM @InsertedRow)`;
772
+ }
773
+ else {
774
+ // we have no default value, so we use NEWID() to generate a new GUID and we manually insert it into the table
775
+ // as part of the sproc
776
+ preInsertCode = `DECLARE @newId UNIQUEIDENTIFIER = NEWID();\n SET @${entity.FirstPrimaryKey.Name} = @newId;\n`;
777
+ additionalFieldList = ',\n [' + entity.FirstPrimaryKey.Name + ']';
778
+ additionalValueList = ',\n @newId';
779
+ selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.FirstPrimaryKey.Name}] = @newId`;
780
+ }
690
781
  }
691
782
  else {
692
783
  selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE `;
@@ -710,13 +801,13 @@ CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
710
801
  AS
711
802
  BEGIN
712
803
  SET NOCOUNT ON;
713
- ${primaryKeyInsertValue}
804
+ ${preInsertCode}
714
805
  INSERT INTO
715
806
  [${entity.SchemaName}].[${entity.BaseTable}]
716
807
  (
717
808
  ${this.createEntityFieldsInsertString(entity, entity.Fields, '')}${additionalFieldList}
718
809
  )
719
- VALUES
810
+ ${outputCode}VALUES
720
811
  (
721
812
  ${this.createEntityFieldsInsertString(entity, entity.Fields, '@')}${additionalValueList}
722
813
  )
@@ -822,14 +913,17 @@ ${updatedAtTrigger}
822
913
  if ((!ef.IsPrimaryKey || !autoGeneratedPrimaryKey) &&
823
914
  ef.IsVirtual === false &&
824
915
  ef.AllowUpdateAPI &&
825
- ef.AutoIncrement === false &&
826
- ef.Type.trim().toLowerCase() !== 'uniqueidentifier') {
916
+ ef.AutoIncrement === false) {
827
917
  if (!isFirst)
828
918
  sOutput += ',\n ';
829
919
  else
830
920
  isFirst = false;
831
- if (prefix !== '' && ef.IsSpecialDateField)
832
- sOutput += `GETUTCDATE()`;
921
+ if (prefix !== '' && ef.IsSpecialDateField) {
922
+ if (ef.IsCreatedAtField || ef.IsUpdatedAtField)
923
+ sOutput += `GETUTCDATE()`; // we set the inserted row value to the current date for created and updated at fields
924
+ else
925
+ sOutput += `NULL`; // we don't set the deleted at field on an insert, only on a delete
926
+ }
833
927
  else {
834
928
  let sVal = prefix + (prefix !== '' ? ef.CodeName : ef.Name); // if we have a prefix, then we need to use the CodeName, otherwise we use the actual field name
835
929
  if (!prefix || prefix.length === 0)
@@ -848,8 +942,7 @@ ${updatedAtTrigger}
848
942
  !ef.IsVirtual &&
849
943
  ef.AllowUpdateAPI &&
850
944
  !ef.AutoIncrement &&
851
- !ef.IsSpecialDateField &&
852
- ef.Type.toLowerCase().trim() !== 'uniqueidentifier') {
945
+ !ef.IsSpecialDateField) {
853
946
  if (!isFirst)
854
947
  sOutput += ',\n ';
855
948
  else
@@ -873,6 +966,23 @@ ${updatedAtTrigger}
873
966
  sSelect += ', ';
874
967
  sSelect += `@${k.CodeName} AS [${k.Name}]`;
875
968
  }
969
+ // next up, create the delete code which is based on the type of delete the entity is set to
970
+ // start off by creating the where clause first and then prepend the delete or update statement to it
971
+ let deleteCode = ` WHERE
972
+ ${entity.PrimaryKeys.map(k => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
973
+ `;
974
+ if (entity.DeleteType === 'Hard') {
975
+ deleteCode = ` DELETE FROM
976
+ [${entity.SchemaName}].[${entity.BaseTable}]
977
+ ${deleteCode}`;
978
+ }
979
+ else {
980
+ deleteCode = ` UPDATE
981
+ [${entity.SchemaName}].[${entity.BaseTable}]
982
+ SET
983
+ ${core_1.EntityInfo.DeletedAtFieldName} = GETUTCDATE()
984
+ ${deleteCode} AND ${core_1.EntityInfo.DeletedAtFieldName} IS NULL -- don't update the record if it's already been deleted via a soft delete`;
985
+ }
876
986
  return `
877
987
  ------------------------------------------------------------
878
988
  ----- DELETE PROCEDURE FOR ${entity.BaseTable}
@@ -886,10 +996,7 @@ AS
886
996
  BEGIN
887
997
  SET NOCOUNT ON;${sCascadeDeletes}
888
998
 
889
- DELETE FROM
890
- [${entity.SchemaName}].[${entity.BaseTable}]
891
- WHERE
892
- ${entity.PrimaryKeys.map(k => `[${k.Name}] = @${k.CodeName}`).join(' AND ')}
999
+ ${deleteCode}
893
1000
 
894
1001
  SELECT ${sSelect} -- Return the primary key to indicate we successfully deleted the record
895
1002
  END