@memberjunction/codegen-lib 1.8.1 → 2.0.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 (45) 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/Database/manage-metadata.d.ts +23 -8
  16. package/dist/Database/manage-metadata.d.ts.map +1 -1
  17. package/dist/Database/manage-metadata.js +351 -147
  18. package/dist/Database/manage-metadata.js.map +1 -1
  19. package/dist/Database/sql.d.ts +6 -0
  20. package/dist/Database/sql.d.ts.map +1 -1
  21. package/dist/Database/sql.js +82 -12
  22. package/dist/Database/sql.js.map +1 -1
  23. package/dist/Database/sql_codegen.d.ts.map +1 -1
  24. package/dist/Database/sql_codegen.js +60 -19
  25. package/dist/Database/sql_codegen.js.map +1 -1
  26. package/dist/Misc/createNewUser.d.ts.map +1 -1
  27. package/dist/Misc/createNewUser.js +2 -1
  28. package/dist/Misc/createNewUser.js.map +1 -1
  29. package/dist/action_subclasses_codegen.d.ts +1 -1
  30. package/dist/entity_subclasses_codegen.d.ts.map +1 -1
  31. package/dist/entity_subclasses_codegen.js +72 -75
  32. package/dist/entity_subclasses_codegen.js.map +1 -1
  33. package/dist/graphql_server_codegen.d.ts +4 -4
  34. package/dist/graphql_server_codegen.d.ts.map +1 -1
  35. package/dist/graphql_server_codegen.js +65 -58
  36. package/dist/graphql_server_codegen.js.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -0
  40. package/dist/index.js.map +1 -1
  41. package/package.json +7 -7
  42. package/dist/createNewUser.d.ts +0 -12
  43. package/dist/createNewUser.d.ts.map +0 -1
  44. package/dist/createNewUser.js +0 -113
  45. 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');
40
79
  bSuccess = false;
41
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');
86
+ bSuccess = false;
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
  }
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...');
48
100
  if (!await this.manageEntityFields(ds, excludeSchemas, false)) {
49
- (0, logging_1.logError)('Error managing entity fields');
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,88 @@ 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) {
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`);
414
+ (0, logging_1.logStatus)(` Updated entity field display name where null in ${(new Date().getTime() - step5StartTime.getTime()) / 1000} seconds`);
277
415
  const step6StartTime = new Date();
278
416
  if (!await this.manageEntityFieldValues(ds, excludeSchemas)) {
279
417
  (0, logging_1.logError)('Error managing entity field values');
280
418
  bSuccess = false;
281
419
  }
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`);
420
+ (0, logging_1.logStatus)(` Managed entity field values in ${(new Date().getTime() - step6StartTime.getTime()) / 1000} seconds`);
421
+ (0, logging_1.logStatus)(` Total time to manage entity fields: ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
284
422
  return bSuccess;
285
423
  }
424
+ /**
425
+ * 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.
426
+ */
427
+ async ensureDeletedAtFieldsExist(ds, excludeSchemas) {
428
+ try {
429
+ const sqlEntities = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntities WHERE DeleteType='Soft' AND SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})`;
430
+ const entities = await ds.query(sqlEntities);
431
+ let overallResult = true;
432
+ if (entities.length > 0) {
433
+ // we have 1+ entities that need the special fields, so loop through them and ensure the fields exist
434
+ // 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().
435
+ const sql = `SELECT *
436
+ FROM INFORMATION_SCHEMA.COLUMNS
437
+ WHERE
438
+ ${entities.map(e => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
439
+ AND COLUMN_NAME='${core_1.EntityInfo.DeletedAtFieldName}'`;
440
+ const result = await ds.query(sql);
441
+ for (const e of entities) {
442
+ const eResult = result.filter(r => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
443
+ const deletedAt = eResult.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.DeletedAtFieldName.trim().toLowerCase());
444
+ // now, if we have the fields, we need to check the default value and update if necessary
445
+ const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.DeletedAtFieldName, deletedAt, true);
446
+ overallResult = overallResult && fieldResult;
447
+ }
448
+ }
449
+ return overallResult;
450
+ }
451
+ catch (e) {
452
+ (0, logging_1.logError)(e);
453
+ return false;
454
+ }
455
+ }
286
456
  /**
287
457
  * 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
458
  * 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 +468,20 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
298
468
  // we have 1+ entities that need the special fields, so loop through them and ensure the fields exist
299
469
  // validate that each entity has two specific fields, the first one is __mj_CreatedAt and the second one is __mj_UpdatedAt
300
470
  // both are DATETIME fields, NOT NULL and both are fields that have a DEFAULT value of GETUTCDATE().
471
+ const sqlCreatedUpdated = `SELECT *
472
+ FROM INFORMATION_SCHEMA.COLUMNS
473
+ WHERE
474
+ ${entities.map(e => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
475
+ AND COLUMN_NAME IN ('${core_1.EntityInfo.CreatedAtFieldName}','${core_1.EntityInfo.UpdatedAtFieldName}')`;
476
+ const result = await ds.query(sqlCreatedUpdated);
301
477
  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
478
  // 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());
479
+ const eResult = result.filter(r => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
480
+ const createdAt = eResult.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.CreatedAtFieldName.trim().toLowerCase());
481
+ const updatedAt = eResult.find(r => r.COLUMN_NAME.trim().toLowerCase() === core_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase());
312
482
  // 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);
483
+ const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.CreatedAtFieldName, createdAt, false) &&
484
+ await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, e, core_1.EntityInfo.UpdatedAtFieldName, updatedAt, false);
315
485
  overallResult = overallResult && fieldResult;
316
486
  }
317
487
  }
@@ -329,40 +499,57 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
329
499
  * @param fieldName
330
500
  * @param currentFieldData
331
501
  */
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`;
502
+ async ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(ds, entity, fieldName, currentFieldData, allowNull) {
503
+ try {
504
+ if (!currentFieldData) {
505
+ // field doesn't exist, let's create it
506
+ const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD ${fieldName} DATETIMEOFFSET ${allowNull ? 'NULL' : 'NOT NULL DEFAULT GETUTCDATE()'}`;
345
507
  await ds.query(sql);
