@memberjunction/codegen-lib 3.4.0 → 4.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.
- package/README.md +11 -0
- package/dist/Angular/angular-codegen.d.ts +1 -1
- package/dist/Angular/angular-codegen.d.ts.map +1 -1
- package/dist/Angular/angular-codegen.js +80 -137
- package/dist/Angular/angular-codegen.js.map +1 -1
- package/dist/Angular/entity-data-grid-related-entity-component.d.ts +1 -1
- package/dist/Angular/entity-data-grid-related-entity-component.js +6 -9
- package/dist/Angular/entity-data-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts +1 -1
- package/dist/Angular/join-grid-related-entity-component.js +8 -36
- package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/related-entity-components.js +13 -79
- package/dist/Angular/related-entity-components.js.map +1 -1
- package/dist/Angular/timeline-related-entity-component.d.ts +1 -1
- package/dist/Angular/timeline-related-entity-component.js +14 -38
- package/dist/Angular/timeline-related-entity-component.js.map +1 -1
- package/dist/Angular/user-view-grid-related-entity-component.d.ts +43 -0
- package/dist/Angular/user-view-grid-related-entity-component.d.ts.map +1 -0
- package/dist/Angular/user-view-grid-related-entity-component.js +85 -0
- package/dist/Angular/user-view-grid-related-entity-component.js.map +1 -0
- package/dist/Config/config.js +161 -177
- package/dist/Config/config.js.map +1 -1
- package/dist/Config/db-connection.d.ts +1 -1
- package/dist/Config/db-connection.d.ts.map +1 -1
- package/dist/Config/db-connection.js +6 -33
- package/dist/Config/db-connection.js.map +1 -1
- package/dist/Database/dbSchema.js +28 -35
- package/dist/Database/dbSchema.js.map +1 -1
- package/dist/Database/manage-metadata.d.ts +3 -3
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +291 -319
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/reorder-columns.d.ts +1 -1
- package/dist/Database/reorder-columns.d.ts.map +1 -1
- package/dist/Database/reorder-columns.js +1 -5
- package/dist/Database/reorder-columns.js.map +1 -1
- package/dist/Database/sql.d.ts +1 -1
- package/dist/Database/sql.d.ts.map +1 -1
- package/dist/Database/sql.js +67 -98
- package/dist/Database/sql.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +2 -2
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +209 -237
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts +11 -0
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts.map +1 -1
- package/dist/Manifest/GenerateClassRegistrationsManifest.js +43 -41
- package/dist/Manifest/GenerateClassRegistrationsManifest.js.map +1 -1
- package/dist/Misc/action_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/action_subclasses_codegen.js +15 -26
- package/dist/Misc/action_subclasses_codegen.js.map +1 -1
- package/dist/Misc/advanced_generation.d.ts +1 -1
- package/dist/Misc/advanced_generation.js +34 -40
- package/dist/Misc/advanced_generation.js.map +1 -1
- package/dist/Misc/createNewUser.d.ts +1 -1
- package/dist/Misc/createNewUser.js +22 -26
- package/dist/Misc/createNewUser.js.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.d.ts +2 -2
- package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.js +33 -40
- package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
- package/dist/Misc/graphql_server_codegen.js +36 -41
- package/dist/Misc/graphql_server_codegen.js.map +1 -1
- package/dist/Misc/runCommand.d.ts +1 -1
- package/dist/Misc/runCommand.js +13 -20
- package/dist/Misc/runCommand.js.map +1 -1
- package/dist/Misc/sql_logging.d.ts +1 -1
- package/dist/Misc/sql_logging.d.ts.map +1 -1
- package/dist/Misc/sql_logging.js +21 -51
- package/dist/Misc/sql_logging.js.map +1 -1
- package/dist/Misc/status_logging.js +45 -60
- package/dist/Misc/status_logging.js.map +1 -1
- package/dist/Misc/system_integrity.d.ts +1 -1
- package/dist/Misc/system_integrity.d.ts.map +1 -1
- package/dist/Misc/system_integrity.js +12 -16
- package/dist/Misc/system_integrity.js.map +1 -1
- package/dist/Misc/temp_batch_file.js +15 -22
- package/dist/Misc/temp_batch_file.js.map +1 -1
- package/dist/Misc/util.d.ts.map +1 -1
- package/dist/Misc/util.js +17 -28
- package/dist/Misc/util.js.map +1 -1
- package/dist/index.d.ts +21 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -41
- package/dist/index.js.map +1 -1
- package/dist/runCodeGen.d.ts +1 -0
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +150 -178
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +24 -22
|
@@ -1,102 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const sql = __importStar(require("mssql"));
|
|
31
|
-
const config_1 = require("../Config/config");
|
|
32
|
-
const core_1 = require("@memberjunction/core");
|
|
33
|
-
const status_logging_1 = require("../Misc/status_logging");
|
|
34
|
-
const sql_1 = require("./sql");
|
|
35
|
-
const advanced_generation_1 = require("../Misc/advanced_generation");
|
|
36
|
-
const global_1 = require("@memberjunction/global");
|
|
37
|
-
const uuid_1 = require("uuid");
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
39
|
-
const path_1 = __importDefault(require("path"));
|
|
40
|
-
const sql_logging_1 = require("../Misc/sql_logging");
|
|
41
|
-
class ValidatorResult {
|
|
42
|
-
entityName = "";
|
|
43
|
-
fieldName;
|
|
44
|
-
sourceCheckConstraint = "";
|
|
45
|
-
functionText = "";
|
|
46
|
-
functionName = "";
|
|
47
|
-
functionDescription = "";
|
|
48
|
-
/**
|
|
49
|
-
* The ID value in the Generated Codes entity that was created for this validator.
|
|
50
|
-
*/
|
|
51
|
-
generatedCodeId = "";
|
|
52
|
-
/**
|
|
53
|
-
* The ID for the AI Model that was used to generate the code
|
|
54
|
-
*/
|
|
55
|
-
aiModelID = "";
|
|
56
|
-
wasGenerated = true;
|
|
57
|
-
success = false;
|
|
1
|
+
import sql from 'mssql';
|
|
2
|
+
import { configInfo, currentWorkingDirectory, getSettingValue, mj_core_schema, outputDir } from '../Config/config.js';
|
|
3
|
+
import { CodeNameFromString, EntityInfo, ExtractActualDefaultValue, LogError, LogStatus, Metadata, SeverityType } from "@memberjunction/core";
|
|
4
|
+
import { logError, logMessage, logStatus } from "../Misc/status_logging.js";
|
|
5
|
+
import { SQLUtilityBase } from "./sql.js";
|
|
6
|
+
import { AdvancedGeneration } from "../Misc/advanced_generation.js";
|
|
7
|
+
import { convertCamelCaseToHaveSpaces, generatePluralName, MJGlobal, stripTrailingChars } from "@memberjunction/global";
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { SQLLogging } from "../Misc/sql_logging.js";
|
|
12
|
+
export class ValidatorResult {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.entityName = "";
|
|
15
|
+
this.sourceCheckConstraint = "";
|
|
16
|
+
this.functionText = "";
|
|
17
|
+
this.functionName = "";
|
|
18
|
+
this.functionDescription = "";
|
|
19
|
+
/**
|
|
20
|
+
* The ID value in the Generated Codes entity that was created for this validator.
|
|
21
|
+
*/
|
|
22
|
+
this.generatedCodeId = "";
|
|
23
|
+
/**
|
|
24
|
+
* The ID for the AI Model that was used to generate the code
|
|
25
|
+
*/
|
|
26
|
+
this.aiModelID = "";
|
|
27
|
+
this.wasGenerated = true;
|
|
28
|
+
this.success = false;
|
|
29
|
+
}
|
|
58
30
|
}
|
|
59
|
-
exports.ValidatorResult = ValidatorResult;
|
|
60
31
|
/**
|
|
61
32
|
* 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
|
|
62
33
|
* to properly register your subclass with a priority of 1+ to ensure it gets instantiated.
|
|
63
34
|
*/
|
|
64
|
-
class ManageMetadataBase {
|
|
65
|
-
|
|
35
|
+
export class ManageMetadataBase {
|
|
36
|
+
constructor() {
|
|
37
|
+
this._sqlUtilityObject = MJGlobal.Instance.ClassFactory.CreateInstance(SQLUtilityBase);
|
|
38
|
+
}
|
|
66
39
|
get SQLUtilityObject() {
|
|
67
40
|
return this._sqlUtilityObject;
|
|
68
41
|
}
|
|
69
|
-
static _newEntityList = [];
|
|
42
|
+
static { this._newEntityList = []; }
|
|
70
43
|
/**
|
|
71
44
|
* Globally scoped list of entities that have been created during the metadata management process.
|
|
72
45
|
*/
|
|
73
46
|
static get newEntityList() {
|
|
74
47
|
return this._newEntityList;
|
|
75
48
|
}
|
|
76
|
-
static _modifiedEntityList = [];
|
|
49
|
+
static { this._modifiedEntityList = []; }
|
|
77
50
|
/**
|
|
78
51
|
* Globally scoped list of entities that have been modified during the metadata management process.
|
|
79
52
|
*/
|
|
80
53
|
static get modifiedEntityList() {
|
|
81
54
|
return this._modifiedEntityList;
|
|
82
55
|
}
|
|
83
|
-
static _generatedValidators = [];
|
|
56
|
+
static { this._generatedValidators = []; }
|
|
84
57
|
/**
|
|
85
58
|
* Globally scoped list of validators that have been generated during the metadata management process.
|
|
86
59
|
*/
|
|
87
60
|
static get generatedValidators() {
|
|
88
61
|
return this._generatedValidators;
|
|
89
62
|
}
|
|
90
|
-
static _softPKFKConfigCache = null;
|
|
91
|
-
static _softPKFKConfigPath = '';
|
|
63
|
+
static { this._softPKFKConfigCache = null; }
|
|
64
|
+
static { this._softPKFKConfigPath = ''; }
|
|
92
65
|
/**
|
|
93
66
|
* Loads and caches the soft PK/FK configuration from the additionalSchemaInfo file.
|
|
94
67
|
* The file is only loaded once per session to avoid repeated I/O.
|
|
95
68
|
*/
|
|
96
69
|
static getSoftPKFKConfig() {
|
|
97
70
|
// Return cached config if path hasn't changed
|
|
98
|
-
const configPath =
|
|
99
|
-
?
|
|
71
|
+
const configPath = configInfo.additionalSchemaInfo
|
|
72
|
+
? path.join(currentWorkingDirectory, configInfo.additionalSchemaInfo)
|
|
100
73
|
: '';
|
|
101
74
|
if (this._softPKFKConfigCache !== null && this._softPKFKConfigPath === configPath) {
|
|
102
75
|
return this._softPKFKConfigCache;
|
|
@@ -125,73 +98,73 @@ class ManageMetadataBase {
|
|
|
125
98
|
* @returns
|
|
126
99
|
*/
|
|
127
100
|
async manageMetadata(pool, currentUser) {
|
|
128
|
-
const md = new
|
|
129
|
-
const excludeSchemas =
|
|
101
|
+
const md = new Metadata();
|
|
102
|
+
const excludeSchemas = configInfo.excludeSchemas ? configInfo.excludeSchemas : [];
|
|
130
103
|
let bSuccess = true;
|
|
131
104
|
let start = new Date();
|
|
132
|
-
|
|
105
|
+
logStatus(' Creating new entities...');
|
|
133
106
|
if (!await this.createNewEntities(pool, currentUser)) {
|
|
134
|
-
|
|
107
|
+
logError(' Error creating new entities');
|
|
135
108
|
bSuccess = false;
|
|
136
109
|
}
|
|
137
|
-
|
|
110
|
+
logStatus(` > Created new entities in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
138
111
|
start = new Date();
|
|
139
|
-
|
|
112
|
+
logStatus(' Updating existing entities...');
|
|
140
113
|
if (!await this.updateExistingEntitiesFromSchema(pool, excludeSchemas)) {
|
|
141
|
-
|
|
114
|
+
logError(' Error updating existing entities');
|
|
142
115
|
bSuccess = false;
|
|
143
116
|
}
|
|
144
|
-
|
|
117
|
+
logStatus(` > Updated existing entities in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
145
118
|
start = new Date();
|
|
146
|
-
|
|
119
|
+
logStatus(' Scanning for tables that were deleted where entity metadata still exists...');
|
|
147
120
|
if (!await this.checkAndRemoveMetadataForDeletedTables(pool, excludeSchemas)) {
|
|
148
|
-
|
|
121
|
+
logError(' Error removing metadata for tables that were removed');
|
|
149
122
|
bSuccess = false;
|
|
150
123
|
}
|
|
151
|
-
|
|
124
|
+
logStatus(` > Removed metadata for deleted tables in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
152
125
|
start = new Date();
|
|
153
|
-
|
|
154
|
-
const sqlUtility =
|
|
155
|
-
const adminSchema =
|
|
156
|
-
const schemasToExclude =
|
|
126
|
+
logStatus(' Recompiling base views...');
|
|
127
|
+
const sqlUtility = MJGlobal.Instance.ClassFactory.CreateInstance(SQLUtilityBase);
|
|
128
|
+
const adminSchema = getSettingValue('mj_core_schema', '__mj');
|
|
129
|
+
const schemasToExclude = getSettingValue('recompile_mj_views', true)
|
|
157
130
|
? excludeSchemas.filter((s) => s !== adminSchema)
|
|
158
131
|
: excludeSchemas;
|
|
159
132
|
if (!await sqlUtility.recompileAllBaseViews(pool, schemasToExclude, true, ManageMetadataBase._newEntityList /*exclude the newly created entities from the above step the first time we run as those views don't exist yet*/)) {
|
|
160
|
-
|
|
133
|
+
logMessage(' Warning: Non-Fatal error recompiling base views', SeverityType.Warning, false);
|
|
161
134
|
// many times the former versions of base views will NOT succesfully recompile, so don't consider that scenario to be a
|
|
162
135
|
// failure for this entire function
|
|
163
136
|
}
|
|
164
|
-
|
|
137
|
+
logStatus(` > Recompiled base views in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
165
138
|
start = new Date();
|
|
166
|
-
|
|
139
|
+
logStatus(' Managing entity fields...');
|
|
167
140
|
// note that we skip Advanced Generation here because we do it again later when the manageSQLScriptsAndExecution occurs in SQLCodeGen class
|
|
168
141
|
if (!await this.manageEntityFields(pool, excludeSchemas, false, false, currentUser, true)) {
|
|
169
|
-
|
|
142
|
+
logError(' Error managing entity fields');
|
|
170
143
|
bSuccess = false;
|
|
171
144
|
}
|
|
172
|
-
|
|
145
|
+
logStatus(` > Managed entity fields in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
173
146
|
start = new Date();
|
|
174
|
-
|
|
147
|
+
logStatus(' Managing entity relationships...');
|
|
175
148
|
if (!await this.manageEntityRelationships(pool, excludeSchemas, md)) {
|
|
176
|
-
|
|
149
|
+
logError(' Error managing entity relationships');
|
|
177
150
|
bSuccess = false;
|
|
178
151
|
}
|
|
179
|
-
|
|
152
|
+
logStatus(` > Managed entity relationships in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
180
153
|
if (ManageMetadataBase.newEntityList.length > 0) {
|
|
181
154
|
await this.generateNewEntityDescriptions(pool, md, currentUser); // don't pass excludeSchemas becuase by definition this is the NEW entities we created
|
|
182
155
|
}
|
|
183
156
|
const veResult = await this.manageVirtualEntities(pool);
|
|
184
157
|
if (!veResult.success) {
|
|
185
|
-
|
|
158
|
+
logError(' Error managing virtual entities');
|
|
186
159
|
bSuccess = false;
|
|
187
160
|
}
|
|
188
161
|
start = new Date();
|
|
189
|
-
|
|
162
|
+
logStatus(' Syncing schema info from database...');
|
|
190
163
|
if (!await this.updateSchemaInfoFromDatabase(pool, excludeSchemas)) {
|
|
191
|
-
|
|
164
|
+
logError(' Error syncing schema info');
|
|
192
165
|
bSuccess = false;
|
|
193
166
|
}
|
|
194
|
-
|
|
167
|
+
logStatus(` > Synced schema info in ${(new Date().getTime() - start.getTime()) / 1000} seconds`);
|
|
195
168
|
return bSuccess;
|
|
196
169
|
}
|
|
197
170
|
async manageVirtualEntities(pool) {
|
|
@@ -199,7 +172,7 @@ class ManageMetadataBase {
|
|
|
199
172
|
// virtual entities are records defined in the entity metadata and do NOT define a distinct base table
|
|
200
173
|
// but they do specify a base view. We DO NOT generate a base view for a virtual entity, we simply use it to figure
|
|
201
174
|
// out the fields that should be in the entity definition and add/update/delete the entity definition to match what's in the view when this runs
|
|
202
|
-
const sql = `SELECT * FROM [${
|
|
175
|
+
const sql = `SELECT * FROM [${mj_core_schema()}].vwEntities WHERE VirtualEntity = 1`;
|
|
203
176
|
const virtualEntitiesResult = await pool.request().query(sql);
|
|
204
177
|
const virtualEntities = virtualEntitiesResult.recordset;
|
|
205
178
|
let anyUpdates = false;
|
|
@@ -209,7 +182,7 @@ class ManageMetadataBase {
|
|
|
209
182
|
const { success, updatedEntity } = await this.manageSingleVirtualEntity(pool, ve);
|
|
210
183
|
anyUpdates = anyUpdates || updatedEntity;
|
|
211
184
|
if (!success) {
|
|
212
|
-
|
|
185
|
+
logError(` Error managing virtual entity ${ve.Name}`);
|
|
213
186
|
bSuccess = false;
|
|
214
187
|
}
|
|
215
188
|
}
|
|
@@ -240,7 +213,7 @@ class ManageMetadataBase {
|
|
|
240
213
|
if (veFields && veFields.length > 0) {
|
|
241
214
|
// we have 1+ fields, now loop through them and process each one
|
|
242
215
|
// first though, remove any fields that are no longer in the view
|
|
243
|
-
const md = new
|
|
216
|
+
const md = new Metadata();
|
|
244
217
|
const entity = md.EntityByName(virtualEntity.Name);
|
|
245
218
|
if (entity) {
|
|
246
219
|
const removeList = [];
|
|
@@ -249,7 +222,7 @@ class ManageMetadataBase {
|
|
|
249
222
|
removeList.push(f.ID);
|
|
250
223
|
}
|
|
251
224
|
if (removeList.length > 0) {
|
|
252
|
-
const sqlRemove = `DELETE FROM [${
|
|
225
|
+
const sqlRemove = `DELETE FROM [${mj_core_schema()}].EntityField WHERE ID IN (${removeList.map(removeId => `'${removeId}'`).join(',')})`;
|
|
253
226
|
// this removes the fields that shouldn't be there anymore
|
|
254
227
|
await this.LogSQLAndExecute(pool, sqlRemove, `SQL text to remove fields from entity ${virtualEntity.Name}`);
|
|
255
228
|
bUpdated = true;
|
|
@@ -263,7 +236,7 @@ class ManageMetadataBase {
|
|
|
263
236
|
const { success, updatedField } = await this.manageSingleVirtualEntityField(pool, virtualEntity, vef, i + 1, !hasPkey && i === 0);
|
|
264
237
|
bUpdated = bUpdated || updatedField;
|
|
265
238
|
if (!success) {
|
|
266
|
-
|
|
239
|
+
logError(`Error managing virtual entity field ${vef.FieldName} for virtual entity ${virtualEntity.Name}`);
|
|
267
240
|
bSuccess = false;
|
|
268
241
|
}
|
|
269
242
|
}
|
|
@@ -271,13 +244,13 @@ class ManageMetadataBase {
|
|
|
271
244
|
}
|
|
272
245
|
if (bUpdated) {
|
|
273
246
|
// finally make sure we update the UpdatedAt field for the entity if we made changes to its fields
|
|
274
|
-
const sqlUpdate = `UPDATE [${
|
|
247
|
+
const sqlUpdate = `UPDATE [${mj_core_schema()}].Entity SET [${EntityInfo.UpdatedAtFieldName}]=GETUTCDATE() WHERE ID='${virtualEntity.ID}'`;
|
|
275
248
|
await this.LogSQLAndExecute(pool, sqlUpdate, `SQL text to update virtual entity updated date for ${virtualEntity.Name}`);
|
|
276
249
|
}
|
|
277
250
|
return { success: bSuccess, updatedEntity: bUpdated };
|
|
278
251
|
}
|
|
279
252
|
catch (e) {
|
|
280
|
-
|
|
253
|
+
logError(e);
|
|
281
254
|
return { success: false, updatedEntity: bUpdated };
|
|
282
255
|
}
|
|
283
256
|
}
|
|
@@ -285,7 +258,7 @@ class ManageMetadataBase {
|
|
|
285
258
|
// this protected checks to see if the field exists in the entity definition, and if not, adds it
|
|
286
259
|
// if it exist it updates the entity field to match the view's data type and nullability attributes
|
|
287
260
|
// first, get the entity definition
|
|
288
|
-
const md = new
|
|
261
|
+
const md = new Metadata();
|
|
289
262
|
const entity = md.EntityByName(virtualEntity.Name);
|
|
290
263
|
let newEntityFieldUUID = null;
|
|
291
264
|
let didUpdate = false;
|
|
@@ -302,7 +275,7 @@ class ManageMetadataBase {
|
|
|
302
275
|
field.Sequence !== fieldSequence) {
|
|
303
276
|
// the field needs to be updated, so update it
|
|
304
277
|
const sqlUpdate = `UPDATE
|
|
305
|
-
[${
|
|
278
|
+
[${mj_core_schema()}].EntityField
|
|
306
279
|
SET
|
|
307
280
|
Sequence=${fieldSequence},
|
|
308
281
|
Type='${veField.Type}',
|
|
@@ -320,7 +293,7 @@ class ManageMetadataBase {
|
|
|
320
293
|
else {
|
|
321
294
|
// this means that we do NOT have a match so the field does not exist in the entity definition, so we need to add it
|
|
322
295
|
newEntityFieldUUID = this.createNewUUID();
|
|
323
|
-
const sqlAdd = `INSERT INTO [${
|
|
296
|
+
const sqlAdd = `INSERT INTO [${mj_core_schema()}].EntityField (
|
|
324
297
|
ID, EntityID, Name, Type, AllowsNull,
|
|
325
298
|
Length, Precision, Scale,
|
|
326
299
|
Sequence, IsPrimaryKey, IsUnique )
|
|
@@ -370,16 +343,16 @@ class ManageMetadataBase {
|
|
|
370
343
|
try {
|
|
371
344
|
// STEP 1 - search for all foreign keys in the vwEntityFields view, we use the RelatedEntityID field to determine our FKs
|
|
372
345
|
const sSQL = `SELECT *
|
|
373
|
-
FROM ${
|
|
346
|
+
FROM ${mj_core_schema()}.vwEntityFields
|
|
374
347
|
WHERE
|
|
375
348
|
RelatedEntityID IS NOT NULL AND
|
|
376
349
|
IsVirtual = 0 AND
|
|
377
|
-
EntityID NOT IN (SELECT ID FROM ${
|
|
350
|
+
EntityID NOT IN (SELECT ID FROM ${mj_core_schema()}.Entity WHERE SchemaName IN (${excludeSchemas.map(s => `'${s}'`).join(',')}))
|
|
378
351
|
ORDER BY RelatedEntityID`;
|
|
379
352
|
const entityFieldsResult = await pool.request().query(sSQL);
|
|
380
353
|
const entityFields = entityFieldsResult.recordset;
|
|
381
354
|
// Get the relationship counts for each entity
|
|
382
|
-
const sSQLRelationshipCount = `SELECT EntityID, COUNT(*) AS Count FROM ${
|
|
355
|
+
const sSQLRelationshipCount = `SELECT EntityID, COUNT(*) AS Count FROM ${mj_core_schema()}.EntityRelationship GROUP BY EntityID`;
|
|
383
356
|
const relationshipCountsResult = await pool.request().query(sSQLRelationshipCount);
|
|
384
357
|
const relationshipCounts = relationshipCountsResult.recordset;
|
|
385
358
|
const relationshipCountMap = new Map();
|
|
@@ -387,7 +360,7 @@ class ManageMetadataBase {
|
|
|
387
360
|
relationshipCountMap.set(rc.EntityID, rc.Count);
|
|
388
361
|
}
|
|
389
362
|
// get all relationships in one query for performance improvement
|
|
390
|
-
const sSQLRelationship = `SELECT * FROM ${
|
|
363
|
+
const sSQLRelationship = `SELECT * FROM ${mj_core_schema()}.EntityRelationship`;
|
|
391
364
|
const allRelationshipsResult = await pool.request().query(sSQLRelationship);
|
|
392
365
|
const allRelationships = allRelationshipsResult.recordset;
|
|
393
366
|
// Function to process a batch of entity fields
|
|
@@ -406,11 +379,11 @@ class ManageMetadataBase {
|
|
|
406
379
|
batchSQL += `
|
|
407
380
|
IF NOT EXISTS (
|
|
408
381
|
SELECT 1
|
|
409
|
-
FROM [${
|
|
382
|
+
FROM [${mj_core_schema()}].EntityRelationship
|
|
410
383
|
WHERE ID = '${newEntityRelationshipUUID}'
|
|
411
384
|
)
|
|
412
385
|
BEGIN
|
|
413
|
-
INSERT INTO ${
|
|
386
|
+
INSERT INTO ${mj_core_schema()}.EntityRelationship (ID, EntityID, RelatedEntityID, RelatedEntityJoinField, Type, BundleInAPI, DisplayInForm, DisplayName, Sequence)
|
|
414
387
|
VALUES ('${newEntityRelationshipUUID}', '${f.RelatedEntityID}', '${f.EntityID}', '${f.Name}', 'One To Many', 1, 1, '${e.Name}', ${sequence});
|
|
415
388
|
END
|
|
416
389
|
`;
|
|
@@ -430,7 +403,7 @@ class ManageMetadataBase {
|
|
|
430
403
|
return true;
|
|
431
404
|
}
|
|
432
405
|
catch (e) {
|
|
433
|
-
|
|
406
|
+
logError(e);
|
|
434
407
|
return false;
|
|
435
408
|
}
|
|
436
409
|
}
|
|
@@ -442,7 +415,7 @@ class ManageMetadataBase {
|
|
|
442
415
|
*/
|
|
443
416
|
async checkAndRemoveMetadataForDeletedTables(pool, excludeSchemas) {
|
|
444
417
|
try {
|
|
445
|
-
const sql = `SELECT * FROM ${
|
|
418
|
+
const sql = `SELECT * FROM ${mj_core_schema()}.vwEntitiesWithMissingBaseTables WHERE VirtualEntity=0`;
|
|
446
419
|
const entitiesResult = await pool.request().query(sql);
|
|
447
420
|
const entities = entitiesResult.recordset;
|
|
448
421
|
if (entities && entities.length > 0) {
|
|
@@ -453,7 +426,7 @@ class ManageMetadataBase {
|
|
|
453
426
|
try {
|
|
454
427
|
const sqlDelete = `__mj.spDeleteEntityWithCoreDependencies @EntityID='${e.ID}'`;
|
|
455
428
|
await this.LogSQLAndExecute(pool, sqlDelete, `SQL text to remove entity ${e.Name}`);
|
|
456
|
-
|
|
429
|
+
logStatus(` > Removed metadata for table ${e.SchemaName}.${e.BaseTable}`);
|
|
457
430
|
// next up we need to remove the spCreate, spDelete, spUpdate, BaseView, and FullTextSearchFunction, if provided.
|
|
458
431
|
// We only remoe these artifcacts when they are generated which is info we have in the BaseViewGenerated, spCreateGenerated, etc. fields
|
|
459
432
|
await this.checkDropSQLObject(pool, e.BaseViewGenerated, 'view', e.SchemaName, e.BaseView);
|
|
@@ -463,17 +436,17 @@ class ManageMetadataBase {
|
|
|
463
436
|
await this.checkDropSQLObject(pool, e.FullTextSearchFunctionGenerated, 'function', e.SchemaName, e.FullTextSearchFunction);
|
|
464
437
|
}
|
|
465
438
|
catch (ex) {
|
|
466
|
-
|
|
439
|
+
logError(`Error removing metadata for entity ${ex.Name}, error: ${ex}`);
|
|
467
440
|
}
|
|
468
441
|
}
|
|
469
442
|
// if we get here we now need to refresh our metadata object
|
|
470
|
-
const md = new
|
|
443
|
+
const md = new Metadata();
|
|
471
444
|
await md.Refresh();
|
|
472
445
|
}
|
|
473
446
|
return true;
|
|
474
447
|
}
|
|
475
448
|
catch (e) {
|
|
476
|
-
|
|
449
|
+
logError(e);
|
|
477
450
|
return false;
|
|
478
451
|
}
|
|
479
452
|
}
|
|
@@ -487,23 +460,23 @@ class ManageMetadataBase {
|
|
|
487
460
|
const sqlDelete = `IF OBJECT_ID('[${schemaName}].[${name}]', '${objectTypeCode}') IS NOT NULL\n DROP ${upperType} [${schemaName}].[${name}]`;
|
|
488
461
|
await this.LogSQLAndExecute(pool, sqlDelete, `SQL text to remove ${type} ${schemaName}.${name}`);
|
|
489
462
|
// next up, we need to clean up the cache of saved DB objects that may exist for this entity in the appropriate sub-directory.
|
|
490
|
-
const sqlOutputDir =
|
|
463
|
+
const sqlOutputDir = outputDir('SQL', true);
|
|
491
464
|
if (sqlOutputDir) {
|
|
492
465
|
// now do the same thing for the /schema directory within the provided directory
|
|
493
466
|
const fType = type === 'procedure' ? 'sp' : type === 'view' ? 'view' : 'full_text_search_function';
|
|
494
|
-
const filePath =
|
|
495
|
-
const filePathPermissions =
|
|
467
|
+
const filePath = path.join(sqlOutputDir, this.SQLUtilityObject.getDBObjectFileName(fType, schemaName, name, false, true));
|
|
468
|
+
const filePathPermissions = path.join(sqlOutputDir, this.SQLUtilityObject.getDBObjectFileName(fType, schemaName, name, true, true));
|
|
496
469
|
// if the files exist, delete them
|
|
497
470
|
if (fs.existsSync(filePath))
|
|
498
471
|
fs.unlinkSync(filePath);
|
|
499
472
|
if (fs.existsSync(filePathPermissions))
|
|
500
473
|
fs.unlinkSync(filePathPermissions);
|
|
501
474
|
}
|
|
502
|
-
|
|
475
|
+
logStatus(` > Removed ${type} ${schemaName}.${name}`);
|
|
503
476
|
}
|
|
504
477
|
}
|
|
505
478
|
catch (e) {
|
|
506
|
-
|
|
479
|
+
logError(` > Error removing ${type} ${schemaName}.${name}, error: ${e}`);
|
|
507
480
|
}
|
|
508
481
|
}
|
|
509
482
|
/**
|
|
@@ -529,72 +502,72 @@ class ManageMetadataBase {
|
|
|
529
502
|
if (!skipCreatedAtUpdatedAtDeletedAtFieldValidation) {
|
|
530
503
|
if (!await this.ensureCreatedAtUpdatedAtFieldsExist(pool, excludeSchemas) ||
|
|
531
504
|
!await this.ensureDeletedAtFieldsExist(pool, excludeSchemas)) {
|
|
532
|
-
|
|
505
|
+
logError(`Error ensuring ${EntityInfo.CreatedAtFieldName}, ${EntityInfo.UpdatedAtFieldName} and ${EntityInfo.DeletedAtFieldName} fields exist`);
|
|
533
506
|
bSuccess = false;
|
|
534
507
|
}
|
|
535
|
-
|
|
508
|
+
logStatus(` Ensured ${EntityInfo.CreatedAtFieldName}/${EntityInfo.UpdatedAtFieldName}/${EntityInfo.DeletedAtFieldName} fields exist in ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
|
|
536
509
|
}
|
|
537
510
|
const step1StartTime = new Date();
|
|
538
511
|
if (!await this.deleteUnneededEntityFields(pool, excludeSchemas)) {
|
|
539
|
-
|
|
512
|
+
logError('Error deleting unneeded entity fields');
|
|
540
513
|
bSuccess = false;
|
|
541
514
|
}
|
|
542
|
-
|
|
515
|
+
logStatus(` Deleted unneeded entity fields in ${(new Date().getTime() - step1StartTime.getTime()) / 1000} seconds`);
|
|
543
516
|
// AN: 14-June-2025 - See note below about the new order of these steps, this must
|
|
544
517
|
// happen before we update existing entity fields from schema.
|
|
545
518
|
const step2StartTime = new Date();
|
|
546
519
|
if (!await this.createNewEntityFieldsFromSchema(pool)) { // has its own internal filtering for exclude schema/table so don't pass in
|
|
547
|
-
|
|
520
|
+
logError('Error creating new entity fields from schema');
|
|
548
521
|
bSuccess = false;
|
|
549
522
|
}
|
|
550
|
-
|
|
523
|
+
logStatus(` Created new entity fields from schema in ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
|
|
551
524
|
// AN: 14-June-2025 - we are now running this AFTER we create new entity fields from schema
|
|
552
525
|
// which results in the same pattern of behavior as migrations where we first create new fields
|
|
553
526
|
// with VERY HIGH sequence numbers (e.g. 100,000 above what they will be approx) and then
|
|
554
527
|
// we align them properly in sequential order from 1+ via this method below.
|
|
555
528
|
const step3StartTime = new Date();
|
|
556
529
|
if (!await this.updateExistingEntityFieldsFromSchema(pool, excludeSchemas)) {
|
|
557
|
-
|
|
530
|
+
logError('Error updating existing entity fields from schema');
|
|
558
531
|
bSuccess = false;
|
|
559
532
|
}
|
|
560
|
-
|
|
533
|
+
logStatus(` Updated existing entity fields from schema in ${(new Date().getTime() - step3StartTime.getTime()) / 1000} seconds`);
|
|
561
534
|
// Apply soft PK/FK configuration if config file exists
|
|
562
535
|
const stepConfigStartTime = new Date();
|
|
563
536
|
if (!await this.applySoftPKFKConfig(pool)) {
|
|
564
|
-
|
|
537
|
+
logError('Error applying soft PK/FK configuration');
|
|
565
538
|
}
|
|
566
|
-
|
|
539
|
+
logStatus(` Applied soft PK/FK configuration in ${(new Date().getTime() - stepConfigStartTime.getTime()) / 1000} seconds`);
|
|
567
540
|
const step4StartTime = new Date();
|
|
568
541
|
if (!await this.setDefaultColumnWidthWhereNeeded(pool, excludeSchemas)) {
|
|
569
|
-
|
|
542
|
+
logError('Error setting default column width where needed');
|
|
570
543
|
bSuccess = false;
|
|
571
544
|
}
|
|
572
|
-
|
|
545
|
+
logStatus(` Set default column width where needed in ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
|
|
573
546
|
const step5StartTime = new Date();
|
|
574
547
|
if (!await this.updateEntityFieldDisplayNameWhereNull(pool, excludeSchemas)) {
|
|
575
|
-
|
|
548
|
+
logError('Error updating entity field display name where null');
|
|
576
549
|
bSuccess = false;
|
|
577
550
|
}
|
|
578
|
-
|
|
551
|
+
logStatus(` Updated entity field display name where null in ${(new Date().getTime() - step5StartTime.getTime()) / 1000} seconds`);
|
|
579
552
|
if (!skipEntityFieldValues) {
|
|
580
553
|
const step6StartTime = new Date();
|
|
581
|
-
|
|
554
|
+
logStatus(` Starting to manage entity field values...`);
|
|
582
555
|
if (!await this.manageEntityFieldValuesAndValidatorFunctions(pool, excludeSchemas, currentUser, false)) {
|
|
583
|
-
|
|
556
|
+
logError('Error managing entity field values');
|
|
584
557
|
bSuccess = false;
|
|
585
558
|
}
|
|
586
|
-
|
|
559
|
+
logStatus(` Managed entity field values in ${(new Date().getTime() - step6StartTime.getTime()) / 1000} seconds`);
|
|
587
560
|
}
|
|
588
561
|
// Advanced Generation - Smart field identification and form layout
|
|
589
562
|
if (!skipAdvancedGeneration) {
|
|
590
563
|
const step7StartTime = new Date();
|
|
591
564
|
if (!await this.applyAdvancedGeneration(pool, excludeSchemas, currentUser)) {
|
|
592
|
-
|
|
565
|
+
logError('Error applying advanced generation features');
|
|
593
566
|
// Don't fail the entire process - advanced generation is optional
|
|
594
567
|
}
|
|
595
|
-
|
|
568
|
+
logStatus(` Applied advanced generation features in ${(new Date().getTime() - step7StartTime.getTime()) / 1000} seconds`);
|
|
596
569
|
}
|
|
597
|
-
|
|
570
|
+
logStatus(` Total time to manage entity fields: ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
|
|
598
571
|
return bSuccess;
|
|
599
572
|
}
|
|
600
573
|
/**
|
|
@@ -605,7 +578,7 @@ class ManageMetadataBase {
|
|
|
605
578
|
const sqlEntities = `SELECT
|
|
606
579
|
*
|
|
607
580
|
FROM
|
|
608
|
-
[${
|
|
581
|
+
[${mj_core_schema()}].vwEntities
|
|
609
582
|
WHERE
|
|
610
583
|
VirtualEntity=0 AND
|
|
611
584
|
DeleteType='Soft' AND
|
|
@@ -620,21 +593,21 @@ class ManageMetadataBase {
|
|
|
620
593
|
FROM INFORMATION_SCHEMA.COLUMNS
|
|
621
594
|
WHERE
|
|
622
595
|
${entities.map((e) => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
|
|
623
|
-
AND COLUMN_NAME='${
|
|
596
|
+
AND COLUMN_NAME='${EntityInfo.DeletedAtFieldName}'`;
|
|
624
597
|
const resultResult = await pool.request().query(sql);
|
|
625
598
|
const result = resultResult.recordset;
|
|
626
599
|
for (const e of entities) {
|
|
627
600
|
const eResult = result.filter((r) => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
|
|
628
|
-
const deletedAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() ===
|
|
601
|
+
const deletedAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() === EntityInfo.DeletedAtFieldName.trim().toLowerCase());
|
|
629
602
|
// now, if we have the fields, we need to check the default value and update if necessary
|
|
630
|
-
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e,
|
|
603
|
+
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e, EntityInfo.DeletedAtFieldName, deletedAt, true);
|
|
631
604
|
overallResult = overallResult && fieldResult;
|
|
632
605
|
}
|
|
633
606
|
}
|
|
634
607
|
return overallResult;
|
|
635
608
|
}
|
|
636
609
|
catch (e) {
|
|
637
|
-
|
|
610
|
+
logError(e);
|
|
638
611
|
return false;
|
|
639
612
|
}
|
|
640
613
|
}
|
|
@@ -646,27 +619,27 @@ class ManageMetadataBase {
|
|
|
646
619
|
*/
|
|
647
620
|
async applySoftPKFKConfig(pool) {
|
|
648
621
|
// Check if additionalSchemaInfo is configured in mj.config.cjs
|
|
649
|
-
if (!
|
|
622
|
+
if (!configInfo.additionalSchemaInfo) {
|
|
650
623
|
// No additional schema info configured - this is fine, it's optional
|
|
651
624
|
return true;
|
|
652
625
|
}
|
|
653
|
-
const configPath =
|
|
626
|
+
const configPath = path.join(currentWorkingDirectory, configInfo.additionalSchemaInfo);
|
|
654
627
|
if (!fs.existsSync(configPath)) {
|
|
655
|
-
|
|
628
|
+
logStatus(` ⚠️ additionalSchemaInfo configured but file not found: ${configPath}`);
|
|
656
629
|
return true;
|
|
657
630
|
}
|
|
658
631
|
try {
|
|
659
|
-
|
|
632
|
+
logStatus(` Found ${configInfo.additionalSchemaInfo}, applying soft PK/FK configuration...`);
|
|
660
633
|
const config = ManageMetadataBase.getSoftPKFKConfig();
|
|
661
634
|
let totalPKs = 0;
|
|
662
635
|
let totalFKs = 0;
|
|
663
|
-
const schema =
|
|
636
|
+
const schema = mj_core_schema();
|
|
664
637
|
for (const table of config.tables || []) {
|
|
665
638
|
// Look up entity ID (SELECT query - no need to log to migration file)
|
|
666
639
|
const entityLookupSQL = `SELECT ID FROM [${schema}].[Entity] WHERE SchemaName = '${table.schemaName}' AND BaseTable = '${table.tableName}'`;
|
|
667
640
|
const entityResult = await pool.request().query(entityLookupSQL);
|
|
668
641
|
if (entityResult.recordset.length === 0) {
|
|
669
|
-
|
|
642
|
+
logStatus(` ⚠️ Entity not found for ${table.schemaName}.${table.tableName} - skipping`);
|
|
670
643
|
continue;
|
|
671
644
|
}
|
|
672
645
|
const entityId = entityResult.recordset[0].ID;
|
|
@@ -675,13 +648,13 @@ class ManageMetadataBase {
|
|
|
675
648
|
if (table.primaryKeys && table.primaryKeys.length > 0) {
|
|
676
649
|
for (const pk of table.primaryKeys) {
|
|
677
650
|
const sSQL = `UPDATE [${schema}].[EntityField]
|
|
678
|
-
SET ${
|
|
651
|
+
SET ${EntityInfo.UpdatedAtFieldName}=GETUTCDATE(),
|
|
679
652
|
[IsPrimaryKey] = 1,
|
|
680
653
|
[IsSoftPrimaryKey] = 1
|
|
681
654
|
WHERE [EntityID] = '${entityId}' AND [Name] = '${pk.fieldName}'`;
|
|
682
655
|
const result = await this.LogSQLAndExecute(pool, sSQL, `Set soft PK for ${table.schemaName}.${table.tableName}.${pk.fieldName}`);
|
|
683
656
|
if (result !== null) {
|
|
684
|
-
|
|
657
|
+
logStatus(` ✓ Set IsPrimaryKey=1, IsSoftPrimaryKey=1 for ${table.tableName}.${pk.fieldName}`);
|
|
685
658
|
totalPKs++;
|
|
686
659
|
}
|
|
687
660
|
}
|
|
@@ -693,29 +666,29 @@ class ManageMetadataBase {
|
|
|
693
666
|
const relatedLookupSQL = `SELECT ID FROM [${schema}].[Entity] WHERE SchemaName = '${fk.relatedSchema}' AND BaseTable = '${fk.relatedTable}'`;
|
|
694
667
|
const relatedEntityResult = await pool.request().query(relatedLookupSQL);
|
|
695
668
|
if (relatedEntityResult.recordset.length === 0) {
|
|
696
|
-
|
|
669
|
+
logStatus(` ⚠️ Related entity not found for ${fk.relatedSchema}.${fk.relatedTable} - skipping FK ${fk.fieldName}`);
|
|
697
670
|
continue;
|
|
698
671
|
}
|
|
699
672
|
const relatedEntityId = relatedEntityResult.recordset[0].ID;
|
|
700
673
|
const sSQL = `UPDATE [${schema}].[EntityField]
|
|
701
|
-
SET ${
|
|
674
|
+
SET ${EntityInfo.UpdatedAtFieldName}=GETUTCDATE(),
|
|
702
675
|
[RelatedEntityID] = '${relatedEntityId}',
|
|
703
676
|
[RelatedEntityFieldName] = '${fk.relatedField}',
|
|
704
677
|
[IsSoftForeignKey] = 1
|
|
705
678
|
WHERE [EntityID] = '${entityId}' AND [Name] = '${fk.fieldName}'`;
|
|
706
679
|
const result = await this.LogSQLAndExecute(pool, sSQL, `Set soft FK for ${table.schemaName}.${table.tableName}.${fk.fieldName} → ${fk.relatedTable}.${fk.relatedField}`);
|
|
707
680
|
if (result !== null) {
|
|
708
|
-
|
|
681
|
+
logStatus(` ✓ Set soft FK for ${table.tableName}.${fk.fieldName} → ${fk.relatedTable}.${fk.relatedField}`);
|
|
709
682
|
totalFKs++;
|
|
710
683
|
}
|
|
711
684
|
}
|
|
712
685
|
}
|
|
713
686
|
}
|
|
714
|
-
|
|
687
|
+
logStatus(` Applied ${totalPKs} soft PK(s) and ${totalFKs} soft FK(s) from configuration`);
|
|
715
688
|
return true;
|
|
716
689
|
}
|
|
717
690
|
catch (e) {
|
|
718
|
-
|
|
691
|
+
logError(`Error applying soft PK/FK configuration: ${e}`);
|
|
719
692
|
return false;
|
|
720
693
|
}
|
|
721
694
|
}
|
|
@@ -730,7 +703,7 @@ class ManageMetadataBase {
|
|
|
730
703
|
const sqlEntities = `SELECT
|
|
731
704
|
*
|
|
732
705
|
FROM
|
|
733
|
-
[${
|
|
706
|
+
[${mj_core_schema()}].vwEntities
|
|
734
707
|
WHERE
|
|
735
708
|
VirtualEntity = 0 AND
|
|
736
709
|
TrackRecordChanges = 1 AND
|
|
@@ -746,24 +719,24 @@ class ManageMetadataBase {
|
|
|
746
719
|
FROM INFORMATION_SCHEMA.COLUMNS
|
|
747
720
|
WHERE
|
|
748
721
|
${entities.map((e) => `(TABLE_SCHEMA='${e.SchemaName}' AND TABLE_NAME='${e.BaseTable}')`).join(' OR ')}
|
|
749
|
-
AND COLUMN_NAME IN ('${
|
|
722
|
+
AND COLUMN_NAME IN ('${EntityInfo.CreatedAtFieldName}','${EntityInfo.UpdatedAtFieldName}')`;
|
|
750
723
|
const resultResult = await pool.request().query(sqlCreatedUpdated);
|
|
751
724
|
const result = resultResult.recordset;
|
|
752
725
|
for (const e of entities) {
|
|
753
726
|
// 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
|
|
754
727
|
const eResult = result.filter((r) => r.TABLE_NAME === e.BaseTable && r.TABLE_SCHEMA === e.SchemaName); // get just the fields for this entity
|
|
755
|
-
const createdAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() ===
|
|
756
|
-
const updatedAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() ===
|
|
728
|
+
const createdAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() === EntityInfo.CreatedAtFieldName.trim().toLowerCase());
|
|
729
|
+
const updatedAt = eResult.find((r) => r.COLUMN_NAME.trim().toLowerCase() === EntityInfo.UpdatedAtFieldName.trim().toLowerCase());
|
|
757
730
|
// now, if we have the fields, we need to check the default value and update if necessary
|
|
758
|
-
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e,
|
|
759
|
-
await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e,
|
|
731
|
+
const fieldResult = await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e, EntityInfo.CreatedAtFieldName, createdAt, false) &&
|
|
732
|
+
await this.ensureSpecialDateFieldExistsAndHasCorrectDefaultValue(pool, e, EntityInfo.UpdatedAtFieldName, updatedAt, false);
|
|
760
733
|
overallResult = overallResult && fieldResult;
|
|
761
734
|
}
|
|
762
735
|
}
|
|
763
736
|
return overallResult;
|
|
764
737
|
}
|
|
765
738
|
catch (e) {
|
|
766
|
-
|
|
739
|
+
logError(e);
|
|
767
740
|
return false;
|
|
768
741
|
}
|
|
769
742
|
}
|
|
@@ -799,7 +772,7 @@ class ManageMetadataBase {
|
|
|
799
772
|
// field that is NOT NULL
|
|
800
773
|
if (!allowNull) {
|
|
801
774
|
const defaultValue = currentFieldData.COLUMN_DEFAULT;
|
|
802
|
-
const realDefaultValue =
|
|
775
|
+
const realDefaultValue = ExtractActualDefaultValue(defaultValue);
|
|
803
776
|
if (!realDefaultValue || realDefaultValue.trim().toLowerCase() !== 'getutcdate()') {
|
|
804
777
|
await this.dropAndCreateDefaultConstraintForSpecialDateField(pool, entity, fieldName);
|
|
805
778
|
}
|
|
@@ -810,7 +783,7 @@ class ManageMetadataBase {
|
|
|
810
783
|
return true;
|
|
811
784
|
}
|
|
812
785
|
catch (e) {
|
|
813
|
-
|
|
786
|
+
logError(e);
|
|
814
787
|
return false;
|
|
815
788
|
}
|
|
816
789
|
}
|
|
@@ -819,11 +792,11 @@ class ManageMetadataBase {
|
|
|
819
792
|
*/
|
|
820
793
|
async createDefaultConstraintForSpecialDateField(pool, entity, fieldName) {
|
|
821
794
|
try {
|
|
822
|
-
const sqlAddDefaultConstraint = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD CONSTRAINT DF_${entity.SchemaName}_${
|
|
795
|
+
const sqlAddDefaultConstraint = `ALTER TABLE [${entity.SchemaName}].[${entity.BaseTable}] ADD CONSTRAINT DF_${entity.SchemaName}_${CodeNameFromString(entity.BaseTable)}_${fieldName} DEFAULT GETUTCDATE() FOR [${fieldName}]`;
|
|
823
796
|
await this.LogSQLAndExecute(pool, sqlAddDefaultConstraint, `SQL text to add default constraint for special date field ${fieldName} in entity ${entity.SchemaName}.${entity.BaseTable}`);
|
|
824
797
|
}
|
|
825
798
|
catch (e) {
|
|
826
|
-
|
|
799
|
+
logError(e);
|
|
827
800
|
}
|
|
828
801
|
}
|
|
829
802
|
/**
|
|
@@ -867,7 +840,7 @@ class ManageMetadataBase {
|
|
|
867
840
|
await this.LogSQLAndExecute(pool, sqlDropDefaultConstraint, `SQL text to drop default existing default constraints in entity ${entity.SchemaName}.${entity.BaseTable}`);
|
|
868
841
|
}
|
|
869
842
|
catch (e) {
|
|
870
|
-
|
|
843
|
+
logError(e);
|
|
871
844
|
}
|
|
872
845
|
}
|
|
873
846
|
/**
|
|
@@ -879,18 +852,18 @@ class ManageMetadataBase {
|
|
|
879
852
|
*/
|
|
880
853
|
async generateNewEntityDescriptions(pool, md, currentUser) {
|
|
881
854
|
// for the list of new entities, go through and attempt to generate new entity descriptions
|
|
882
|
-
const ag = new
|
|
855
|
+
const ag = new AdvancedGeneration();
|
|
883
856
|
if (ag.featureEnabled('EntityDescriptions')) {
|
|
884
857
|
// we have the feature enabled, so let's loop through the new entities and generate descriptions for them
|
|
885
858
|
for (let e of ManageMetadataBase.newEntityList) {
|
|
886
|
-
const dataResult = await pool.request().query(`SELECT * FROM [${
|
|
859
|
+
const dataResult = await pool.request().query(`SELECT * FROM [${mj_core_schema()}].vwEntities WHERE Name = '${e}'`);
|
|
887
860
|
const data = dataResult.recordset;
|
|
888
|
-
const fieldsResult = await pool.request().query(`SELECT * FROM [${
|
|
861
|
+
const fieldsResult = await pool.request().query(`SELECT * FROM [${mj_core_schema()}].vwEntityFields WHERE EntityID='${data[0].ID}'`);
|
|
889
862
|
const fields = fieldsResult.recordset;
|
|
890
863
|
// Use new API to generate entity description
|
|
891
864
|
const result = await ag.generateEntityDescription(e, data[0].BaseTable, fields.map((f) => ({ Name: f.Name, Type: f.Type, IsNullable: f.AllowsNull, Description: f.Description })), currentUser);
|
|
892
865
|
if (result?.entityDescription && result.entityDescription.length > 0) {
|
|
893
|
-
const sSQL = `UPDATE [${
|
|
866
|
+
const sSQL = `UPDATE [${mj_core_schema()}].Entity SET Description = '${result.entityDescription}' WHERE Name = '${e}'`;
|
|
894
867
|
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update entity description for entity ${e}`);
|
|
895
868
|
}
|
|
896
869
|
else {
|
|
@@ -912,9 +885,9 @@ class ManageMetadataBase {
|
|
|
912
885
|
const sql = `SELECT
|
|
913
886
|
ef.ID, ef.Name
|
|
914
887
|
FROM
|
|
915
|
-
[${
|
|
888
|
+
[${mj_core_schema()}].vwEntityFields ef
|
|
916
889
|
INNER JOIN
|
|
917
|
-
[${
|
|
890
|
+
[${mj_core_schema()}].vwEntities e
|
|
918
891
|
ON
|
|
919
892
|
ef.EntityID = e.ID
|
|
920
893
|
WHERE
|
|
@@ -927,16 +900,16 @@ class ManageMetadataBase {
|
|
|
927
900
|
const fields = fieldsResult.recordset;
|
|
928
901
|
if (fields && fields.length > 0)
|
|
929
902
|
for (const field of fields) {
|
|
930
|
-
const sDisplayName =
|
|
903
|
+
const sDisplayName = stripTrailingChars(convertCamelCaseToHaveSpaces(field.Name), 'ID', true).trim();
|
|
931
904
|
if (sDisplayName.length > 0 && sDisplayName.toLowerCase().trim() !== field.Name.toLowerCase().trim()) {
|
|
932
|
-
const sSQL = `UPDATE [${
|
|
905
|
+
const sSQL = `UPDATE [${mj_core_schema()}].EntityField SET ${EntityInfo.UpdatedAtFieldName}=GETUTCDATE(), DisplayName = '${sDisplayName}' WHERE ID = '${field.ID}'`;
|
|
933
906
|
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update display name for field ${field.Name}`);
|
|
934
907
|
}
|
|
935
908
|
}
|
|
936
909
|
return true;
|
|
937
910
|
}
|
|
938
911
|
catch (e) {
|
|
939
|
-
|
|
912
|
+
logError(e);
|
|
940
913
|
return false;
|
|
941
914
|
}
|
|
942
915
|
}
|
|
@@ -950,12 +923,12 @@ class ManageMetadataBase {
|
|
|
950
923
|
*/
|
|
951
924
|
async setDefaultColumnWidthWhereNeeded(pool, excludeSchemas) {
|
|
952
925
|
try {
|
|
953
|
-
const sSQL = `EXEC ${
|
|
926
|
+
const sSQL = `EXEC ${mj_core_schema()}.spSetDefaultColumnWidthWhereNeeded @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
954
927
|
await this.LogSQLAndExecute(pool, sSQL, `SQL text to set default column width where needed`, true);
|
|
955
928
|
return true;
|
|
956
929
|
}
|
|
957
930
|
catch (e) {
|
|
958
|
-
|
|
931
|
+
logError(e);
|
|
959
932
|
return false;
|
|
960
933
|
}
|
|
961
934
|
}
|
|
@@ -977,7 +950,7 @@ class ManageMetadataBase {
|
|
|
977
950
|
EntityID,
|
|
978
951
|
ISNULL(MAX(Sequence), 0) AS MaxSequence
|
|
979
952
|
FROM
|
|
980
|
-
[${
|
|
953
|
+
[${mj_core_schema()}].EntityField
|
|
981
954
|
GROUP BY
|
|
982
955
|
EntityID
|
|
983
956
|
),
|
|
@@ -996,9 +969,9 @@ NumberedRows AS (
|
|
|
996
969
|
sf.AllowsNull,
|
|
997
970
|
sf.DefaultValue,
|
|
998
971
|
sf.AutoIncrement,
|
|
999
|
-
IIF(sf.IsVirtual = 1, 0, IIF(sf.FieldName = '${
|
|
1000
|
-
sf.FieldName = '${
|
|
1001
|
-
sf.FieldName = '${
|
|
972
|
+
IIF(sf.IsVirtual = 1, 0, IIF(sf.FieldName = '${EntityInfo.CreatedAtFieldName}' OR
|
|
973
|
+
sf.FieldName = '${EntityInfo.UpdatedAtFieldName}' OR
|
|
974
|
+
sf.FieldName = '${EntityInfo.DeletedAtFieldName}' OR
|
|
1002
975
|
pk.ColumnName IS NOT NULL, 0, 1)) AllowUpdateAPI,
|
|
1003
976
|
sf.IsVirtual,
|
|
1004
977
|
e.RelationshipDefaultDisplayType,
|
|
@@ -1020,34 +993,34 @@ NumberedRows AS (
|
|
|
1020
993
|
END,
|
|
1021
994
|
ROW_NUMBER() OVER (PARTITION BY sf.EntityID, sf.FieldName ORDER BY (SELECT NULL)) AS rn
|
|
1022
995
|
FROM
|
|
1023
|
-
[${
|
|
996
|
+
[${mj_core_schema()}].vwSQLColumnsAndEntityFields sf
|
|
1024
997
|
LEFT OUTER JOIN
|
|
1025
998
|
MaxSequences ms
|
|
1026
999
|
ON
|
|
1027
1000
|
sf.EntityID = ms.EntityID
|
|
1028
1001
|
LEFT OUTER JOIN
|
|
1029
|
-
[${
|
|
1002
|
+
[${mj_core_schema()}].Entity e
|
|
1030
1003
|
ON
|
|
1031
1004
|
sf.EntityID = e.ID
|
|
1032
1005
|
LEFT OUTER JOIN
|
|
1033
|
-
[${
|
|
1006
|
+
[${mj_core_schema()}].vwForeignKeys fk
|
|
1034
1007
|
ON
|
|
1035
1008
|
sf.FieldName = fk.[column] AND
|
|
1036
1009
|
e.BaseTable = fk.[table] AND
|
|
1037
1010
|
e.SchemaName = fk.[schema_name]
|
|
1038
1011
|
LEFT OUTER JOIN
|
|
1039
|
-
[${
|
|
1012
|
+
[${mj_core_schema()}].Entity re -- Related Entity
|
|
1040
1013
|
ON
|
|
1041
1014
|
re.BaseTable = fk.referenced_table AND
|
|
1042
1015
|
re.SchemaName = fk.[referenced_schema]
|
|
1043
1016
|
LEFT OUTER JOIN
|
|
1044
|
-
[${
|
|
1017
|
+
[${mj_core_schema()}].vwTablePrimaryKeys pk
|
|
1045
1018
|
ON
|
|
1046
1019
|
e.BaseTable = pk.TableName AND
|
|
1047
1020
|
sf.FieldName = pk.ColumnName AND
|
|
1048
1021
|
e.SchemaName = pk.SchemaName
|
|
1049
1022
|
LEFT OUTER JOIN
|
|
1050
|
-
[${
|
|
1023
|
+
[${mj_core_schema()}].vwTableUniqueKeys uk
|
|
1051
1024
|
ON
|
|
1052
1025
|
e.BaseTable = uk.TableName AND
|
|
1053
1026
|
sf.FieldName = uk.ColumnName AND
|
|
@@ -1088,22 +1061,22 @@ NumberedRows AS (
|
|
|
1088
1061
|
const isPrimaryKey = n.FieldName?.trim().toLowerCase() === 'id';
|
|
1089
1062
|
const isForeignKey = n.RelatedEntityID && n.RelatedEntityID.length > 0; // Foreign keys have RelatedEntityID set
|
|
1090
1063
|
const isNameField = n.FieldName?.trim().toLowerCase() === 'name' || n.IsNameField;
|
|
1091
|
-
const isEarlySequence = n.Sequence <=
|
|
1064
|
+
const isEarlySequence = n.Sequence <= configInfo.newEntityDefaults?.IncludeFirstNFieldsAsDefaultInView;
|
|
1092
1065
|
const bDefaultInView = (isNameField || isEarlySequence) && !isPrimaryKey && !isForeignKey;
|
|
1093
1066
|
const escapedDescription = n.Description ? `'${n.Description.replace(/'/g, "''")}'` : 'NULL';
|
|
1094
1067
|
let fieldDisplayName = '';
|
|
1095
1068
|
switch (n.FieldName.trim().toLowerCase()) {
|
|
1096
|
-
case
|
|
1069
|
+
case EntityInfo.CreatedAtFieldName.trim().toLowerCase():
|
|
1097
1070
|
fieldDisplayName = "Created At";
|
|
1098
1071
|
break;
|
|
1099
|
-
case
|
|
1072
|
+
case EntityInfo.UpdatedAtFieldName.trim().toLowerCase():
|
|
1100
1073
|
fieldDisplayName = "Updated At";
|
|
1101
1074
|
break;
|
|
1102
|
-
case
|
|
1075
|
+
case EntityInfo.DeletedAtFieldName.trim().toLowerCase():
|
|
1103
1076
|
fieldDisplayName = "Deleted At";
|
|
1104
1077
|
break;
|
|
1105
1078
|
default:
|
|
1106
|
-
fieldDisplayName =
|
|
1079
|
+
fieldDisplayName = convertCamelCaseToHaveSpaces(n.FieldName).trim();
|
|
1107
1080
|
break;
|
|
1108
1081
|
}
|
|
1109
1082
|
const parsedDefaultValue = this.parseDefaultValue(n.DefaultValue);
|
|
@@ -1112,13 +1085,13 @@ NumberedRows AS (
|
|
|
1112
1085
|
// in the above we are setting quotedDefaultValue to NULL if the parsed default value is an empty string or the string 'NULL' (case insensitive)
|
|
1113
1086
|
return `
|
|
1114
1087
|
IF NOT EXISTS (
|
|
1115
|
-
SELECT 1 FROM [${
|
|
1088
|
+
SELECT 1 FROM [${mj_core_schema()}].EntityField
|
|
1116
1089
|
WHERE ID = '${newEntityFieldUUID}' OR
|
|
1117
1090
|
(EntityID = '${n.EntityID}' AND Name = '${n.FieldName}')
|
|
1118
1091
|
-- check to make sure we're not inserting a duplicate entity field metadata record
|
|
1119
1092
|
)
|
|
1120
1093
|
BEGIN
|
|
1121
|
-
INSERT INTO [${
|
|
1094
|
+
INSERT INTO [${mj_core_schema()}].EntityField
|
|
1122
1095
|
(
|
|
1123
1096
|
ID,
|
|
1124
1097
|
EntityID,
|
|
@@ -1219,7 +1192,7 @@ NumberedRows AS (
|
|
|
1219
1192
|
}
|
|
1220
1193
|
catch (e) {
|
|
1221
1194
|
// this is here so we can catch the error for debug. We want the transaction to die
|
|
1222
|
-
|
|
1195
|
+
logError(`Error inserting new entity field. SQL: \n${sSQLInsert}`);
|
|
1223
1196
|
throw e;
|
|
1224
1197
|
}
|
|
1225
1198
|
}
|
|
@@ -1237,7 +1210,7 @@ NumberedRows AS (
|
|
|
1237
1210
|
return true;
|
|
1238
1211
|
}
|
|
1239
1212
|
catch (e) {
|
|
1240
|
-
|
|
1213
|
+
logError(e);
|
|
1241
1214
|
return false;
|
|
1242
1215
|
}
|
|
1243
1216
|
}
|
|
@@ -1250,20 +1223,20 @@ NumberedRows AS (
|
|
|
1250
1223
|
*/
|
|
1251
1224
|
async updateEntityFieldRelatedEntityNameFieldMap(pool, entityFieldID, relatedEntityNameFieldMap) {
|
|
1252
1225
|
try {
|
|
1253
|
-
const sSQL = `EXEC [${
|
|
1226
|
+
const sSQL = `EXEC [${mj_core_schema()}].spUpdateEntityFieldRelatedEntityNameFieldMap
|
|
1254
1227
|
@EntityFieldID='${entityFieldID}',
|
|
1255
1228
|
@RelatedEntityNameFieldMap='${relatedEntityNameFieldMap}'`;
|
|
1256
1229
|
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update entity field related entity name field map for entity field ID ${entityFieldID}`);
|
|
1257
1230
|
return true;
|
|
1258
1231
|
}
|
|
1259
1232
|
catch (e) {
|
|
1260
|
-
|
|
1233
|
+
logError(e);
|
|
1261
1234
|
return false;
|
|
1262
1235
|
}
|
|
1263
1236
|
}
|
|
1264
1237
|
async updateExistingEntitiesFromSchema(pool, excludeSchemas) {
|
|
1265
1238
|
try {
|
|
1266
|
-
const sSQL = `EXEC [${
|
|
1239
|
+
const sSQL = `EXEC [${mj_core_schema()}].spUpdateExistingEntitiesFromSchema @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
1267
1240
|
const result = await this.LogSQLAndExecute(pool, sSQL, `SQL text to update existing entities from schema`, true);
|
|
1268
1241
|
// result contains the updated entities, and there is a property of each row called Name which has the entity name that was modified
|
|
1269
1242
|
// add these to the modified entity list if they're not already in there
|
|
@@ -1273,7 +1246,7 @@ NumberedRows AS (
|
|
|
1273
1246
|
return true;
|
|
1274
1247
|
}
|
|
1275
1248
|
catch (e) {
|
|
1276
|
-
|
|
1249
|
+
logError(e);
|
|
1277
1250
|
return false;
|
|
1278
1251
|
}
|
|
1279
1252
|
}
|
|
@@ -1288,7 +1261,7 @@ NumberedRows AS (
|
|
|
1288
1261
|
}
|
|
1289
1262
|
async updateExistingEntityFieldsFromSchema(pool, excludeSchemas) {
|
|
1290
1263
|
try {
|
|
1291
|
-
const sSQL = `EXEC [${
|
|
1264
|
+
const sSQL = `EXEC [${mj_core_schema()}].spUpdateExistingEntityFieldsFromSchema @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
1292
1265
|
const result = await this.LogSQLAndExecute(pool, sSQL, `SQL text to update existing entity fields from schema`, true);
|
|
1293
1266
|
// result contains the updated entity fields
|
|
1294
1267
|
// there is a field in there called EntityName. Get a distinct list of entity names from this and add them
|
|
@@ -1299,7 +1272,7 @@ NumberedRows AS (
|
|
|
1299
1272
|
return true;
|
|
1300
1273
|
}
|
|
1301
1274
|
catch (e) {
|
|
1302
|
-
|
|
1275
|
+
logError(e);
|
|
1303
1276
|
return false;
|
|
1304
1277
|
}
|
|
1305
1278
|
}
|
|
@@ -1313,21 +1286,21 @@ NumberedRows AS (
|
|
|
1313
1286
|
*/
|
|
1314
1287
|
async updateSchemaInfoFromDatabase(pool, excludeSchemas) {
|
|
1315
1288
|
try {
|
|
1316
|
-
const sSQL = `EXEC [${
|
|
1289
|
+
const sSQL = `EXEC [${mj_core_schema()}].spUpdateSchemaInfoFromDatabase @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
1317
1290
|
const result = await this.LogSQLAndExecute(pool, sSQL, `SQL text to sync schema info from database schemas`, true);
|
|
1318
1291
|
if (result && result.length > 0) {
|
|
1319
|
-
|
|
1292
|
+
logStatus(` > Updated/created ${result.length} SchemaInfo records`);
|
|
1320
1293
|
}
|
|
1321
1294
|
return true;
|
|
1322
1295
|
}
|
|
1323
1296
|
catch (e) {
|
|
1324
|
-
|
|
1297
|
+
logError(e);
|
|
1325
1298
|
return false;
|
|
1326
1299
|
}
|
|
1327
1300
|
}
|
|
1328
1301
|
async deleteUnneededEntityFields(pool, excludeSchemas) {
|
|
1329
1302
|
try {
|
|
1330
|
-
const sSQL = `EXEC [${
|
|
1303
|
+
const sSQL = `EXEC [${mj_core_schema()}].spDeleteUnneededEntityFields @ExcludedSchemaNames='${excludeSchemas.join(',')}'`;
|
|
1331
1304
|
const result = await this.LogSQLAndExecute(pool, sSQL, `SQL text to delete unneeded entity fields`, true);
|
|
1332
1305
|
// result contains the DELETED entity fields
|
|
1333
1306
|
// there is a field in there called Entity. Get a distinct list of entity names from this and add them
|
|
@@ -1338,7 +1311,7 @@ NumberedRows AS (
|
|
|
1338
1311
|
return true;
|
|
1339
1312
|
}
|
|
1340
1313
|
catch (e) {
|
|
1341
|
-
|
|
1314
|
+
logError(e);
|
|
1342
1315
|
return false;
|
|
1343
1316
|
}
|
|
1344
1317
|
}
|
|
@@ -1349,13 +1322,13 @@ NumberedRows AS (
|
|
|
1349
1322
|
// for the field and sync that up with the EntityFieldValue table. If it is not a simple series of OR statements, we will not be able to parse it and we'll
|
|
1350
1323
|
// just ignore it.
|
|
1351
1324
|
const filter = excludeSchemas && excludeSchemas.length > 0 ? ` WHERE SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})` : '';
|
|
1352
|
-
const sSQL = `SELECT * FROM [${
|
|
1325
|
+
const sSQL = `SELECT * FROM [${mj_core_schema()}].vwEntityFieldsWithCheckConstraints${filter}`;
|
|
1353
1326
|
const resultResult = await pool.request().query(sSQL);
|
|
1354
1327
|
const result = resultResult.recordset;
|
|
1355
|
-
const efvSQL = `SELECT * FROM [${
|
|
1328
|
+
const efvSQL = `SELECT * FROM [${mj_core_schema()}].EntityFieldValue`;
|
|
1356
1329
|
const allEntityFieldValuesResult = await pool.request().query(efvSQL);
|
|
1357
1330
|
const allEntityFieldValues = allEntityFieldValuesResult.recordset;
|
|
1358
|
-
const efSQL = `SELECT * FROM [${
|
|
1331
|
+
const efSQL = `SELECT * FROM [${mj_core_schema()}].vwEntityFields ORDER BY EntityID, Sequence`;
|
|
1359
1332
|
const allEntityFieldsResult = await pool.request().query(efSQL);
|
|
1360
1333
|
const allEntityFields = allEntityFieldsResult.recordset;
|
|
1361
1334
|
const generationPromises = [];
|
|
@@ -1377,11 +1350,11 @@ NumberedRows AS (
|
|
|
1377
1350
|
await this.syncEntityFieldValues(pool, r.EntityFieldID, parsedValues, allEntityFieldValues);
|
|
1378
1351
|
// 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.
|
|
1379
1352
|
// check to see if the ValueListType is already set to "List", if not, update it
|
|
1380
|
-
const sSQLCheck = `SELECT ValueListType FROM [${
|
|
1353
|
+
const sSQLCheck = `SELECT ValueListType FROM [${mj_core_schema()}].EntityField WHERE ID='${r.EntityFieldID}'`;
|
|
1381
1354
|
const checkResultResult = await pool.request().query(sSQLCheck);
|
|
1382
1355
|
const checkResult = checkResultResult.recordset;
|
|
1383
1356
|
if (checkResult && checkResult.length > 0 && checkResult[0].ValueListType.trim().toLowerCase() !== 'list') {
|
|
1384
|
-
const sSQL = `UPDATE [${
|
|
1357
|
+
const sSQL = `UPDATE [${mj_core_schema()}].EntityField SET ValueListType='List' WHERE ID='${r.EntityFieldID}'`;
|
|
1385
1358
|
await this.LogSQLAndExecute(pool, sSQL, `SQL text to update ValueListType for entity field ID ${r.EntityFieldID}`);
|
|
1386
1359
|
}
|
|
1387
1360
|
}
|
|
@@ -1392,8 +1365,8 @@ NumberedRows AS (
|
|
|
1392
1365
|
else {
|
|
1393
1366
|
// if we get here that means we don't have a simple condition in the check constraint that the RegEx could parse. If Advanced Generation is enabled, we will
|
|
1394
1367
|
// attempt to use an LLM to do things fancier now
|
|
1395
|
-
if (
|
|
1396
|
-
|
|
1368
|
+
if (configInfo.advancedGeneration?.enableAdvancedGeneration &&
|
|
1369
|
+
configInfo.advancedGeneration?.features.find(f => f.name === 'ParseCheckConstraints' && f.enabled)) {
|
|
1397
1370
|
// the user has the feature turned on, let's generate a description of the constraint and then build a Validate function for the constraint
|
|
1398
1371
|
// run this in parallel
|
|
1399
1372
|
generationPromises.push(this.runValidationGeneration(r, allEntityFields, !skipDBUpdate, currentUser));
|
|
@@ -1403,8 +1376,8 @@ NumberedRows AS (
|
|
|
1403
1376
|
}
|
|
1404
1377
|
// now for the table level constraints run the process for advanced generation
|
|
1405
1378
|
for (const r of tableLevelResults) {
|
|
1406
|
-
if (
|
|
1407
|
-
|
|
1379
|
+
if (configInfo.advancedGeneration?.enableAdvancedGeneration &&
|
|
1380
|
+
configInfo.advancedGeneration?.features.find(f => f.name === 'ParseCheckConstraints' && f.enabled)) {
|
|
1408
1381
|
// the user has the feature turned on, let's generate a description of the constraint and then build a Validate function for the constraint
|
|
1409
1382
|
// run this in parallel
|
|
1410
1383
|
generationPromises.push(this.runValidationGeneration(r, allEntityFields, !skipDBUpdate, currentUser));
|
|
@@ -1415,7 +1388,7 @@ NumberedRows AS (
|
|
|
1415
1388
|
return true;
|
|
1416
1389
|
}
|
|
1417
1390
|
catch (e) {
|
|
1418
|
-
|
|
1391
|
+
logError(e);
|
|
1419
1392
|
return false;
|
|
1420
1393
|
}
|
|
1421
1394
|
}
|
|
@@ -1430,7 +1403,7 @@ NumberedRows AS (
|
|
|
1430
1403
|
return await this.manageEntityFieldValuesAndValidatorFunctions(pool, [], currentUser, true);
|
|
1431
1404
|
}
|
|
1432
1405
|
catch (e) {
|
|
1433
|
-
|
|
1406
|
+
logError(e);
|
|
1434
1407
|
return false;
|
|
1435
1408
|
}
|
|
1436
1409
|
}
|
|
@@ -1476,9 +1449,9 @@ NumberedRows AS (
|
|
|
1476
1449
|
}
|
|
1477
1450
|
}
|
|
1478
1451
|
try {
|
|
1479
|
-
if (generateNewCode &&
|
|
1452
|
+
if (generateNewCode && configInfo.advancedGeneration?.enableAdvancedGeneration && configInfo.advancedGeneration?.features.find(f => f.name === 'ParseCheckConstraints' && f.enabled)) {
|
|
1480
1453
|
// feature is enabled, so let's call the AI to generate a function for us
|
|
1481
|
-
const ag = new
|
|
1454
|
+
const ag = new AdvancedGeneration();
|
|
1482
1455
|
const entityFieldListInfo = allEntityFields.filter(item => item.Entity.trim().toLowerCase() === data.EntityName.trim().toLowerCase()).map(item => ` * ${item.Name} - ${item.Type}${item.AllowsNull ? ' (nullable)' : ' (not null)'}`).join('\n');
|
|
1483
1456
|
// Use new API to parse check constraint
|
|
1484
1457
|
const result = await ag.parseCheckConstraint(constraintDefinition, entityFieldListInfo, generatedValidationFunctionName, currentUser);
|
|
@@ -1491,12 +1464,12 @@ NumberedRows AS (
|
|
|
1491
1464
|
returnResult.success = true;
|
|
1492
1465
|
}
|
|
1493
1466
|
else {
|
|
1494
|
-
|
|
1467
|
+
logError(`Error generating field validator function from check constraint for entity ${entityName} and field ${fieldName}. LLM returned invalid result.`);
|
|
1495
1468
|
}
|
|
1496
1469
|
}
|
|
1497
1470
|
}
|
|
1498
1471
|
catch (e) {
|
|
1499
|
-
|
|
1472
|
+
logError(e);
|
|
1500
1473
|
}
|
|
1501
1474
|
finally {
|
|
1502
1475
|
return returnResult;
|
|
@@ -1515,7 +1488,7 @@ NumberedRows AS (
|
|
|
1515
1488
|
for (const ev of existingValues) {
|
|
1516
1489
|
if (!possibleValues.find(v => v === ev.Value)) {
|
|
1517
1490
|
// delete the value from the database
|
|
1518
|
-
const sSQLDelete = `DELETE FROM [${
|
|
1491
|
+
const sSQLDelete = `DELETE FROM [${mj_core_schema()}].EntityFieldValue WHERE ID='${ev.ID}'`;
|
|
1519
1492
|
await this.LogSQLAndExecute(ds, sSQLDelete, `SQL text to delete entity field value ID ${ev.ID}`);
|
|
1520
1493
|
numRemoved++;
|
|
1521
1494
|
}
|
|
@@ -1525,9 +1498,9 @@ NumberedRows AS (
|
|
|
1525
1498
|
for (const v of possibleValues) {
|
|
1526
1499
|
if (!existingValues.find((ev) => ev.Value === v)) {
|
|
1527
1500
|
// Generate a UUID for this new EntityFieldValue record
|
|
1528
|
-
const newId = (
|
|
1501
|
+
const newId = uuidv4();
|
|
1529
1502
|
// add the value to the database with explicit ID
|
|
1530
|
-
const sSQLInsert = `INSERT INTO [${
|
|
1503
|
+
const sSQLInsert = `INSERT INTO [${mj_core_schema()}].EntityFieldValue
|
|
1531
1504
|
(ID, EntityFieldID, Sequence, Value, Code)
|
|
1532
1505
|
VALUES
|
|
1533
1506
|
('${newId}', '${entityFieldID}', ${1 + possibleValues.indexOf(v)}, '${v}', '${v}')`;
|
|
@@ -1541,7 +1514,7 @@ NumberedRows AS (
|
|
|
1541
1514
|
const ev = existingValues.find((ev) => ev.Value === v);
|
|
1542
1515
|
if (ev && ev.Sequence !== 1 + possibleValues.indexOf(v)) {
|
|
1543
1516
|
// update the sequence to match the order in the possible values list, if it doesn't already match
|
|
1544
|
-
const sSQLUpdate = `UPDATE [${
|
|
1517
|
+
const sSQLUpdate = `UPDATE [${mj_core_schema()}].EntityFieldValue SET Sequence=${1 + possibleValues.indexOf(v)} WHERE ID='${ev.ID}'`;
|
|
1545
1518
|
await this.LogSQLAndExecute(ds, sSQLUpdate, `SQL text to update entity field value sequence`);
|
|
1546
1519
|
numUpdated++;
|
|
1547
1520
|
}
|
|
@@ -1555,7 +1528,7 @@ NumberedRows AS (
|
|
|
1555
1528
|
return true;
|
|
1556
1529
|
}
|
|
1557
1530
|
catch (e) {
|
|
1558
|
-
|
|
1531
|
+
logError(e);
|
|
1559
1532
|
return false;
|
|
1560
1533
|
}
|
|
1561
1534
|
}
|
|
@@ -1614,9 +1587,9 @@ NumberedRows AS (
|
|
|
1614
1587
|
createExcludeTablesAndSchemasFilter(fieldPrefix) {
|
|
1615
1588
|
let sExcludeTables = '';
|
|
1616
1589
|
let sExcludeSchemas = '';
|
|
1617
|
-
if (
|
|
1618
|
-
for (let i = 0; i <
|
|
1619
|
-
const t =
|
|
1590
|
+
if (configInfo.excludeTables) {
|
|
1591
|
+
for (let i = 0; i < configInfo.excludeTables.length; ++i) {
|
|
1592
|
+
const t = configInfo.excludeTables[i];
|
|
1620
1593
|
sExcludeTables += (sExcludeTables.length > 0 ? ' AND ' : '') +
|
|
1621
1594
|
(t.schema.indexOf('%') > -1 ? ` NOT ( ${fieldPrefix}SchemaName LIKE '${t.schema}'` :
|
|
1622
1595
|
` NOT ( ${fieldPrefix}SchemaName = '${t.schema}'`);
|
|
@@ -1624,9 +1597,9 @@ NumberedRows AS (
|
|
|
1624
1597
|
` AND ${fieldPrefix}TableName = '${t.table}') `);
|
|
1625
1598
|
}
|
|
1626
1599
|
}
|
|
1627
|
-
if (
|
|
1628
|
-
for (let i = 0; i <
|
|
1629
|
-
const s =
|
|
1600
|
+
if (configInfo.excludeSchemas) {
|
|
1601
|
+
for (let i = 0; i < configInfo.excludeSchemas.length; ++i) {
|
|
1602
|
+
const s = configInfo.excludeSchemas[i];
|
|
1630
1603
|
sExcludeSchemas += (sExcludeSchemas.length > 0 ? ' AND ' : '') +
|
|
1631
1604
|
(s.indexOf('%') > -1 ? `${fieldPrefix}SchemaName NOT LIKE '${s}'` : `${fieldPrefix}SchemaName <> '${s}'`);
|
|
1632
1605
|
}
|
|
@@ -1638,11 +1611,11 @@ NumberedRows AS (
|
|
|
1638
1611
|
}
|
|
1639
1612
|
async createNewEntities(pool, currentUser) {
|
|
1640
1613
|
try {
|
|
1641
|
-
const sSQL = `SELECT * FROM [${
|
|
1614
|
+
const sSQL = `SELECT * FROM [${mj_core_schema()}].vwSQLTablesAndEntities WHERE EntityID IS NULL ` + this.createExcludeTablesAndSchemasFilter('');
|
|
1642
1615
|
const newEntitiesResult = await pool.request().query(sSQL);
|
|
1643
1616
|
const newEntities = newEntitiesResult.recordset;
|
|
1644
1617
|
if (newEntities && newEntities.length > 0) {
|
|
1645
|
-
const md = new
|
|
1618
|
+
const md = new Metadata();
|
|
1646
1619
|
const transaction = new sql.Transaction(pool);
|
|
1647
1620
|
await transaction.begin();
|
|
1648
1621
|
try {
|
|
@@ -1659,14 +1632,14 @@ NumberedRows AS (
|
|
|
1659
1632
|
}
|
|
1660
1633
|
if (ManageMetadataBase.newEntityList.length > 0) {
|
|
1661
1634
|
// only do this if we actually created new entities
|
|
1662
|
-
|
|
1635
|
+
LogStatus(` Done creating entities, refreshing metadata to reflect new entities...`);
|
|
1663
1636
|
await md.Refresh(); // refresh now since we've added some new entities
|
|
1664
1637
|
}
|
|
1665
1638
|
}
|
|
1666
1639
|
return true; // if we get here, we succeeded
|
|
1667
1640
|
}
|
|
1668
1641
|
catch (e) {
|
|
1669
|
-
|
|
1642
|
+
LogError(e);
|
|
1670
1643
|
return false;
|
|
1671
1644
|
}
|
|
1672
1645
|
}
|
|
@@ -1675,14 +1648,14 @@ NumberedRows AS (
|
|
|
1675
1648
|
// criteria:
|
|
1676
1649
|
// 1) entity has a field that is a primary key
|
|
1677
1650
|
// validate all of these factors by getting the sql from SQL Server and check the result, if failure, shouldCreate=false and generate validation message, otherwise return empty validation message and true for shouldCreate.
|
|
1678
|
-
const query = `EXEC ${
|
|
1651
|
+
const query = `EXEC ${Metadata.Provider.ConfigData.MJCoreSchemaName}.spGetPrimaryKeyForTable @TableName='${newEntity.TableName}', @SchemaName='${newEntity.SchemaName}'`;
|
|
1679
1652
|
try {
|
|
1680
1653
|
const resultResult = await ds.request().query(query);
|
|
1681
1654
|
const result = resultResult.recordset;
|
|
1682
1655
|
if (result.length === 0) {
|
|
1683
1656
|
// No database PK constraint found - check if there's a soft PK defined in config
|
|
1684
1657
|
if (this.hasSoftPrimaryKeyInConfig(newEntity.SchemaName, newEntity.TableName)) {
|
|
1685
|
-
|
|
1658
|
+
logStatus(` ✓ No database PK for ${newEntity.SchemaName}.${newEntity.TableName}, but soft PK found in config - allowing entity creation`);
|
|
1686
1659
|
return { shouldCreate: true, validationMessage: '' };
|
|
1687
1660
|
}
|
|
1688
1661
|
return { shouldCreate: false, validationMessage: "No primary key found" };
|
|
@@ -1700,35 +1673,35 @@ NumberedRows AS (
|
|
|
1700
1673
|
*/
|
|
1701
1674
|
hasSoftPrimaryKeyInConfig(schemaName, tableName) {
|
|
1702
1675
|
// Check if additionalSchemaInfo is configured
|
|
1703
|
-
if (!
|
|
1676
|
+
if (!configInfo.additionalSchemaInfo) {
|
|
1704
1677
|
return false;
|
|
1705
1678
|
}
|
|
1706
|
-
const configPath =
|
|
1679
|
+
const configPath = path.join(currentWorkingDirectory, configInfo.additionalSchemaInfo);
|
|
1707
1680
|
if (!fs.existsSync(configPath)) {
|
|
1708
|
-
|
|
1681
|
+
logStatus(` [Soft PK Check] Config file not found at: ${configPath}`);
|
|
1709
1682
|
return false;
|
|
1710
1683
|
}
|
|
1711
1684
|
try {
|
|
1712
1685
|
const config = ManageMetadataBase.getSoftPKFKConfig();
|
|
1713
1686
|
if (!config || !config.tables) {
|
|
1714
|
-
|
|
1687
|
+
logStatus(` [Soft PK Check] Config file found but no tables array`);
|
|
1715
1688
|
return false;
|
|
1716
1689
|
}
|
|
1717
1690
|
const tableConfig = config.tables.find((t) => t.schemaName?.toLowerCase() === schemaName?.toLowerCase() &&
|
|
1718
1691
|
t.tableName?.toLowerCase() === tableName?.toLowerCase());
|
|
1719
1692
|
const found = Boolean(tableConfig?.primaryKeys && tableConfig.primaryKeys.length > 0);
|
|
1720
1693
|
if (!found) {
|
|
1721
|
-
|
|
1694
|
+
logStatus(` [Soft PK Check] No config found for ${schemaName}.${tableName} (config has ${config.tables.length} tables)`);
|
|
1722
1695
|
}
|
|
1723
1696
|
return found;
|
|
1724
1697
|
}
|
|
1725
1698
|
catch (e) {
|
|
1726
|
-
|
|
1699
|
+
logStatus(` [Soft PK Check] Error reading config: ${e}`);
|
|
1727
1700
|
return false;
|
|
1728
1701
|
}
|
|
1729
1702
|
}
|
|
1730
1703
|
async createNewEntityName(newEntity, currentUser) {
|
|
1731
|
-
const ag = new
|
|
1704
|
+
const ag = new AdvancedGeneration();
|
|
1732
1705
|
if (ag.featureEnabled('EntityNames')) {
|
|
1733
1706
|
return this.newEntityNameWithAdvancedGeneration(ag, newEntity, currentUser);
|
|
1734
1707
|
}
|
|
@@ -1768,8 +1741,8 @@ NumberedRows AS (
|
|
|
1768
1741
|
}
|
|
1769
1742
|
}
|
|
1770
1743
|
simpleNewEntityName(schemaName, tableName) {
|
|
1771
|
-
const convertedTableName =
|
|
1772
|
-
const pluralName =
|
|
1744
|
+
const convertedTableName = convertCamelCaseToHaveSpaces(tableName);
|
|
1745
|
+
const pluralName = generatePluralName(convertedTableName, { capitalizeFirstLetterOnly: true });
|
|
1773
1746
|
return this.markupEntityName(schemaName, pluralName);
|
|
1774
1747
|
}
|
|
1775
1748
|
/**
|
|
@@ -1789,18 +1762,18 @@ NumberedRows AS (
|
|
|
1789
1762
|
}
|
|
1790
1763
|
}
|
|
1791
1764
|
getNewEntityNameRule(schemaName) {
|
|
1792
|
-
const rule =
|
|
1765
|
+
const rule = configInfo.newEntityDefaults?.NameRulesBySchema?.find(r => {
|
|
1793
1766
|
let schemaNameToUse = r.SchemaName;
|
|
1794
1767
|
if (schemaNameToUse?.trim().toLowerCase() === '${mj_core_schema}') {
|
|
1795
1768
|
// markup for this is to be replaced with the mj_core_schema() config
|
|
1796
|
-
schemaNameToUse =
|
|
1769
|
+
schemaNameToUse = mj_core_schema();
|
|
1797
1770
|
}
|
|
1798
1771
|
return schemaNameToUse.trim().toLowerCase() === schemaName.trim().toLowerCase();
|
|
1799
1772
|
});
|
|
1800
1773
|
return rule;
|
|
1801
1774
|
}
|
|
1802
1775
|
createNewUUID() {
|
|
1803
|
-
return (
|
|
1776
|
+
return uuidv4();
|
|
1804
1777
|
}
|
|
1805
1778
|
async createNewEntity(pool, newEntity, md, currentUser) {
|
|
1806
1779
|
try {
|
|
@@ -1816,7 +1789,7 @@ NumberedRows AS (
|
|
|
1816
1789
|
// the generated name is already in place, so we need another name
|
|
1817
1790
|
suffix = '__' + newEntity.SchemaName;
|
|
1818
1791
|
newEntityName = newEntityName + suffix;
|
|
1819
|
-
|
|
1792
|
+
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.`);
|
|
1820
1793
|
}
|
|
1821
1794
|
const isNewSchema = await this.isSchemaNew(pool, newEntity.SchemaName);
|
|
1822
1795
|
const newEntityID = this.createNewUUID();
|
|
@@ -1828,7 +1801,7 @@ NumberedRows AS (
|
|
|
1828
1801
|
// 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
|
|
1829
1802
|
// our config option is set to create new applications from new schemas, then create a new application for this schema
|
|
1830
1803
|
let apps;
|
|
1831
|
-
if (isNewSchema &&
|
|
1804
|
+
if (isNewSchema && configInfo.newSchemaDefaults.CreateNewApplicationWithSchemaName) {
|
|
1832
1805
|
// new schema and config option is to create a new application from the schema name so do that
|
|
1833
1806
|
// check to see if the app already exists
|
|
1834
1807
|
apps = await this.getApplicationIDForSchema(pool, newEntity.SchemaName);
|
|
@@ -1840,7 +1813,7 @@ NumberedRows AS (
|
|
|
1840
1813
|
apps = [newAppID];
|
|
1841
1814
|
}
|
|
1842
1815
|
else {
|
|
1843
|
-
|
|
1816
|
+
LogError(` >>>> ERROR: Unable to create new application for schema ${newEntity.SchemaName}`);
|
|
1844
1817
|
}
|
|
1845
1818
|
await md.Refresh(); // refresh now since we've added a new application, not super efficient to do this for each new application but that won't happen super
|
|
1846
1819
|
// often so not a huge deal, would be more efficient do this in batch after all new apps are created but that would be an over optimization IMO
|
|
@@ -1851,12 +1824,12 @@ NumberedRows AS (
|
|
|
1851
1824
|
apps = await this.getApplicationIDForSchema(pool, newEntity.SchemaName);
|
|
1852
1825
|
}
|
|
1853
1826
|
if (apps && apps.length > 0) {
|
|
1854
|
-
if (
|
|
1827
|
+
if (configInfo.newEntityDefaults.AddToApplicationWithSchemaName) {
|
|
1855
1828
|
// only do this if the configuration setting is set to add new entities to applications for schema names
|
|
1856
1829
|
for (const appUUID of apps) {
|
|
1857
|
-
const sSQLInsertApplicationEntity = `INSERT INTO ${
|
|
1830
|
+
const sSQLInsertApplicationEntity = `INSERT INTO ${mj_core_schema()}.ApplicationEntity
|
|
1858
1831
|
(ApplicationID, EntityID, Sequence) VALUES
|
|
1859
|
-
('${appUUID}', '${newEntityID}', (SELECT ISNULL(MAX(Sequence),0)+1 FROM ${
|
|
1832
|
+
('${appUUID}', '${newEntityID}', (SELECT ISNULL(MAX(Sequence),0)+1 FROM ${mj_core_schema()}.ApplicationEntity WHERE ApplicationID = '${appUUID}'))`;
|
|
1860
1833
|
await this.LogSQLAndExecute(pool, sSQLInsertApplicationEntity, `SQL generated to add new entity ${newEntityName} to application ID: '${appUUID}'`);
|
|
1861
1834
|
}
|
|
1862
1835
|
}
|
|
@@ -1866,38 +1839,38 @@ NumberedRows AS (
|
|
|
1866
1839
|
}
|
|
1867
1840
|
else {
|
|
1868
1841
|
// this is an error condition, we should have an application for this schema, if we don't, log an error, non fatal, but should be logged
|
|
1869
|
-
|
|
1842
|
+
LogError(` >>>> ERROR: Unable to add new entity ${newEntityName} to an application because an Application record for schema ${newEntity.SchemaName} does not exist.`);
|
|
1870
1843
|
}
|
|
1871
1844
|
// next up, we need to check if we're configured to add permissions for new entities, and if so, add them
|
|
1872
|
-
if (
|
|
1845
|
+
if (configInfo.newEntityDefaults.PermissionDefaults && configInfo.newEntityDefaults.PermissionDefaults.AutoAddPermissionsForNewEntities) {
|
|
1873
1846
|
// we are asked to add permissions for new entities, so do that by looping through the permissions and adding them
|
|
1874
|
-
const permissions =
|
|
1847
|
+
const permissions = configInfo.newEntityDefaults.PermissionDefaults.Permissions;
|
|
1875
1848
|
for (const p of permissions) {
|
|
1876
1849
|
const RoleID = md.Roles.find(r => r.Name.trim().toLowerCase() === p.RoleName.trim().toLowerCase())?.ID;
|
|
1877
1850
|
if (RoleID) {
|
|
1878
|
-
const sSQLInsertPermission = `INSERT INTO ${
|
|
1851
|
+
const sSQLInsertPermission = `INSERT INTO ${mj_core_schema()}.EntityPermission
|
|
1879
1852
|
(EntityID, RoleID, CanRead, CanCreate, CanUpdate, CanDelete) VALUES
|
|
1880
1853
|
('${newEntityID}', '${RoleID}', ${p.CanRead ? 1 : 0}, ${p.CanCreate ? 1 : 0}, ${p.CanUpdate ? 1 : 0}, ${p.CanDelete ? 1 : 0})`;
|
|
1881
1854
|
await this.LogSQLAndExecute(pool, sSQLInsertPermission, `SQL generated to add new permission for entity ${newEntityName} for role ${p.RoleName}`);
|
|
1882
1855
|
}
|
|
1883
1856
|
else
|
|
1884
|
-
|
|
1857
|
+
LogError(` >>>> ERROR: Unable to find Role ID for role ${p.RoleName} to add permissions for new entity ${newEntityName}`);
|
|
1885
1858
|
}
|
|
1886
1859
|
}
|
|
1887
|
-
|
|
1860
|
+
LogStatus(` Created new entity ${newEntityName} for table ${newEntity.SchemaName}.${newEntity.TableName}`);
|
|
1888
1861
|
}
|
|
1889
1862
|
else {
|
|
1890
|
-
|
|
1863
|
+
LogStatus(` Skipping new entity ${newEntity.TableName} because it doesn't qualify to be created. Reason: ${validationMessage}`);
|
|
1891
1864
|
return;
|
|
1892
1865
|
}
|
|
1893
1866
|
}
|
|
1894
1867
|
catch (e) {
|
|
1895
|
-
|
|
1868
|
+
LogError(`Failed to create new entity ${newEntity?.TableName}`);
|
|
1896
1869
|
}
|
|
1897
1870
|
}
|
|
1898
1871
|
async isSchemaNew(pool, schemaName) {
|
|
1899
1872
|
// check to see if there are any entities in the db with this schema name
|
|
1900
|
-
const sSQL = `SELECT COUNT(*) AS Count FROM [${
|
|
1873
|
+
const sSQL = `SELECT COUNT(*) AS Count FROM [${mj_core_schema()}].Entity WHERE SchemaName = '${schemaName}'`;
|
|
1901
1874
|
const resultResult = await pool.request().query(sSQL);
|
|
1902
1875
|
const result = resultResult.recordset;
|
|
1903
1876
|
return result && result.length > 0 ? result[0].Count === 0 : true;
|
|
@@ -1925,26 +1898,26 @@ NumberedRows AS (
|
|
|
1925
1898
|
.replace(/[^a-z0-9-]/g, '') // remove special chars
|
|
1926
1899
|
.replace(/-+/g, '-') // collapse multiple hyphens
|
|
1927
1900
|
.replace(/^-|-$/g, ''); // trim hyphens from start/end
|
|
1928
|
-
const sSQL = `INSERT INTO [${
|
|
1901
|
+
const sSQL = `INSERT INTO [${mj_core_schema()}].Application (ID, Name, Description, SchemaAutoAddNewEntities, Path, AutoUpdatePath)
|
|
1929
1902
|
VALUES ('${appID}', '${appName}', 'Generated for schema', '${schemaName}', '${path}', 1)`;
|
|
1930
1903
|
await this.LogSQLAndExecute(pool, sSQL, `SQL generated to create new application ${appName}`);
|
|
1931
|
-
|
|
1904
|
+
LogStatus(`Created new application ${appName} with Path: ${path}`);
|
|
1932
1905
|
return appID;
|
|
1933
1906
|
}
|
|
1934
1907
|
catch (e) {
|
|
1935
|
-
|
|
1908
|
+
LogError(`Failed to create new application ${appName} for schema ${schemaName}`, null, e);
|
|
1936
1909
|
return null;
|
|
1937
1910
|
}
|
|
1938
1911
|
}
|
|
1939
1912
|
async applicationExists(pool, applicationName) {
|
|
1940
|
-
const sSQL = `SELECT ID FROM [${
|
|
1913
|
+
const sSQL = `SELECT ID FROM [${mj_core_schema()}].Application WHERE Name = '${applicationName}'`;
|
|
1941
1914
|
const resultResult = await pool.request().query(sSQL);
|
|
1942
1915
|
const result = resultResult.recordset;
|
|
1943
1916
|
return result && result.length > 0 ? result[0].ID.length > 0 : false;
|
|
1944
1917
|
}
|
|
1945
1918
|
async getApplicationIDForSchema(pool, schemaName) {
|
|
1946
1919
|
// get all the apps each time from DB as we might be adding, don't use Metadata here for that reason
|
|
1947
|
-
const sSQL = `SELECT ID, Name, SchemaAutoAddNewEntities FROM [${
|
|
1920
|
+
const sSQL = `SELECT ID, Name, SchemaAutoAddNewEntities FROM [${mj_core_schema()}].vwApplications`;
|
|
1948
1921
|
const resultResult = await pool.request().query(sSQL);
|
|
1949
1922
|
const result = resultResult.recordset;
|
|
1950
1923
|
if (!result || result.length === 0) {
|
|
@@ -1964,10 +1937,10 @@ NumberedRows AS (
|
|
|
1964
1937
|
}
|
|
1965
1938
|
}
|
|
1966
1939
|
createNewEntityInsertSQL(newEntityUUID, newEntityName, newEntity, newEntitySuffix, newEntityDisplayName) {
|
|
1967
|
-
const newEntityDefaults =
|
|
1940
|
+
const newEntityDefaults = configInfo.newEntityDefaults;
|
|
1968
1941
|
const newEntityDescriptionEscaped = newEntity.Description ? `'${newEntity.Description.replace(/'/g, "''")}` : null;
|
|
1969
1942
|
const sSQLInsert = `
|
|
1970
|
-
INSERT INTO [${
|
|
1943
|
+
INSERT INTO [${mj_core_schema()}].Entity (
|
|
1971
1944
|
ID,
|
|
1972
1945
|
Name,
|
|
1973
1946
|
DisplayName,
|
|
@@ -1994,7 +1967,7 @@ NumberedRows AS (
|
|
|
1994
1967
|
${newEntityDescriptionEscaped ? newEntityDescriptionEscaped : 'NULL' /*if no description, then null*/},
|
|
1995
1968
|
${newEntitySuffix && newEntitySuffix.length > 0 ? `'${newEntitySuffix}'` : 'NULL'},
|
|
1996
1969
|
'${newEntity.TableName}',
|
|
1997
|
-
'vw${
|
|
1970
|
+
'vw${generatePluralName(newEntity.TableName, { capitalizeFirstLetterOnly: true }) + (newEntitySuffix && newEntitySuffix.length > 0 ? newEntitySuffix : '')}',
|
|
1998
1971
|
'${newEntity.SchemaName}',
|
|
1999
1972
|
1,
|
|
2000
1973
|
${newEntityDefaults.AllowUserSearchAPI === undefined ? 1 : newEntityDefaults.AllowUserSearchAPI ? 1 : 0}
|
|
@@ -2015,7 +1988,7 @@ NumberedRows AS (
|
|
|
2015
1988
|
*/
|
|
2016
1989
|
async applyAdvancedGeneration(pool, excludeSchemas, currentUser) {
|
|
2017
1990
|
try {
|
|
2018
|
-
const ag = new
|
|
1991
|
+
const ag = new AdvancedGeneration();
|
|
2019
1992
|
if (!ag.enabled) {
|
|
2020
1993
|
return true;
|
|
2021
1994
|
}
|
|
@@ -2024,13 +1997,13 @@ NumberedRows AS (
|
|
|
2024
1997
|
// Otherwise, only process new or modified entities
|
|
2025
1998
|
let entitiesToProcess = [];
|
|
2026
1999
|
let whereClause = '';
|
|
2027
|
-
if (
|
|
2000
|
+
if (configInfo.forceRegeneration?.enabled) {
|
|
2028
2001
|
// Force regeneration mode - process all entities (or filtered by entityWhereClause)
|
|
2029
|
-
|
|
2002
|
+
logStatus(` Force regeneration enabled - processing all entities...`);
|
|
2030
2003
|
whereClause = 'e.VirtualEntity = 0';
|
|
2031
|
-
if (
|
|
2032
|
-
whereClause += ` AND (${
|
|
2033
|
-
|
|
2004
|
+
if (configInfo.forceRegeneration.entityWhereClause && configInfo.forceRegeneration.entityWhereClause.trim().length > 0) {
|
|
2005
|
+
whereClause += ` AND (${configInfo.forceRegeneration.entityWhereClause})`;
|
|
2006
|
+
logStatus(` Filtered by: ${configInfo.forceRegeneration.entityWhereClause}`);
|
|
2034
2007
|
}
|
|
2035
2008
|
whereClause += ` AND e.SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})`;
|
|
2036
2009
|
}
|
|
@@ -2046,7 +2019,7 @@ NumberedRows AS (
|
|
|
2046
2019
|
if (entitiesToProcess.length === 0) {
|
|
2047
2020
|
return true;
|
|
2048
2021
|
}
|
|
2049
|
-
|
|
2022
|
+
logStatus(` Advanced Generation enabled, processing ${entitiesToProcess.length} entities...`);
|
|
2050
2023
|
whereClause = `e.VirtualEntity = 0 AND e.Name IN (${entitiesToProcess.map(name => `'${name}'`).join(',')}) AND e.SchemaName NOT IN (${excludeSchemas.map(s => `'${s}'`).join(',')})`;
|
|
2051
2024
|
}
|
|
2052
2025
|
// Get entity details for entities that need processing
|
|
@@ -2058,7 +2031,7 @@ NumberedRows AS (
|
|
|
2058
2031
|
e.SchemaName,
|
|
2059
2032
|
e.BaseTable
|
|
2060
2033
|
FROM
|
|
2061
|
-
[${
|
|
2034
|
+
[${mj_core_schema()}].vwEntities e
|
|
2062
2035
|
WHERE
|
|
2063
2036
|
${whereClause}
|
|
2064
2037
|
ORDER BY
|
|
@@ -2091,7 +2064,7 @@ NumberedRows AS (
|
|
|
2091
2064
|
ef.EntityIDFieldName,
|
|
2092
2065
|
ef.RelatedEntity
|
|
2093
2066
|
FROM
|
|
2094
|
-
[${
|
|
2067
|
+
[${mj_core_schema()}].vwEntityFields ef
|
|
2095
2068
|
WHERE
|
|
2096
2069
|
ef.EntityID IN (${entityIds})
|
|
2097
2070
|
ORDER BY
|
|
@@ -2107,7 +2080,7 @@ NumberedRows AS (
|
|
|
2107
2080
|
es.Name,
|
|
2108
2081
|
es.Value
|
|
2109
2082
|
FROM
|
|
2110
|
-
[${
|
|
2083
|
+
[${mj_core_schema()}].EntitySetting es
|
|
2111
2084
|
WHERE
|
|
2112
2085
|
es.EntityID IN (${entityIds})
|
|
2113
2086
|
AND es.Name IN ('FieldCategoryIcons', 'FieldCategoryInfo')
|
|
@@ -2130,7 +2103,7 @@ NumberedRows AS (
|
|
|
2130
2103
|
return await this.processEntitiesBatched(pool, entities, allFields, ag, currentUser);
|
|
2131
2104
|
}
|
|
2132
2105
|
catch (error) {
|
|
2133
|
-
|
|
2106
|
+
logError(`Advanced Generation failed: ${error}`);
|
|
2134
2107
|
return false;
|
|
2135
2108
|
}
|
|
2136
2109
|
}
|
|
@@ -2158,12 +2131,12 @@ NumberedRows AS (
|
|
|
2158
2131
|
}
|
|
2159
2132
|
else {
|
|
2160
2133
|
errorCount++;
|
|
2161
|
-
|
|
2134
|
+
logError(` Error processing entity: ${result.reason}`);
|
|
2162
2135
|
}
|
|
2163
2136
|
}
|
|
2164
|
-
|
|
2137
|
+
logStatus(` Progress: ${processedCount}/${entities.length} entities processed`);
|
|
2165
2138
|
}
|
|
2166
|
-
|
|
2139
|
+
logStatus(` Advanced Generation complete: ${processedCount} entities processed, ${errorCount} errors`);
|
|
2167
2140
|
return errorCount === 0;
|
|
2168
2141
|
}
|
|
2169
2142
|
/**
|
|
@@ -2203,7 +2176,7 @@ NumberedRows AS (
|
|
|
2203
2176
|
}, currentUser, isNewEntity);
|
|
2204
2177
|
if (layoutAnalysis) {
|
|
2205
2178
|
await this.applyFormLayout(pool, entity.ID, fields, layoutAnalysis, isNewEntity);
|
|
2206
|
-
|
|
2179
|
+
logStatus(` Applied form layout for ${entity.Name}`);
|
|
2207
2180
|
}
|
|
2208
2181
|
}
|
|
2209
2182
|
}
|
|
@@ -2216,26 +2189,26 @@ NumberedRows AS (
|
|
|
2216
2189
|
const nameField = fields.find(f => f.Name === result.nameField);
|
|
2217
2190
|
if (nameField && nameField.AutoUpdateIsNameField && nameField.ID) {
|
|
2218
2191
|
sqlStatements.push(`
|
|
2219
|
-
UPDATE [${
|
|
2192
|
+
UPDATE [${mj_core_schema()}].EntityField
|
|
2220
2193
|
SET IsNameField = 1
|
|
2221
2194
|
WHERE ID = '${nameField.ID}'
|
|
2222
2195
|
AND AutoUpdateIsNameField = 1
|
|
2223
2196
|
`);
|
|
2224
2197
|
}
|
|
2225
2198
|
else if (!nameField) {
|
|
2226
|
-
|
|
2199
|
+
logError(`Smart field identification returned invalid nameField: '${result.nameField}' not found in entity fields`);
|
|
2227
2200
|
}
|
|
2228
2201
|
// Find all default in view fields (one or more)
|
|
2229
2202
|
const defaultInViewFields = fields.filter(f => result.defaultInView.includes(f.Name) && f.AutoUpdateDefaultInView && f.ID);
|
|
2230
2203
|
// Warn about any fields that weren't found
|
|
2231
2204
|
const missingFields = result.defaultInView.filter(name => !fields.some(f => f.Name === name));
|
|
2232
2205
|
if (missingFields.length > 0) {
|
|
2233
|
-
|
|
2206
|
+
logError(`Smart field identification returned invalid defaultInView fields: ${missingFields.join(', ')} not found in entity`);
|
|
2234
2207
|
}
|
|
2235
2208
|
// Build update statements for all default in view fields
|
|
2236
2209
|
for (const field of defaultInViewFields) {
|
|
2237
2210
|
sqlStatements.push(`
|
|
2238
|
-
UPDATE [${
|
|
2211
|
+
UPDATE [${mj_core_schema()}].EntityField
|
|
2239
2212
|
SET DefaultInView = 1
|
|
2240
2213
|
WHERE ID = '${field.ID}'
|
|
2241
2214
|
AND AutoUpdateDefaultInView = 1
|
|
@@ -2247,12 +2220,12 @@ NumberedRows AS (
|
|
|
2247
2220
|
// Warn about any fields that weren't found
|
|
2248
2221
|
const missingSearchableFields = result.searchableFields.filter(name => !fields.some(f => f.Name === name));
|
|
2249
2222
|
if (missingSearchableFields.length > 0) {
|
|
2250
|
-
|
|
2223
|
+
logError(`Smart field identification returned invalid searchableFields: ${missingSearchableFields.join(', ')} not found in entity`);
|
|
2251
2224
|
}
|
|
2252
2225
|
// Build update statements for all searchable fields
|
|
2253
2226
|
for (const field of searchableFields) {
|
|
2254
2227
|
sqlStatements.push(`
|
|
2255
|
-
UPDATE [${
|
|
2228
|
+
UPDATE [${mj_core_schema()}].EntityField
|
|
2256
2229
|
SET IncludeInUserSearchAPI = 1
|
|
2257
2230
|
WHERE ID = '${field.ID}'
|
|
2258
2231
|
AND AutoUpdateIncludeInUserSearchAPI = 1
|
|
@@ -2303,7 +2276,7 @@ NumberedRows AS (
|
|
|
2303
2276
|
if (fieldHasExistingCategory && categoryIsNew) {
|
|
2304
2277
|
// LLM is trying to move an existing field to a brand new category
|
|
2305
2278
|
// This could be an attempt to rename a category - reject it
|
|
2306
|
-
|
|
2279
|
+
logStatus(` Rejected category change for field '${field.Name}': cannot move from existing category '${field.Category}' to new category '${category}'. Keeping original category.`);
|
|
2307
2280
|
category = field.Category; // Keep the original category
|
|
2308
2281
|
}
|
|
2309
2282
|
// Build SET clause with all available metadata
|
|
@@ -2325,14 +2298,14 @@ NumberedRows AS (
|
|
|
2325
2298
|
const codeType = fieldCategory.codeType === null ? 'NULL' : `'${fieldCategory.codeType.replace(/'/g, "''")}'`;
|
|
2326
2299
|
setClauses.push(`CodeType = ${codeType}`);
|
|
2327
2300
|
}
|
|
2328
|
-
const updateSQL = `UPDATE [${
|
|
2301
|
+
const updateSQL = `UPDATE [${mj_core_schema()}].EntityField
|
|
2329
2302
|
SET ${setClauses.join(',\n ')}
|
|
2330
2303
|
WHERE ID = '${field.ID}'
|
|
2331
2304
|
AND AutoUpdateCategory = 1`;
|
|
2332
2305
|
sqlStatements.push(updateSQL);
|
|
2333
2306
|
}
|
|
2334
2307
|
else if (!field) {
|
|
2335
|
-
|
|
2308
|
+
logError(`Form layout generation returned invalid fieldName: '${fieldCategory.fieldName}' not found in entity`);
|
|
2336
2309
|
}
|
|
2337
2310
|
}
|
|
2338
2311
|
// Execute all field updates in one batch
|
|
@@ -2344,7 +2317,7 @@ NumberedRows AS (
|
|
|
2344
2317
|
if (result.entityIcon && result.entityIcon.trim().length > 0) {
|
|
2345
2318
|
// Check if entity already has an icon
|
|
2346
2319
|
const checkEntitySQL = `
|
|
2347
|
-
SELECT Icon FROM [${
|
|
2320
|
+
SELECT Icon FROM [${mj_core_schema()}].Entity
|
|
2348
2321
|
WHERE ID = '${entityId}'
|
|
2349
2322
|
`;
|
|
2350
2323
|
const entityCheck = await pool.request().query(checkEntitySQL);
|
|
@@ -2354,13 +2327,13 @@ NumberedRows AS (
|
|
|
2354
2327
|
if (!currentIcon || currentIcon.trim().length === 0) {
|
|
2355
2328
|
const escapedIcon = result.entityIcon.replace(/'/g, "''");
|
|
2356
2329
|
const updateEntitySQL = `
|
|
2357
|
-
UPDATE [${
|
|
2330
|
+
UPDATE [${mj_core_schema()}].Entity
|
|
2358
2331
|
SET Icon = '${escapedIcon}',
|
|
2359
2332
|
__mj_UpdatedAt = GETUTCDATE()
|
|
2360
2333
|
WHERE ID = '${entityId}'
|
|
2361
2334
|
`;
|
|
2362
2335
|
await this.LogSQLAndExecute(pool, updateEntitySQL, `Set entity icon to ${result.entityIcon}`, false);
|
|
2363
|
-
|
|
2336
|
+
logStatus(` ✓ Set entity icon: ${result.entityIcon}`);
|
|
2364
2337
|
}
|
|
2365
2338
|
}
|
|
2366
2339
|
}
|
|
@@ -2374,14 +2347,14 @@ NumberedRows AS (
|
|
|
2374
2347
|
const infoJSON = JSON.stringify(categoryInfoToStore).replace(/'/g, "''");
|
|
2375
2348
|
// First check if new format setting already exists
|
|
2376
2349
|
const checkNewSQL = `
|
|
2377
|
-
SELECT ID FROM [${
|
|
2350
|
+
SELECT ID FROM [${mj_core_schema()}].EntitySetting
|
|
2378
2351
|
WHERE EntityID = '${entityId}' AND Name = 'FieldCategoryInfo'
|
|
2379
2352
|
`;
|
|
2380
2353
|
const existingNew = await pool.request().query(checkNewSQL);
|
|
2381
2354
|
if (existingNew.recordset.length > 0) {
|
|
2382
2355
|
// Update existing setting
|
|
2383
2356
|
const updateSQL = `
|
|
2384
|
-
UPDATE [${
|
|
2357
|
+
UPDATE [${mj_core_schema()}].EntitySetting
|
|
2385
2358
|
SET Value = '${infoJSON}',
|
|
2386
2359
|
__mj_UpdatedAt = GETUTCDATE()
|
|
2387
2360
|
WHERE EntityID = '${entityId}' AND Name = 'FieldCategoryInfo'
|
|
@@ -2390,9 +2363,9 @@ NumberedRows AS (
|
|
|
2390
2363
|
}
|
|
2391
2364
|
else {
|
|
2392
2365
|
// Insert new setting
|
|
2393
|
-
const newId = (
|
|
2366
|
+
const newId = uuidv4();
|
|
2394
2367
|
const insertSQL = `
|
|
2395
|
-
INSERT INTO [${
|
|
2368
|
+
INSERT INTO [${mj_core_schema()}].EntitySetting (ID, EntityID, Name, Value, __mj_CreatedAt, __mj_UpdatedAt)
|
|
2396
2369
|
VALUES ('${newId}', '${entityId}', 'FieldCategoryInfo', '${infoJSON}', GETUTCDATE(), GETUTCDATE())
|
|
2397
2370
|
`;
|
|
2398
2371
|
await this.LogSQLAndExecute(pool, insertSQL, `Insert FieldCategoryInfo setting for entity`, false);
|
|
@@ -2407,13 +2380,13 @@ NumberedRows AS (
|
|
|
2407
2380
|
}
|
|
2408
2381
|
const iconsJSON = JSON.stringify(iconsOnly).replace(/'/g, "''");
|
|
2409
2382
|
const checkLegacySQL = `
|
|
2410
|
-
SELECT ID FROM [${
|
|
2383
|
+
SELECT ID FROM [${mj_core_schema()}].EntitySetting
|
|
2411
2384
|
WHERE EntityID = '${entityId}' AND Name = 'FieldCategoryIcons'
|
|
2412
2385
|
`;
|
|
2413
2386
|
const existingLegacy = await pool.request().query(checkLegacySQL);
|
|
2414
2387
|
if (existingLegacy.recordset.length > 0) {
|
|
2415
2388
|
const updateSQL = `
|
|
2416
|
-
UPDATE [${
|
|
2389
|
+
UPDATE [${mj_core_schema()}].EntitySetting
|
|
2417
2390
|
SET Value = '${iconsJSON}',
|
|
2418
2391
|
__mj_UpdatedAt = GETUTCDATE()
|
|
2419
2392
|
WHERE EntityID = '${entityId}' AND Name = 'FieldCategoryIcons'
|
|
@@ -2421,9 +2394,9 @@ NumberedRows AS (
|
|
|
2421
2394
|
await this.LogSQLAndExecute(pool, updateSQL, `Update FieldCategoryIcons setting for entity (legacy format)`, false);
|
|
2422
2395
|
}
|
|
2423
2396
|
else {
|
|
2424
|
-
const newId = (
|
|
2397
|
+
const newId = uuidv4();
|
|
2425
2398
|
const insertSQL = `
|
|
2426
|
-
INSERT INTO [${
|
|
2399
|
+
INSERT INTO [${mj_core_schema()}].EntitySetting (ID, EntityID, Name, Value, __mj_CreatedAt, __mj_UpdatedAt)
|
|
2427
2400
|
VALUES ('${newId}', '${entityId}', 'FieldCategoryIcons', '${iconsJSON}', GETUTCDATE(), GETUTCDATE())
|
|
2428
2401
|
`;
|
|
2429
2402
|
await this.LogSQLAndExecute(pool, insertSQL, `Insert FieldCategoryIcons setting for entity (legacy format)`, false);
|
|
@@ -2435,14 +2408,14 @@ NumberedRows AS (
|
|
|
2435
2408
|
const defaultForNewUser = result.entityImportance.defaultForNewUser ? 1 : 0;
|
|
2436
2409
|
// Update all ApplicationEntity records for this entity
|
|
2437
2410
|
const updateAppEntitySQL = `
|
|
2438
|
-
UPDATE [${
|
|
2411
|
+
UPDATE [${mj_core_schema()}].ApplicationEntity
|
|
2439
2412
|
SET DefaultForNewUser = ${defaultForNewUser},
|
|
2440
2413
|
__mj_UpdatedAt = GETUTCDATE()
|
|
2441
2414
|
WHERE EntityID = '${entityId}'
|
|
2442
2415
|
`;
|
|
2443
2416
|
await this.LogSQLAndExecute(pool, updateAppEntitySQL, `Set DefaultForNewUser=${defaultForNewUser} for NEW entity based on AI analysis (category: ${result.entityImportance.entityCategory}, confidence: ${result.entityImportance.confidence})`, false);
|
|
2444
|
-
|
|
2445
|
-
|
|
2417
|
+
logStatus(` ✓ Entity importance (NEW Entity): ${result.entityImportance.entityCategory} (defaultForNewUser: ${result.entityImportance.defaultForNewUser}, confidence: ${result.entityImportance.confidence})`);
|
|
2418
|
+
logStatus(` Reasoning: ${result.entityImportance.reasoning}`);
|
|
2446
2419
|
}
|
|
2447
2420
|
}
|
|
2448
2421
|
/**
|
|
@@ -2456,8 +2429,7 @@ NumberedRows AS (
|
|
|
2456
2429
|
* @returns - The result of the query execution.
|
|
2457
2430
|
*/
|
|
2458
2431
|
async LogSQLAndExecute(pool, query, description, isRecurringScript = false) {
|
|
2459
|
-
return await
|
|
2432
|
+
return await SQLLogging.LogSQLAndExecute(pool, query, description, isRecurringScript);
|
|
2460
2433
|
}
|
|
2461
2434
|
}
|
|
2462
|
-
exports.ManageMetadataBase = ManageMetadataBase;
|
|
2463
2435
|
//# sourceMappingURL=manage-metadata.js.map
|