@memberjunction/codegen-lib 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/advanced_generation.d.ts +3 -0
  2. package/dist/advanced_generation.d.ts.map +1 -1
  3. package/dist/advanced_generation.js +39 -26
  4. package/dist/advanced_generation.js.map +1 -1
  5. package/dist/angular_client_codegen.d.ts +45 -2
  6. package/dist/angular_client_codegen.d.ts.map +1 -1
  7. package/dist/angular_client_codegen.js +423 -399
  8. package/dist/angular_client_codegen.js.map +1 -1
  9. package/dist/createNewUser.d.ts +6 -1
  10. package/dist/createNewUser.d.ts.map +1 -1
  11. package/dist/createNewUser.js +68 -53
  12. package/dist/createNewUser.js.map +1 -1
  13. package/dist/dbSchema.d.ts +10 -3
  14. package/dist/dbSchema.d.ts.map +1 -1
  15. package/dist/dbSchema.js +144 -130
  16. package/dist/dbSchema.js.map +1 -1
  17. package/dist/entity_subclasses_codegen.d.ts +11 -6
  18. package/dist/entity_subclasses_codegen.d.ts.map +1 -1
  19. package/dist/entity_subclasses_codegen.js +144 -131
  20. package/dist/entity_subclasses_codegen.js.map +1 -1
  21. package/dist/graphql_server_codegen.d.ts +28 -5
  22. package/dist/graphql_server_codegen.d.ts.map +1 -1
  23. package/dist/graphql_server_codegen.js +552 -531
  24. package/dist/graphql_server_codegen.js.map +1 -1
  25. package/dist/logging.d.ts +20 -0
  26. package/dist/logging.d.ts.map +1 -1
  27. package/dist/logging.js +61 -26
  28. package/dist/logging.js.map +1 -1
  29. package/dist/manageMetadata.d.ts +128 -7
  30. package/dist/manageMetadata.d.ts.map +1 -1
  31. package/dist/manageMetadata.js +992 -898
  32. package/dist/manageMetadata.js.map +1 -1
  33. package/dist/runCodeGen.d.ts +16 -0
  34. package/dist/runCodeGen.d.ts.map +1 -1
  35. package/dist/runCodeGen.js +185 -140
  36. package/dist/runCodeGen.js.map +1 -1
  37. package/dist/runCommand.d.ts +7 -2
  38. package/dist/runCommand.d.ts.map +1 -1
  39. package/dist/runCommand.js +104 -90
  40. package/dist/runCommand.js.map +1 -1
  41. package/dist/sql.d.ts +21 -16
  42. package/dist/sql.d.ts.map +1 -1
  43. package/dist/sql.js +98 -87
  44. package/dist/sql.js.map +1 -1
  45. package/dist/sql_codegen.d.ts +56 -13
  46. package/dist/sql_codegen.d.ts.map +1 -1
  47. package/dist/sql_codegen.js +836 -795
  48. package/dist/sql_codegen.js.map +1 -1
  49. package/dist/util.js +0 -2
  50. package/dist/util.js.map +1 -1
  51. package/package.json +32 -32
  52. package/readme.md +2 -2
  53. package/dist/graphql_client_codegen.d.ts +0 -4
  54. package/dist/graphql_client_codegen.js +0 -161
  55. package/dist/graphql_client_codegen.js.map +0 -1
  56. package/dist/react_client_codegen.d.ts +0 -4
  57. package/dist/react_client_codegen.js +0 -147
  58. package/dist/react_client_codegen.js.map +0 -1
@@ -15,6 +15,12 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
18
24
  var __importStar = (this && this.__importStar) || function (mod) {
19
25
  if (mod && mod.__esModule) return mod;
20
26
  var result = {};
@@ -26,7 +32,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
32
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
33
  };
28
34
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.SPType = exports.generateSingleEntitySQLFileHeader = exports.generateAllEntitiesSQLFileHeader = exports.generateEntitySQL = exports.getEntityPermissionFileNames = exports.getSPName = exports.generateSingleEntitySQLToSeparateFiles = exports.generateAndExecuteSingleEntitySQLToSeparateFiles = exports.createCombinedEntitySQLFiles = exports.generateAndExecuteAllEntitiesSQLToSeparateFiles = exports.applyPermissions = exports.runCustomSQLScripts = exports.manageSQLScriptsAndExecution = void 0;
35
+ exports.SQLCodeGenBase = exports.SPType = void 0;
30
36
  const core_1 = require("@memberjunction/core");
31
37
  const logging_1 = require("./logging");
32
38
  const fs = __importStar(require("fs"));
@@ -36,625 +42,696 @@ const config_1 = require("./config");
36
42
  const manageMetadata_1 = require("./manageMetadata");
37
43
  const sqlserver_dataprovider_1 = require("@memberjunction/sqlserver-dataprovider");
38
44
  const util_1 = require("./util");