346
- await this.createDefaultConstraintForSpecialDateField(ds, entity, fieldName);
347
508
  }
348
509
  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);
510
+ // field does exist, let's first check the data type/nullability
511
+ if (currentFieldData.DATA_TYPE.trim().toLowerCase() !== 'datetimeoffset' ||
512
+ (currentFieldData.IS_NULLABLE.trim().toLowerCase() !== 'no' && !allowNull) ||
513
+ (currentFieldData.IS_NULLABLE.trim().toLowerCase() === 'no' && allowNull)) {
514
+ // the column is the wrong type, or has wrong nullability attribute, so let's update it, first removing the default constraint, then
515
+ // modifying the column, and finally adding the default constraint back in.
516
+ await this.dropExistingDefaultConstraint(ds, entity, fieldName);
517
+ const sql = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ALTER COLUMN ${fieldName} DATETIMEOFFSET ${allowNull ? 'NULL' : 'NOT NULL'}`;
518
+ await ds.query(sql);
519
+ if (!allowNull)
520
+ await this.createDefaultConstraintForSpecialDateField(ds, entity, fieldName);
521
+ }
522
+ else {
523
+ // 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
524
+ // field that is NOT NULL
525
+ if (!allowNull) {
526
+ const defaultValue = currentFieldData.COLUMN_DEFAULT;
527
+ const realDefaultValue = (0, core_1.ExtractActualDefaultValue)(defaultValue);
528
+ if (!realDefaultValue || realDefaultValue.trim().toLowerCase() !== 'getutcdate()') {
529
+ await this.dropAndCreateDefaultConstraintForSpecialDateField(ds, entity, fieldName);
530
+ }
531
+ }
354
532
  }
355
533
  }
534
+ // if we get here, we're good
535
+ return true;
536
+ }
537
+ catch (e) {
538
+ (0, logging_1.logError)(e);
539
+ return false;
356
540
  }
357
- // if we get here, we're good
358
- return true;
359
541
  }
360
542
  /**
361
543
  * 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
544
  */
363
545
  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);
546
+ try {
547
+ const sqlAddDefaultConstraint = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD CONSTRAINT DF_${entity.SchemaName}_${(0, core_1.CodeNameFromString)(entity.BaseTable)}_${fieldName} DEFAULT GETUTCDATE() FOR [${fieldName}]`;
548
+ await ds.query(sqlAddDefaultConstraint);
549
+ }
550
+ catch (e) {
551
+ (0, logging_1.logError)(e);
552
+ }
366
553
  }
367
554
  /**
368
555
  * 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 +569,8 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
382
569
  * @param fieldName
383
570
  */
