-
Notifications
You must be signed in to change notification settings - Fork 27
/
dbh.js
executable file
·281 lines (236 loc) · 9.99 KB
/
dbh.js
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
const DBH_CONSTANTS = require('./dbh-constants');
const jhipsterConstants = require('generator-jhipster/generators/generator-constants.js');
const jhipsterCore = require('jhipster-core');
const jhipsterModuleSubgenerator = require('generator-jhipster/generators/generator-base.js');
const pluralize = require('pluralize');
const fs = require('fs');
/**
* return the missing property jhipsterVar.jhipsterConfig for unit tests
* @param path : path to .yo-rc.json
*/
const getAppConfig = configFilePath => new Promise((resolve, reject) => {
// if file exists, return it as a JSON object
if (fs.existsSync(configFilePath)) {
fs.readFile(configFilePath, 'utf8', (err, data) => {
if (err) {
reject(new Error(`getAppConfig: fs.readFile error.\nPath was ${configFilePath}\n${err}`));
}
// TODO : search if this statement should be in a try/catch or not
const appConfigToJson = JSON.parse(data);
// handle undefined object
if (appConfigToJson) {
resolve(appConfigToJson);
} else {
reject(new Error(`getAppConfig: output error.\nOutput type: ${typeof appConfigToJson}, value:\n${appConfigToJson}`));
}
});
} else {
reject(new Error(`getAppConfig: file ${configFilePath} not found`));
}
});
/**
* Get a polyfill for the jhipsterVar and jhipsterFunc properties gone missing when testing
* because of a [yeoman-test](https://github.com/bastienmichaux/generator-jhipster-db-helper/issues/19) issue.
*
* @param {string} appConfigPath - path to the current .yo-rc.json application file
*/
const postAppPolyfill = (appConfigPath) => {
// stop if file not found
if (!fs.existsSync(appConfigPath)) {
throw new Error(`_getPolyfill: File ${appConfigPath} not found`);
}
// else return a promise holding the polyfill
return getAppConfig(appConfigPath)
.then(
(onResolve) => {
const conf = onResolve['generator-jhipster'];
const poly = {};
// @todo: defensive programming with these properties (hasOwnProperty ? throw ?)
// jhipsterVar polyfill :
poly.baseName = conf.baseName;
poly.packageName = conf.packageName;
poly.angularAppName = conf.angularAppName || null; // handle an undefined value (JSON properties can't be undefined)
poly.clientFramework = conf.clientFramework;
poly.clientPackageManager = conf.clientPackageManager;
poly.buildTool = conf.buildTool;
// jhipsterFunc polyfill :
poly.registerModule = jhipsterModuleSubgenerator.prototype.registerModule;
poly.updateEntityConfig = jhipsterModuleSubgenerator.prototype.updateEntityConfig;
// @todo : handle this.options.testMode ?
return poly;
},
(onError) => {
throw new Error(onError);
}
);
};
/**
* get a polyfill for the jhipsterVar and jhipsterFunc properties gone missing when testing
* because of a [yeoman-test](https://github.com/bastienmichaux/generator-jhipster-db-helper/issues/19) issue
*
* @param {string} appConfigPath - path to the current .yo-rc.json application file
*/
const postEntityPolyfill = (appConfigPath) => {
// stop if file not found
if (!fs.existsSync(appConfigPath)) {
throw new Error(`_getPolyfill: File ${appConfigPath} not found`);
}
// else return a promise holding the polyfill
return getAppConfig(appConfigPath)
.then(
(onResolve) => {
const conf = onResolve['generator-jhipster'];
const poly = {};
// jhipsterVar polyfill :
poly.jhipsterConfig = conf;
poly.javaDir = `${jhipsterConstants.SERVER_MAIN_SRC_DIR + conf.packageFolder}/`;
poly.resourceDir = jhipsterConstants.SERVER_MAIN_RES_DIR;
poly.replaceContent = jhipsterModuleSubgenerator.prototype.replaceContent;
poly.updateEntityConfig = jhipsterModuleSubgenerator.prototype.updateEntityConfig;
return poly;
},
(onError) => {
console.error(onError);
}
);
};
/**
* get hibernate SnakeCase in JHipster preferred style.
*
* @param {string} value - table column name or table name string
* @see org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
*/
const hibernateSnakeCase = (value) => {
let res = '';
if (value) {
value = value.replace('.', '_');
res = value[0];
for (let i = 1, len = value.length - 1; i < len; i++) {
if (value[i - 1] !== value[i - 1].toUpperCase() &&
value[i] !== value[i].toLowerCase() &&
value[i + 1] !== value[i + 1].toUpperCase()
) {
res += `_${value[i]}`;
} else {
res += value[i];
}
}
res += value[value.length - 1];
res = res.toLowerCase();
}
return res;
};
/** Check that the build tool isn't unknown */
const isValidBuildTool = buildTool => DBH_CONSTANTS.buildTools.includes(buildTool);
// We need the two following functions to be able to find JHipster generated values and match them in a search and replace.
/**
* @param create a column id name from the relationship name
*/
const getColumnIdName = name => `${hibernateSnakeCase(name)}_id`;
/**
* @param create a column id name from the relationship name for a to-many relationship (either one-to-many or many-to-many)
*/
const getPluralColumnIdName = name => getColumnIdName(pluralize(name));
/**
* From the JHipster files where the original Spring naming strategies can be found,
* remove the files that don't exist, depending on the current application build tool (Maven or Gradle)
* - if the app uses Maven, remove the Gradle file(s)
* - if the app uses Gradle, remove the Maven file(s)
*
* @returns The returned array holds the configuration files where references to the naming strategies can be found
*/
const getFilesWithNamingStrategy = (buildTool) => {
// if build tool is valid, return the files with naming strategy,
// including those specific to the application build tool
const baseFiles = DBH_CONSTANTS.filesWithNamingStrategy.base;
const result = baseFiles.concat(DBH_CONSTANTS.filesWithNamingStrategy[buildTool]);
return result;
};
/**
* Check if these relationships add constraints.
* Typically, a one-to-many relationship doesn't add a constraint to the entity on the one side
* but it does on the many side.
*
* @param relationships - an array of relationship to check
* @returns true if and only if it contains at least one relationship with a constraint, false otherwise
* TODO complex condition could be rewritten as a function
*/
const hasConstraints = (relationships) => {
if (!Array.isArray((relationships))) {
throw new TypeError(`hasConstraints: 'relationships' parameter must be an array, was ${typeof relationships}`);
}
let res = false;
relationships.forEach((relationship) => {
if (
(relationship.relationshipType === 'many-to-one') ||
(relationship.relationshipType === 'one-to-one' && relationship.ownerSide) ||
(relationship.relationshipType === 'many-to-many' && relationship.ownerSide)
) {
res = true;
}
});
return res;
};
/**
* Assert parameter is a non-empty string
*
* Note : Currently unused, remove if no uses in the future
*/
const isNotEmptyString = x => typeof x === 'string' && x !== '';
/**
* Duplicate of a JHipster function where we have replaced how the path is handled, because we use absolute paths
*/
const replaceContent = (absolutePath, pattern, content, regex) => {
if (!fs.existsSync(absolutePath)) {
throw new Error(`_replaceContent: file not found.\n${absolutePath}`);
}
const re = regex ? new RegExp(pattern, 'g') : pattern;
let body = fs.readFileSync(absolutePath);
body = `${body}`;
body = body.replace(re, content);
fs.writeFileSync(absolutePath, body); // fs.createWriteStream is recommended
};
/** Validate user input when asking for a SQL column name */
const validateColumnName = (input, dbType) => {
if (input === '') {
return 'Your column name cannot be empty';
} else if (!/^([a-zA-Z0-9_]*)$/.test(input)) {
return 'Your column name cannot contain special characters';
} else if (dbType === 'oracle' && input.length > DBH_CONSTANTS.oracleLimitations.tableNameHardMaxLength) {
return 'Your column name is too long for Oracle, try a shorter name';
}
return true;
};
/**
* Validate user input when asking for a SQL table name
* This function closely follows JHipster's own validateTableName function
* (in generator-jhipster/generators/entity/index.js)
*/
const validateTableName = (input, dbType) => {
if (input === '') {
return 'The table name cannot be empty';
} else if (!/^([a-zA-Z0-9_]*)$/.test(input)) {
return 'The table name cannot contain special characters';
} else if (dbType === 'oracle' && input.length > DBH_CONSTANTS.oracleLimitations.tableNameHardMaxLength) {
return 'The table name is too long for Oracle, try a shorter name';
} else if (dbType === 'oracle' && input.length > DBH_CONSTANTS.oracleLimitations.tableNameSoftMaxLength) {
return 'The table name is long for Oracle, long table names can cause issues when used to create constraint names and join table names';
} else if (jhipsterCore.isReservedTableName(input, dbType)) {
return `'${input}' is a ${dbType} reserved keyword.`;
}
return true;
};
module.exports = {
getAppConfig,
getColumnIdName,
getFilesWithNamingStrategy,
getPluralColumnIdName,
hasConstraints,
isNotEmptyString,
isValidBuildTool,
postAppPolyfill,
postEntityPolyfill,
replaceContent,
validateColumnName,
validateTableName
};