39
- //import { LoadGeneratedEntities } from 'mj_generatedentities';
40
- //LoadGeneratedEntities(); // make sure we have everything loaded up
41
- async function manageSQLScriptsAndExecution(ds, entities, directory) {
42
- try {
43
- // STEP 1 - execute any custom SQL scripts for object creation that need to happen first - for example, if
44
- // we have custom base views, need to have them defined before we do
45
- // the rest as the generated stuff might use custom base views in compiled
46
- // objects like spCreate for a given entity might reference the vw for that entity
47
- const startTime = new Date();
48
- if (!await runCustomSQLScripts(ds, 'before-sql'))
49
- return false;
50
- (0, logging_1.logStatus)(` Time to run custom SQL scripts: ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
51
- // ALWAYS use the first filter where we only include entities that have IncludeInAPI = 1
52
- const baselineEntities = entities.filter(e => e.IncludeInAPI);
53
- const includedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) === undefined); //only include entities that are NOT in the excludeSchemas list
54
- const excludedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) !== undefined); //only include entities that ARE in the excludeSchemas list in this array
55
- // STEP 2(a) - generate all the SQL files and execute them
56
- const step2StartTime = new Date();
57
- if (!await generateAndExecuteAllEntitiesSQLToSeparateFiles(ds, includedEntities, directory, false)) {
58
- (0, logging_1.logError)('Error generating and executing all entities SQL to separate files');
59
- return false;
45
+ const global_1 = require("@memberjunction/global");
46
+ exports.SPType = {
47
+ Create: 'Create',
48
+ Update: 'Update',
49
+ Delete: 'Delete',
50
+ };
51
+ /**
52
+ * This class is responsible for generating database level objects like views, stored procedures, permissions, etc. You can sub-class this class to create your own SQL generation logic or implement support for other
53
+ * databases. The base class implements support for SQL Server. In future versions of MJ, we will break out an abstract base class that has the skeleton of the logic and then the SQL Server version will be a sub-class
54
+ * of that abstract base class and other databases will be sub-classes of the abstract base class as well.
55
+ */
56
+ let SQLCodeGenBase = class SQLCodeGenBase {
57
+ constructor() {
58
+ this._sqlUtilityObject = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
59
+ }
60
+ get SQLUtilityObject() {
61
+ return this._sqlUtilityObject;
62
+ }
63
+ async manageSQLScriptsAndExecution(ds, entities, directory) {
64
+ try {
65
+ // STEP 1 - execute any custom SQL scripts for object creation that need to happen first - for example, if
66
+ // we have custom base views, need to have them defined before we do
67
+ // the rest as the generated stuff might use custom base views in compiled
68
+ // objects like spCreate for a given entity might reference the vw for that entity
69
+ const startTime = new Date();
70
+ if (!await this.runCustomSQLScripts(ds, 'before-sql'))
71
+ return false;
72
+ (0, logging_1.logStatus)(` Time to run custom SQL scripts: ${(new Date().getTime() - startTime.getTime()) / 1000} seconds`);
73
+ // ALWAYS use the first filter where we only include entities that have IncludeInAPI = 1
74
+ const baselineEntities = entities.filter(e => e.IncludeInAPI);
75
+ const includedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) === undefined); //only include entities that are NOT in the excludeSchemas list
76
+ const excludedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) !== undefined); //only include entities that ARE in the excludeSchemas list in this array
77
+ // STEP 2(a) - generate all the SQL files and execute them
78
+ const step2StartTime = new Date();
79
+ if (!await this.generateAndExecuteEntitySQLToSeparateFiles(ds, includedEntities, directory, false)) {
80
+ (0, logging_1.logError)('Error generating and executing all entities SQL to separate files');
81
+ return false;
82
+ }
83
+ // STEP 2(b) - for the excludedEntities, while we don't want to generate SQL, we do want to generate the permissions files for them
84
+ if (!await this.generateAndExecuteEntitySQLToSeparateFiles(ds, excludedEntities, directory, true)) {
85
+ (0, logging_1.logError)('Error generating and executing permissions SQL for excluded entities to separate files');
86
+ return false;
87
+ }
88
+ (0, logging_1.logStatus)(` Time to Generate/Execute Entity SQL: ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
89
+ // now that we've generated the SQL, let's create a combined file in each schema sub-directory for convenience for a DBA
90
+ this.createCombinedEntitySQLFiles(directory, baselineEntities);
91
+ const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manageMetadata_1.ManageMetadataBase);
92
+ // STEP 3 - re-run the process to manage entity fields since the Step 1 and 2 above might have resulted in differences in base view columns compared to what we had at first
93
+ if (!await manageMD.manageEntityFields(ds, config_1.configInfo.excludeSchemas)) {
94
+ (0, logging_1.logError)('Error managing entity fields');
95
+ return false;
96
+ }
97
+ // no logStatus/timer for this because manageEntityFields() has its own internal logging for this including the total, so it is redundant to log it here
98
+ // STEP 4- Apply permissions, executing all .permissions files
99
+ const step4StartTime = new Date();
100
+ if (!await this.applyPermissions(ds, directory, baselineEntities)) {
101
+ (0, logging_1.logError)('Error applying permissions');
102
+ return false;
103
+ }
104
+ (0, logging_1.logStatus)(` Time to Apply Permissions: ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
105
+ // STEP 5 - execute any custom SQL scripts that should run afterwards
106
+ const step5StartTime = new Date();
107
+ if (!await this.runCustomSQLScripts(ds, 'after-sql'))
108
+ return false;
109
+ (0, logging_1.logStatus)(` Time to run custom SQL scripts: ${(new Date().getTime() - step5StartTime.getTime()) / 1000} seconds`);
110
+ (0, logging_1.logStatus)(' Total time to run generate and execute SQL scripts: ' + ((new Date().getTime() - startTime.getTime()) / 1000) + ' seconds');
111
+ // now - we need to tell our metadata object to refresh itself
112
+ const md = new core_1.Metadata();
113
+ await md.Refresh();
114
+ return true;
60
115
  }
61
- // STEP 2(b) - for the excludedEntities, while we don't want to generate SQL, we do want to generate the permissions files for them
62
- if (!await generateAndExecuteAllEntitiesSQLToSeparateFiles(ds, excludedEntities, directory, true)) {
63
- (0, logging_1.logError)('Error generating and executing permissions SQL for excluded entities to separate files');
116
+ catch (err) {
117
+ (0, logging_1.logError)(err);
64
118
  return false;
65
119
  }
66
- (0, logging_1.logStatus)(` Time to Generate/Execute Entity SQL: ${(new Date().getTime() - step2StartTime.getTime()) / 1000} seconds`);
67
- // now that we've generated the SQL, let's create a combined file in each schema sub-directory for convenience for a DBA
68
- createCombinedEntitySQLFiles(directory, baselineEntities);
69
- // STEP 3 - re-run the process to manage entity fields since the Step 1 and 2 above might have resulted in differences in base view columns compared to what we had at first
70
- if (!await (0, manageMetadata_1.manageEntityFields)(ds, config_1.configInfo.excludeSchemas)) {
71
- (0, logging_1.logError)('Error managing entity fields');
72
- return false;
120
+ }
121
+ async runCustomSQLScripts(ds, when) {
122
+ try {
123
+ const scripts = (0, config_1.customSqlScripts)(when);
124
+ let bSuccess = true;
125
+ if (scripts) {
126
+ for (let i = 0; i < scripts.length; ++i) {
127
+ const s = scripts[i];
128
+ if (!await this.SQLUtilityObject.executeSQLFile(ds, s.scriptFile, true)) {
129
+ (0, logging_1.logError)(`Error executing custom '${when}' SQL script ${s.scriptFile}`);
130
+ bSuccess = false; // keep going if we have more scripts, but make sure we return false
131
+ }
132
+ }
133
+ }
134
+ return bSuccess;
73
135
  }
74
- // no logStatus/timer for this because manageEntityFields() has its own internal logging for this including the total, so it is redundant to log it here
75
- // STEP 4- Apply permissions, executing all .permissions files
76
- const step4StartTime = new Date();
77
- if (!await applyPermissions(ds, directory, baselineEntities)) {
78
- (0, logging_1.logError)('Error applying permissions');
136
+ catch (e) {
137
+ (0, logging_1.logError)(e);
79
138
  return false;
80
139
  }
81
- (0, logging_1.logStatus)(` Time to Apply Permissions: ${(new Date().getTime() - step4StartTime.getTime()) / 1000} seconds`);
82
- // STEP 5 - execute any custom SQL scripts that should run afterwards
83
- const step5StartTime = new Date();
84
- if (!await runCustomSQLScripts(ds, 'after-sql'))
85
- return false;
86
- (0, logging_1.logStatus)(` Time to run custom SQL scripts: ${(new Date().getTime() - step5StartTime.getTime()) / 1000} seconds`);
87
- (0, logging_1.logStatus)(' Total time to run generate and execute SQL scripts: ' + ((new Date().getTime() - startTime.getTime()) / 1000) + ' seconds');
88
- // now - we need to tell our metadata object to refresh itself
89
- const md = new core_1.Metadata();
90
- await md.Refresh();
91
- return true;
92
- }
93
- catch (err) {
94
- (0, logging_1.logError)(err);
95
- return false;
96
- }
97
- }
98
- exports.manageSQLScriptsAndExecution = manageSQLScriptsAndExecution;
99
- async function runCustomSQLScripts(ds, when) {
100
- try {
101
- const scripts = (0, config_1.customSqlScripts)(when);
102
- let bSuccess = true;
103
- if (scripts) {
104
- for (let i = 0; i < scripts.length; ++i) {
105
- const s = scripts[i];
106
- if (!await (0, sql_1.executeSQLFile)(ds, s.scriptFile, true)) {
107
- (0, logging_1.logError)(`Error executing custom '${when}' SQL script ${s.scriptFile}`);
108
- bSuccess = false; // keep going if we have more scripts, but make sure we return false
140
+ }
141
+ async applyPermissions(ds, directory, entities, batchSize = 5) {
142
+ try {
143
+ let bSuccess = true;
144
+ for (let i = 0; i < entities.length; i += batchSize) {
145
+ const batch = entities.slice(i, i + batchSize);
146
+ const promises = batch.map(async (e) => {
147
+ // generate the file names for the entity
148
+ const files = this.getEntityPermissionFileNames(e);
149
+ let innerSuccess = true;
150
+ for (const f of files) {
151
+ const fullPath = path_1.default.join(directory, f);
152
+ if (fs.existsSync(fullPath)) {
153
+ if (!await this.SQLUtilityObject.executeSQLFile(ds, fullPath, true))
154
+ innerSuccess = false; // we keep going, just note that something failed
155
+ }
156
+ else {
157
+ // we don't have the file, so we can't execute it, but we should log it as an error
158
+ // and then keep going
159
+ (0, logging_1.logError)(`Permissions file ${fullPath} does not exist for entity ${e.Name}`);
160
+ }
161
+ }
162
+ return innerSuccess;
163
+ });
164
+ const results = await Promise.all(promises);
165
+ if (results.includes(false)) {
166
+ (0, logging_1.logError)(`Error executing one or more permissions files in batch starting from index ${i}`);
167
+ bSuccess = false; // keep going, but will return false at the end
109
168
  }
110
169
  }
170
+ return bSuccess;
111
171
  }
112
- return bSuccess;
113
- }
114
- catch (e) {
115
- (0, logging_1.logError)(e);
116
- return false;
117
- }
118
- }
119
- exports.runCustomSQLScripts = runCustomSQLScripts;
120
- async function applyPermissions(ds, directory, entities, batchSize = 5) {
121
- try {
122
- let bSuccess = true;
123
- for (let i = 0; i < entities.length; i += batchSize) {
124
- const batch = entities.slice(i, i + batchSize);
125
- const promises = batch.map(async (e) => {
126
- // generate the file names for the entity
127
- const files = getEntityPermissionFileNames(e);
128
- let innerSuccess = true;
129
- for (const f of files) {
130
- const fullPath = path_1.default.join(directory, f);
131
- if (fs.existsSync(fullPath)) {
132
- if (!await (0, sql_1.executeSQLFile)(ds, fullPath, true))
133
- innerSuccess = false; // we keep going, just note that something failed
134
- }
135
- else {
136
- // we don't have the file, so we can't execute it, but we should log it as an error
137
- // and then keep going
138
- (0, logging_1.logError)(`Permissions file ${fullPath} does not exist for entity ${e.Name}`);
172
+ catch (err) {
173
+ (0, logging_1.logError)(err);
174
+ return false;
175
+ }
176
+ }
177
+ /**
178
+ * This function will handle the process of creating all of the generated objects like base view, stored procedures, etc. for all entities in the entities array. It will generate the SQL for each entity and then execute it.
179
+ * @param ds The DataSource object to use to execute the SQL
180
+ * @param entities The array of EntityInfo objects to generate SQL for
181
+ * @param directory The directory to save the generated SQL files to
182
+ * @param onlyPermissions If true, only the permissions files will be generated and executed, not the actual SQL files. Use this if you are simply setting permission changes but no actual changes to the entities have occured.
183
+ */
184
+ async generateAndExecuteEntitySQLToSeparateFiles(ds, entities, directory, onlyPermissions, writeFiles = true, batchSize = 5) {
185
+ try {
186
+ let bFail = false;
187
+ const totalEntities = entities.length;
188
+ for (let i = 0; i < totalEntities; i += batchSize) {
189
+ const batch = entities.slice(i, i + batchSize);
190
+ const promises = batch.map(async (e) => {
191
+ const pkeyField = e.Fields.find(f => f.IsPrimaryKey);
192
+ if (!pkeyField) {
193
+ (0, logging_1.logError)(`SKIPPING ENTITY: Entity ${e.Name}, because it does not have a primary key field defined. A table must have a primary key defined to quality to be a MemberJunction entity`);
194
+ return false;
139
195
  }
196
+ return this.generateAndExecuteSingleEntitySQLToSeparateFiles(ds, e, directory, onlyPermissions, writeFiles);
197
+ });
198
+ const results = await Promise.all(promises);
199
+ if (results.includes(false)) {
200
+ bFail = true; // keep going, but will return false at the end
140
201
  }
141
- return innerSuccess;
142
- });
143
- const results = await Promise.all(promises);
144
- if (results.includes(false)) {
145
- (0, logging_1.logError)(`Error executing one or more permissions files in batch starting from index ${i}`);
146
- bSuccess = false; // keep going, but will return false at the end
147
202
  }
203
+ return !bFail;
148
204
  }
149
- return bSuccess;
150
- }
151
- catch (err) {
152
- (0, logging_1.logError)(err);
153
- return false;
154
- }
155
- }
156
- exports.applyPermissions = applyPermissions;
157
- async function generateAndExecuteAllEntitiesSQLToSeparateFiles(ds, entities, directory, onlyPermissions, batchSize = 5) {
158
- try {
159
- let bFail = false;
160
- const totalEntities = entities.length;
161
- for (let i = 0; i < totalEntities; i += batchSize) {
162
- const batch = entities.slice(i, i + batchSize);
163
- const promises = batch.map(async (e) => {
164
- const pkeyField = e.Fields.find(f => f.IsPrimaryKey);
165
- if (!pkeyField) {
166
- (0, logging_1.logError)(`SKIPPING ENTITY: Entity ${e.Name}, because it does not have a primary key field defined. A table must have a primary key defined to quality to be a MemberJunction entity`);
167
- return false;
168
- }
169
- return generateAndExecuteSingleEntitySQLToSeparateFiles(ds, e, directory, onlyPermissions);
170
- });
171
- const results = await Promise.all(promises);
172
- if (results.includes(false)) {
173
- bFail = true; // keep going, but will return false at the end
205
+ catch (err) {
206
+ (0, logging_1.logError)(err);
207
+ return false;
208
+ }
209
+ }
210
+ async createCombinedEntitySQLFiles(directory, entities) {
211
+ // first, get a disinct list of schemanames from the entities
212
+ const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
213
+ for (const s of schemaNames) {
214
+ // generate the all-entities.sql file and all-entities.permissions.sql file in each schema folder
215
+ const fullPath = path_1.default.join(directory, s);
216
+ if (fs.statSync(fullPath).isDirectory()) {
217
+ (0, util_1.combineFiles)(fullPath, '_all_entities.sql', '*.generated.sql', true);
218
+ (0, util_1.combineFiles)(fullPath, '_all_entities.permissions.sql', '*.permissions.generated.sql', true);
174
219
  }
175
220
  }
176
- return !bFail;
177
- }
178
- catch (err) {
179
- (0, logging_1.logError)(err);
180
- return false;
181
- }
182
- }
183
- exports.generateAndExecuteAllEntitiesSQLToSeparateFiles = generateAndExecuteAllEntitiesSQLToSeparateFiles;
184
- async function createCombinedEntitySQLFiles(directory, entities) {
185
- // first, get a disinct list of schemanames from the entities
186
- const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
187
- for (const s of schemaNames) {
188
- // generate the all-entities.sql file and all-entities.permissions.sql file in each schema folder
189
- const fullPath = path_1.default.join(directory, s);
190
- if (fs.statSync(fullPath).isDirectory()) {
191
- (0, util_1.combineFiles)(fullPath, '_all_entities.sql', '*.generated.sql', true);
192
- (0, util_1.combineFiles)(fullPath, '_all_entities.permissions.sql', '*.permissions.generated.sql', true);
193
- }
194
- }
195
- }
196
- exports.createCombinedEntitySQLFiles = createCombinedEntitySQLFiles;
197
- async function generateAndExecuteSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions) {
198
- try {
199
- const sSQL = await generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions); // this creates the files and returns a single string with all the SQL we can then execute
200
- return await (0, sql_1.executeSQLScript)(ds, sSQL, true);
201
- }
202
- catch (err) {
203
- (0, logging_1.logError)(err);
204
- return false;
205
- }
206
- }
207
- exports.generateAndExecuteSingleEntitySQLToSeparateFiles = generateAndExecuteSingleEntitySQLToSeparateFiles;
208
- async function generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions) {
209
- try {
210
- // create the directory if it doesn't exist
211
- if (!fs.existsSync(directory))
212
- fs.mkdirSync(directory, { recursive: true });
213
- // now do the same thing for the /schema directory within the provided directory
214
- const schemaDirectory = path_1.default.join(directory, entity.SchemaName);
215
- if (!fs.existsSync(schemaDirectory))
216
- fs.mkdirSync(schemaDirectory, { recursive: true }); // create the directory if it doesn't exist
217
- let sRet = '';
218
- // BASE VIEW
219
- if (!onlyPermissions && entity.BaseViewGenerated && !entity.VirtualEntity) {
220
- // generate the base view
221
- const s = generateSingleEntitySQLFileHeader(entity, entity.BaseView) + await generateBaseView(ds, entity);
222
- const filePath = path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('view', entity.SchemaName, entity.BaseView, false, true));
223
- fs.writeFileSync(filePath, s);
224
- sRet += s + '\nGO\n';
225
- }
226
- // always generate permissions for the base view
227
- const s = generateSingleEntitySQLFileHeader(entity, 'Permissions for ' + entity.BaseView) + generateViewPermissions(entity);
228
- fs.writeFileSync(path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('view', entity.SchemaName, entity.BaseView, true, true)), s);
229
- // now, append the permissions to the return string IF we did NOT generate the base view - because if we generated the base view, that
230
- // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
231
- if (!entity.BaseViewGenerated)
232
- sRet += s + '\nGO\n';
233
- // CREATE SP
234
- if (entity.AllowCreateAPI && !entity.VirtualEntity) {
235
- const spName = getSPName(entity, exports.SPType.Create);
236
- if (!onlyPermissions && entity.spCreateGenerated) {
237
- // generate the create SP
238
- const s = generateSingleEntitySQLFileHeader(entity, spName) + generateSPCreate(entity);
239
- fs.writeFileSync(path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, spName, false, true)), s);
221
+ }
222
+ async generateAndExecuteSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, writeFiles = true) {
223
+ try {
224
+ const { sql, permissionsSQL } = await this.generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, writeFiles); // this creates the files and returns a single string with all the SQL we can then execute
225
+ return await this.SQLUtilityObject.executeSQLScript(ds, sql, true) &&
226
+ await this.SQLUtilityObject.executeSQLScript(ds, permissionsSQL, true);
227
+ }
228
+ catch (err) {
229
+ (0, logging_1.logError)(err);
230
+ return false;
231
+ }
232
+ }
233
+ async generateSingleEntitySQLToSeparateFiles(ds, entity, directory, onlyPermissions, writeFiles = true) {
234
+ try {
235
+ // create the directory if it doesn't exist
236
+ if (writeFiles && !fs.existsSync(directory))
237
+ fs.mkdirSync(directory, { recursive: true });
238
+ // now do the same thing for the /schema directory within the provided directory
239
+ const schemaDirectory = path_1.default.join(directory, entity.SchemaName);
240
+ if (writeFiles && !fs.existsSync(schemaDirectory))
241
+ fs.mkdirSync(schemaDirectory, { recursive: true }); // create the directory if it doesn't exist
242
+ let sRet = '';
243
+ let permissionsSQL = '';
244
+ // BASE VIEW
245
+ if (!onlyPermissions && entity.BaseViewGenerated && !entity.VirtualEntity) {
246
+ // generate the base view
247
+ const s = this.generateSingleEntitySQLFileHeader(entity, entity.BaseView) + await this.generateBaseView(ds, entity);
248
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('view', entity.SchemaName, entity.BaseView, false, true));
249
+ if (writeFiles)
250
+ fs.writeFileSync(filePath, s);
240
251
  sRet += s + '\nGO\n';
241
252
  }
242
- const s = generateSPPermissions(entity, spName, exports.SPType.Create) + '\n\n';
243
- fs.writeFileSync(path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, spName, true, true)), s);
244
- // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
253
+ // always generate permissions for the base view
254
+ const s = this.generateSingleEntitySQLFileHeader(entity, 'Permissions for ' + entity.BaseView) + this.generateViewPermissions(entity);
255
+ if (s.length > 0)
256
+ permissionsSQL += s + '\nGO\n';
257
+ if (writeFiles)
258
+ fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('view', entity.SchemaName, entity.BaseView, true, true)), s);
259
+ // now, append the permissions to the return string IF we did NOT generate the base view - because if we generated the base view, that
245
260
  // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
246
- if (!entity.spCreateGenerated)
261
+ if (!entity.BaseViewGenerated)
247
262
  sRet += s + '\nGO\n';
263
+ // CREATE SP
264
+ if (entity.AllowCreateAPI && !entity.VirtualEntity) {
265
+ const spName = this.getSPName(entity, exports.SPType.Create);
266
+ if (!onlyPermissions && entity.spCreateGenerated) {
267
+ // generate the create SP
268
+ const s = this.generateSingleEntitySQLFileHeader(entity, spName) + this.generateSPCreate(entity);
269
+ if (writeFiles)
270
+ fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true)), s);
271
+ sRet += s + '\nGO\n';
272
+ }
273
+ const s = this.generateSPPermissions(entity, spName, exports.SPType.Create) + '\n\n';
274
+ if (s.length > 0)
275
+ permissionsSQL += s + '\nGO\n';
276
+ if (writeFiles)
277
+ fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true)), s);
278
+ // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
279
+ // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
280
+ if (!entity.spCreateGenerated)
281
+ sRet += s + '\nGO\n';
282
+ }
283
+ // UPDATE SP
284
+ if (entity.AllowUpdateAPI && !entity.VirtualEntity) {
285
+ const spName = this.getSPName(entity, exports.SPType.Update);
286
+ if (!onlyPermissions && entity.spUpdateGenerated) {
287
+ // generate the update SP
288
+ const s = this.generateSingleEntitySQLFileHeader(entity, spName) + this.generateSPUpdate(entity);
289
+ if (writeFiles)
290
+ fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true)), s);
291
+ sRet += s + '\nGO\n';
292
+ }
293
+ const s = this.generateSPPermissions(entity, spName, exports.SPType.Update) + '\n\n';
294
+ if (s.length > 0)
295
+ permissionsSQL += s + '\nGO\n';
296
+ if (writeFiles)
297
+ fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true)), s);
298
+ // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
299
+ // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
300
+ if (!entity.spUpdateGenerated)
301
+ sRet += s + '\nGO\n';
302
+ }
303
+ // DELETE SP
304
+ if (entity.AllowDeleteAPI && !entity.VirtualEntity) {
305
+ const spName = this.getSPName(entity, exports.SPType.Delete);
306
+ if (!onlyPermissions && entity.spDeleteGenerated) {
307
+ // generate the delete SP
308
+ const s = this.generateSingleEntitySQLFileHeader(entity, spName) + this.generateSPDelete(entity);
309
+ if (writeFiles)
310
+ fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, false, true)), s);
311
+ sRet += s + '\nGO\n';
312
+ }
313
+ const s = this.generateSPPermissions(entity, spName, exports.SPType.Delete) + '\n\n';
314
+ if (s.length > 0)
315
+ permissionsSQL += s + '\nGO\n';
316
+ if (writeFiles)
317
+ fs.writeFileSync(path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, spName, true, true)), s);
318
+ // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
319
+ // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
320
+ if (!entity.spDeleteGenerated)
321
+ sRet += s + '\nGO\n';
322
+ }
323
+ // check to see if the entity supports full text search or not
324
+ if (entity.FullTextSearchEnabled) {
325
+ // always generate the code so we can get the function name from the below function call
326
+ const ft = await this.generateEntityFullTextSearchSQL(ds, entity);
327
+ if (!onlyPermissions) {
328
+ // only write the actual sql out if we're not only generating permissions
329
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', entity.SchemaName, entity.BaseTable, false, true));
330
+ if (writeFiles)
331
+ fs.writeFileSync(filePath, ft.sql);
332
+ sRet += ft.sql + '\nGO\n';
333
+ }
334
+ const sP = this.generateFullTextSearchFunctionPermissions(entity, ft.functionName) + '\n\n';
335
+ if (sP.length > 0)
336
+ permissionsSQL += sP + '\nGO\n';
337
+ const filePath = path_1.default.join(directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', entity.SchemaName, entity.BaseTable, true, true));
338
+ if (writeFiles)
339
+ fs.writeFileSync(filePath, sP);
340
+ // now, append the permissions to the return string IF we did NOT generate the function - because if we generated the function, that
341
+ // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
342
+ if (!entity.FullTextSearchFunctionGenerated)
343
+ sRet += sP + '\nGO\n';
344
+ }
345
+ return { sql: sRet, permissionsSQL: permissionsSQL };
346
+ }
347
+ catch (err) {
348
+ (0, logging_1.logError)(err);
349
+ return null;
350
+ }
351
+ }
352
+ getSPName(entity, type) {
353
+ switch (type) {
354
+ case exports.SPType.Create:
355
+ return entity.spCreate && entity.spCreate.length > 0 ? entity.spCreate : 'spCreate' + entity.ClassName;
356
+ case exports.SPType.Update:
357
+ return entity.spUpdate && entity.spUpdate.length > 0 ? entity.spUpdate : 'spUpdate' + entity.ClassName;
358
+ case exports.SPType.Delete:
359
+ return entity.spDelete && entity.spDelete.length > 0 ? entity.spDelete : 'spDelete' + entity.ClassName;
360
+ }
361
+ }
362
+ getEntityPermissionFileNames(entity) {
363
+ const files = [];
364
+ // all entities have a base view - and we always generate permissions for the base view even if not generated base view
365
+ files.push(this.SQLUtilityObject.getDBObjectFileName('view', entity.SchemaName, entity.BaseView, true, true));
366
+ // only add the SP files if the entity is not a virtual entity
367
+ if (!entity.VirtualEntity) {
368
+ // only add each SP file if the Allow flags are set to true, doesn't matter if the SPs are generated or not, we always generate permissions
369
+ if (entity.AllowCreateAPI)
370
+ files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, exports.SPType.Create), true, true));
371
+ if (entity.AllowUpdateAPI)
372
+ files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, exports.SPType.Update), true, true));
373
+ if (entity.AllowDeleteAPI)
374
+ files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, exports.SPType.Delete), true, true));
375
+ }
376
+ if (entity.FullTextSearchEnabled)
377
+ files.push(this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', entity.SchemaName, entity.BaseTable, true, true));
378
+ return files;
379
+ }
380
+ async generateEntitySQL(ds, entity) {
381
+ let sOutput = '';
382
+ if (entity.BaseViewGenerated && !entity.VirtualEntity)
383
+ // generated the base view (will include permissions)
384
+ sOutput += await this.generateBaseView(ds, entity) + '\n\n';
385
+ else
386
+ // still generate the permissions for the view even if a custom view
387
+ sOutput += this.generateViewPermissions(entity) + '\n\n';
388
+ if (entity.AllowCreateAPI && !entity.VirtualEntity) {
389
+ if (entity.spCreateGenerated)
390
+ // generated SP, will include permissions
391
+ sOutput += this.generateSPCreate(entity) + '\n\n';
392
+ else
393
+ // custom SP, still generate the permissions
394
+ sOutput += this.generateSPPermissions(entity, entity.spCreate, exports.SPType.Create) + '\n\n';
248
395
  }
249
- // UPDATE SP
250
396
  if (entity.AllowUpdateAPI && !entity.VirtualEntity) {
251
- const spName = getSPName(entity, exports.SPType.Update);
252
- if (!onlyPermissions && entity.spUpdateGenerated) {
253
- // generate the update SP
254
- const s = generateSingleEntitySQLFileHeader(entity, spName) + generateSPUpdate(entity);
255
- fs.writeFileSync(path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, spName, false, true)), s);
256
- sRet += s + '\nGO\n';
257
- }
258
- const s = generateSPPermissions(entity, spName, exports.SPType.Update) + '\n\n';
259
- fs.writeFileSync(path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, spName, true, true)), s);
260
- // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
261
- // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
262
- if (!entity.spUpdateGenerated)
263
- sRet += s + '\nGO\n';
397
+ if (entity.spUpdateGenerated)
398
+ // generated SP, will include permissions
399
+ sOutput += this.generateSPUpdate(entity) + '\n\n';
400
+ else
401
+ // custom SP, still generate the permissions
402
+ sOutput += this.generateSPPermissions(entity, entity.spUpdate, exports.SPType.Update) + '\n\n';
264
403
  }
265
- // DELETE SP
266
404
  if (entity.AllowDeleteAPI && !entity.VirtualEntity) {
267
- const spName = getSPName(entity, exports.SPType.Delete);
268
- if (!onlyPermissions && entity.spDeleteGenerated) {
269
- // generate the delete SP
270
- const s = generateSingleEntitySQLFileHeader(entity, spName) + generateSPDelete(entity);
271
- fs.writeFileSync(path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, spName, false, true)), s);
272
- sRet += s + '\nGO\n';
273
- }
274
- const s = generateSPPermissions(entity, spName, exports.SPType.Delete) + '\n\n';
275
- fs.writeFileSync(path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, spName, true, true)), s);
276
- // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
277
- // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
278
- if (!entity.spDeleteGenerated)
279
- sRet += s + '\nGO\n';
405
+ if (entity.spDeleteGenerated)
406
+ // generated SP, will include permissions
407
+ sOutput += this.generateSPDelete(entity) + '\n\n';
408
+ else
409
+ // custom SP, still generate the permissions
410
+ sOutput += this.generateSPPermissions(entity, entity.spDelete, exports.SPType.Delete) + '\n\n';
280
411
  }
281
412
  // check to see if the entity supports full text search or not
282
413
  if (entity.FullTextSearchEnabled) {
283
- // always generate the code so we can get the function name from the below function call
284
- const ft = await generateEntityFullTextSearchSQL(ds, entity);
285
- if (!onlyPermissions) {
286
- // only write the actual sql out if we're not only generating permissions
287
- const filePath = path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('full_text_search_function', entity.SchemaName, entity.BaseTable, false, true));
288
- fs.writeFileSync(filePath, ft.sql);
289
- sRet += ft.sql + '\nGO\n';
414
+ sOutput += await this.generateEntityFullTextSearchSQL(ds, entity) + '\n\n';
415
+ }
416
+ return sOutput;
417
+ }
418
+ async generateEntityFullTextSearchSQL(ds, entity) {
419
+ let sql = '';
420
+ const catalogName = entity.FullTextCatalog && entity.FullTextCatalog.length > 0 ? entity.FullTextCatalog : config_1.dbDatabase + '_FullTextCatalog';
421
+ if (entity.FullTextCatalogGenerated) {
422
+ // this situation means we have a generated catalog and the user has provided a name specific to THIS entity
423
+ sql += ` -- CREATE THE FULL TEXT CATALOG FOR THE ENTITY, IF NOT ALREADY CREATED
424
+ IF NOT EXISTS (
425
+ SELECT *
426
+ FROM sys.fulltext_catalogs
427
+ WHERE name = '${catalogName}'
428
+ )
429
+ CREATE FULLTEXT CATALOG ${catalogName};
430
+ GO
431
+ `;
432
+ }
433
+ if (entity.FullTextIndexGenerated) {
434
+ const fullTextFields = entity.Fields.filter(f => f.FullTextSearchEnabled).map(f => `${f.Name} LANGUAGE 'English'`).join(', ');
435
+ if (fullTextFields.length === 0)
436
+ throw new Error(`FullTextIndexGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
437
+ // drop and recreate the full text index
438
+ const entity_pk_name = await this.getEntityPrimaryKeyIndexName(ds, entity);
439
+ sql += ` -- DROP AND RECREATE THE FULL TEXT INDEX
440
+ IF EXISTS (
441
+ SELECT *
442
+ FROM sys.fulltext_indexes
443
+ WHERE object_id = OBJECT_ID('${entity.SchemaName}.${entity.BaseTable}')
444
+ )
445
+ BEGIN
446
+ DROP FULLTEXT INDEX ON [${entity.SchemaName}].[${entity.BaseTable}];
447
+ END
448
+ GO
449
+
450
+ IF NOT EXISTS (
451
+ SELECT *
452
+ FROM sys.fulltext_indexes
453
+ WHERE object_id = OBJECT_ID('${entity.SchemaName}.${entity.BaseTable}')
454
+ )
455
+ BEGIN
456
+ CREATE FULLTEXT INDEX ON [${entity.SchemaName}].[${entity.BaseTable}]
457
+ (
458
+ ${fullTextFields}
459
+ )
460
+ KEY INDEX ${entity_pk_name}
461
+ ON ${catalogName};
462
+ END
463
+ GO
464
+ `;
465
+ }
466
+ const functionName = entity.FullTextSearchFunction && entity.FullTextSearchFunction.length > 0 ? entity.FullTextSearchFunction : `fnSearch${entity.CodeName}`;
467
+ if (entity.FullTextSearchFunctionGenerated) {
468
+ const fullTextFieldsSimple = entity.Fields.filter(f => f.FullTextSearchEnabled).map(f => '[' + f.Name + ']').join(', ');
469
+ if (fullTextFieldsSimple.length === 0)
470
+ throw new Error(`FullTextSearchFunctionGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
471
+ if (!entity.FullTextSearchFunction || entity.FullTextSearchFunction.length === 0) {
472
+ // update this in the DB
473
+ const md = new core_1.Metadata();
474
+ const u = sqlserver_dataprovider_1.UserCache.Instance.Users[0];
475
+ if (!u)
476
+ throw new Error('Could not find the first user in the cache, cant generate the full text search function without a user');
477
+ const e = await md.GetEntityObject('Entities', u);
478
+ await e.Load(entity.ID);
479
+ e.FullTextSearchFunction = functionName;
480
+ if (!await e.Save())
481
+ throw new Error(`Could not update the FullTextSearchFunction for entity ${entity.Name}`);
290
482
  }
291
- const sP = generateFullTextSearchFunctionPermissions(entity, ft.functionName) + '\n\n';
292
- const filePath = path_1.default.join(directory, (0, sql_1.getDBObjectFileName)('full_text_search_function', entity.SchemaName, entity.BaseTable, true, true));
293
- fs.writeFileSync(filePath, sP);
294
- // now, append the permissions to the return string IF we did NOT generate the function - because if we generated the function, that
295
- // means we already generated the permissions for it above and it is part of sRet already, but we always save it to a file, (per above line)
296
- if (!entity.FullTextSearchFunctionGenerated)
297
- sRet += sP + '\nGO\n';
298
- }
299
- return sRet;
300
- }
301
- catch (err) {
302
- (0, logging_1.logError)(err);
303
- return null;
304
- }
305
- }
306
- exports.generateSingleEntitySQLToSeparateFiles = generateSingleEntitySQLToSeparateFiles;
307
- function getSPName(entity, type) {
308
- switch (type) {
309
- case exports.SPType.Create:
310
- return entity.spCreate && entity.spCreate.length > 0 ? entity.spCreate : 'spCreate' + entity.ClassName;
311
- case exports.SPType.Update:
312
- return entity.spUpdate && entity.spUpdate.length > 0 ? entity.spUpdate : 'spUpdate' + entity.ClassName;
313
- case exports.SPType.Delete:
314
- return entity.spDelete && entity.spDelete.length > 0 ? entity.spDelete : 'spDelete' + entity.ClassName;
315
- }
316
- }
317
- exports.getSPName = getSPName;
318
- function getEntityPermissionFileNames(entity) {
319
- const files = [];
320
- // all entities have a base view - and we always generate permissions for the base view even if not generated base view
321
- files.push((0, sql_1.getDBObjectFileName)('view', entity.SchemaName, entity.BaseView, true, true));
322
- // only add the SP files if the entity is not a virtual entity
323
- if (!entity.VirtualEntity) {
324
- // only add each SP file if the Allow flags are set to true, doesn't matter if the SPs are generated or not, we always generate permissions
325
- if (entity.AllowCreateAPI)
326
- files.push((0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, getSPName(entity, exports.SPType.Create), true, true));
327
- if (entity.AllowUpdateAPI)
328
- files.push((0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, getSPName(entity, exports.SPType.Update), true, true));
329
- if (entity.AllowDeleteAPI)
330
- files.push((0, sql_1.getDBObjectFileName)('sp', entity.SchemaName, getSPName(entity, exports.SPType.Delete), true, true));
331
- }
332
- if (entity.FullTextSearchEnabled)
333
- files.push((0, sql_1.getDBObjectFileName)('full_text_search_function', entity.SchemaName, entity.BaseTable, true, true));
334
- return files;
335
- }
336
- exports.getEntityPermissionFileNames = getEntityPermissionFileNames;
337
- async function generateEntitySQL(ds, entity) {
338
- let sOutput = '';
339
- if (entity.BaseViewGenerated && !entity.VirtualEntity)
340
- // generated the base view (will include permissions)
341
- sOutput += await generateBaseView(ds, entity) + '\n\n';
342
- else
343
- // still generate the permissions for the view even if a custom view
344
- sOutput += generateViewPermissions(entity) + '\n\n';
345
- if (entity.AllowCreateAPI && !entity.VirtualEntity) {
346
- if (entity.spCreateGenerated)
347
- // generated SP, will include permissions
348
- sOutput += generateSPCreate(entity) + '\n\n';
349
- else
350
- // custom SP, still generate the permissions
351
- sOutput += generateSPPermissions(entity, entity.spCreate, exports.SPType.Create) + '\n\n';
352
- }
353
- if (entity.AllowUpdateAPI && !entity.VirtualEntity) {
354
- if (entity.spUpdateGenerated)
355
- // generated SP, will include permissions
356
- sOutput += generateSPUpdate(entity) + '\n\n';
357
- else
358
- // custom SP, still generate the permissions
359
- sOutput += generateSPPermissions(entity, entity.spUpdate, exports.SPType.Update) + '\n\n';
360
- }
361
- if (entity.AllowDeleteAPI && !entity.VirtualEntity) {
362
- if (entity.spDeleteGenerated)
363
- // generated SP, will include permissions
364
- sOutput += generateSPDelete(entity) + '\n\n';
483
+ const pkeyList = entity.PrimaryKeys.map(pk => '[' + pk.Name + ']').join(', ');
484
+ // drop and recreate the full text search function
485
+ sql += ` -- DROP AND RECREATE THE FULL TEXT SEARCH FUNCTION
486
+ -- Create an inline table-valued function to perform full-text search
487
+ -- Drop the function if it already exists
488
+ IF OBJECT_ID('${entity.SchemaName}.${functionName}', 'IF') IS NOT NULL
489
+ DROP FUNCTION ${entity.SchemaName}.${functionName};
490
+ GO
491
+ CREATE FUNCTION ${entity.SchemaName}.${functionName} (@searchTerm NVARCHAR(255))
492
+ RETURNS TABLE
493
+ AS
494
+ RETURN (
495
+ SELECT ${pkeyList}
496
+ FROM [${entity.SchemaName}].[${entity.BaseTable}]
497
+ WHERE CONTAINS((${fullTextFieldsSimple}), @searchTerm)
498
+ )
499
+ GO
500
+ `;
501
+ sql += this.generateFullTextSearchFunctionPermissions(entity, functionName) + '\n\nGO\n';
502
+ }
503
+ return { sql, functionName };
504
+ }
505
+ async getEntityPrimaryKeyIndexName(ds, entity) {
506
+ const sSQL = ` SELECT
507
+ i.name AS IndexName
508
+ FROM
509
+ sys.indexes i
510
+ INNER JOIN
511
+ sys.objects o ON i.object_id = o.object_id
512
+ INNER JOIN
513
+ sys.key_constraints kc ON i.object_id = kc.parent_object_id AND
514
+ i.index_id = kc.unique_index_id
515
+ WHERE
516
+ o.name = '${entity.BaseTable}' AND
517
+ o.schema_id = SCHEMA_ID('${entity.SchemaName}') AND
518
+ kc.type = 'PK';
519
+ `;
520
+ const result = await ds.query(sSQL);
521
+ if (result && result.length > 0)
522
+ return result[0].IndexName;
365
523
  else
366
- // custom SP, still generate the permissions
367
- sOutput += generateSPPermissions(entity, entity.spDelete, exports.SPType.Delete) + '\n\n';
368
- }
369
- // check to see if the entity supports full text search or not
370
- if (entity.FullTextSearchEnabled) {
371
- sOutput += await generateEntityFullTextSearchSQL(ds, entity) + '\n\n';
372
- }
373
- return sOutput;
374
- }
375
- exports.generateEntitySQL = generateEntitySQL;
376
- async function generateEntityFullTextSearchSQL(ds, entity) {
377
- let sql = '';
378
- const catalogName = entity.FullTextCatalog && entity.FullTextCatalog.length > 0 ? entity.FullTextCatalog : config_1.dbDatabase + '_FullTextCatalog';
379
- if (entity.FullTextCatalogGenerated) {
380
- // this situation means we have a generated catalog and the user has provided a name specific to THIS entity
381
- sql += ` -- CREATE THE FULL TEXT CATALOG FOR THE ENTITY, IF NOT ALREADY CREATED
382
- IF NOT EXISTS (
383
- SELECT *
384
- FROM sys.fulltext_catalogs
385
- WHERE name = '${catalogName}'
386
- )
387
- CREATE FULLTEXT CATALOG ${catalogName};
388
- GO
524
+ throw new Error(`Could not find primary key index for entity ${entity.Name}`);
525
+ }
526
+ generateAllEntitiesSQLFileHeader() {
527
+ return `-----------------------------------------------------------------
528
+ -- SQL Code Generation for Entities
529
+ -- Generated: ${new Date().toLocaleString()}
530
+ --
531
+ -- This file contains the SQL code for the entities in the database
532
+ -- that are included in the API and have generated SQL elements like views and
533
+ -- stored procedures.
534
+ --
535
+ -- It is generated by the MemberJunction CodeGen tool.
536
+ -- It is not intended to be edited by hand.
537
+ -----------------------------------------------------------------
389
538
  `;
390
539
  }
391
- if (entity.FullTextIndexGenerated) {
392
- const fullTextFields = entity.Fields.filter(f => f.FullTextSearchEnabled).map(f => `${f.Name} LANGUAGE 'English'`).join(', ');
393
- if (fullTextFields.length === 0)
394
- throw new Error(`FullTextIndexGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
395
- // drop and recreate the full text index
396
- const entity_pk_name = await getEntityPrimaryKeyIndexName(ds, entity);
397
- sql += ` -- DROP AND RECREATE THE FULL TEXT INDEX
398
- IF EXISTS (
399
- SELECT *
400
- FROM sys.fulltext_indexes
401
- WHERE object_id = OBJECT_ID('${entity.SchemaName}.${entity.BaseTable}')
402
- )
403
- BEGIN
404
- DROP FULLTEXT INDEX ON [${entity.SchemaName}].[${entity.BaseTable}];
405
- END
406
- GO
407
-
408
- IF NOT EXISTS (
409
- SELECT *
410
- FROM sys.fulltext_indexes
411
- WHERE object_id = OBJECT_ID('${entity.SchemaName}.${entity.BaseTable}')
412
- )
413
- BEGIN
414
- CREATE FULLTEXT INDEX ON [${entity.SchemaName}].[${entity.BaseTable}]
415
- (
416
- ${fullTextFields}
417
- )
418
- KEY INDEX ${entity_pk_name}
419
- ON ${catalogName};
420
- END
421
- GO
540
+ generateSingleEntitySQLFileHeader(entity, itemName) {
541
+ return `-----------------------------------------------------------------
542
+ -- SQL Code Generation
543
+ -- Entity: ${entity.Name}
544
+ -- Item: ${itemName}
545
+ -- Generated: ${new Date().toLocaleString()}
546
+ --
547
+ -- This was generated by the MemberJunction CodeGen tool.
548
+ -- This file should NOT be edited by hand.
549
+ -----------------------------------------------------------------
422
550
  `;
423
551
  }
424
- const functionName = entity.FullTextSearchFunction && entity.FullTextSearchFunction.length > 0 ? entity.FullTextSearchFunction : `fnSearch${entity.CodeName}`;
425
- if (entity.FullTextSearchFunctionGenerated) {
426
- const fullTextFieldsSimple = entity.Fields.filter(f => f.FullTextSearchEnabled).map(f => '[' + f.Name + ']').join(', ');
427
- if (fullTextFieldsSimple.length === 0)
428
- throw new Error(`FullTextSearchFunctionGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
429
- if (!entity.FullTextSearchFunction || entity.FullTextSearchFunction.length === 0) {
430
- // update this in the DB
431
- const md = new core_1.Metadata();
432
- const u = sqlserver_dataprovider_1.UserCache.Instance.Users[0];
433
- if (!u)
434
- throw new Error('Could not find the first user in the cache, cant generate the full text search function without a user');
435
- const e = await md.GetEntityObject('Entities', u);
436
- await e.Load(entity.ID);
437
- e.FullTextSearchFunction = functionName;
438
- if (!await e.Save())
439
- throw new Error(`Could not update the FullTextSearchFunction for entity ${entity.Name}`);
440
- }
441
- const pkeyList = entity.PrimaryKeys.map(pk => '[' + pk.Name + ']').join(', ');
442
- // drop and recreate the full text search function
443
- sql += ` -- DROP AND RECREATE THE FULL TEXT SEARCH FUNCTION
444
- -- Create an inline table-valued function to perform full-text search
445
- -- Drop the function if it already exists
446
- IF OBJECT_ID('${entity.SchemaName}.${functionName}', 'IF') IS NOT NULL
447
- DROP FUNCTION ${entity.SchemaName}.${functionName};
448
- GO
449
- CREATE FUNCTION ${entity.SchemaName}.${functionName} (@searchTerm NVARCHAR(255))
450
- RETURNS TABLE
451
- AS
452
- RETURN (
453
- SELECT ${pkeyList}
454
- FROM [${entity.SchemaName}].[${entity.BaseTable}]
455
- WHERE CONTAINS((${fullTextFieldsSimple}), @searchTerm)
456
- )
457
- GO
458
- `;
459
- sql += generateFullTextSearchFunctionPermissions(entity, functionName) + '\n\nGO\n';
460
- }
461
- return { sql, functionName };
462
- }
463
- async function getEntityPrimaryKeyIndexName(ds, entity) {
464
- const sSQL = ` SELECT
465
- i.name AS IndexName
466
- FROM
467
- sys.indexes i
468
- INNER JOIN
469
- sys.objects o ON i.object_id = o.object_id
470
- INNER JOIN
471
- sys.key_constraints kc ON i.object_id = kc.parent_object_id AND
472
- i.index_id = kc.unique_index_id
473
- WHERE
474
- o.name = '${entity.BaseTable}' AND
475
- o.schema_id = SCHEMA_ID('${entity.SchemaName}') AND
476
- kc.type = 'PK';
552
+ async generateBaseView(ds, entity) {
553
+ const viewName = entity.BaseView ? entity.BaseView : `vw${entity.CodeName}`;
554
+ const baseTableFirstChar = entity.BaseTable.charAt(0).toLowerCase();
555
+ const relatedFieldsString = await this.generateBaseViewRelatedFieldsString(ds, entity.Fields);
556
+ const relatedFieldsJoinString = this.generateBaseViewJoins(entity.Fields);
557
+ const permissions = this.generateViewPermissions(entity);
558
+ return `
559
+ ------------------------------------------------------------
560
+ ----- BASE VIEW FOR ENTITY: ${entity.Name}
561
+ ----- SCHEMA: ${entity.SchemaName}
562
+ ----- BASE TABLE: ${entity.BaseTable}
563
+ ----- PRIMARY KEY: ${entity.PrimaryKey.Name}
564
+ ------------------------------------------------------------
565
+ DROP VIEW IF EXISTS [${entity.SchemaName}].[${viewName}]
566
+ GO
567
+
568
+ CREATE VIEW [${entity.SchemaName}].[${viewName}]
569
+ AS
570
+ SELECT
571
+ ${baseTableFirstChar}.*${relatedFieldsString.length > 0 ? ',' : ''}${relatedFieldsString}
572
+ FROM
573
+ [${entity.SchemaName}].[${entity.BaseTable}] AS ${baseTableFirstChar}${relatedFieldsJoinString ? '\n' + relatedFieldsJoinString : ''}
574
+ GO${permissions}
477
575
  `;
478
- const result = await ds.query(sSQL);
479
- if (result && result.length > 0)
480
- return result[0].IndexName;
481
- else
482
- throw new Error(`Could not find primary key index for entity ${entity.Name}`);
483
- }
484
- function generateAllEntitiesSQLFileHeader() {
485
- return `-----------------------------------------------------------------
486
- -- SQL Code Generation for Entities
487
- -- Generated: ${new Date().toLocaleString()}
488
- --
489
- -- This file contains the SQL code for the entities in the database
490
- -- that are included in the API and have generated SQL elements like views and
491
- -- stored procedures.
492
- --
493
- -- It is generated by the MemberJunction CodeGen tool.
494
- -- It is not intended to be edited by hand.
495
- -----------------------------------------------------------------
496
- `;
497
- }
498
- exports.generateAllEntitiesSQLFileHeader = generateAllEntitiesSQLFileHeader;
499
- function generateSingleEntitySQLFileHeader(entity, itemName) {
500
- return `-----------------------------------------------------------------
501
- -- SQL Code Generation
502
- -- Entity: ${entity.Name}
503
- -- Item: ${itemName}
504
- -- Generated: ${new Date().toLocaleString()}
505
- --
506
- -- This was generated by the MemberJunction CodeGen tool.
507
- -- This file should NOT be edited by hand.
508
- -----------------------------------------------------------------
509
- `;
510
- }
511
- exports.generateSingleEntitySQLFileHeader = generateSingleEntitySQLFileHeader;
512
- async function generateBaseView(ds, entity) {
513
- const viewName = entity.BaseView ? entity.BaseView : `vw${entity.CodeName}`;
514
- const baseTableFirstChar = entity.BaseTable.charAt(0).toLowerCase();
515
- const relatedFieldsString = await generateBaseViewRelatedFieldsString(ds, entity.Fields);
516
- const relatedFieldsJoinString = generateBaseViewJoins(entity.Fields);
517
- const permissions = generateViewPermissions(entity);
518
- return `
519
- ------------------------------------------------------------
520
- ----- BASE VIEW FOR ENTITY: ${entity.Name}
521
- ----- SCHEMA: ${entity.SchemaName}
522
- ----- BASE TABLE: ${entity.BaseTable}
523
- ----- PRIMARY KEY: ${entity.PrimaryKey.Name}
524
- ------------------------------------------------------------
525
- DROP VIEW IF EXISTS [${entity.SchemaName}].[${viewName}]
526
- GO
527
-
528
- CREATE VIEW [${entity.SchemaName}].[${viewName}]
529
- AS
530
- SELECT
531
- ${baseTableFirstChar}.*${relatedFieldsString.length > 0 ? ',' : ''}${relatedFieldsString}
532
- FROM
533
- [${entity.SchemaName}].[${entity.BaseTable}] AS ${baseTableFirstChar}${relatedFieldsJoinString ? '\n' + relatedFieldsJoinString : ''}
534
- GO${permissions}
535
- `;
536
- }
537
- function generateViewPermissions(entity) {
538
- let sOutput = '';
539
- for (let i = 0; i < entity.Permissions.length; i++) {
540
- const ep = entity.Permissions[i];
541
- sOutput += (sOutput == '' ? `GRANT SELECT ON [${entity.SchemaName}].[${entity.BaseView}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
542
- }
543
- return (sOutput == '' ? '' : '\n') + sOutput;
544
- }
545
- function generateBaseViewJoins(entityFields) {
546
- let sOutput = '';
547
- for (let i = 0; i < entityFields.length; i++) {
548
- const ef = entityFields[i];
549
- if (ef.RelatedEntityID && ef.IncludeRelatedEntityNameFieldInBaseView && ef._RelatedEntityTableAlias) {
550
- sOutput += sOutput == '' ? '' : '\n';
551
- sOutput += `${ef.AllowsNull ? 'LEFT OUTER' : 'INNER'} JOIN\n ${ef._RelatedEntityNameFieldIsVirtual ? '' : '[' + ef.RelatedEntitySchemaName + '].'}[${ef._RelatedEntityNameFieldIsVirtual ? ef.RelatedEntityBaseView : ef.RelatedEntityBaseTable}] AS ${ef._RelatedEntityTableAlias}\n ON\n [${ef.Entity.charAt(0).toLowerCase()}].[${ef.Name}] = ${ef._RelatedEntityTableAlias}.[${ef.RelatedEntityFieldName}]`;
552
- }
553
- }
554
- return sOutput;
555
- }
556
- async function generateBaseViewRelatedFieldsString(ds, entityFields) {
557
- let sOutput = '';
558
- let fieldCount = 0;
559
- for (let i = 0; i < entityFields.length; i++) {
560
- const ef = entityFields[i];
561
- if (ef.RelatedEntityID && ef.IncludeRelatedEntityNameFieldInBaseView) {
562
- const { nameField, nameFieldIsVirtual } = getIsNameFieldForSingleEntity(ef.RelatedEntity);
563
- if (nameField !== '') {
564
- // only add to the output, if we found a name field for the related entity.
565
- ef._RelatedEntityTableAlias = ef.RelatedEntityClassName + '_' + ef.Name;
566
- ef._RelatedEntityNameFieldIsVirtual = nameFieldIsVirtual;
567
- // This next section generates a field name for the new virtual field and makes sure it doesn't collide with a field in the base table
568
- const candidateName = stripID(ef.Name);
569
- // check to make sure candidateName is not already a field name in the base table (other than a virtual field of course, as that is what we're creating)
570
- // because if it is, we need to change it to something else
571
- const bFound = entityFields.find(f => f.IsVirtual === false && f.Name.trim().toLowerCase() === candidateName.trim().toLowerCase()) !== undefined;
572
- if (bFound)
573
- ef._RelatedEntityNameFieldMap = candidateName + '_Virtual';
574
- else
575
- ef._RelatedEntityNameFieldMap = candidateName;
576
- // now we have a safe field name alias for the new virtual field in the _RelatedEntityNameFieldMap property, so use it...
577
- sOutput += `${fieldCount == 0 ? '' : ','}\n ${ef._RelatedEntityTableAlias}.[${nameField}] AS [${ef._RelatedEntityNameFieldMap}]`;
578
- // check to see if the database already knows about the RelatedEntityNameFieldMap or not
579
- if (ef.RelatedEntityNameFieldMap === null ||
580
- ef.RelatedEntityNameFieldMap === undefined ||
581
- ef.RelatedEntityNameFieldMap.trim().length === 0) {
582
- // the database doesn't yet know about this RelatedEntityNameFieldMap, so we need to update it
583
- // first update the actul field in the metadata object so it can be used from this point forward
584
- // and it also reflects what the DB will hold
585
- ef.RelatedEntityNameFieldMap = ef._RelatedEntityNameFieldMap;
586
- // then update the database itself
587
- await (0, manageMetadata_1.updateEntityFieldRelatedEntityNameFieldMap)(ds, ef.ID, ef.RelatedEntityNameFieldMap);
576
+ }
577
+ generateViewPermissions(entity) {
578
+ let sOutput = '';
579
+ for (let i = 0; i < entity.Permissions.length; i++) {
580
+ const ep = entity.Permissions[i];
581
+ sOutput += (sOutput == '' ? `GRANT SELECT ON [${entity.SchemaName}].[${entity.BaseView}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
582
+ }
583
+ return (sOutput == '' ? '' : '\n') + sOutput;
584
+ }
585
+ generateBaseViewJoins(entityFields) {
586
+ let sOutput = '';
587
+ for (let i = 0; i < entityFields.length; i++) {
588
+ const ef = entityFields[i];
589
+ if (ef.RelatedEntityID && ef.IncludeRelatedEntityNameFieldInBaseView && ef._RelatedEntityTableAlias) {
590
+ sOutput += sOutput == '' ? '' : '\n';
591
+ sOutput += `${ef.AllowsNull ? 'LEFT OUTER' : 'INNER'} JOIN\n ${ef._RelatedEntityNameFieldIsVirtual ? '' : '[' + ef.RelatedEntitySchemaName + '].'}[${ef._RelatedEntityNameFieldIsVirtual ? ef.RelatedEntityBaseView : ef.RelatedEntityBaseTable}] AS ${ef._RelatedEntityTableAlias}\n ON\n [${ef.Entity.charAt(0).toLowerCase()}].[${ef.Name}] = ${ef._RelatedEntityTableAlias}.[${ef.RelatedEntityFieldName}]`;
592
+ }
593
+ }
594
+ return sOutput;
595
+ }
596
+ async generateBaseViewRelatedFieldsString(ds, entityFields) {
597
+ let sOutput = '';
598
+ let fieldCount = 0;
599
+ const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manageMetadata_1.ManageMetadataBase);
600
+ for (let i = 0; i < entityFields.length; i++) {
601
+ const ef = entityFields[i];
602
+ if (ef.RelatedEntityID && ef.IncludeRelatedEntityNameFieldInBaseView) {
603
+ const { nameField, nameFieldIsVirtual } = this.getIsNameFieldForSingleEntity(ef.RelatedEntity);
604
+ if (nameField !== '') {
605
+ // only add to the output, if we found a name field for the related entity.
606
+ ef._RelatedEntityTableAlias = ef.RelatedEntityClassName + '_' + ef.Name;
607
+ ef._RelatedEntityNameFieldIsVirtual = nameFieldIsVirtual;
608
+ // This next section generates a field name for the new virtual field and makes sure it doesn't collide with a field in the base table
609
+ const candidateName = this.stripID(ef.Name);
610
+ // check to make sure candidateName is not already a field name in the base table (other than a virtual field of course, as that is what we're creating)
611
+ // because if it is, we need to change it to something else
612
+ const bFound = entityFields.find(f => f.IsVirtual === false && f.Name.trim().toLowerCase() === candidateName.trim().toLowerCase()) !== undefined;
613
+ if (bFound)
614
+ ef._RelatedEntityNameFieldMap = candidateName + '_Virtual';
615
+ else
616
+ ef._RelatedEntityNameFieldMap = candidateName;
617
+ // now we have a safe field name alias for the new virtual field in the _RelatedEntityNameFieldMap property, so use it...
618
+ sOutput += `${fieldCount == 0 ? '' : ','}\n ${ef._RelatedEntityTableAlias}.[${nameField}] AS [${ef._RelatedEntityNameFieldMap}]`;
619
+ // check to see if the database already knows about the RelatedEntityNameFieldMap or not
620
+ if (ef.RelatedEntityNameFieldMap === null ||
621
+ ef.RelatedEntityNameFieldMap === undefined ||
622
+ ef.RelatedEntityNameFieldMap.trim().length === 0) {
623
+ // the database doesn't yet know about this RelatedEntityNameFieldMap, so we need to update it
624
+ // first update the actul field in the metadata object so it can be used from this point forward
625
+ // and it also reflects what the DB will hold
626
+ ef.RelatedEntityNameFieldMap = ef._RelatedEntityNameFieldMap;
627
+ // then update the database itself
628
+ await manageMD.updateEntityFieldRelatedEntityNameFieldMap(ds, ef.ID, ef.RelatedEntityNameFieldMap);
629
+ }
630
+ fieldCount++;
588
631
  }
589
- fieldCount++;
590
632
  }
591
633
  }
634
+ return sOutput;
635
+ }
636
+ getIsNameFieldForSingleEntity(entityName) {
637
+ const md = new core_1.Metadata(); // use the full metadata entity list, not the filtered version that we receive
638
+ const e = md.Entities.find(e => e.Name === entityName);
639
+ if (e) {
640
+ const ef = e.NameField;
641
+ if (e.NameField)
642
+ return { nameField: ef.Name, nameFieldIsVirtual: ef.IsVirtual };
643
+ }
644
+ else
645
+ (0, logging_1.logStatus)(`ERROR: Could not find entity with name ${entityName}`);
646
+ return { nameField: '', nameFieldIsVirtual: false };
592
647
  }
593
- return sOutput;
594
- }
595
- function getIsNameFieldForSingleEntity(entityName) {
596
- const md = new core_1.Metadata(); // use the full metadata entity list, not the filtered version that we receive
597
- const e = md.Entities.find(e => e.Name === entityName);
598
- if (e) {
599
- const ef = e.NameField;
600
- if (e.NameField)
601
- return { nameField: ef.Name, nameFieldIsVirtual: ef.IsVirtual };
602
- }
603
- else
604
- (0, logging_1.logStatus)(`ERROR: Could not find entity with name ${entityName}`);
605
- return { nameField: '', nameFieldIsVirtual: false };
606
- }
607
- function stripID(name) {
608
- if (name.endsWith('ID'))
609
- return name.substring(0, name.length - 2);
610
- else
611
- return name;
612
- }
613
- exports.SPType = {
614
- Create: 'Create',
615
- Update: 'Update',
616
- Delete: 'Delete',
617
- };
618
- function generateSPPermissions(entity, spName, type) {
619
- let sOutput = '';
620
- for (let i = 0; i < entity.Permissions.length; i++) {
621
- const ep = entity.Permissions[i];
622
- if ((type == exports.SPType.Create && ep.CanCreate) ||
623
- (type == exports.SPType.Update && ep.CanUpdate) ||
624
- (type == exports.SPType.Delete && ep.CanDelete))
625
- sOutput += (sOutput == '' ? `GRANT EXECUTE ON [${entity.SchemaName}].[${spName}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
626
- }
627
- return (sOutput == '' ? '' : '\n') + sOutput;
628
- }
629
- function generateFullTextSearchFunctionPermissions(entity, functionName) {
630
- let sOutput = '';
631
- for (let i = 0; i < entity.Permissions.length; i++) {
632
- const ep = entity.Permissions[i];
633
- if (ep.CanRead)
634
- sOutput += (sOutput == '' ? `GRANT SELECT ON [${entity.SchemaName}].[${functionName}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
635
- }
636
- return (sOutput == '' ? '' : '\n') + sOutput;
637
- }
638
- function generateSPCreate(entity) {
639
- const spName = entity.spCreate ? entity.spCreate : `spCreate${entity.ClassName}`;
640
- const autoGeneratedPrimaryKey = entity.PrimaryKey.AutoIncrement || entity.PrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier';
641
- const efString = createEntityFieldsParamString(entity.Fields, !autoGeneratedPrimaryKey);
642
- const permissions = generateSPPermissions(entity, spName, exports.SPType.Create);
643
- let primaryKeyInsertValue = '';
644
- let selectInsertedRecord = '';
645
- let additionalFieldList = '';
646
- let additionalValueList = '';
647
- if (entity.PrimaryKey.AutoIncrement) {
648
- selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.PrimaryKey.Name}] = SCOPE_IDENTITY()`;
649
- }
650
- else if (entity.PrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier') {
651
- primaryKeyInsertValue = `DECLARE @newId UNIQUEIDENTIFIER = NEWID();\n SET @${entity.PrimaryKey.Name} = @newId;\n`;
652
- additionalFieldList = ',\n [' + entity.PrimaryKey.CodeName + ']';
653
- additionalValueList = ',\n @newId';
654
- selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.PrimaryKey.Name}] = @newId`;
655
- }
656
- else {
657
- selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE `;
648
+ stripID(name) {
649
+ if (name.endsWith('ID'))
650
+ return name.substring(0, name.length - 2);
651
+ else
652
+ return name;
653
+ }
654
+ generateSPPermissions(entity, spName, type) {
655
+ let sOutput = '';
656
+ for (let i = 0; i < entity.Permissions.length; i++) {
657
+ const ep = entity.Permissions[i];
658
+ if ((type == exports.SPType.Create && ep.CanCreate) ||
659
+ (type == exports.SPType.Update && ep.CanUpdate) ||
660
+ (type == exports.SPType.Delete && ep.CanDelete))
661
+ sOutput += (sOutput == '' ? `GRANT EXECUTE ON [${entity.SchemaName}].[${spName}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
662
+ }
663
+ return (sOutput == '' ? '' : '\n') + sOutput;
664
+ }
665
+ generateFullTextSearchFunctionPermissions(entity, functionName) {
666
+ let sOutput = '';
667
+ for (let i = 0; i < entity.Permissions.length; i++) {
668
+ const ep = entity.Permissions[i];
669
+ if (ep.CanRead)
670
+ sOutput += (sOutput == '' ? `GRANT SELECT ON [${entity.SchemaName}].[${functionName}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
671
+ }
672
+ return (sOutput == '' ? '' : '\n') + sOutput;
673
+ }
674
+ generateSPCreate(entity) {
675
+ const spName = entity.spCreate ? entity.spCreate : `spCreate${entity.ClassName}`;
676
+ const autoGeneratedPrimaryKey = entity.PrimaryKey.AutoIncrement || entity.PrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier';
677
+ const efString = this.createEntityFieldsParamString(entity.Fields, !autoGeneratedPrimaryKey);
678
+ const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Create);
679
+ let primaryKeyInsertValue = '';
680
+ let selectInsertedRecord = '';
681
+ let additionalFieldList = '';
682
+ let additionalValueList = '';
683
+ if (entity.PrimaryKey.AutoIncrement) {
684
+ selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.PrimaryKey.Name}] = SCOPE_IDENTITY()`;
685
+ }
686
+ else if (entity.PrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier') {
687
+ primaryKeyInsertValue = `DECLARE @newId UNIQUEIDENTIFIER = NEWID();\n SET @${entity.PrimaryKey.Name} = @newId;\n`;
688
+ additionalFieldList = ',\n [' + entity.PrimaryKey.CodeName + ']';
689
+ additionalValueList = ',\n @newId';
690
+ selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE [${entity.PrimaryKey.Name}] = @newId`;
691
+ }
692
+ else {
693
+ selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE `;
694
+ let isFirst = true;
695
+ for (let k of entity.PrimaryKeys) {
696
+ if (!isFirst)
697
+ selectInsertedRecord += ' AND ';
698
+ selectInsertedRecord += `[${k.CodeName}] = @${k.CodeName}`;
699
+ isFirst = false;
700
+ }
701
+ }
702
+ return `
703
+ ------------------------------------------------------------
704
+ ----- CREATE PROCEDURE FOR ${entity.BaseTable}
705
+ ------------------------------------------------------------
706
+ DROP PROCEDURE IF EXISTS [${entity.SchemaName}].[${spName}]
707
+ GO
708
+
709
+ CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
710
+ ${efString}
711
+ AS
712
+ BEGIN
713
+ SET NOCOUNT ON;
714
+ ${primaryKeyInsertValue}
715
+ INSERT INTO
716
+ [${entity.SchemaName}].[${entity.BaseTable}]
717
+ (
718
+ ${this.createEntityFieldsInsertString(entity, entity.Fields, '')}${additionalFieldList}
719
+ )
720
+ VALUES
721
+ (
722
+ ${this.createEntityFieldsInsertString(entity, entity.Fields, '@')}${additionalValueList}
723
+ )
724
+ -- return the new record from the base view, which might have some calculated fields
725
+ ${selectInsertedRecord}
726
+ END
727
+ GO${permissions}
728
+ `;
729
+ }
730
+ generateSPUpdate(entity) {
731
+ const spName = entity.spUpdate ? entity.spUpdate : `spUpdate${entity.ClassName}`;
732
+ const efParamString = this.createEntityFieldsParamString(entity.Fields, true);
733
+ const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Update);
734
+ let selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE `;
658
735
  let isFirst = true;
659
736
  for (let k of entity.PrimaryKeys) {
660
737
  if (!isFirst)
@@ -662,226 +739,190 @@ function generateSPCreate(entity) {
662
739
  selectInsertedRecord += `[${k.CodeName}] = @${k.CodeName}`;
663
740
  isFirst = false;
664
741
  }
665
- }
666
- return `
667
- ------------------------------------------------------------
668
- ----- CREATE PROCEDURE FOR ${entity.BaseTable}
669
- ------------------------------------------------------------
670
- DROP PROCEDURE IF EXISTS [${entity.SchemaName}].[${spName}]
671
- GO
672
-
673
- CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
674
- ${efString}
675
- AS
676
- BEGIN
677
- SET NOCOUNT ON;
678
- ${primaryKeyInsertValue}
679
- INSERT INTO
680
- [${entity.SchemaName}].[${entity.BaseTable}]
681
- (
682
- ${createEntityFieldsInsertString(entity, entity.Fields, '')}${additionalFieldList}
683
- )
684
- VALUES
685
- (
686
- ${createEntityFieldsInsertString(entity, entity.Fields, '@')}${additionalValueList}
687
- )
688
- -- return the new record from the base view, which might have some calculated fields
689
- ${selectInsertedRecord}
690
- END
691
- GO${permissions}
692
- `;
693
- }
694
- function generateSPUpdate(entity) {
695
- const spName = entity.spUpdate ? entity.spUpdate : `spUpdate${entity.ClassName}`;
696
- const efParamString = createEntityFieldsParamString(entity.Fields, true);
697
- const permissions = generateSPPermissions(entity, spName, exports.SPType.Update);
698
- let selectInsertedRecord = `SELECT * FROM [${entity.SchemaName}].[${entity.BaseView}] WHERE `;
699
- let isFirst = true;
700
- for (let k of entity.PrimaryKeys) {
701
- if (!isFirst)
702
- selectInsertedRecord += ' AND ';
703
- selectInsertedRecord += `[${k.CodeName}] = @${k.CodeName}`;
704
- isFirst = false;
705
- }
706
- return `
707
- ------------------------------------------------------------
708
- ----- UPDATE PROCEDURE FOR ${entity.BaseTable}
709
- ------------------------------------------------------------
710
- DROP PROCEDURE IF EXISTS [${entity.SchemaName}].[${spName}]
711
- GO
712
-
713
- CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
714
- ${efParamString}
715
- AS
716
- BEGIN
717
- SET NOCOUNT ON;
718
- UPDATE
719
- [${entity.SchemaName}].[${entity.BaseTable}]
720
- SET
721
- ${createEntityFieldsUpdateString(entity.Fields)}
722
- WHERE
723
- [${entity.PrimaryKey.Name}] = @${entity.PrimaryKey.Name}
724
-
725
- -- return the updated record so the caller can see the updated values and any calculated fields
726
- ${selectInsertedRecord}
727
- END
728
- GO${permissions}
729
- `;
730
- }
731
- function createEntityFieldsParamString(entityFields, isUpdate) {
732
- let sOutput = '', isFirst = true;
733
- for (let i = 0; i < entityFields.length; ++i) {
734
- const ef = entityFields[i];
735
- const autoGeneratedPrimaryKey = ef.AutoIncrement || ef.Type.toLowerCase().trim() === 'uniqueidentifier';
736
- if ((ef.AllowUpdateAPI || (ef.IsPrimaryKey && isUpdate)) &&
737
- !ef.IsVirtual &&
738
- ((!ef.IsPrimaryKey || !autoGeneratedPrimaryKey) || isUpdate) &&
739
- ef.Name.toLowerCase().trim() !== 'updatedat' &&
740
- ef.Name.toLowerCase().trim() !== 'createdat' &&
741
- ef.Type.toLowerCase().trim() !== 'uniqueidentifier') {
742
- if (!isFirst)
743
- sOutput += ',\n ';
744
- else
745
- isFirst = false;
746
- sOutput += `@${ef.CodeName} ${ef.SQLFullType}`;
747
- }
748
- }
749
- return sOutput;
750
- }
751
- function createEntityFieldsInsertString(entity, entityFields, prefix) {
752
- const autoGeneratedPrimaryKey = entity.PrimaryKey.AutoIncrement || entity.PrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier';
753
- let sOutput = '', isFirst = true;
754
- for (let i = 0; i < entityFields.length; ++i) {
755
- const ef = entityFields[i];
756
- if ((!ef.IsPrimaryKey || !autoGeneratedPrimaryKey) &&
757
- ef.IsVirtual === false &&
758
- ef.AllowUpdateAPI &&
759
- ef.AutoIncrement === false &&
760
- ef.Type.trim().toLowerCase() !== 'uniqueidentifier') {
761
- if (!isFirst)
762
- sOutput += ',\n ';
763
- else
764
- isFirst = false;
765
- if (prefix !== '' && (ef.Name.toLowerCase().trim() === 'updatedat' ||
766
- ef.Name.toLowerCase().trim() === 'createdat'))
767
- sOutput += `GETDATE()`;
768
- else {
769
- let sVal = prefix + (prefix !== '' ? ef.CodeName : ef.Name); // if we have a prefix, then we need to use the CodeName, otherwise we use the actual field name
770
- if (!prefix || prefix.length === 0)
771
- sVal = '[' + sVal + ']'; // always put field names in brackets so that if reserved words are being used for field names in a table like "USER" and so on, they still work
772
- sOutput += sVal;
742
+ return `
743
+ ------------------------------------------------------------
744
+ ----- UPDATE PROCEDURE FOR ${entity.BaseTable}
745
+ ------------------------------------------------------------
746
+ DROP PROCEDURE IF EXISTS [${entity.SchemaName}].[${spName}]
747
+ GO
748
+
749
+ CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
750
+ ${efParamString}
751
+ AS
752
+ BEGIN
753
+ SET NOCOUNT ON;
754
+ UPDATE
755
+ [${entity.SchemaName}].[${entity.BaseTable}]
756
+ SET
757
+ ${this.createEntityFieldsUpdateString(entity.Fields)}
758
+ WHERE
759
+ [${entity.PrimaryKey.Name}] = @${entity.PrimaryKey.Name}
760
+
761
+ -- return the updated record so the caller can see the updated values and any calculated fields
762
+ ${selectInsertedRecord}
763
+ END
764
+ GO${permissions}
765
+ `;
766
+ }
767
+ createEntityFieldsParamString(entityFields, isUpdate) {
768
+ let sOutput = '', isFirst = true;
769
+ for (let i = 0; i < entityFields.length; ++i) {
770
+ const ef = entityFields[i];
771
+ const autoGeneratedPrimaryKey = ef.AutoIncrement || ef.Type.toLowerCase().trim() === 'uniqueidentifier';
772
+ if ((ef.AllowUpdateAPI || (ef.IsPrimaryKey && isUpdate)) &&
773
+ !ef.IsVirtual &&
774
+ ((!ef.IsPrimaryKey || !autoGeneratedPrimaryKey) || isUpdate) &&
775
+ ef.Name.toLowerCase().trim() !== 'updatedat' &&
776
+ ef.Name.toLowerCase().trim() !== 'createdat' &&
777
+ ef.Type.toLowerCase().trim() !== 'uniqueidentifier') {
778
+ if (!isFirst)
779
+ sOutput += ',\n ';
780
+ else
781
+ isFirst = false;
782
+ sOutput += `@${ef.CodeName} ${ef.SQLFullType}`;
773
783
  }
774
784
  }
775
- }
776
- return sOutput;
777
- }
778
- function createEntityFieldsUpdateString(entityFields) {
779
- let sOutput = '', isFirst = true;
780
- for (let i = 0; i < entityFields.length; ++i) {
781
- const ef = entityFields[i];
782
- if (!ef.IsPrimaryKey &&
783
- ef.IsVirtual === false &&
784
- ef.AllowUpdateAPI &&
785
- ef.AutoIncrement === false &&
786
- ef.Name.toLowerCase().trim() !== 'createdat' &&
787
- ef.Name.toLowerCase().trim() !== 'updatedat' &&
788
- ef.Type.toLowerCase().trim() !== 'uniqueidentifier') {
789
- if (!isFirst)
790
- sOutput += ',\n ';
791
- else
792
- isFirst = false;
793
- sOutput += `[${ef.Name}] = @${ef.CodeName}`; // always put field names in brackets for field names that have spaces or use reserved words. Also, we use CodeName for the param name, which is the field name unless it has spaces
785
+ return sOutput;
786
+ }
787
+ createEntityFieldsInsertString(entity, entityFields, prefix) {
788
+ const autoGeneratedPrimaryKey = entity.PrimaryKey.AutoIncrement || entity.PrimaryKey.Type.toLowerCase().trim() === 'uniqueidentifier';
789
+ let sOutput = '', isFirst = true;
790
+ for (let i = 0; i < entityFields.length; ++i) {
791
+ const ef = entityFields[i];
792
+ if ((!ef.IsPrimaryKey || !autoGeneratedPrimaryKey) &&
793
+ ef.IsVirtual === false &&
794
+ ef.AllowUpdateAPI &&
795
+ ef.AutoIncrement === false &&
796
+ ef.Type.trim().toLowerCase() !== 'uniqueidentifier') {
797
+ if (!isFirst)
798
+ sOutput += ',\n ';
799
+ else
800
+ isFirst = false;
801
+ if (prefix !== '' && (ef.Name.toLowerCase().trim() === 'updatedat' ||
802
+ ef.Name.toLowerCase().trim() === 'createdat'))
803
+ sOutput += `GETDATE()`;
804
+ else {
805
+ let sVal = prefix + (prefix !== '' ? ef.CodeName : ef.Name); // if we have a prefix, then we need to use the CodeName, otherwise we use the actual field name
806
+ if (!prefix || prefix.length === 0)
807
+ sVal = '[' + sVal + ']'; // always put field names in brackets so that if reserved words are being used for field names in a table like "USER" and so on, they still work
808
+ sOutput += sVal;
809
+ }
810
+ }
794
811
  }
795
- else if (ef.Name.trim().toLowerCase() === 'updatedat') {
796
- if (!isFirst)
797
- sOutput += ',\n ';
798
- else
799
- isFirst = false;
800
- sOutput += `[${ef.Name}] = GETDATE()`;
801
- }
802
- }
803
- return sOutput;
804
- }
805
- function generateSPDelete(entity) {
806
- const spName = entity.spDelete ? entity.spDelete : `spDelete${entity.ClassName}`;
807
- const sCascadeDeletes = generateCascadeDeletes(entity);
808
- const permissions = generateSPPermissions(entity, spName, exports.SPType.Delete);
809
- let sVariables = '';
810
- let sSelect = '';
811
- for (let k of entity.PrimaryKeys) {
812
- if (sVariables !== '')
813
- sVariables += ', ';
814
- sVariables += `@${k.CodeName} ${k.SQLFullType}`;
815
- if (sSelect !== '')
816
- sSelect += ', ';
817
- sSelect += `@${k.CodeName} AS [${k.CodeName}]`;
818
- }
819
- return `
820
- ------------------------------------------------------------
821
- ----- DELETE PROCEDURE FOR ${entity.BaseTable}
822
- ------------------------------------------------------------
823
- DROP PROCEDURE IF EXISTS [${entity.SchemaName}].[${spName}]
824
- GO
825
-
826
- CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
827
- ${sVariables}
828
- AS
829
- BEGIN
830
- SET NOCOUNT ON;${sCascadeDeletes}
831
-
832
- DELETE FROM
833
- [${entity.SchemaName}].[${entity.BaseTable}]
834
- WHERE
835
- [${entity.PrimaryKey.Name}] = @${entity.PrimaryKey.CodeName}
836
-
837
- SELECT ${sSelect} -- Return the primary key to indicate we successfully deleted the record
838
- END
839
- GO${permissions}
840
- `;
841
- }
842
- function generateCascadeDeletes(entity) {
843
- let sOutput = '';
844
- if (entity.CascadeDeletes) {
845
- const md = new core_1.Metadata();
846
- // we need to find all of the fields in other entities that are foreign keys to this entity
847
- // and generate DELETE statements for those tables
848
- for (let i = 0; i < md.Entities.length; ++i) {
849
- const e = md.Entities[i];
850
- for (let j = 0; j < e.Fields.length; ++j) {
851
- const ef = e.Fields[j];
852
- if (ef.RelatedEntityID === entity.ID && ef.IsVirtual === false) {
853
- let sql = '';
854
- if (ef.AllowsNull === false) {
855
- // we have a non-virtual field that is a foreign key to this entity
856
- // and only those that are non-null. If they allow null we want to UPDATE those rows to be null
857
- // so we need to generate a DELETE statement for that table
858
- sql = `
859
- -- Cascade delete from ${e.BaseTable}
860
- DELETE FROM
861
- [${e.SchemaName}].[${e.BaseTable}]
862
- WHERE
812
+ return sOutput;
813
+ }
814
+ createEntityFieldsUpdateString(entityFields) {
815
+ let sOutput = '', isFirst = true;
816
+ for (let i = 0; i < entityFields.length; ++i) {
817
+ const ef = entityFields[i];
818
+ if (!ef.IsPrimaryKey &&
819
+ ef.IsVirtual === false &&
820
+ ef.AllowUpdateAPI &&
821
+ ef.AutoIncrement === false &&
822
+ ef.Name.toLowerCase().trim() !== 'createdat' &&
823
+ ef.Name.toLowerCase().trim() !== 'updatedat' &&
824
+ ef.Type.toLowerCase().trim() !== 'uniqueidentifier') {
825
+ if (!isFirst)
826
+ sOutput += ',\n ';
827
+ else
828
+ isFirst = false;
829
+ sOutput += `[${ef.Name}] = @${ef.CodeName}`; // always put field names in brackets for field names that have spaces or use reserved words. Also, we use CodeName for the param name, which is the field name unless it has spaces
830
+ }
831
+ else if (ef.Name.trim().toLowerCase() === 'updatedat') {
832
+ if (!isFirst)
833
+ sOutput += ',\n ';
834
+ else
835
+ isFirst = false;
836
+ sOutput += `[${ef.Name}] = GETDATE()`;
837
+ }
838
+ }
839
+ return sOutput;
840
+ }
841
+ generateSPDelete(entity) {
842
+ const spName = entity.spDelete ? entity.spDelete : `spDelete${entity.ClassName}`;
843
+ const sCascadeDeletes = this.generateCascadeDeletes(entity);
844
+ const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Delete);
845
+ let sVariables = '';
846
+ let sSelect = '';
847
+ for (let k of entity.PrimaryKeys) {
848
+ if (sVariables !== '')
849
+ sVariables += ', ';
850
+ sVariables += `@${k.CodeName} ${k.SQLFullType}`;
851
+ if (sSelect !== '')
852
+ sSelect += ', ';
853
+ sSelect += `@${k.CodeName} AS [${k.CodeName}]`;
854
+ }
855
+ return `
856
+ ------------------------------------------------------------
857
+ ----- DELETE PROCEDURE FOR ${entity.BaseTable}
858
+ ------------------------------------------------------------
859
+ DROP PROCEDURE IF EXISTS [${entity.SchemaName}].[${spName}]
860
+ GO
861
+
862
+ CREATE PROCEDURE [${entity.SchemaName}].[${spName}]
863
+ ${sVariables}
864
+ AS
865
+ BEGIN
866
+ SET NOCOUNT ON;${sCascadeDeletes}
867
+
868
+ DELETE FROM
869
+ [${entity.SchemaName}].[${entity.BaseTable}]
870
+ WHERE
871
+ [${entity.PrimaryKey.Name}] = @${entity.PrimaryKey.CodeName}
872
+
873
+ SELECT ${sSelect} -- Return the primary key to indicate we successfully deleted the record
874
+ END
875
+ GO${permissions}
876
+ `;
877
+ }
878
+ generateCascadeDeletes(entity) {
879
+ let sOutput = '';
880
+ if (entity.CascadeDeletes) {
881
+ const md = new core_1.Metadata();
882
+ // we need to find all of the fields in other entities that are foreign keys to this entity
883
+ // and generate DELETE statements for those tables
884
+ for (let i = 0; i < md.Entities.length; ++i) {
885
+ const e = md.Entities[i];
886
+ for (let j = 0; j < e.Fields.length; ++j) {
887
+ const ef = e.Fields[j];
888
+ if (ef.RelatedEntityID === entity.ID && ef.IsVirtual === false) {
889
+ let sql = '';
890
+ if (ef.AllowsNull === false) {
891
+ // we have a non-virtual field that is a foreign key to this entity
892
+ // and only those that are non-null. If they allow null we want to UPDATE those rows to be null
893
+ // so we need to generate a DELETE statement for that table
894
+ sql = `
895
+ -- Cascade delete from ${e.BaseTable}
896
+ DELETE FROM
897
+ [${e.SchemaName}].[${e.BaseTable}]
898
+ WHERE
863
899
  [${ef.CodeName}] = @${entity.PrimaryKey.CodeName}`;
864
- }
865
- else {
866
- // we have a non-virtual field that is a foreign key to this entity
867
- // and this field ALLOWS nulls, which means we don't delete the rows, we just update them to be null
868
- // so they don't have an orphaned foreign key
869
- sql = `
870
- -- Cascade update on ${e.BaseTable} - set FK to null before deleting rows in ${entity.BaseTable}
871
- UPDATE
872
- [${e.SchemaName}].[${e.BaseTable}]
873
- SET
874
- [${ef.CodeName}] = NULL
875
- WHERE
900
+ }
901
+ else {
902
+ // we have a non-virtual field that is a foreign key to this entity
903
+ // and this field ALLOWS nulls, which means we don't delete the rows, we just update them to be null
904
+ // so they don't have an orphaned foreign key
905
+ sql = `
906
+ -- Cascade update on ${e.BaseTable} - set FK to null before deleting rows in ${entity.BaseTable}
907
+ UPDATE
908
+ [${e.SchemaName}].[${e.BaseTable}]
909
+ SET
910
+ [${ef.CodeName}] = NULL
911
+ WHERE
876
912
  [${ef.CodeName}] = @${entity.PrimaryKey.CodeName}`;
913
+ }
914
+ if (sOutput !== '')
915
+ sOutput += '\n ';
916
+ sOutput += sql;
877
917
  }
878
- if (sOutput !== '')
879
- sOutput += '\n ';
880
- sOutput += sql;
881
918
  }
882
919
  }
883
920
  }
921
+ return sOutput === '' ? '' : `${sOutput}\n `;
884
922
  }
885
- return sOutput === '' ? '' : `${sOutput}\n `;
886
- }
923
+ };
924
+ exports.SQLCodeGenBase = SQLCodeGenBase;
925
+ exports.SQLCodeGenBase = SQLCodeGenBase = __decorate([
926
+ (0, global_1.RegisterClass)(SQLCodeGenBase)
927
+ ], SQLCodeGenBase);
887
928
  //# sourceMappingURL=sql_codegen.js.map