384
571
  async dropExistingDefaultConstraint(ds, entity, fieldName) {
385
- const sqlDropDefaultConstraint = `
572
+ try {
573
+ const sqlDropDefaultConstraint = `
386
574
  DECLARE @constraintName NVARCHAR(255);
387
575
 
388
576
  -- Get the default constraint name
@@ -400,8 +588,12 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
400
588
  BEGIN
401
589
  EXEC('ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] DROP CONSTRAINT ' + @constraintName);
402
590
  END
403
- `;
404
- await ds.query(sqlDropDefaultConstraint);
591
+ `;
592
+ await ds.query(sqlDropDefaultConstraint);
593
+ }
594
+ catch (e) {
595
+ (0, logging_1.logError)(e);
596
+ }
405
597
  }
406
598
  /**
407
599
  * 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 +613,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
421
613
  // now loop through the new entities and generate descriptions for them
422
614
  for (let e of ManageMetadataBase_1.newEntityList) {
423
615
  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}`);
616
+ const fields = await ds.query(`SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFields WHERE EntityID='${data[0].ID}'`);
425
617
  const entityUserMessage = userMessage + `Entity Name: ${e},
426
618
  Base Table: ${data[0].BaseTable},
427
619
  Schema: ${data[0].SchemaName}.
@@ -491,7 +683,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
491
683
  for (const field of fields) {
492
684
  const sDisplayName = this.stripTrailingChars(this.convertCamelCaseToHaveSpaces(field.Name), 'ID', true).trim();
493
685
  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}`;
686
+ const sSQL = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ${core_1.EntityInfo.UpdatedAtFieldName}=GETUTCDATE(), DisplayName = '${sDisplayName}' WHERE ID = '${field.ID}'`;
495
687
  await ds.query(sSQL);
496
688
  }
497
689
  }
@@ -538,7 +730,10 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
538
730
  sf.AllowsNull,
539
731
  sf.DefaultValue,
540
732
  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,
733
+ IIF(sf.IsVirtual = 1, 0, IIF(sf.FieldName = '${core_1.EntityInfo.CreatedAtFieldName}' OR
734
+ sf.FieldName = '${core_1.EntityInfo.UpdatedAtFieldName}' OR
735
+ sf.FieldName = '${core_1.EntityInfo.DeletedAtFieldName}' OR
736
+ pk.ColumnName IS NOT NULL, 0, 1)) AllowUpdateAPI,
542
737
  sf.IsVirtual,
543
738
  e.RelationshipDefaultDisplayType,
544
739
  re.ID RelatedEntityID,
@@ -592,7 +787,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
592
787
  SELECT
593
788
  *
594
789
  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
790
+ 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
791
  ORDER BY EntityID, Sequence`;
597
792
  return sSQL;
598
793
  }
@@ -607,14 +802,17 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
607
802
  n.Sequence <= config_1.configInfo.newEntityDefaults?.IncludeFirstNFieldsAsDefaultInView ||
608
803
  n.IsNameField ? true : false);
609
804
  const escapedDescription = n.Description ? `'${n.Description.replace(/'/g, "''")}'` : 'NULL';
610
- let fieldDisplayName;
805
+ let fieldDisplayName = '';
611
806
  switch (n.FieldName.trim().toLowerCase()) {
612
- case "__mj_createdat":
807
+ case core_1.EntityInfo.CreatedAtFieldName.trim().toLowerCase():
613
808
  fieldDisplayName = "Created At";
614
809
  break;
615
- case "__mj_updatedat":
810
+ case core_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase():
616
811
  fieldDisplayName = "Updated At";
617
812
  break;
813
+ case core_1.EntityInfo.DeletedAtFieldName.trim().toLowerCase():
814
+ fieldDisplayName = "Deleted At";
815
+ break;
618
816
  default:
619
817
  fieldDisplayName = this.convertCamelCaseToHaveSpaces(n.FieldName).trim();
620
818
  break;
@@ -648,7 +846,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
648
846
  )
649
847
  VALUES
650
848
  (
651
- ${n.EntityID},
849
+ '${n.EntityID}',
652
850
  ${n.Sequence},
653
851
  '${n.FieldName}',
654
852
  '${fieldDisplayName}',
@@ -662,7 +860,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
662
860
  ${n.AutoIncrement ? 1 : 0},
663
861
  ${n.AllowUpdateAPI ? 1 : 0},
664
862
  ${n.IsVirtual ? 1 : 0},
665
- ${n.RelatedEntityID},
863
+ ${n.RelatedEntityID && n.RelatedEntityID.length > 0 ? `'${n.RelatedEntityID}'` : 'NULL'},
666
864
  ${n.RelatedEntityFieldName && n.RelatedEntityFieldName.length > 0 ? `'${n.RelatedEntityFieldName}'` : 'NULL'},
667
865
  ${n.IsNameField !== null ? n.IsNameField : 0},
668
866
  ${n.FieldName === 'ID' || n.IsNameField ? 1 : 0},
@@ -704,7 +902,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
704
902
  // wrap in a transaction so we get all of it or none of it
705
903
  for (let i = 0; i < newEntityFields.length; ++i) {
706
904
  const n = newEntityFields[i];
707
- if (n.EntityID !== null && n.EntityID !== undefined && n.EntityID > 0) {
905
+ if (n.EntityID !== null && n.EntityID !== undefined && n.EntityID.length > 0) {
708
906
  // 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
907
  // that would have been created violate rules - such as not having an ID column, etc.
710
908
  const sSQLInsert = this.getPendingEntityFieldINSERTSQL(n);
@@ -730,7 +928,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
730
928
  async updateEntityFieldRelatedEntityNameFieldMap(ds, entityFieldID, relatedEntityNameFieldMap) {
731
929
  try {
732
930
  const sSQL = `EXEC [${(0, config_1.mj_core_schema)()}].spUpdateEntityFieldRelatedEntityNameFieldMap
733
- @EntityFieldID=${entityFieldID} ,
931
+ @EntityFieldID='${entityFieldID}',
734
932
  @RelatedEntityNameFieldMap='${relatedEntityNameFieldMap}'`;
735
933
  await ds.query(sSQL);
736
934
  return true;
@@ -779,6 +977,8 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
779
977
  const filter = excludeSchemas && excludeSchemas.length > 0 ? ` WHERE SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})` : '';
780
978
  const sSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].vwEntityFieldsWithCheckConstraints${filter}`;
781
979
  const result = await ds.query(sSQL);
980
+ const efvSQL = `SELECT * FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue`;
981
+ const allEntityFieldValues = await ds.query(efvSQL);
782
982
  // 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
983
  for (const r of result) {
784
984
  if (r.ConstraintDefinition && r.ConstraintDefinition.length > 0) {
@@ -787,9 +987,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
787
987
  // flip the order of parsedValues because they come out in reverse order from SQL Server
788
988
  parsedValues.reverse();
789
989
  // 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);
990
+ await this.syncEntityFieldValues(ds, r.EntityFieldID, parsedValues, allEntityFieldValues);
791
991
  // 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}'`);
992
+ await ds.query(`UPDATE [${(0, config_1.mj_core_schema)()}].EntityField SET ValueListType='List' WHERE ID='${r.EntityFieldID}'`);
793
993
  }
794
994
  }
795
995
  }
@@ -800,11 +1000,10 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
800
1000
  return false;
801
1001
  }
802
1002
  }
803
- async syncEntityFieldValues(ds, entityID, entityFieldName, possibleValues) {
1003
+ async syncEntityFieldValues(ds, entityFieldID, possibleValues, allEntityFieldValues) {
804
1004
  try {
805
1005
  // 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);
1006
+ const existingValues = allEntityFieldValues.filter(efv => efv.EntityFieldID === entityFieldID);
808
1007
  // now, loop through the possible values and add any that are not already in the database
809
1008
  // Step 1: for any existing value that is NOT in the list of possible Values, delete it
810
1009
  let numRemoved = 0;
@@ -812,7 +1011,7 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
812
1011
  for (const ev of existingValues) {
813
1012
  if (!possibleValues.find(v => v === ev.Value)) {
814
1013
  // delete the value from the database
815
- const sSQLDelete = `DELETE FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue WHERE ID=${ev.ID}`;
1014
+ const sSQLDelete = `DELETE FROM [${(0, config_1.mj_core_schema)()}].EntityFieldValue WHERE ID='${ev.ID}'`;
816
1015
  await ds.query(sSQLDelete);
817
1016
  numRemoved++;
818
1017
  }
@@ -823,9 +1022,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
823
1022
  if (!existingValues.find(ev => ev.Value === v)) {
824
1023
  // add the value to the database
825
1024
  const sSQLInsert = `INSERT INTO [${(0, config_1.mj_core_schema)()}].EntityFieldValue
826
- (EntityID, EntityFieldName, Sequence, Value, Code)
1025
+ (EntityFieldID, Sequence, Value, Code)
827
1026
  VALUES
828
- (${entityID}, '${entityFieldName}', ${1 + possibleValues.indexOf(v)}, '${v}', '${v}')`;
1027
+ ('${entityFieldID}', ${1 + possibleValues.indexOf(v)}, '${v}', '${v}')`;
829
1028
  await ds.query(sSQLInsert);
830
1029
  numAdded++;
831
1030
  }
@@ -834,9 +1033,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
834
1033
  let numUpdated = 0;
835
1034
  for (const v of possibleValues) {
836
1035
  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}`;
1036
+ if (ev && ev.Sequence !== 1 + possibleValues.indexOf(v)) {
1037
+ // update the sequence to match the order in the possible values list, if it doesn't already match
1038
+ const sSQLUpdate = `UPDATE [${(0, config_1.mj_core_schema)()}].EntityFieldValue SET Sequence=${1 + possibleValues.indexOf(v)} WHERE ID='${ev.ID}'`;
840
1039
  await ds.query(sSQLUpdate);
841
1040
  numUpdated++;
842
1041
  }
@@ -1012,55 +1211,57 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1012
1211
  newEntityName = newEntityName + suffix;
1013
1212
  (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
1213
  }
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
1214
+ const isNewSchema = await this.isSchemaNew(ds, newEntity.SchemaName);
1215
+ const sSQLInsert = this.createNewEntityInsertSQL(newEntityName, newEntity, suffix);
1216
+ const newEntityResult = await ds.query(sSQLInsert);
1217
+ const newEntityID = newEntityResult && newEntityResult.length > 0 ? newEntityResult[0].ID : null;
1218
+ if (!newEntityID)
1219
+ throw new Error(`Failed to create new entity ${newEntityName} for table ${newEntity.SchemaName}.${newEntity.TableName}`);
1220
+ // if we get here we created a new entity safely, otherwise we get exception
1221
+ // add it to the new entity list
1222
+ ManageMetadataBase_1.newEntityList.push(newEntityName);
1223
+ // 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
1224
+ // our config option is set to create new applications from new schemas, then create a new application for this schema
1225
+ if (isNewSchema && config_1.configInfo.newSchemaDefaults.CreateNewApplicationWithSchemaName) {
1226
+ // new schema and config option is to create a new application from the schema name so do that
1227
+ if (!await this.applicationExists(ds, newEntity.SchemaName))
1228
+ await this.createNewApplication(ds, newEntity.SchemaName);
1229
+ }
1230
+ else {
1231
+ // not a new schema, attempt to look up the application for this schema
1232
+ await this.getApplicationIDForSchema(ds, newEntity.SchemaName);
1233
+ }
1234
+ // now we have an application ID, but make sure that we are configured to add this new entity to an application at all
1235
+ if (config_1.configInfo.newEntityDefaults.AddToApplicationWithSchemaName) {
1236
+ // we should add this entity to the application
1237
+ 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
1238
+ const app = md.Applications.find(a => a.Name.trim().toLowerCase() === appName.trim().toLowerCase());
1239
+ if (app) {
1042
1240
  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}'))`;
1241
+ (ApplicationID, EntityID, Sequence) VALUES
1242
+ ('${app.ID}', '${newEntityID}', (SELECT ISNULL(MAX(Sequence),0)+1 FROM ${(0, config_1.mj_core_schema)()}.ApplicationEntity WHERE ApplicationID = '${app.ID}'))`;
1045
1243
  await ds.query(sSQLInsertApplicationEntity);
1046
1244
  }
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) {
1245
+ else
1246
+ (0, core_1.LogError)(` >>>> ERROR: Unable to find Application ID for application ${appName} to add new entity ${newEntityName} to it`);
1247
+ }
1248
+ // next up, we need to check if we're configured to add permissions for new entities, and if so, add them
1249
+ if (config_1.configInfo.newEntityDefaults.PermissionDefaults && config_1.configInfo.newEntityDefaults.PermissionDefaults.AutoAddPermissionsForNewEntities) {
1250
+ // we are asked to add permissions for new entities, so do that by looping through the permissions and adding them
1251
+ const permissions = config_1.configInfo.newEntityDefaults.PermissionDefaults.Permissions;
1252
+ for (const p of permissions) {
1253
+ const RoleID = md.Roles.find(r => r.Name.trim().toLowerCase() === p.RoleName.trim().toLowerCase())?.ID;
1254
+ if (RoleID) {
1052
1255
  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})`;
1256
+ (EntityID, RoleID, CanRead, CanCreate, CanUpdate, CanDelete) VALUES
1257
+ ('${newEntityID}', '${RoleID}', ${p.CanRead ? 1 : 0}, ${p.CanCreate ? 1 : 0}, ${p.CanUpdate ? 1 : 0}, ${p.CanDelete ? 1 : 0})`;
1055
1258
  await ds.query(sSQLInsertPermission);
1056
1259
  }
1260
+ else
1261
+ (0, core_1.LogError)(` >>>> ERROR: Unable to find Role ID for role ${p.RoleName} to add permissions for new entity ${newEntityName}`);
1057
1262
  }
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
1263
  }
1264
+ (0, core_1.LogStatus)(` Created new entity ${newEntityName} for table ${newEntity.SchemaName}.${newEntity.TableName}`);
1064
1265
  }
1065
1266
  else {
1066
1267
  (0, core_1.LogStatus)(` Skipping new entity ${newEntity.TableName} because it doesn't qualify to be created. Reason: ${validationMessage}`);
@@ -1092,11 +1293,12 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1092
1293
  const result = await ds.query(sSQL);
1093
1294
  return result && result.length > 0 ? result[0].ID : null;
1094
1295
  }
1095
- createNewEntityInsertSQL(newEntityID, newEntityName, newEntity, newEntitySuffix) {
1296
+ createNewEntityInsertSQL(newEntityName, newEntity, newEntitySuffix) {
1096
1297
  const newEntityDefaults = config_1.configInfo.newEntityDefaults;
1097
1298
  const newEntityDescriptionEscaped = newEntity.Description ? `'${newEntity.Description.replace(/'/g, "''")}` : null;
1098
- const sSQLInsert = `INSERT INTO [${(0, config_1.mj_core_schema)()}].Entity (
1099
- ID,
1299
+ const sSQLInsert = `
1300
+ DECLARE @InsertedRow TABLE ([ID] UNIQUEIDENTIFIER)
1301
+ INSERT INTO [${(0, config_1.mj_core_schema)()}].Entity (
1100
1302
  Name,
1101
1303
  Description,
1102
1304
  NameSuffix,
@@ -1113,9 +1315,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1113
1315
  ${newEntityDefaults.AllowUpdateAPI === undefined ? '' : ', AllowUpdateAPI'}
1114
1316
  ${newEntityDefaults.AllowDeleteAPI === undefined ? '' : ', AllowDeleteAPI'}
1115
1317
  ${newEntityDefaults.UserViewMaxRows === undefined ? '' : ', UserViewMaxRows'}
1116
- )
1117
- VALUES (
1118
- ${newEntityID},
1318
+ )
1319
+ OUTPUT INSERTED.[ID] INTO @InsertedRow
1320
+ VALUES (
1119
1321
  '${newEntityName}',
1120
1322
  ${newEntityDescriptionEscaped ? newEntityDescriptionEscaped : 'NULL' /*if no description, then null*/},
1121
1323
  ${newEntitySuffix && newEntitySuffix.length > 0 ? `'${newEntitySuffix}'` : 'NULL'},
@@ -1132,7 +1334,9 @@ let ManageMetadataBase = ManageMetadataBase_1 = class ManageMetadataBase {
1132
1334
  ${newEntityDefaults.AllowUpdateAPI === undefined ? '' : ', ' + (newEntityDefaults.AllowUpdateAPI ? '1' : '0')}
1133
1335
  ${newEntityDefaults.AllowDeleteAPI === undefined ? '' : ', ' + (newEntityDefaults.AllowDeleteAPI ? '1' : '0')}
1134
1336
  ${newEntityDefaults.UserViewMaxRows === undefined ? '' : ', ' + (newEntityDefaults.UserViewMaxRows)}
1135
- )`;
1337
+ )
1338
+ SELECT * FROM [__mj].vwEntities WHERE [ID] = (SELECT [ID] FROM @InsertedRow)
1339
+ `;
1136
1340
  return sSQLInsert;
1137
1341
  }
1138
1342
  stripTrailingChars(s, charsToStrip, skipIfExactMatch) {