@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.
- package/dist/Angular/angular-codegen.js +1 -1
- package/dist/Angular/angular-codegen.js.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts +0 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.js +0 -13
- package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/related-entity-components.d.ts +11 -1
- package/dist/Angular/related-entity-components.d.ts.map +1 -1
- package/dist/Angular/related-entity-components.js +26 -1
- package/dist/Angular/related-entity-components.js.map +1 -1
- package/dist/Angular/timeline-related-entity-component.d.ts +36 -0
- package/dist/Angular/timeline-related-entity-component.d.ts.map +1 -0
- package/dist/Angular/timeline-related-entity-component.js +67 -0
- package/dist/Angular/timeline-related-entity-component.js.map +1 -0
- package/dist/Config/db-connection.d.ts +3 -0
- package/dist/Config/db-connection.d.ts.map +1 -1
- package/dist/Config/db-connection.js +46 -1
- package/dist/Config/db-connection.js.map +1 -1
- package/dist/Database/manage-metadata.d.ts +23 -8
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +362 -155
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/sql.d.ts +12 -1
- package/dist/Database/sql.d.ts.map +1 -1
- package/dist/Database/sql.js +196 -36
- package/dist/Database/sql.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +12 -4
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +210 -103
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Misc/createNewUser.d.ts.map +1 -1
- package/dist/Misc/createNewUser.js +2 -1
- package/dist/Misc/createNewUser.js.map +1 -1
- package/dist/Misc/util.d.ts +1 -0
- package/dist/Misc/util.d.ts.map +1 -1
- package/dist/Misc/util.js +24 -2
- package/dist/Misc/util.js.map +1 -1
- package/dist/action_subclasses_codegen.d.ts +1 -1
- package/dist/action_subclasses_codegen.js +1 -1
- package/dist/action_subclasses_codegen.js.map +1 -1
- package/dist/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/entity_subclasses_codegen.js +72 -75
- package/dist/entity_subclasses_codegen.js.map +1 -1
- package/dist/entity_types_codegen.d.ts +15 -0
- package/dist/entity_types_codegen.d.ts.map +1 -0
- package/dist/entity_types_codegen.js +106 -0
- package/dist/entity_types_codegen.js.map +1 -0
- package/dist/graphql_server_codegen.d.ts +4 -4
- package/dist/graphql_server_codegen.d.ts.map +1 -1
- package/dist/graphql_server_codegen.js +65 -58
- package/dist/graphql_server_codegen.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +27 -4
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +8 -7
- package/dist/createNewUser.d.ts +0 -12
- package/dist/createNewUser.d.ts.map +0 -1
- package/dist/createNewUser.js +0 -113
- 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
|
-
|
|
49
|
-
|
|
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
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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,
|
|
374
|
+
async manageEntityFields(ds, excludeSchemas, skipCreatedAtUpdatedAtDeletedAtFieldValidation, skipEntityFieldValues) {
|
|
240
375
|
let bSuccess = true;
|
|
241
376
|
const startTime = new Date();
|
|
242
|
-
if (!
|
|
243
|
-
(
|
|
244
|
-
|
|
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)(`
|
|
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)(`
|
|
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)(`
|
|
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)(`
|
|
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)(`
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
(0, logging_1.
|
|
280
|
-
|
|
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)(`
|
|
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
|
|
311
|
-
const
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
365
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
810
|
+
case core_1.EntityInfo.CreatedAtFieldName.trim().toLowerCase():
|
|
613
811
|
fieldDisplayName = "Created At";
|
|
614
812
|
break;
|
|
615
|
-
case
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
(
|
|
1028
|
+
(EntityFieldID, Sequence, Value, Code)
|
|
827
1029
|
VALUES
|
|
828
|
-
(
|
|
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
|
|
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)(`
|
|
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
|
-
|
|
1016
|
-
const
|
|
1017
|
-
const
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
//
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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
|
-
(
|
|
1044
|
-
('${
|
|
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
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
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
|
-
|
|
1054
|
-
|
|
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(
|
|
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 = `
|
|
1099
|
-
|
|
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
|
-
|
|
1118
|
-
|
|
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) {
|