@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
@@ -1,10 +1,36 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
2
18
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
19
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
20
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
21
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
22
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
23
  };
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ var __importDefault = (this && this.__importDefault) || function (mod) {
32
+ return (mod && mod.__esModule) ? mod : { "default": mod };
33
+ };
8
34
  var ManageMetadataBase_1;
9
35
  Object.defineProperty(exports, "__esModule", { value: true });
10
36
  exports.ManageMetadataBase = void 0;
@@ -14,11 +40,19 @@ const logging_1 = require("../Misc/logging");
14
40
  const sql_1 = require("./sql");
15
41
  const advanced_generation_1 = require("../Misc/advanced_generation");
16
42
  const global_1 = require("@memberjunction/global");
43
+ const fs = __importStar(require("fs"));
44
+ const path_1 = __importDefault(require("path"));
17
45
  /**
18
46
  * Base class for managing metadata within the CodeGen system. This class can be sub-classed to extend/override base class functionality. Make sure to use the RegisterClass decorator from the @memberjunction/global package
19
47
  * to properly register your subclass with a priority of 1+ to ensure it gets instantiated.
20
48
  */
21
49
  let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
50
+ constructor() {
51
+ this._sqlUtilityObject = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
52
+ }
53
+ get SQLUtilityObject() {
54
+ return this._sqlUtilityObject;
55
+ }
22
56
  static get newEntityList() {
23
57
  return this._newEntityList;
24
58
  }
@@ -31,33 +65,55 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
31
65
  const md = new core_1.Metadata();
32
66
  const excludeSchemas = config_1.configInfo.excludeSchemas ? config_1.configInfo.excludeSchemas : [];
33
67
  let bSuccess = true;
68
+ let start = new Date();
69
+ (0, logging_1.logStatus)(' Creating new entities...');
34
70
  if (!await this.createNewEntities(ds)) {
35
- (0, logging_1.logError)('Error creating new entities');
71
+ (0, logging_1.logError)(' Error creating new entities');
36
72
  bSuccess = false;
37
73
  }
74
+ (0, logging_1.logStatus)(` > Created new entities in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
75
+ start = new Date();
76
+ (0, logging_1.logStatus)(' Updating existing entities...');
38
77
  if (!await this.updateExistingEntitiesFromSchema(ds, excludeSchemas)) {
39
- (0, logging_1.logError)('Error updating existing entities');
78
+ (0, logging_1.logError)(' Error updating existing entities');
79
+ bSuccess = false;
80
+ }
81
+ (0, logging_1.logStatus)(` > Updated existing entities in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
82
+ start = new Date();
83
+ (0, logging_1.logStatus)(' Scanning for tables that were deleted where entity metadata still exists...');
84
+ if (!await this.checkAndRemoveMetadataForDeletedTables(ds, excludeSchemas)) {
85
+ (0, logging_1.logError)(' Error removing metadata for tables that were removed');
40
86
  bSuccess = false;
41
87
  }
88
+ (0, logging_1.logStatus)(` > Removed metadata for deleted tables in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
89
+ start = new Date();
90
+ (0, logging_1.logStatus)(' Recompiling base views...');
42
91
  const sqlUtility = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
43
92
  if (!await sqlUtility.recompileAllBaseViews(ds, excludeSchemas, true)) {
44
- (0, logging_1.logMessage)('Warning: Non-Fatal error recompiling base views', core_1.SeverityType.Warning, false);
93
+ (0, logging_1.logMessage)(' Warning: Non-Fatal error recompiling base views', core_1.SeverityType.Warning, false);
45
94
  // many times the former versions of base views will NOT succesfully recompile, so don't consider that scenario to be a
46
95
  // failure for this entire function
47
96
  }
48
- if (!await this.manageEntityFields(ds, excludeSchemas, false)) {
49
- (0, logging_1.logError)('Error managing entity fields');
97
+ (0, logging_1.logStatus)(` > Recompiled base views in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
98
+ start = new Date();
99
+ (0, logging_1.logStatus)(' Managing entity fields...');
100
+ if (!await this.manageEntityFields(ds, excludeSchemas, false, false)) {
101
+ (0, logging_1.logError)(' Error managing entity fields');
50
102
  bSuccess = false;
51
103
  }
104
+ (0, logging_1.logStatus)(` > Managed entity fields in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
105
+ start = new Date();
106
+ (0, logging_1.logStatus)(' Managing entity relationships...');
52
107
  if (!await this.manageEntityRelationships(ds, excludeSchemas, md)) {
53
- (0, logging_1.logError)('Error managing entity relationships');
108
+ (0, logging_1.logError)(' Error managing entity relationships');
54
109
  bSuccess = false;
55
110
  }
111
+ (0, logging_1.logStatus)(` > Managed entity relationships in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
56
112
  if (ManageMetadataBase_1.newEntityList.length > 0) {
57
113
  await this.generateNewEntityDescriptions(ds, md); // don't pass excludeSchemas becuase by definition this is the NEW entities we created
58
114
  }
59
115
  if (!await this.manageVirtualEntities(ds)) {
60
- (0, logging_1.logError)('Error managing virtual entities');
116
+ (0, logging_1.logError)(' Error managing virtual entities');
61
117
  bSuccess = false;
62
118
  }
63
119
  // now - we need to tell our metadata object to refresh itself
@@ -90,7 +146,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
90
146
  // // for a given virtual entity, we need to loop through the fields that exist in the current SQL definition for the view
91
147
  // // and add/update/delete the entity fields to match what's in the view
92
148
  // let bSuccess = true;
93
- // const sql = `SELECT * FROM vwSQLColumnsAndEntityFields WHERE EntityID = ${ve.ID}`;
149
+ // const sql = `SELECT * FROM vwSQLColumnsAndEntityFields WHERE EntityID = '${ve.ID}'`;
94
150
  // const veFields = await ds.query(sql);
95
151
  // if (veFields && veFields.length > 0) {
96
152
  // // we have 1+ fields, now loop through them and process each one
@@ -151,10 +207,10 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
151
207
  * @param md
152
208
  * @returns
153
209
  */
154
- async manageEntityRelationships(ds, excludeSchemas, md) {
210
+ async manageEntityRelationships(ds, excludeSchemas, md, batchItems = 5) {
155
211
  let bResult = true;
156
- bResult = bResult && await this.manageManyToManyEntityRelationships(ds, excludeSchemas);
157
- bResult = bResult && await this.manageOneToManyEntityRelationships(ds, excludeSchemas, md);
212
+ bResult = bResult && await this.manageManyToManyEntityRelationships(ds, excludeSchemas, batchItems);
213
+ bResult = bResult && await this.manageOneToManyEntityRelationships(ds, excludeSchemas, md, batchItems);
158
214
  return bResult;
159
215
  }
160
216
  /**
@@ -164,7 +220,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
164
220
  * @param md
165
221
  * @returns
166
222
  */
167
- async manageOneToManyEntityRelationships(ds, excludeSchemas, md) {
223
+ async manageOneToManyEntityRelationships(ds, excludeSchemas, md, batchItems = 5) {
168
224
  // the way this works is that we look for entities in our catalog and we look for
169
225
  // foreign keys in those entities. For example, if we saw an entity called Persons and that entity
170
226
  // had a foreign key linking to an entity called Organizations via a field called OrganizationID, then we would create a relationship
@@ -194,23 +250,77 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
194
250
  for (const rc of relationshipCounts) {
195
251
  relationshipCountMap.set(rc.EntityID, rc.Count);
196
252
  }
197
- // now loop through all of our fkey fields
198
- for (const f of entityFields) {
199
- // for each field determine if an existing relationship exists, if not, create it
200
- const sSQLRelationship = `SELECT * FROM ${(0, config_1.mj_core_schema)()}.EntityRelationship WHERE EntityID = ${f.RelatedEntityID} AND RelatedEntityID = ${f.EntityID}`;
201
- const relationships = await ds.query(sSQLRelationship);
202
- if (relationships && relationships.length === 0) {
203
- // no relationship exists, so create it
204
- const e = md.Entities.find(e => e.ID === f.EntityID);
205
- // calculate the sequence by getting the count of existing relationships for the entity and adding 1 and then increment the count for future inserts in this loop
206
- const relCount = relationshipCountMap.get(f.EntityID) ? relationshipCountMap.get(f.EntityID) : 0;
207
- const sequence = relCount + 1;
208
- const sSQLInsert = `INSERT INTO ${(0, config_1.mj_core_schema)()}.EntityRelationship (EntityID, RelatedEntityID, RelatedEntityJoinField, Type, BundleInAPI, DisplayInForm, DisplayName, Sequence)
209
- VALUES (${f.RelatedEntityID}, ${f.EntityID}, '${f.Name}', 'One To Many', 1, 1, '${e.Name}', ${sequence})`;
210
- // now update the map for the relationship count
211
- relationshipCountMap.set(f.EntityID, sequence);
212
- await ds.query(sSQLInsert);
253
+ // get all relationships in one query for performance improvement
254
+ const sSQLRelationship = `SELECT * FROM ${(0, config_1.mj_core_schema)()}.EntityRelationship`;
255
+ const allRelationships = await ds.query(sSQLRelationship);
256
+ // Function to process a batch of entity fields
257
+ const processBatch = async (batch) => {
258
+ let batchSQL = '';
259
+ batch.forEach((f) => {
260
+ // for each field determine if an existing relationship exists, if not, create it
261
+ const relationships = allRelationships.filter(r => r.EntityID === f.RelatedEntityID && r.RelatedEntityID === f.EntityID);
262
+ if (relationships && relationships.length === 0) {
263
+ // no relationship exists, so create it
264
+ const e = md.Entities.find(e => e.ID === f.EntityID);
265
+ // calculate the sequence by getting the count of existing relationships for the entity and adding 1 and then increment the count for future inserts in this loop
266
+ const relCount = relationshipCountMap.get(f.EntityID) ? relationshipCountMap.get(f.EntityID) : 0;
267
+ const sequence = relCount + 1;
268
+ batchSQL += `INSERT INTO ${(0, config_1.mj_core_schema)()}.EntityRelationship (EntityID, RelatedEntityID, RelatedEntityJoinField, Type, BundleInAPI, DisplayInForm, DisplayName, Sequence)
269
+ VALUES ('${f.RelatedEntityID}', '${f.EntityID}', '${f.Name}', 'One To Many', 1, 1, '${e.Name}', ${sequence});
270
+ `;
271
+ // now update the map for the relationship count
272
+ relationshipCountMap.set(f.EntityID, sequence);
273
+ }
274
+ });
275
+ if (batchSQL.length > 0)
276
+ await ds.query(batchSQL);
277
+ };
278
+ // Split entityFields into batches and process each batch
279
+ for (let i = 0; i < entityFields.length; i += batchItems) {
280
+ const batch = entityFields.slice(i, i + batchItems);
281
+ await processBatch(batch);
282
+ }
283
+ return true;
284
+ }
285
+ catch (e) {
286
+ (0, logging_1.logError)(e);
287
+ return false;
288
+ }
289
+ }
290
+ /**
291
+ * This method will look for situations where entity metadata exist in the entities metadata table but the underlying table has been deleted. In this case, the metadata for the entity
292
+ * should be removed. This method is called as part of the manageMetadata method and is not intended to be called directly.
293
+ * @param ds
294
+ * @param excludeSchemas
295
+ */
296
+ async checkAndRemoveMetadataForDeletedTables(ds, excludeSchemas) {
297
+ try {
298
+ const sql = `SELECT * FROM ${(0, config_1.mj_core_schema)()}.vwEntitiesWithMissingBaseTables WHERE VirtualEntity=0`;
299
+ const entities = await ds.query(sql);
300
+ if (entities && entities.length > 0) {
301
+ for (const e of entities) {
302
+ // for the given entity, wipe out the entity metadata and its core deps.
303
+ // the below could fail if there are non-core dependencies on the entity, but that's ok, we will flag that in the console
304
+ // for the admin to handle manually
305
+ try {
306
+ const sqlDelete = `__mj.spDeleteEntityWithCoreDependencies @EntityID='${e.ID}'`;
307
+ await ds.query(sqlDelete);
308
+ (0, logging_1.logStatus)(` > Removed metadata for table ${e.SchemaName}.${e.BaseTable}`);
309
+ // next up we need to remove the spCreate, spDelete, spUpdate, BaseView, and FullTextSearchFunction, if provided.
310
+ // We only remoe these artifcacts when they are generated which is info we have in the BaseViewGenerated, spCreateGenerated, etc. fields
311
+ await this.checkDropSQLObject(ds, e.BaseViewGenerated, 'view', e.SchemaName, e.BaseView);
312
+ await this.checkDropSQLObject(ds, e.spCreateGenerated, 'procedure', e.SchemaName, e.spCreate ? e.spCreate : `spCreate${e.ClassName}`);
313
+ await this.checkDropSQLObject(ds, e.spDeleteGenerated, 'procedure', e.SchemaName, e.spDelete ? e.spDelete : `spDelete${e.ClassName}`);
314
+ await this.checkDropSQLObject(ds, e.spUpdateGenerated, 'procedure', e.SchemaName, e.spUpdate ? e.spUpdate : `spUpdate${e.ClassName}`);
315
+ await this.checkDropSQLObject(ds, e.FullTextSearchFunctionGenerated, 'function', e.SchemaName, e.FullTextSearchFunction);
316
+ }
317
+ catch (ex) {
318
+ (0, logging_1.logError)(`Error removing metadata for entity ${ex.Name}, error: ${ex}`);
319
+ }
213
320
  }
321
+ // if we get here we now need to refresh our metadata object
322
+ const md = new core_1.Metadata();
323
+ await md.Refresh();
214
324
  }
215
325
  return true;
216
326
  }
@@ -219,6 +329,31 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
219
329
  return false;
220
330
  }
221
331
  }
332
+ async checkDropSQLObject(ds, proceed, type, schemaName, name) {
333
+ try {
334
+ if (proceed && schemaName && name && schemaName.trim().length > 0 && name.trim().length > 0) {
335
+ const sqlDelete = `DROP ${type} IF EXISTS [${schemaName}].[${name}]`;
336
+ await ds.query(sqlDelete);
337
+ // next up, we need to clean up the cache of saved DB objects that may exist for this entity in the appropriate sub-directory.
338
+ const sqlOutputDir = (0, config_1.outputDir)('SQL', true);
339
+ if (sqlOutputDir) {
340
+ // now do the same thing for the /schema directory within the provided directory
341
+ const fType = type === 'procedure' ? 'sp' : type === 'view' ? 'view' : 'full_text_search_function';
342
+ const filePath = path_1.default.join(sqlOutputDir, this.SQLUtilityObject.getDBObjectFileName(fType, schemaName, name, false, true));
343
+ const filePathPermissions = path_1.default.join(sqlOutputDir, this.SQLUtilityObject.getDBObjectFileName(fType, schemaName, name, true, true));
344
+ // if the files exist, delete them
345
+ if (fs.existsSync(filePath))
346
+ fs.unlinkSync(filePath);
347
+ if (fs.existsSync(filePathPermissions))
348
+ fs.unlinkSync(filePathPermissions);
349
+ }
350
+ (0, logging_1.logStatus)(` > Removed ${type} ${schemaName}.${name}`);
351
+ }
352
+ }
353
+ catch (e) {
354
+ (0, logging_1.logError)(` > Error removing ${type} ${schemaName}.${name}, error: ${e}`);
355
+ }
356
+ }
222
357
  /**
223
358
  * Manages M->M relationships between entities in the metadata based on foreign key relationships in the database.
224
359
  * NOT IMPLEMENTED IN CURRENT VERSION IN BASE CLASS. M->M relationships ARE supported fully, but they are not AUTO generated by this
@@ -227,7 +362,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
227
362
  * @param excludeSchemas
228
363
  * @returns
229
364
  */
230
- async manageManyToManyEntityRelationships(ds, excludeSchemas) {
365
+ async manageManyToManyEntityRelationships(ds, excludeSchemas, batchItems = 5) {
231
366
  return true; // not implemented for now, require the admin to manually create these relationships
232
367
  }
233
368
  /**
@@ -236,53 +371,91 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
236
371
  * @param excludeSchemas
237
372
  * @returns
238
373
  */
239
- async manageEntityFields(ds, excludeSchemas, skipCreatedAtUpdatedAtFieldValidation) {
374
+ async manageEntityFields(ds, excludeSchemas, skipCreatedAtUpdatedAtDeletedAtFieldValidation, skipEntityFieldValues) {
240
375
  let bSuccess = true;
241
376
  const startTime = new Date();
242
- if (!skipCreatedAtUpdatedAtFieldValidation && !await this.ensureCreatedAtUpdatedAtFieldsExist(ds, excludeSchemas)) {
243
- (0, logging_1.logError)(`Error ensuring ${core_1.EntityInfo.CreatedAtFieldName} and ${core_1.EntityInfo.UpdatedAtFieldName} fields exist`);
244
- bSuccess = false;
377
+ if (!skipCreatedAtUpdatedAtDeletedAtFieldValidation) {
378
+ if (!await this.ensureCreatedAtUpdatedAtFieldsExist(ds, excludeSchemas) ||
379
+ !await this.ensureDeletedAtFieldsExist(ds, excludeSchemas)) {
380
+ (0, logging_1.logError)(`Error ensuring ${core_1.EntityInfo.CreatedAtFieldName}, ${core_1.EntityInfo.UpdatedAtFieldName} and ${core_1.EntityInfo.DeletedAtFieldName} fields exist`);
381
+ bSuccess = false;
382
+ }
383
+ (0, logging_1.logStatus)(` Ensured ${core_1.EntityInfo.CreatedAtFieldName}/${core_1.EntityInfo.UpdatedAtFieldName}/${core_1.EntityInfo.DeletedAtFieldName} fields exist in ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
245
384
  }
246
- (0, logging_1.logStatus)(` Ensured ${core_1.EntityInfo.CreatedAtFieldName}/${core_1.EntityInfo.UpdatedAtFieldName} fields exist in ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
247
385
  const step1StartTime = new Date();
248
386
  if (!await this.deleteUnneededEntityFields(ds, excludeSchemas)) {
249
387
  (0, logging_1.logError)('Error deleting unneeded entity fields');
250
388
  bSuccess = false;
251
389
  }
252
- (0, logging_1.logStatus)(` Deleted unneeded entity fields in ${(new Date().getTime() - step1StartTime.getTime()) / 1000} seconds`);
390
+ (0, logging_1.logStatus)(` Deleted unneeded entity fields in ${(new Date().getTime() - step1StartTime.getTime()) / 1000} seconds`);
253
391
  const step2StartTime = new Date();
254
392
  if (!await this.updateExistingEntityFieldsFromSchema(ds, excludeSchemas)) {
255
393
  (0, logging_1.logError)('Error updating existing entity fields from schema');
256
394
  bSuccess = false;
257
395
  }
258
- (0, logging_1.logStatus)(` Updated existing entity fields from schema in ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
396
+ (0, logging_1.logStatus)(` Updated existing entity fields from schema in ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
259
397
  const step3StartTime = new Date();
260
398
  if (!await this.createNewEntityFieldsFromSchema(ds)) { // has its own internal filtering for exclude schema/table so don't pass in
261
399
  (0, logging_1.logError)('Error creating new entity fields from schema');
262
400
  bSuccess = false;
263
401
  }
264
- (0, logging_1.logStatus)(` Created new entity fields from schema in ${(new Date().getTime() - step3StartTime.getTime()) / 1000} seconds`);
402
+ (0, logging_1.logStatus)(` Created new entity fields from schema in ${(new Date().getTime() - step3StartTime.getTime()) / 1000} seconds`);
265
403
  const step4StartTime = new Date();
266
404
  if (!await this.setDefaultColumnWidthWhereNeeded(ds, excludeSchemas)) {
267
405
  (0, logging_1.logError)('Error setting default column width where needed');
268
406
  bSuccess = false;
269
407
  }
270
- (0, logging_1.logStatus)(` Set default column width where needed in ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
408
+ (0, logging_1.logStatus)(` Set default column width where needed in ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
271
409
  const step5StartTime = new Date();
272
410
  if (!await this.updateEntityFieldDisplayNameWhereNull(ds, excludeSchemas)) {
273
411
  (0, logging_1.logError)('Error updating entity field display name where null');
274
412
  bSuccess = false;
275
413
  }
276
- (0, logging_1.logStatus)(` Updated entity field display name where null in ${(new Date().getTime() - step5StartTime.getTime()) / 1000} seconds`);
277
- const step6StartTime = new Date();
278
- if (!await this.manageEntityFieldValues(ds, excludeSchemas)) {
279
- (0, logging_1.logError)('Error managing entity field values');
280
- bSuccess = false;
414
+ (0, logging_1.logStatus)(` Updated entity field display name where null in ${(new Date().getTime() - step5StartTime.getTime()) / 1000} seconds`);
415
+ if (!skipEntityFieldValues) {
416
+ const step6StartTime = new Date();
417
+ (0, logging_1.logStatus)(` Starting to manage entity field values...`);
418
+ if (!await this.manageEntityFieldValues(ds, excludeSchemas)) {
419
+ (0, logging_1.logError)('Error managing entity field values');
420
+ bSuccess = false;
421
+ }
422
+ (0, logging_1.logStatus)(` Managed entity field values in ${(new Date().getTime() - step6StartTime.getTime()) / 1000} seconds`);
281
423
  }
282
- (0, logging_1.logStatus)(` Managed entity field values in ${(new Date().getTime() - step6StartTime.getTime()) / 1000} seconds`);
283
- (0, logging_1.logStatus)(` Total time to manage entity fields: ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
424
+ (0, logging_1.logStatus)(` Total time to manage entity fields: ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
284
425
  return bSuccess;
285
426
  }
427
+ /**
428
+ * This method ensures that the __mj_DeletedAt field exists in each entity that has DeleteType=Soft. If the field does not exist, it is created.
429
+ */
430
+ async ensureDeletedAtFieldsExist(ds, excludeSchemas) {
431
+ try {
432
+ const sqlEntities = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntities WHERE DeleteType='Soft' AND SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})`;
433
+ const entities = await ds.query(sqlEntities);
434
+ let overallResult = true;
435
+ if (entities.length > 0) {
436
+ // we have 1+ entities that need the special fields, so loop through them and ensure the fields exist
437
+ // validate that each entity has the __mj_DeletedAt field, and it is a DATETIMEOFFSET fields, NOT NULL and both are fields that have a DEFAULT value of GETUTCDATE().
438
+ const sql = `SELECT *
439
+ FROM INFORMATION_SCHEMA.COLUMNS
440
+ WHERE
441
+ ${entities.map(e => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
442
+ AND COLUMN_NAME='${core_1.EntityInfo.DeletedAtFieldName}'`;
443
+ const result = await ds.query(sql);
444
+ for (const e of entities) {
445
+ const eResult = result.filter(r => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
446
+ const deletedAt = eResult.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.DeletedAtFieldName.trim().toLowerCase());
447
+ // now, if we have the fields, we need to check the default value and update if necessary
448
+ const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.DeletedAtFieldName, deletedAt, true);
449
+ overallResult = overallResult && fieldResult;
450
+ }
451
+ }
452
+ return overallResult;
453
+ }
454
+ catch (e) {
455
+ (0, logging_1.logError)(e);
456
+ return false;
457
+ }
458
+ }
286
459
  /**
287
460
  * This method ensures that the __mj_CreatedAt and __mj_UpdatedAt fields exist in each entity that has TrackRecordChanges set to true. If the fields do not exist, they are created.
288
461
  * If the fields exist but have incorrect default values, the default values are updated. The default value that is to be used for these special fields is GETUTCDATE() which is the
@@ -298,20 +471,20 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
298
471
  // we have 1+ entities that need the special fields, so loop through them and ensure the fields exist
299
472
  // validate that each entity has two specific fields, the first one is __mj_CreatedAt and the second one is __mj_UpdatedAt
300
473
  // both are DATETIME fields, NOT NULL and both are fields that have a DEFAULT value of GETUTCDATE().
474
+ const sqlCreatedUpdated = `SELECT *
475
+ FROM INFORMATION_SCHEMA.COLUMNS
476
+ WHERE
477
+ ${entities.map(e => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
478
+ AND COLUMN_NAME IN ('${core_1.EntityInfo.CreatedAtFieldName}','${core_1.EntityInfo.UpdatedAtFieldName}')`;
479
+ const result = await ds.query(sqlCreatedUpdated);
301
480
  for (const e of entities) {
302
- const sqlCreatedUpdated = `SELECT *
303
- FROM INFORMATION_SCHEMA.COLUMNS
304
- WHERE
305
- TABLE_SCHEMA='${e.SchemaName}'
306
- AND TABLE_NAME = '${e.BaseTable}'
307
- AND COLUMN_NAME IN ('${core_1.EntityInfo.CreatedAtFieldName}','${core_1.EntityInfo.UpdatedAtFieldName}')`;
308
- const result = await ds.query(sqlCreatedUpdated);
309
481
  // result has both created at and updated at fields, so filter on the result for each and do what we need to based on that
310
- const createdAt = result.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.CreatedAtFieldName.trim().toLowerCase());
311
- const updatedAt = result.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase());
482
+ const eResult = result.filter(r => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
483
+ const createdAt = eResult.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.CreatedAtFieldName.trim().toLowerCase());
484
+ const updatedAt = eResult.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase());
312
485
  // now, if we have the fields, we need to check the default value and update if necessary
313
- const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.CreatedAtFieldName, createdAt) &&
314
- await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.UpdatedAtFieldName, updatedAt);
486
+ const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.CreatedAtFieldName, createdAt, false) &&
487
+ await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.UpdatedAtFieldName, updatedAt, false);
315
488
  overallResult = overallResult && fieldResult;
316
489
  }
317
490
  }
@@ -329,40 +502,57 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
329
502
  * @param fieldName
330
503
  * @param currentFieldData
331
504
  */
332
- async ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, entity, fieldName, currentFieldData) {
333
- if (!currentFieldData) {
334
- // field doesn't exist, let's create it
335
- const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD ${fieldName} DATETIMEOFFSET NOT NULL DEFAULT GETUTCDATE()`;
336
- await ds.query(sql);
337
- }
338
- else {
339
- // field does exist, let's first check the data type/nullability
340
- if (currentFieldData.DATA_TYPE.trim().toLowerCase() !== 'datetimeoffset' || currentFieldData.IS_NULLABLE.trim().toLowerCase() !== 'no') {
341
- // the column is the wrong type, so let's update it, first removing the default constraint, then
342
- // modifying the column, and finally adding the default constraint back in.
343
- await this.dropExistingDefaultConstraint(ds, entity, fieldName);
344
- const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ALTER COLUMN ${fieldName} DATETIMEOFFSET NOT NULL`;
505
+ async ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, entity, fieldName, currentFieldData, allowNull) {
506
+ try {
507
+ if (!currentFieldData) {
508
+ // field doesn't exist, let's create it
509
+ const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD ${fieldName} DATETIMEOFFSET ${allowNull ? 'NULL' : 'NOT NULL DEFAULT GETUTCDATE()'}`;
345
510
  await ds.query(sql);
346
- await this.createDefaultConstraintForSpecialDateField(ds, entity, fieldName);
347
511
  }
348
512
  else {
349
- // if we get here that means the column is the correct type and nullability, so now let's check the default value
350
- const defaultValue = currentFieldData.COLUMN_DEFAULT;
351
- const realDefaultValue = (0, core_1.ExtractActualDefaultValue)(defaultValue);
352
- if (realDefaultValue.trim().toLowerCase() !== 'getutcdate()') {
353
- await this.dropAndCreateDefaultConstraintForSpecialDateField(ds, entity, fieldName);
513
+ // field does exist, let's first check the data type/nullability
514
+ if (currentFieldData.DATA_TYPE.trim().toLowerCase() !== 'datetimeoffset' ||
515
+ (currentFieldData.IS_NULLABLE.trim().toLowerCase() !== 'no' && !allowNull) ||
516
+ (currentFieldData.IS_NULLABLE.trim().toLowerCase() === 'no' && allowNull)) {
517
+ // the column is the wrong type, or has wrong nullability attribute, so let's update it, first removing the default constraint, then
518
+ // modifying the column, and finally adding the default constraint back in.
519
+ await this.dropExistingDefaultConstraint(ds, entity, fieldName);
520
+ const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ALTER COLUMN ${fieldName} DATETIMEOFFSET ${allowNull ? 'NULL' : 'NOT NULL'}`;
521
+ await ds.query(sql);
522
+ if (!allowNull)
523
+ await this.createDefaultConstraintForSpecialDateField(ds, entity, fieldName);
524
+ }
525
+ else {
526
+ // if we get here that means the column is the correct type and nullability, so now let's check the default value, but we only do that if we are dealing with a
527
+ // field that is NOT NULL
528
+ if (!allowNull) {
529
+ const defaultValue = currentFieldData.COLUMN_DEFAULT;
530
+ const realDefaultValue = (0, core_1.ExtractActualDefaultValue)(defaultValue);
531
+ if (!realDefaultValue || realDefaultValue.trim().toLowerCase() !== 'getutcdate()') {
532
+ await this.dropAndCreateDefaultConstraintForSpecialDateField(ds, entity, fieldName);
533
+ }
534
+ }
354
535
  }
355
536
  }
537
+ // if we get here, we're good
538
+ return true;
539
+ }
540
+ catch (e) {
541
+ (0, logging_1.logError)(e);
542
+ return false;
356
543
  }
357
- // if we get here, we're good
358
- return true;
359
544
  }
360
545
  /**
361
546
  * Creates the default constraint for a special date field. This method is called as part of the ensureSpecialDateFieldExistsAndHasCorrectDefaultValue method and is not intended to be called directly.
362
547
  */
363
548
  async createDefaultConstraintForSpecialDateField(ds, entity, fieldName) {
364
- const sqlAddDefaultConstraint = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD CONSTRAINT DF_${entity.SchemaName}_${(0, core_1.CodeNameFromString)(entity.BaseTable)}_${fieldName} DEFAULT GETUTCDATE() FOR [${fieldName}]`;
365
- await ds.query(sqlAddDefaultConstraint);
549
+ try {
550
+ const sqlAddDefaultConstraint = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD CONSTRAINT DF_${entity.SchemaName}_${(0, core_1.CodeNameFromString)(entity.BaseTable)}_${fieldName} DEFAULT GETUTCDATE() FOR [${fieldName}]`;
551
+ await ds.query(sqlAddDefaultConstraint);
552
+ }
553
+ catch (e) {
554
+ (0, logging_1.logError)(e);
555
+ }
366
556
  }
367
557
  /**
368
558
  * Drops and recreates the default constraint for a special date field. This method is called as part of the ensureSpecialDateFieldExistsAndHasCorrectDefaultValue method and is not intended to be called directly.
@@ -382,7 +572,8 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
382
572
  * @param fieldName
383
573
  */
384
574
  async dropExistingDefaultConstraint(ds, entity, fieldName) {
385
- const sqlDropDefaultConstraint = `
575
+ try {
576
+ const sqlDropDefaultConstraint = `
386
577
  DECLARE @constraintName NVARCHAR(255);
387
578
 
388
579
  -- Get the default constraint name
@@ -400,8 +591,12 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
400
591
  BEGIN
401
592
  EXEC('ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] DROP CONSTRAINT ' + @constraintName);
402
593
  END
403
- `;
404
- await ds.query(sqlDropDefaultConstraint);
594
+ `;
595
+ await ds.query(sqlDropDefaultConstraint);
596
+ }
597
+ catch (e) {
598
+ (0, logging_1.logError)(e);
599
+ }
405
600
  }
406
601
  /**
407
602
  * This method generates descriptions for entities in teh system where there is no existing description. This is an experimental feature and is done using AI. In order for it
@@ -421,7 +616,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
421
616
  // now loop through the new entities and generate descriptions for them
422
617
  for (let e of ManageMetadataBase_1.newEntityList) {
423
618
  const data = await ds.query(`SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntities WHERE Name = '${e}'`);
424
- const fields = await ds.query(`SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFields WHERE EntityID = ${data[0].ID}`);
619
+ const fields = await ds.query(`SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFields WHERE EntityID='${data[0].ID}'`);
425
620
  const entityUserMessage = userMessage + `Entity Name: ${e},
426
621
  Base Table: ${data[0].BaseTable},
427
622
  Schema: ${data[0].SchemaName}.
@@ -491,7 +686,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
491
686
  for (const field of fields) {
492
687
  const sDisplayName = this.stripTrailingChars(this.convertCamelCaseToHaveSpaces(field.Name), 'ID', true).trim();
493
688
  if (sDisplayName.length > 0 && sDisplayName.toLowerCase().trim() !== field.Name.toLowerCase().trim()) {
494
- const sSQL = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ${core_1.EntityInfo.UpdatedAtFieldName}=GETUTCDATE(), DisplayName = '${sDisplayName}' WHERE ID = ${field.ID}`;
689
+ const sSQL = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ${core_1.EntityInfo.UpdatedAtFieldName}=GETUTCDATE(), DisplayName = '${sDisplayName}' WHERE ID = '${field.ID}'`;
495
690
  await ds.query(sSQL);
496
691
  }
497
692
  }
@@ -538,7 +733,10 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
538
733
  sf.AllowsNull,
539
734
  sf.DefaultValue,
540
735
  sf.AutoIncrement,
541
- IIF(sf.IsVirtual = 1, 0, IIF(sf.FieldName = '${core_1.EntityInfo.CreatedAtFieldName}' OR sf.FieldName = '${core_1.EntityInfo.UpdatedAtFieldName}' OR sf.FieldName = 'ID', 0, 1)) AllowUpdateAPI,
736
+ IIF(sf.IsVirtual = 1, 0, IIF(sf.FieldName = '${core_1.EntityInfo.CreatedAtFieldName}' OR
737
+ sf.FieldName = '${core_1.EntityInfo.UpdatedAtFieldName}' OR
738
+ sf.FieldName = '${core_1.EntityInfo.DeletedAtFieldName}' OR
739
+ pk.ColumnName IS NOT NULL, 0, 1)) AllowUpdateAPI,
542
740
  sf.IsVirtual,
543
741
  e.RelationshipDefaultDisplayType,
544
742
  re.ID RelatedEntityID,
@@ -592,7 +790,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
592
790
  SELECT
593
791
  *
594
792
  FROM
595
- NumberedRows WHERE rn = 1 -- if someone has two foreign keys with same to/from table and field name this makes sure we only get the field info ONCE
793
+ NumberedRows -- REMOVED - Need all fkey fields WHERE rn = 1 -- if someone has two foreign keys with same to/from table and field name this makes sure we only get the field info ONCE
596
794
  ORDER BY EntityID, Sequence`;
597
795
  return sSQL;
598
796
  }
@@ -607,14 +805,17 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
607
805
  n.Sequence <= config_1.configInfo.newEntityDefaults?.IncludeFirstNFieldsAsDefaultInView ||
608
806
  n.IsNameField ? true : false);
609
807
  const escapedDescription = n.Description ? `'${n.Description.replace(/'/g, "''")}'` : 'NULL';
610
- let fieldDisplayName;
808
+ let fieldDisplayName = '';
611
809
  switch (n.FieldName.trim().toLowerCase()) {
612
- case "__mj_createdat":
810
+ case core_1.EntityInfo.CreatedAtFieldName.trim().toLowerCase():
613
811
  fieldDisplayName = "Created At";
614
812
  break;
615
- case "__mj_updatedat":
813
+ case core_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase():
616
814
  fieldDisplayName = "Updated At";
617
815
  break;
816
+ case core_1.EntityInfo.DeletedAtFieldName.trim().toLowerCase():
817
+ fieldDisplayName = "Deleted At";
818
+ break;
618
819
  default:
619
820
  fieldDisplayName = this.convertCamelCaseToHaveSpaces(n.FieldName).trim();
620
821
  break;
@@ -648,7 +849,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
648
849
  )
649
850
  VALUES
650
851
  (
651
- ${n.EntityID},
852
+ '${n.EntityID}',
652
853
  ${n.Sequence},
653
854
  '${n.FieldName}',
654
855
  '${fieldDisplayName}',
@@ -662,11 +863,11 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
662
863
  ${n.AutoIncrement ? 1 : 0},
663
864
  ${n.AllowUpdateAPI ? 1 : 0},
664
865
  ${n.IsVirtual ? 1 : 0},
665
- ${n.RelatedEntityID},
866
+ ${n.RelatedEntityID && n.RelatedEntityID.length > 0 ? `'${n.RelatedEntityID}'` : 'NULL'},
666
867
  ${n.RelatedEntityFieldName && n.RelatedEntityFieldName.length > 0 ? `'${n.RelatedEntityFieldName}'` : 'NULL'},
667
868
  ${n.IsNameField !== null ? n.IsNameField : 0},
668
869
  ${n.FieldName === 'ID' || n.IsNameField ? 1 : 0},
669
- ${n.RelatedEntityID && n.RelatedEntityID > 0 && n.Type.trim().toLowerCase() === 'int' ? 1 : 0},
870
+ ${n.RelatedEntityID && n.RelatedEntityID.length > 0 ? 1 : 0},
670
871
  ${bDefaultInView ? 1 : 0},
671
872
  ${n.IsPrimaryKey},
672
873
  ${n.IsUnique},
@@ -704,7 +905,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
704
905
  // wrap in a transaction so we get all of it or none of it
705
906
  for (let i = 0; i < newEntityFields.length; ++i) {
706
907
  const n = newEntityFields[i];
707
- if (n.EntityID !== null && n.EntityID !== undefined && n.EntityID > 0) {
908
+ if (n.EntityID !== null && n.EntityID !== undefined && n.EntityID.length > 0) {
708
909
  // need to check for null entity id = that is because the above query can return candidate Entity Fields but the entities may not have been created if the entities
709
910
  // that would have been created violate rules - such as not having an ID column, etc.
710
911
  const sSQLInsert = this.getPendingEntityFieldINSERTSQL(n);
@@ -730,7 +931,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
730
931
  async updateEntityFieldRelatedEntityNameFieldMap(ds, entityFieldID, relatedEntityNameFieldMap) {
731
932
  try {
732
933
  const sSQL = `EXEC [${(0, config_1.mj_core_schema)()}].spUpdateEntityFieldRelatedEntityNameFieldMap
733
- @EntityFieldID=${entityFieldID} ,
934
+ @EntityFieldID='${entityFieldID}',
734
935
  @RelatedEntityNameFieldMap='${relatedEntityNameFieldMap}'`;
735
936
  await ds.query(sSQL);
736
937
  return true;
@@ -779,6 +980,8 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
779
980
  const filter = excludeSchemas && excludeSchemas.length > 0 ? ` WHERE SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})` : '';
780
981
  const sSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFieldsWithCheckConstraints${filter}`;
781
982
  const result = await ds.query(sSQL);
983
+ const efvSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue`;
984
+ const allEntityFieldValues = await ds.query(efvSQL);
782
985
  // now, for each of the constraints we get back here, loop through and evaluate if they're simple and if they're simple, parse and sync with entity field values for that field
783
986
  for (const r of result) {
784
987
  if (r.ConstraintDefinition && r.ConstraintDefinition.length > 0) {
@@ -787,9 +990,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
787
990
  // flip the order of parsedValues because they come out in reverse order from SQL Server
788
991
  parsedValues.reverse();
789
992
  // we have parsed values from the check constraint, so sync them with the entity field values
790
- await this.syncEntityFieldValues(ds, r.EntityID, r.ColumnName, parsedValues);
993
+ await this.syncEntityFieldValues(ds, r.EntityFieldID, parsedValues, allEntityFieldValues);
791
994
  // finally, make sure the ValueListType column within the EntityField table is set to "List" because for check constraints we only allow the values specified in the list.
792
- await ds.query(`UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ValueListType='List' WHERE EntityID=${r.EntityID} AND Name='${r.ColumnName}'`);
995
+ await ds.query(`UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ValueListType='List' WHERE ID='${r.EntityFieldID}'`);
793
996
  }
794
997
  }
795
998
  }
@@ -800,11 +1003,10 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
800
1003
  return false;
801
1004
  }
802
1005
  }
803
- async syncEntityFieldValues(ds, entityID, entityFieldName, possibleValues) {
1006
+ async syncEntityFieldValues(ds, entityFieldID, possibleValues, allEntityFieldValues) {
804
1007
  try {
805
1008
  // first, get a list of all of the existing entity field values for the field already in the database
806
- const sSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue WHERE EntityID=${entityID} AND EntityFieldName = '${entityFieldName}'`;
807
- const existingValues = await ds.query(sSQL);
1009
+ const existingValues = allEntityFieldValues.filter(efv => efv.EntityFieldID === entityFieldID);
808
1010
  // now, loop through the possible values and add any that are not already in the database
809
1011
  // Step 1: for any existing value that is NOT in the list of possible Values, delete it
810
1012
  let numRemoved = 0;
@@ -812,7 +1014,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
812
1014
  for (const ev of existingValues) {
813
1015
  if (!possibleValues.find(v => v === ev.Value)) {
814
1016
  // delete the value from the database
815
- const sSQLDelete = `DELETE FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue WHERE ID=${ev.ID}`;
1017
+ const sSQLDelete = `DELETE FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue WHERE ID='${ev.ID}'`;
816
1018
  await ds.query(sSQLDelete);
817
1019
  numRemoved++;
818
1020
  }
@@ -823,9 +1025,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
823
1025
  if (!existingValues.find(ev => ev.Value === v)) {
824
1026
  // add the value to the database
825
1027
  const sSQLInsert = `INSERT INTO [${(0, config_1.mj_core_schema)()}].EntityFieldValue
826
- (EntityID, EntityFieldName, Sequence, Value, Code)
1028
+ (EntityFieldID, Sequence, Value, Code)
827
1029
  VALUES
828
- (${entityID}, '${entityFieldName}', ${1 + possibleValues.indexOf(v)}, '${v}', '${v}')`;
1030
+ ('${entityFieldID}', ${1 + possibleValues.indexOf(v)}, '${v}', '${v}')`;
829
1031
  await ds.query(sSQLInsert);
830
1032
  numAdded++;
831
1033
  }
@@ -834,9 +1036,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
834
1036
  let numUpdated = 0;
835
1037
  for (const v of possibleValues) {
836
1038
  const ev = existingValues.find(ev => ev.Value === v);
837
- if (ev) {
838
- // update the sequence to match the order in the possible values list
839
- const sSQLUpdate = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityFieldValue SET Sequence=${1 + possibleValues.indexOf(v)} WHERE ID=${ev.ID}`;
1039
+ if (ev && ev.Sequence !== 1 + possibleValues.indexOf(v)) {
1040
+ // update the sequence to match the order in the possible values list, if it doesn't already match
1041
+ const sSQLUpdate = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityFieldValue SET Sequence=${1 + possibleValues.indexOf(v)} WHERE ID='${ev.ID}'`;
840
1042
  await ds.query(sSQLUpdate);
841
1043
  numUpdated++;
842
1044
  }
@@ -856,7 +1058,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
856
1058
  // Note: Assuming fieldName does not contain regex special characters; otherwise, it needs to be escaped as well.
857
1059
  const structureRegex = new RegExp(`^\\(\\[${fieldName}\\]='[^']+'(?: OR \\[${fieldName}\\]='[^']+?')+\\)$`);
858
1060
  if (!structureRegex.test(constraintDefinition)) {
859
- (0, logging_1.logWarning)(` [${entityName}].[${fieldName}] constraint does not match the simple OR condition pattern or field name does not match: ${constraintDefinition}`);
1061
+ (0, logging_1.logWarning)(` Can't extract value list from [${entityName}].[${fieldName}]. The check constraint does not match the simple OR condition pattern or field name does not match: ${constraintDefinition}`);
860
1062
  return null;
861
1063
  }
862
1064
  // Regular expression to match the values within the single quotes specifically for the field
@@ -1012,55 +1214,57 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1012
1214
  newEntityName = newEntityName + suffix;
1013
1215
  (0, core_1.LogError)(` >>>> WARNING: Entity name already exists, so using ${newEntityName} instead. If you did not intend for this, please rename the ${newEntity.SchemaName}.${newEntity.TableName} table in the database.`);
1014
1216
  }
1015
- // get the next entity ID
1016
- const params = [newEntity.SchemaName];
1017
- const sSQLNewEntityID = `EXEC [${(0, config_1.mj_core_schema)()}].spGetNextEntityID @SchemaName=@0`;
1018
- const newEntityIDRaw = await ds.query(sSQLNewEntityID, params);
1019
- const newEntityID = newEntityIDRaw && newEntityIDRaw.length > 0 ? newEntityIDRaw[0].NextID : null;
1020
- if (newEntityID && newEntityID > 0) {
1021
- const isNewSchema = await this.isSchemaNew(ds, newEntity.SchemaName);
1022
- const sSQLInsert = this.createNewEntityInsertSQL(newEntityID, newEntityName, newEntity, suffix);
1023
- await ds.query(sSQLInsert);
1024
- // if we get here we created a new entity safely, otherwise we get exception
1025
- // add it to the new entity list
1026
- ManageMetadataBase_1.newEntityList.push(newEntityName);
1027
- // next, check if this entity is in a schema that is new (e.g. no other entities have been added to this schema yet), if so and if
1028
- // our config option is set to create new applications from new schemas, then create a new application for this schema
1029
- if (isNewSchema && config_1.configInfo.newSchemaDefaults.CreateNewApplicationWithSchemaName) {
1030
- // new schema and config option is to create a new application from the schema name so do that
1031
- if (!await this.applicationExists(ds, newEntity.SchemaName))
1032
- await this.createNewApplication(ds, newEntity.SchemaName);
1033
- }
1034
- else {
1035
- // not a new schema, attempt to look up the application for this schema
1036
- await this.getApplicationIDForSchema(ds, newEntity.SchemaName);
1037
- }
1038
- // now we have an application ID, but make sure that we are configured to add this new entity to an application at all
1039
- if (config_1.configInfo.newEntityDefaults.AddToApplicationWithSchemaName) {
1040
- // we should add this entity to the application
1041
- const appName = newEntity.SchemaName === (0, config_1.mj_core_schema)() ? 'Admin' : newEntity.SchemaName; // for the __mj schema or whatever it is installed as for mj_core - we want to drop stuff into the admin app
1217
+ const isNewSchema = await this.isSchemaNew(ds, newEntity.SchemaName);
1218
+ const sSQLInsert = this.createNewEntityInsertSQL(newEntityName, newEntity, suffix);
1219
+ const newEntityResult = await ds.query(sSQLInsert);
1220
+ const newEntityID = newEntityResult && newEntityResult.length > 0 ? newEntityResult[0].ID : null;
1221
+ if (!newEntityID)
1222
+ throw new Error(`Failed to create new entity ${newEntityName} for table ${newEntity.SchemaName}.${newEntity.TableName}`);
1223
+ // if we get here we created a new entity safely, otherwise we get exception
1224
+ // add it to the new entity list
1225
+ ManageMetadataBase_1.newEntityList.push(newEntityName);
1226
+ // next, check if this entity is in a schema that is new (e.g. no other entities have been added to this schema yet), if so and if
1227
+ // our config option is set to create new applications from new schemas, then create a new application for this schema
1228
+ if (isNewSchema && config_1.configInfo.newSchemaDefaults.CreateNewApplicationWithSchemaName) {
1229
+ // new schema and config option is to create a new application from the schema name so do that
1230
+ if (!await this.applicationExists(ds, newEntity.SchemaName))
1231
+ await this.createNewApplication(ds, newEntity.SchemaName);
1232
+ }
1233
+ else {
1234
+ // not a new schema, attempt to look up the application for this schema
1235
+ await this.getApplicationIDForSchema(ds, newEntity.SchemaName);
1236
+ }
1237
+ // now we have an application ID, but make sure that we are configured to add this new entity to an application at all
1238
+ if (config_1.configInfo.newEntityDefaults.AddToApplicationWithSchemaName) {
1239
+ // we should add this entity to the application
1240
+ const appName = newEntity.SchemaName === (0, config_1.mj_core_schema)() ? 'Admin' : newEntity.SchemaName; // for the __mj schema or whatever it is installed as for mj_core - we want to drop stuff into the admin app
1241
+ const app = md.Applications.find(a => a.Name.trim().toLowerCase() === appName.trim().toLowerCase());
1242
+ if (app) {
1042
1243
  const sSQLInsertApplicationEntity = `INSERT INTO ${(0, config_1.mj_core_schema)()}.ApplicationEntity
1043
- (ApplicationName, EntityID, Sequence) VALUES
1044
- ('${appName}', ${newEntityID}, (SELECT ISNULL(MAX(Sequence),0)+1 FROM ${(0, config_1.mj_core_schema)()}.ApplicationEntity WHERE ApplicationName = '${appName}'))`;
1244
+ (ApplicationID, EntityID, Sequence) VALUES
1245
+ ('${app.ID}', '${newEntityID}', (SELECT ISNULL(MAX(Sequence),0)+1 FROM ${(0, config_1.mj_core_schema)()}.ApplicationEntity WHERE ApplicationID = '${app.ID}'))`;
1045
1246
  await ds.query(sSQLInsertApplicationEntity);
1046
1247
  }
1047
- // next up, we need to check if we're configured to add permissions for new entities, and if so, add them
1048
- if (config_1.configInfo.newEntityDefaults.PermissionDefaults && config_1.configInfo.newEntityDefaults.PermissionDefaults.AutoAddPermissionsForNewEntities) {
1049
- // we are asked to add permissions for new entities, so do that by looping through the permissions and adding them
1050
- const permissions = config_1.configInfo.newEntityDefaults.PermissionDefaults.Permissions;
1051
- for (const p of permissions) {
1248
+ else
1249
+ (0, core_1.LogError)(` >>>> ERROR: Unable to find Application ID for application ${appName} to add new entity ${newEntityName} to it`);
1250
+ }
1251
+ // next up, we need to check if we're configured to add permissions for new entities, and if so, add them
1252
+ if (config_1.configInfo.newEntityDefaults.PermissionDefaults && config_1.configInfo.newEntityDefaults.PermissionDefaults.AutoAddPermissionsForNewEntities) {
1253
+ // we are asked to add permissions for new entities, so do that by looping through the permissions and adding them
1254
+ const permissions = config_1.configInfo.newEntityDefaults.PermissionDefaults.Permissions;
1255
+ for (const p of permissions) {
1256
+ const RoleID = md.Roles.find(r => r.Name.trim().toLowerCase() === p.RoleName.trim().toLowerCase())?.ID;
1257
+ if (RoleID) {
1052
1258
  const sSQLInsertPermission = `INSERT INTO ${(0, config_1.mj_core_schema)()}.EntityPermission
1053
- (EntityID, RoleName, CanRead, CanCreate, CanUpdate, CanDelete) VALUES
1054
- (${newEntityID}, '${p.RoleName}', ${p.CanRead ? 1 : 0}, ${p.CanCreate ? 1 : 0}, ${p.CanUpdate ? 1 : 0}, ${p.CanDelete ? 1 : 0})`;
1259
+ (EntityID, RoleID, CanRead, CanCreate, CanUpdate, CanDelete) VALUES
1260
+ ('${newEntityID}', '${RoleID}', ${p.CanRead ? 1 : 0}, ${p.CanCreate ? 1 : 0}, ${p.CanUpdate ? 1 : 0}, ${p.CanDelete ? 1 : 0})`;
1055
1261
  await ds.query(sSQLInsertPermission);
1056
1262
  }
1263
+ else
1264
+ (0, core_1.LogError)(` >>>> ERROR: Unable to find Role ID for role ${p.RoleName} to add permissions for new entity ${newEntityName}`);
1057
1265
  }
1058
- (0, core_1.LogStatus)(` Created new entity ${newEntityName} for table ${newEntity.SchemaName}.${newEntity.TableName}`);
1059
- }
1060
- else {
1061
- (0, core_1.LogError)(`ERROR: Unable to get next entity ID for ${newEntity.SchemaName}.${newEntity.TableName} - it is possible that the schema has reached its MAX Id,
1062
- check the Schema Info entity for this schema to see if all ID values have been allocated.`);
1063
1266
  }
1267
+ (0, core_1.LogStatus)(` Created new entity ${newEntityName} for table ${newEntity.SchemaName}.${newEntity.TableName}`);
1064
1268
  }
1065
1269
  else {
1066
1270
  (0, core_1.LogStatus)(` Skipping new entity ${newEntity.TableName} because it doesn't qualify to be created. Reason: ${validationMessage}`);
@@ -1085,18 +1289,19 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1085
1289
  async applicationExists(ds, applicationName) {
1086
1290
  const sSQL = `SELECT ID FROM [${(0, config_1.mj_core_schema)()}].Application WHERE Name = '${applicationName}'`;
1087
1291
  const result = await ds.query(sSQL);
1088
- return result && result.length > 0 ? result[0].ID > 0 : false;
1292
+ return result && result.length > 0 ? result[0].ID.length > 0 : false;
1089
1293
  }
1090
1294
  async getApplicationIDForSchema(ds, schemaName) {
1091
1295
  const sSQL = `SELECT ID FROM [${(0, config_1.mj_core_schema)()}].Application WHERE Name = '${schemaName}'`;
1092
1296
  const result = await ds.query(sSQL);
1093
1297
  return result && result.length > 0 ? result[0].ID : null;
1094
1298
  }
1095
- createNewEntityInsertSQL(newEntityID, newEntityName, newEntity, newEntitySuffix) {
1299
+ createNewEntityInsertSQL(newEntityName, newEntity, newEntitySuffix) {
1096
1300
  const newEntityDefaults = config_1.configInfo.newEntityDefaults;
1097
1301
  const newEntityDescriptionEscaped = newEntity.Description ? `'${newEntity.Description.replace(/'/g, "''")}` : null;
1098
- const sSQLInsert = `INSERT INTO [${(0, config_1.mj_core_schema)()}].Entity (
1099
- ID,
1302
+ const sSQLInsert = `
1303
+ DECLARE @InsertedRow TABLE ([ID] UNIQUEIDENTIFIER)
1304
+ INSERT INTO [${(0, config_1.mj_core_schema)()}].Entity (
1100
1305
  Name,
1101
1306
  Description,
1102
1307
  NameSuffix,
@@ -1113,9 +1318,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1113
1318
  ${newEntityDefaults.AllowUpdateAPI === undefined ? '' : ', AllowUpdateAPI'}
1114
1319
  ${newEntityDefaults.AllowDeleteAPI === undefined ? '' : ', AllowDeleteAPI'}
1115
1320
  ${newEntityDefaults.UserViewMaxRows === undefined ? '' : ', UserViewMaxRows'}
1116
- )
1117
- VALUES (
1118
- ${newEntityID},
1321
+ )
1322
+ OUTPUT INSERTED.[ID] INTO @InsertedRow
1323
+ VALUES (
1119
1324
  '${newEntityName}',
1120
1325
  ${newEntityDescriptionEscaped ? newEntityDescriptionEscaped : 'NULL' /*if no description, then null*/},
1121
1326
  ${newEntitySuffix && newEntitySuffix.length > 0 ? `'${newEntitySuffix}'` : 'NULL'},
@@ -1132,7 +1337,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1132
1337
  ${newEntityDefaults.AllowUpdateAPI === undefined ? '' : ', ' + (newEntityDefaults.AllowUpdateAPI ? '1' : '0')}
1133
1338
  ${newEntityDefaults.AllowDeleteAPI === undefined ? '' : ', ' + (newEntityDefaults.AllowDeleteAPI ? '1' : '0')}
1134
1339
  ${newEntityDefaults.UserViewMaxRows === undefined ? '' : ', ' + (newEntityDefaults.UserViewMaxRows)}
1135
- )`;
1340
+ )
1341
+ SELECT * FROM [__mj].vwEntities WHERE [ID] = (SELECT [ID] FROM @InsertedRow)
1342
+ `;
1136
1343
  return sSQLInsert;
1137
1344
  }
1138
1345
  stripTrailingChars(s, charsToStrip, skipIfExactMatch) {