@depup/typeorm-extension 3.9.0-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.MD +674 -0
- package/README.md +34 -0
- package/bin/cli.cjs +275 -0
- package/bin/cli.mjs +273 -0
- package/changes.json +22 -0
- package/dist/cli/commands/database/create.d.ts +27 -0
- package/dist/cli/commands/database/drop.d.ts +23 -0
- package/dist/cli/commands/database/index.d.ts +2 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/seed/create.d.ts +21 -0
- package/dist/cli/commands/seed/index.d.ts +2 -0
- package/dist/cli/commands/seed/run.d.ts +24 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/data-source/find/index.d.ts +2 -0
- package/dist/data-source/find/module.d.ts +3 -0
- package/dist/data-source/find/type.d.ts +22 -0
- package/dist/data-source/index.d.ts +4 -0
- package/dist/data-source/options/index.d.ts +4 -0
- package/dist/data-source/options/module.d.ts +8 -0
- package/dist/data-source/options/singleton.d.ts +4 -0
- package/dist/data-source/options/type.d.ts +26 -0
- package/dist/data-source/options/utils/env.d.ts +4 -0
- package/dist/data-source/options/utils/index.d.ts +2 -0
- package/dist/data-source/options/utils/merge.d.ts +2 -0
- package/dist/data-source/singleton.d.ts +5 -0
- package/dist/data-source/type.d.ts +2 -0
- package/dist/database/driver/cockroachdb.d.ts +4 -0
- package/dist/database/driver/index.d.ts +9 -0
- package/dist/database/driver/mongodb.d.ts +6 -0
- package/dist/database/driver/mssql.d.ts +6 -0
- package/dist/database/driver/mysql.d.ts +7 -0
- package/dist/database/driver/oracle.d.ts +6 -0
- package/dist/database/driver/postgres.d.ts +8 -0
- package/dist/database/driver/sqlite.d.ts +3 -0
- package/dist/database/driver/types.d.ts +20 -0
- package/dist/database/driver/utils/build.d.ts +3 -0
- package/dist/database/driver/utils/character-set.d.ts +2 -0
- package/dist/database/driver/utils/charset.d.ts +2 -0
- package/dist/database/driver/utils/create.d.ts +2 -0
- package/dist/database/driver/utils/index.d.ts +4 -0
- package/dist/database/index.d.ts +3 -0
- package/dist/database/methods/check/index.d.ts +2 -0
- package/dist/database/methods/check/module.d.ts +7 -0
- package/dist/database/methods/check/types.d.ts +51 -0
- package/dist/database/methods/create/index.d.ts +1 -0
- package/dist/database/methods/create/module.d.ts +10 -0
- package/dist/database/methods/drop/index.d.ts +1 -0
- package/dist/database/methods/drop/module.d.ts +10 -0
- package/dist/database/methods/index.d.ts +4 -0
- package/dist/database/methods/type.d.ts +43 -0
- package/dist/database/utils/context.d.ts +3 -0
- package/dist/database/utils/index.d.ts +5 -0
- package/dist/database/utils/migration.d.ts +2 -0
- package/dist/database/utils/query.d.ts +3 -0
- package/dist/database/utils/schema.d.ts +3 -0
- package/dist/database/utils/type.d.ts +32 -0
- package/dist/env/constants.d.ts +68 -0
- package/dist/env/index.d.ts +3 -0
- package/dist/env/module.d.ts +4 -0
- package/dist/env/type.d.ts +36 -0
- package/dist/env/utils.d.ts +3 -0
- package/dist/errors/base.d.ts +2 -0
- package/dist/errors/driver.d.ts +6 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/options.d.ts +7 -0
- package/dist/helpers/entity/error.d.ts +20 -0
- package/dist/helpers/entity/index.d.ts +5 -0
- package/dist/helpers/entity/join-columns.d.ts +16 -0
- package/dist/helpers/entity/metadata.d.ts +10 -0
- package/dist/helpers/entity/property-names.d.ts +11 -0
- package/dist/helpers/entity/uniqueness.d.ts +28 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/index.cjs +3258 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +3136 -0
- package/dist/index.mjs.map +1 -0
- package/dist/query/index.d.ts +4 -0
- package/dist/query/module.d.ts +5 -0
- package/dist/query/parameter/fields/index.d.ts +2 -0
- package/dist/query/parameter/fields/module.d.ts +25 -0
- package/dist/query/parameter/fields/type.d.ts +6 -0
- package/dist/query/parameter/filters/index.d.ts +2 -0
- package/dist/query/parameter/filters/module.d.ts +35 -0
- package/dist/query/parameter/filters/type.d.ts +12 -0
- package/dist/query/parameter/index.d.ts +5 -0
- package/dist/query/parameter/pagination/index.d.ts +2 -0
- package/dist/query/parameter/pagination/module.d.ts +26 -0
- package/dist/query/parameter/pagination/type.d.ts +3 -0
- package/dist/query/parameter/relations/index.d.ts +2 -0
- package/dist/query/parameter/relations/module.d.ts +27 -0
- package/dist/query/parameter/relations/type.d.ts +8 -0
- package/dist/query/parameter/sort/index.d.ts +2 -0
- package/dist/query/parameter/sort/module.d.ts +26 -0
- package/dist/query/parameter/sort/type.d.ts +6 -0
- package/dist/query/type.d.ts +16 -0
- package/dist/query/utils/alias.d.ts +2 -0
- package/dist/query/utils/index.d.ts +3 -0
- package/dist/query/utils/key.d.ts +1 -0
- package/dist/query/utils/option.d.ts +1 -0
- package/dist/seeder/entity.d.ts +42 -0
- package/dist/seeder/executor.d.ts +24 -0
- package/dist/seeder/factory/index.d.ts +4 -0
- package/dist/seeder/factory/manager.d.ts +8 -0
- package/dist/seeder/factory/module.d.ts +17 -0
- package/dist/seeder/factory/type.d.ts +12 -0
- package/dist/seeder/factory/utils.d.ts +7 -0
- package/dist/seeder/index.d.ts +6 -0
- package/dist/seeder/module.d.ts +5 -0
- package/dist/seeder/type.d.ts +44 -0
- package/dist/seeder/utils/file-path.d.ts +7 -0
- package/dist/seeder/utils/index.d.ts +3 -0
- package/dist/seeder/utils/prepare.d.ts +2 -0
- package/dist/seeder/utils/template.d.ts +1 -0
- package/dist/utils/code-transformation/constants.d.ts +4 -0
- package/dist/utils/code-transformation/index.d.ts +2 -0
- package/dist/utils/code-transformation/module.d.ts +3 -0
- package/dist/utils/entity.d.ts +2 -0
- package/dist/utils/file-path.d.ts +9 -0
- package/dist/utils/file-system.d.ts +1 -0
- package/dist/utils/has-property.d.ts +2 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/object.d.ts +2 -0
- package/dist/utils/promise.d.ts +1 -0
- package/dist/utils/separator.d.ts +3 -0
- package/dist/utils/slash.d.ts +2 -0
- package/dist/utils/tsconfig/index.d.ts +2 -0
- package/dist/utils/tsconfig/module.d.ts +2 -0
- package/dist/utils/tsconfig/type.d.ts +13 -0
- package/package.json +134 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3258 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var locter = require('locter');
|
|
4
|
+
var rapiq = require('rapiq');
|
|
5
|
+
var process$1 = require('node:process');
|
|
6
|
+
var typeorm = require('typeorm');
|
|
7
|
+
var path = require('node:path');
|
|
8
|
+
var fs = require('node:fs');
|
|
9
|
+
var consola = require('consola');
|
|
10
|
+
var envix = require('envix');
|
|
11
|
+
var smob = require('smob');
|
|
12
|
+
var DriverUtils = require('typeorm/driver/DriverUtils');
|
|
13
|
+
var DriverFactory = require('typeorm/driver/DriverFactory');
|
|
14
|
+
var pascalCase = require('pascal-case');
|
|
15
|
+
var MigrationGenerateCommand = require('typeorm/commands/MigrationGenerateCommand');
|
|
16
|
+
|
|
17
|
+
class TypeormExtensionError extends Error {
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class DriverError extends TypeormExtensionError {
|
|
21
|
+
constructor(message){
|
|
22
|
+
super(message || 'A database driver related error has occurred.');
|
|
23
|
+
}
|
|
24
|
+
static undeterminable() {
|
|
25
|
+
return new DriverError('The driver could not be determined.');
|
|
26
|
+
}
|
|
27
|
+
static notSupported(driverName) {
|
|
28
|
+
return new DriverError(`The driver ${driverName} is not supported yet.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class OptionsError extends TypeormExtensionError {
|
|
33
|
+
constructor(message){
|
|
34
|
+
super(message || 'A database options related error has occurred');
|
|
35
|
+
}
|
|
36
|
+
static undeterminable() {
|
|
37
|
+
return new OptionsError('The database options could not be determined.');
|
|
38
|
+
}
|
|
39
|
+
static notFound() {
|
|
40
|
+
return new OptionsError('The database options could not be located/loaded.');
|
|
41
|
+
}
|
|
42
|
+
static databaseNotDefined() {
|
|
43
|
+
return new OptionsError('The database name to connect to is not defined.');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var CodeTransformation = /*#__PURE__*/ function(CodeTransformation) {
|
|
48
|
+
CodeTransformation["JUST_IN_TIME"] = "jit";
|
|
49
|
+
CodeTransformation["NONE"] = "none";
|
|
50
|
+
return CodeTransformation;
|
|
51
|
+
}({});
|
|
52
|
+
|
|
53
|
+
function detectCodeTransformation() {
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
if (process$1[Symbol.for('ts-node.register.instance')]) {
|
|
57
|
+
return CodeTransformation.JUST_IN_TIME;
|
|
58
|
+
}
|
|
59
|
+
return CodeTransformation.NONE;
|
|
60
|
+
}
|
|
61
|
+
function isCodeTransformation(input) {
|
|
62
|
+
return detectCodeTransformation() === input;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getEntityName(entity) {
|
|
66
|
+
if (typeof entity === 'function') {
|
|
67
|
+
return entity.name;
|
|
68
|
+
}
|
|
69
|
+
if (typeorm.InstanceChecker.isEntitySchema(entity)) {
|
|
70
|
+
return entity.options.name;
|
|
71
|
+
}
|
|
72
|
+
return new entity().constructor.name;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function canReplaceWindowsSeparator(input) {
|
|
76
|
+
// https://superuser.com/questions/176388/why-does-windows-use-backslashes-for-paths-and-unix-forward-slashes/176395#176395
|
|
77
|
+
if (input.startsWith('\\\\?\\')) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
let characterIndex;
|
|
81
|
+
const specialCharacters = [
|
|
82
|
+
'[',
|
|
83
|
+
'{',
|
|
84
|
+
'(',
|
|
85
|
+
'^',
|
|
86
|
+
'$',
|
|
87
|
+
'.',
|
|
88
|
+
'|',
|
|
89
|
+
'?',
|
|
90
|
+
'*',
|
|
91
|
+
'+'
|
|
92
|
+
];
|
|
93
|
+
for(let i = 0; i < specialCharacters.length; i++){
|
|
94
|
+
characterIndex = input.indexOf(specialCharacters[i]);
|
|
95
|
+
if (characterIndex !== -1) {
|
|
96
|
+
// special character is prefixed with \, no transformation allowed
|
|
97
|
+
if (characterIndex !== 0 && input[characterIndex - 1] === '\\') {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
function replaceWindowSeparator(input) {
|
|
105
|
+
return input.replace(/\\/g, '/');
|
|
106
|
+
}
|
|
107
|
+
function safeReplaceWindowsSeparator(input) {
|
|
108
|
+
if (input.indexOf('\\') === -1 || !canReplaceWindowsSeparator(input)) {
|
|
109
|
+
return input;
|
|
110
|
+
}
|
|
111
|
+
return replaceWindowSeparator(input);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const TRAILING_SLASH_RE = /\/$|\/\?/;
|
|
115
|
+
function hasTrailingSlash(input = '', queryParams = false) {
|
|
116
|
+
if (!queryParams) {
|
|
117
|
+
return input.endsWith('/');
|
|
118
|
+
}
|
|
119
|
+
return TRAILING_SLASH_RE.test(input);
|
|
120
|
+
}
|
|
121
|
+
function withoutTrailingSlash(input = '', queryParams = false) {
|
|
122
|
+
if (!queryParams) {
|
|
123
|
+
return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || '/';
|
|
124
|
+
}
|
|
125
|
+
if (!hasTrailingSlash(input, true)) {
|
|
126
|
+
return input || '/';
|
|
127
|
+
}
|
|
128
|
+
const [s0, ...s] = input.split('?');
|
|
129
|
+
return (s0.slice(0, -1) || '/') + (s.length ? `?${s.join('?')}` : '');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function readTSConfig(input) {
|
|
133
|
+
input = input || process.cwd();
|
|
134
|
+
input = path.isAbsolute(input) ? input : path.resolve(process.cwd(), input);
|
|
135
|
+
const filePath = input.indexOf('.json') === -1 ? path.join(input, 'tsconfig.json') : input;
|
|
136
|
+
try {
|
|
137
|
+
const tsConfig = await locter.load(filePath);
|
|
138
|
+
if (locter.isObject(tsConfig)) {
|
|
139
|
+
return tsConfig;
|
|
140
|
+
}
|
|
141
|
+
} catch (e) {
|
|
142
|
+
// don't do anything ;)
|
|
143
|
+
}
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const stripLeadingModifier = (text)=>{
|
|
148
|
+
if (text.startsWith('./')) {
|
|
149
|
+
text = text.substring(2);
|
|
150
|
+
}
|
|
151
|
+
return text;
|
|
152
|
+
};
|
|
153
|
+
function transformFilePath(input, dist, src) {
|
|
154
|
+
let separator = path.sep;
|
|
155
|
+
const windowsSeparatorReplaceable = canReplaceWindowsSeparator(input);
|
|
156
|
+
if (windowsSeparatorReplaceable) {
|
|
157
|
+
separator = '/';
|
|
158
|
+
input = replaceWindowSeparator(input);
|
|
159
|
+
}
|
|
160
|
+
let base = input;
|
|
161
|
+
let baseIndex = input.lastIndexOf(separator);
|
|
162
|
+
if (baseIndex !== -1) {
|
|
163
|
+
base = base.substring(baseIndex + 1);
|
|
164
|
+
}
|
|
165
|
+
if (src) {
|
|
166
|
+
if (windowsSeparatorReplaceable) {
|
|
167
|
+
src = replaceWindowSeparator(src);
|
|
168
|
+
}
|
|
169
|
+
src = withoutTrailingSlash(stripLeadingModifier(src));
|
|
170
|
+
}
|
|
171
|
+
src = src || 'src';
|
|
172
|
+
if (dist) {
|
|
173
|
+
if (windowsSeparatorReplaceable) {
|
|
174
|
+
dist = replaceWindowSeparator(dist);
|
|
175
|
+
}
|
|
176
|
+
dist = withoutTrailingSlash(stripLeadingModifier(dist));
|
|
177
|
+
}
|
|
178
|
+
dist = dist || 'dist';
|
|
179
|
+
if (input.indexOf(src) !== -1 && input.indexOf(dist) === -1) {
|
|
180
|
+
const lastIndex = input.lastIndexOf(src);
|
|
181
|
+
const prevCharacter = input.substring(lastIndex - 1, lastIndex);
|
|
182
|
+
if (!prevCharacter || prevCharacter === separator) {
|
|
183
|
+
input = input.substring(0, lastIndex) + dist + input.substring(lastIndex + src.length);
|
|
184
|
+
baseIndex = input.lastIndexOf(separator);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// if the path already contains a js file extension, we are done
|
|
188
|
+
const jsExtensions = [
|
|
189
|
+
'js',
|
|
190
|
+
'cjs',
|
|
191
|
+
'mjs'
|
|
192
|
+
];
|
|
193
|
+
for(let i = 0; i < jsExtensions.length; i++){
|
|
194
|
+
if (base.indexOf(jsExtensions[i]) !== -1) {
|
|
195
|
+
return input;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const tsExtensions = [
|
|
199
|
+
'ts',
|
|
200
|
+
'cts',
|
|
201
|
+
'mts'
|
|
202
|
+
];
|
|
203
|
+
for(let i = 0; i < tsExtensions.length; i++){
|
|
204
|
+
const regex = new RegExp(`(\\.${tsExtensions[i]}|${tsExtensions[i]})`, 'g');
|
|
205
|
+
let matchesSum;
|
|
206
|
+
const matches = base.match(regex);
|
|
207
|
+
if (Array.isArray(matches)) {
|
|
208
|
+
matchesSum = matches.length;
|
|
209
|
+
}
|
|
210
|
+
let matchesCounter = 0;
|
|
211
|
+
const bracketIndex = base.lastIndexOf('{');
|
|
212
|
+
base = base.replace(regex, (...args)=>{
|
|
213
|
+
matchesCounter++;
|
|
214
|
+
// if the file extension name comes after the last bracket index,
|
|
215
|
+
// we can be pretty sure that the extension name is not part of a filename
|
|
216
|
+
if (args[2] >= bracketIndex && bracketIndex !== -1 || bracketIndex === -1 && matchesCounter === matchesSum) {
|
|
217
|
+
return args[0].startsWith('.') ? `.${jsExtensions[i]}` : jsExtensions[i];
|
|
218
|
+
}
|
|
219
|
+
return args[0];
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (baseIndex !== -1) {
|
|
223
|
+
base = input.substring(0, baseIndex + 1) + base;
|
|
224
|
+
}
|
|
225
|
+
return stripLeadingModifier(base);
|
|
226
|
+
}
|
|
227
|
+
async function adjustFilePath(input, tsconfig) {
|
|
228
|
+
if (isCodeTransformation(CodeTransformation.JUST_IN_TIME)) {
|
|
229
|
+
return input;
|
|
230
|
+
}
|
|
231
|
+
if (!locter.isObject(tsconfig)) {
|
|
232
|
+
tsconfig = await readTSConfig(tsconfig);
|
|
233
|
+
}
|
|
234
|
+
const { compilerOptions } = tsconfig;
|
|
235
|
+
if (typeof input === 'string') {
|
|
236
|
+
return transformFilePath(input, compilerOptions?.outDir);
|
|
237
|
+
}
|
|
238
|
+
if (Array.isArray(input)) {
|
|
239
|
+
for(let i = 0; i < input.length; i++){
|
|
240
|
+
if (typeof input[i] === 'string') {
|
|
241
|
+
input[i] = transformFilePath(input[i], compilerOptions?.outDir);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return input;
|
|
246
|
+
}
|
|
247
|
+
async function adjustFilePaths(input, keys, tsconfig) {
|
|
248
|
+
if (isCodeTransformation(CodeTransformation.JUST_IN_TIME)) {
|
|
249
|
+
return input;
|
|
250
|
+
}
|
|
251
|
+
if (!locter.isObject(tsconfig)) {
|
|
252
|
+
tsconfig = await readTSConfig(tsconfig);
|
|
253
|
+
}
|
|
254
|
+
keys = keys || Object.keys(input);
|
|
255
|
+
for(let i = 0; i < keys.length; i++){
|
|
256
|
+
input[keys[i]] = await adjustFilePath(input[keys[i]], tsconfig);
|
|
257
|
+
}
|
|
258
|
+
return input;
|
|
259
|
+
}
|
|
260
|
+
function resolveFilePath(filePath, root) {
|
|
261
|
+
if (path.isAbsolute(filePath)) {
|
|
262
|
+
return filePath;
|
|
263
|
+
}
|
|
264
|
+
return filePath.startsWith('/') ? filePath : path.resolve(root || process.cwd(), filePath);
|
|
265
|
+
}
|
|
266
|
+
function parseFilePath(filePath, root) {
|
|
267
|
+
const fullPath = resolveFilePath(filePath, root);
|
|
268
|
+
const directory = path.dirname(fullPath);
|
|
269
|
+
const name = path.basename(fullPath);
|
|
270
|
+
return {
|
|
271
|
+
directory,
|
|
272
|
+
name
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function isDirectory(input) {
|
|
277
|
+
try {
|
|
278
|
+
const stat = await fs.promises.stat(input);
|
|
279
|
+
return stat.isDirectory();
|
|
280
|
+
} catch (e) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function hasOwnProperty(obj, prop) {
|
|
286
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
287
|
+
}
|
|
288
|
+
function hasStringProperty(obj, prop) {
|
|
289
|
+
return hasOwnProperty(obj, prop) && typeof obj[prop] === 'string';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function pickRecord(data, keys) {
|
|
293
|
+
const output = {};
|
|
294
|
+
for(let i = 0; i < keys.length; i++){
|
|
295
|
+
output[keys[i]] = data[keys[i]];
|
|
296
|
+
}
|
|
297
|
+
return output;
|
|
298
|
+
}
|
|
299
|
+
function extendObject(target, source) {
|
|
300
|
+
const keys = Object.keys(source);
|
|
301
|
+
for(let i = 0; i < keys.length; i++){
|
|
302
|
+
target[keys[i]] = source[keys[i]];
|
|
303
|
+
}
|
|
304
|
+
return target;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function isPromise(p) {
|
|
308
|
+
return locter.isObject(p) && (p instanceof Promise || // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
309
|
+
// @ts-ignore
|
|
310
|
+
typeof p.then === 'function');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function getAliasForPath(items, path) {
|
|
314
|
+
if (typeof path === 'undefined' || typeof items === 'undefined') {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
for(let i = 0; i < items.length; i++){
|
|
318
|
+
if (items[i].key === path) {
|
|
319
|
+
return items[i].value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function buildKeyWithPrefix(name, prefix) {
|
|
326
|
+
if (prefix) {
|
|
327
|
+
return `${prefix}.${name}`;
|
|
328
|
+
}
|
|
329
|
+
return name;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function isQueryOptionDefined(input, option) {
|
|
333
|
+
if (typeof input === 'boolean') {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
const options = Array.isArray(option) ? option : [
|
|
337
|
+
option
|
|
338
|
+
];
|
|
339
|
+
for(let i = 0; i < options.length; i++){
|
|
340
|
+
if (hasOwnProperty(input, options[i])) {
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Apply parsed fields parameter data on the db query.
|
|
349
|
+
*
|
|
350
|
+
* @param query
|
|
351
|
+
* @param data
|
|
352
|
+
*/ /* istanbul ignore next */ function applyQueryFieldsParseOutput(query, data, options = {}) {
|
|
353
|
+
if (data.length === 0) {
|
|
354
|
+
return data;
|
|
355
|
+
}
|
|
356
|
+
query.select(data.map((field)=>{
|
|
357
|
+
const alias = getAliasForPath(options.relations, field.path) || options.defaultAlias || options.defaultPath;
|
|
358
|
+
return buildKeyWithPrefix(field.key, alias);
|
|
359
|
+
}));
|
|
360
|
+
return data;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Apply raw fields parameter data on the db query.
|
|
364
|
+
*
|
|
365
|
+
* @param query
|
|
366
|
+
* @param data
|
|
367
|
+
* @param options
|
|
368
|
+
*/ function applyQueryFields(query, data, options) {
|
|
369
|
+
options = options || {};
|
|
370
|
+
if (options.defaultAlias) {
|
|
371
|
+
options.defaultPath = options.defaultAlias;
|
|
372
|
+
}
|
|
373
|
+
return applyQueryFieldsParseOutput(query, rapiq.parseQueryFields(data, options), options);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Apply raw fields parameter data on the db query.
|
|
377
|
+
*
|
|
378
|
+
* @param query
|
|
379
|
+
* @param data
|
|
380
|
+
* @param options
|
|
381
|
+
*/ function applyFields(query, data, options) {
|
|
382
|
+
return applyQueryFields(query, data, options);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// --------------------------------------------------
|
|
386
|
+
function transformParsedFilters(data, options = {}) {
|
|
387
|
+
const items = [];
|
|
388
|
+
for(let i = 0; i < data.length; i++){
|
|
389
|
+
const alias = getAliasForPath(options.relations, data[i].path) || options.defaultAlias || options.defaultPath;
|
|
390
|
+
const fullKey = buildKeyWithPrefix(data[i].key, alias);
|
|
391
|
+
const filter = data[i];
|
|
392
|
+
const statement = [
|
|
393
|
+
fullKey
|
|
394
|
+
];
|
|
395
|
+
let bindingKey;
|
|
396
|
+
if (options.bindingKey) {
|
|
397
|
+
bindingKey = options.bindingKey(fullKey).replace('.', '_');
|
|
398
|
+
} else {
|
|
399
|
+
bindingKey = `filter_${fullKey.replace('.', '_')}`;
|
|
400
|
+
}
|
|
401
|
+
if (filter.value === null || typeof filter.value === 'undefined') {
|
|
402
|
+
statement.push('IS');
|
|
403
|
+
if (filter.operator === rapiq.FilterComparisonOperator.NOT_EQUAL) {
|
|
404
|
+
statement.push('NOT');
|
|
405
|
+
}
|
|
406
|
+
statement.push('NULL');
|
|
407
|
+
items.push({
|
|
408
|
+
statement: statement.join(' '),
|
|
409
|
+
binding: {}
|
|
410
|
+
});
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
switch(filter.operator){
|
|
414
|
+
case rapiq.FilterComparisonOperator.EQUAL:
|
|
415
|
+
case rapiq.FilterComparisonOperator.NOT_EQUAL:
|
|
416
|
+
{
|
|
417
|
+
if (filter.operator === rapiq.FilterComparisonOperator.EQUAL) {
|
|
418
|
+
statement.push('=');
|
|
419
|
+
} else {
|
|
420
|
+
statement.push('!=');
|
|
421
|
+
}
|
|
422
|
+
statement.push(`:${bindingKey}`);
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case rapiq.FilterComparisonOperator.LIKE:
|
|
426
|
+
case rapiq.FilterComparisonOperator.NOT_LIKE:
|
|
427
|
+
{
|
|
428
|
+
if (filter.operator === rapiq.FilterComparisonOperator.NOT_LIKE) {
|
|
429
|
+
statement.push('NOT');
|
|
430
|
+
}
|
|
431
|
+
statement.push('LIKE');
|
|
432
|
+
statement.push(`:${bindingKey}`);
|
|
433
|
+
filter.value += '%';
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
case rapiq.FilterComparisonOperator.IN:
|
|
437
|
+
case rapiq.FilterComparisonOperator.NOT_IN:
|
|
438
|
+
{
|
|
439
|
+
if (filter.operator === rapiq.FilterComparisonOperator.NOT_IN) {
|
|
440
|
+
statement.push('NOT');
|
|
441
|
+
}
|
|
442
|
+
statement.push('IN');
|
|
443
|
+
statement.push(`(:...${bindingKey})`);
|
|
444
|
+
if (Array.isArray(filter.value)) {
|
|
445
|
+
const nullIndex = filter.value.indexOf(null);
|
|
446
|
+
if (nullIndex !== -1) {
|
|
447
|
+
filter.value.splice(nullIndex, 1);
|
|
448
|
+
statement.unshift('(');
|
|
449
|
+
if (filter.operator === rapiq.FilterComparisonOperator.NOT_IN) {
|
|
450
|
+
statement.push('AND');
|
|
451
|
+
} else {
|
|
452
|
+
statement.push('OR');
|
|
453
|
+
}
|
|
454
|
+
statement.push(fullKey);
|
|
455
|
+
statement.push('IS');
|
|
456
|
+
if (filter.operator === rapiq.FilterComparisonOperator.NOT_IN) {
|
|
457
|
+
statement.push('NOT');
|
|
458
|
+
}
|
|
459
|
+
statement.push('NULL');
|
|
460
|
+
statement.push(')');
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
case rapiq.FilterComparisonOperator.LESS_THAN:
|
|
466
|
+
case rapiq.FilterComparisonOperator.LESS_THAN_EQUAL:
|
|
467
|
+
case rapiq.FilterComparisonOperator.GREATER_THAN:
|
|
468
|
+
case rapiq.FilterComparisonOperator.GREATER_THAN_EQUAL:
|
|
469
|
+
{
|
|
470
|
+
if (filter.operator === rapiq.FilterComparisonOperator.LESS_THAN) {
|
|
471
|
+
statement.push('<');
|
|
472
|
+
} else if (filter.operator === rapiq.FilterComparisonOperator.LESS_THAN_EQUAL) {
|
|
473
|
+
statement.push('<=');
|
|
474
|
+
} else if (filter.operator === rapiq.FilterComparisonOperator.GREATER_THAN) {
|
|
475
|
+
statement.push('>');
|
|
476
|
+
} else {
|
|
477
|
+
statement.push('>=');
|
|
478
|
+
}
|
|
479
|
+
statement.push(`:${bindingKey}`);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
items.push({
|
|
484
|
+
statement: statement.join(' '),
|
|
485
|
+
binding: {
|
|
486
|
+
[bindingKey]: filter.value
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
return items;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Apply transformed filter[s] parameter data on the db query.
|
|
494
|
+
*
|
|
495
|
+
* @param query
|
|
496
|
+
* @param data
|
|
497
|
+
*/ function applyFiltersTransformed(query, data) {
|
|
498
|
+
if (data.length === 0) {
|
|
499
|
+
return data;
|
|
500
|
+
}
|
|
501
|
+
/* istanbul ignore next */ query.andWhere(new typeorm.Brackets((qb)=>{
|
|
502
|
+
for(let i = 0; i < data.length; i++){
|
|
503
|
+
if (i === 0) {
|
|
504
|
+
qb.where(data[i].statement, data[i].binding);
|
|
505
|
+
} else {
|
|
506
|
+
qb.andWhere(data[i].statement, data[i].binding);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}));
|
|
510
|
+
return data;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Apply parsed filter[s] parameter data on the db query.
|
|
514
|
+
*
|
|
515
|
+
* @param query
|
|
516
|
+
* @param data
|
|
517
|
+
* @param options
|
|
518
|
+
*/ function applyQueryFiltersParseOutput(query, data, options) {
|
|
519
|
+
applyFiltersTransformed(query, transformParsedFilters(data, options));
|
|
520
|
+
return data;
|
|
521
|
+
}
|
|
522
|
+
// --------------------------------------------------
|
|
523
|
+
/**
|
|
524
|
+
* Apply raw filter[s] parameter data on the db query.
|
|
525
|
+
*
|
|
526
|
+
* @param query
|
|
527
|
+
* @param data
|
|
528
|
+
* @param options
|
|
529
|
+
*/ function applyQueryFilters(query, data, options) {
|
|
530
|
+
options = options || {};
|
|
531
|
+
if (options.defaultAlias) {
|
|
532
|
+
options.defaultPath = options.defaultAlias;
|
|
533
|
+
}
|
|
534
|
+
return applyQueryFiltersParseOutput(query, rapiq.parseQueryFilters(data, options), options);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Apply raw filter[s] parameter data on the db query.
|
|
538
|
+
*
|
|
539
|
+
* @param query
|
|
540
|
+
* @param data
|
|
541
|
+
* @param options
|
|
542
|
+
*/ function applyFilters(query, data, options) {
|
|
543
|
+
return applyQueryFilters(query, data, options);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Apply parsed page/pagination parameter data on the db query.
|
|
548
|
+
*
|
|
549
|
+
* @param query
|
|
550
|
+
* @param data
|
|
551
|
+
*/ function applyQueryPaginationParseOutput(query, data) {
|
|
552
|
+
/* istanbul ignore next */ if (typeof data.limit !== 'undefined') {
|
|
553
|
+
query.take(data.limit);
|
|
554
|
+
if (typeof data.offset === 'undefined') {
|
|
555
|
+
query.skip(0);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
/* istanbul ignore next */ if (typeof data.offset !== 'undefined') {
|
|
559
|
+
query.skip(data.offset);
|
|
560
|
+
}
|
|
561
|
+
return data;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Apply raw page/pagination parameter data on the db query.
|
|
565
|
+
*
|
|
566
|
+
* @param query
|
|
567
|
+
* @param data
|
|
568
|
+
* @param options
|
|
569
|
+
*/ function applyQueryPagination(query, data, options) {
|
|
570
|
+
return applyQueryPaginationParseOutput(query, rapiq.parseQueryPagination(data, options));
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Apply raw page/pagination parameter data on the db query.
|
|
574
|
+
*
|
|
575
|
+
* @param query
|
|
576
|
+
* @param data
|
|
577
|
+
* @param options
|
|
578
|
+
*/ function applyPagination(query, data, options) {
|
|
579
|
+
return applyQueryPagination(query, data, options);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Apply parsed include/relation parameter data on the db query.
|
|
584
|
+
*
|
|
585
|
+
* @param query
|
|
586
|
+
* @param data
|
|
587
|
+
* @param options
|
|
588
|
+
*/ function applyQueryRelationsParseOutput(query, data, options = {}) {
|
|
589
|
+
for(let i = 0; i < data.length; i++){
|
|
590
|
+
const parts = data[i].key.split('.');
|
|
591
|
+
let key;
|
|
592
|
+
if (parts.length > 1) {
|
|
593
|
+
key = parts.slice(-2).join('.');
|
|
594
|
+
} else {
|
|
595
|
+
key = buildKeyWithPrefix(data[i].key, options.defaultAlias);
|
|
596
|
+
}
|
|
597
|
+
data[i].key = key;
|
|
598
|
+
/* istanbul ignore next */ query.leftJoinAndSelect(key, data[i].value);
|
|
599
|
+
if (options.onJoin) {
|
|
600
|
+
options.onJoin(key, data[i].value, query);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return data;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Apply raw include/relations parameter data on the db query.
|
|
607
|
+
*
|
|
608
|
+
* @param query
|
|
609
|
+
* @param data
|
|
610
|
+
* @param options
|
|
611
|
+
*/ function applyQueryRelations(query, data, options = {}) {
|
|
612
|
+
return applyQueryRelationsParseOutput(query, rapiq.parseQueryRelations(data, options), options);
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Apply raw include/relations parameter data on the db query.
|
|
616
|
+
*
|
|
617
|
+
* @param query
|
|
618
|
+
* @param data
|
|
619
|
+
* @param options
|
|
620
|
+
*/ function applyRelations(query, data, options = {}) {
|
|
621
|
+
return applyQueryRelations(query, data, options);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// --------------------------------------------------
|
|
625
|
+
/**
|
|
626
|
+
* Apply parsed sort parameter data on the db query.
|
|
627
|
+
*
|
|
628
|
+
* @param query
|
|
629
|
+
* @param data
|
|
630
|
+
*/ function applyQuerySortParseOutput(query, data) {
|
|
631
|
+
if (data.length === 0) {
|
|
632
|
+
return data;
|
|
633
|
+
}
|
|
634
|
+
const sort = {};
|
|
635
|
+
for(let i = 0; i < data.length; i++){
|
|
636
|
+
const key = buildKeyWithPrefix(data[i].key, data[i].path);
|
|
637
|
+
sort[key] = data[i].value;
|
|
638
|
+
}
|
|
639
|
+
query.orderBy(sort);
|
|
640
|
+
return data;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Apply raw sort parameter data on the db query.
|
|
644
|
+
*
|
|
645
|
+
* @param query
|
|
646
|
+
* @param data
|
|
647
|
+
* @param options
|
|
648
|
+
*/ function applyQuerySort(query, data, options) {
|
|
649
|
+
options = options || {};
|
|
650
|
+
if (options.defaultAlias) {
|
|
651
|
+
options.defaultPath = options.defaultAlias;
|
|
652
|
+
}
|
|
653
|
+
return applyQuerySortParseOutput(query, rapiq.parseQuerySort(data, options));
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Apply raw sort parameter data on the db query.
|
|
657
|
+
*
|
|
658
|
+
* @param query
|
|
659
|
+
* @param data
|
|
660
|
+
* @param options
|
|
661
|
+
*/ function applySort(query, data, options) {
|
|
662
|
+
return applyQuerySort(query, data, options);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function applyQueryParseOutput(query, context, options = {}) {
|
|
666
|
+
if (context.fields) {
|
|
667
|
+
applyQueryFieldsParseOutput(query, context.fields, {
|
|
668
|
+
defaultAlias: context.defaultPath,
|
|
669
|
+
relations: context.relations
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
if (context.filters) {
|
|
673
|
+
applyQueryFiltersParseOutput(query, context.filters, {
|
|
674
|
+
defaultAlias: context.defaultPath,
|
|
675
|
+
relations: context.relations
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
if (context.pagination) {
|
|
679
|
+
applyQueryPaginationParseOutput(query, context.pagination);
|
|
680
|
+
}
|
|
681
|
+
if (context.relations) {
|
|
682
|
+
const relations = {
|
|
683
|
+
defaultAlias: context.defaultPath
|
|
684
|
+
};
|
|
685
|
+
if (locter.isObject(options.relations)) {
|
|
686
|
+
extendObject(relations, options.relations);
|
|
687
|
+
}
|
|
688
|
+
applyQueryRelationsParseOutput(query, context.relations, relations);
|
|
689
|
+
}
|
|
690
|
+
if (context.sort) {
|
|
691
|
+
applyQuerySortParseOutput(query, context.sort);
|
|
692
|
+
}
|
|
693
|
+
return context;
|
|
694
|
+
}
|
|
695
|
+
function applyQuery(query, input, options = {}) {
|
|
696
|
+
if (options.defaultAlias) {
|
|
697
|
+
options.defaultPath = options.defaultAlias;
|
|
698
|
+
}
|
|
699
|
+
if (typeof options.fields === 'undefined' || !isQueryOptionDefined(options.fields, [
|
|
700
|
+
'allowed',
|
|
701
|
+
'default'
|
|
702
|
+
])) {
|
|
703
|
+
options.fields = false;
|
|
704
|
+
}
|
|
705
|
+
if (typeof options.filters === 'undefined' || !isQueryOptionDefined(options.filters, [
|
|
706
|
+
'allowed',
|
|
707
|
+
'default'
|
|
708
|
+
])) {
|
|
709
|
+
options.filters = false;
|
|
710
|
+
}
|
|
711
|
+
if (options.pagination) {
|
|
712
|
+
if (!input.pagination && !input.page) {
|
|
713
|
+
input.pagination = input.pagination || {};
|
|
714
|
+
}
|
|
715
|
+
} else {
|
|
716
|
+
options.pagination = false;
|
|
717
|
+
}
|
|
718
|
+
if (typeof options.relations === 'undefined' || !isQueryOptionDefined(options.relations, [
|
|
719
|
+
'allowed'
|
|
720
|
+
])) {
|
|
721
|
+
options.relations = false;
|
|
722
|
+
}
|
|
723
|
+
if (typeof options.sort === 'undefined' || !isQueryOptionDefined(options.sort, [
|
|
724
|
+
'allowed',
|
|
725
|
+
'default'
|
|
726
|
+
])) {
|
|
727
|
+
options.sort = false;
|
|
728
|
+
}
|
|
729
|
+
const output = applyQueryParseOutput(query, rapiq.parseQuery(input, options), options);
|
|
730
|
+
return {
|
|
731
|
+
...output,
|
|
732
|
+
...options.defaultAlias ? {
|
|
733
|
+
defaultAlias: options.defaultAlias
|
|
734
|
+
} : {}
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async function findDataSource(context = {}) {
|
|
739
|
+
let tsconfig;
|
|
740
|
+
if (!context.preserveFilePaths) {
|
|
741
|
+
if (locter.isObject(context.tsconfig)) {
|
|
742
|
+
tsconfig = context.tsconfig;
|
|
743
|
+
} else {
|
|
744
|
+
tsconfig = await readTSConfig(context.tsconfig);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
const files = [
|
|
748
|
+
'data-source'
|
|
749
|
+
];
|
|
750
|
+
if (context.fileName) {
|
|
751
|
+
context.fileName = locter.removeFileNameExtension(context.fileName, [
|
|
752
|
+
'.ts',
|
|
753
|
+
'.mts',
|
|
754
|
+
'.cts',
|
|
755
|
+
'.js',
|
|
756
|
+
'.mjs',
|
|
757
|
+
'.cjs'
|
|
758
|
+
]);
|
|
759
|
+
if (context.fileName !== 'data-source') {
|
|
760
|
+
files.unshift(context.fileName);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
let { directory } = context;
|
|
764
|
+
let directoryIsPattern = false;
|
|
765
|
+
if (context.directory) {
|
|
766
|
+
if (path.isAbsolute(context.directory)) {
|
|
767
|
+
directory = context.directory;
|
|
768
|
+
} else {
|
|
769
|
+
directoryIsPattern = true;
|
|
770
|
+
directory = safeReplaceWindowsSeparator(context.directory);
|
|
771
|
+
}
|
|
772
|
+
if (!context.preserveFilePaths) {
|
|
773
|
+
directory = await adjustFilePath(directory, tsconfig);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const lookupPaths = [];
|
|
777
|
+
for(let j = 0; j < files.length; j++){
|
|
778
|
+
if (directory && directoryIsPattern) {
|
|
779
|
+
lookupPaths.push(path.posix.join(directory, files[j]));
|
|
780
|
+
}
|
|
781
|
+
lookupPaths.push(...[
|
|
782
|
+
path.posix.join('src', files[j]),
|
|
783
|
+
path.posix.join('src/{db,database}', files[j])
|
|
784
|
+
]);
|
|
785
|
+
}
|
|
786
|
+
files.push(...lookupPaths);
|
|
787
|
+
if (!context.preserveFilePaths) {
|
|
788
|
+
for(let j = 0; j < files.length; j++){
|
|
789
|
+
files[j] = await adjustFilePath(files[j], tsconfig);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
for(let i = 0; i < files.length; i++){
|
|
793
|
+
const info = await locter.locate(`${files[i]}.{js,cjs,mjs,ts,cts,mts}`, {
|
|
794
|
+
path: [
|
|
795
|
+
process.cwd(),
|
|
796
|
+
...directory && !directoryIsPattern ? [
|
|
797
|
+
directory
|
|
798
|
+
] : []
|
|
799
|
+
],
|
|
800
|
+
ignore: [
|
|
801
|
+
'**/*.d.ts'
|
|
802
|
+
]
|
|
803
|
+
});
|
|
804
|
+
if (info) {
|
|
805
|
+
let moduleRecord = await locter.load(info);
|
|
806
|
+
if (isPromise(moduleRecord)) {
|
|
807
|
+
moduleRecord = await moduleRecord;
|
|
808
|
+
}
|
|
809
|
+
if (typeorm.InstanceChecker.isDataSource(moduleRecord)) {
|
|
810
|
+
return moduleRecord;
|
|
811
|
+
}
|
|
812
|
+
if (!locter.isObject(moduleRecord)) {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
const keys = Object.keys(moduleRecord);
|
|
816
|
+
for(let j = 0; j < keys.length; j++){
|
|
817
|
+
let value = moduleRecord[keys[j]];
|
|
818
|
+
if (isPromise(value)) {
|
|
819
|
+
value = await value;
|
|
820
|
+
}
|
|
821
|
+
if (typeorm.InstanceChecker.isDataSource(value)) {
|
|
822
|
+
return value;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return undefined;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/*
|
|
831
|
+
* Copyright (c) 2023-2023.
|
|
832
|
+
* Author Peter Placzek (tada5hi)
|
|
833
|
+
* For the full copyright and license information,
|
|
834
|
+
* view the LICENSE file that was distributed with this source code.
|
|
835
|
+
*/ var EnvironmentName = /*#__PURE__*/ function(EnvironmentName) {
|
|
836
|
+
EnvironmentName["DEVELOPMENT"] = "development";
|
|
837
|
+
EnvironmentName["PRODUCTION"] = "production";
|
|
838
|
+
EnvironmentName["TEST"] = "test";
|
|
839
|
+
return EnvironmentName;
|
|
840
|
+
}({});
|
|
841
|
+
var EnvironmentVariableName = /*#__PURE__*/ function(EnvironmentVariableName) {
|
|
842
|
+
EnvironmentVariableName["ENV"] = "NODE_ENV";
|
|
843
|
+
// Seeder
|
|
844
|
+
EnvironmentVariableName["SEEDS"] = "DB_SEEDS";
|
|
845
|
+
EnvironmentVariableName["SEEDS_ALT"] = "TYPEORM_SEEDING_SEEDS";
|
|
846
|
+
EnvironmentVariableName["FACTORIES"] = "DB_FACTORIES";
|
|
847
|
+
EnvironmentVariableName["FACTORIES_ALT"] = "TYPEORM_SEEDING_FACTORIES";
|
|
848
|
+
// Database
|
|
849
|
+
EnvironmentVariableName["TYPE"] = "DB_TYPE";
|
|
850
|
+
EnvironmentVariableName["TYPE_ALT"] = "TYPEORM_CONNECTION";
|
|
851
|
+
EnvironmentVariableName["URL"] = "DB_URL";
|
|
852
|
+
EnvironmentVariableName["URL_ALT"] = "TYPEORM_URL";
|
|
853
|
+
EnvironmentVariableName["HOST"] = "DB_HOST";
|
|
854
|
+
EnvironmentVariableName["HOST_ALT"] = "TYPEORM_HOST";
|
|
855
|
+
EnvironmentVariableName["PORT"] = "DB_PORT";
|
|
856
|
+
EnvironmentVariableName["PORT_ALT"] = "TYPEORM_PORT";
|
|
857
|
+
EnvironmentVariableName["USERNAME"] = "DB_USERNAME";
|
|
858
|
+
EnvironmentVariableName["USERNAME_ALT"] = "TYPEORM_USERNAME";
|
|
859
|
+
EnvironmentVariableName["PASSWORD"] = "DB_PASSWORD";
|
|
860
|
+
EnvironmentVariableName["PASSWORD_ALT"] = "TYPEORM_PASSWORD";
|
|
861
|
+
EnvironmentVariableName["DATABASE"] = "DB_DATABASE";
|
|
862
|
+
EnvironmentVariableName["DATABASE_ALT"] = "TYPEORM_DATABASE";
|
|
863
|
+
EnvironmentVariableName["SID"] = "DB_SID";
|
|
864
|
+
EnvironmentVariableName["SID_ALT"] = "TYPEORM_SID";
|
|
865
|
+
EnvironmentVariableName["SCHEMA"] = "DB_SCHEMA";
|
|
866
|
+
EnvironmentVariableName["SCHEMA_ALT"] = "TYPEORM_SCHEMA";
|
|
867
|
+
EnvironmentVariableName["SCHEMA_DROP"] = "DB_DROP_SCHEMA";
|
|
868
|
+
EnvironmentVariableName["SCHEMA_DROP_ALT"] = "TYPEORM_DROP_SCHEMA";
|
|
869
|
+
EnvironmentVariableName["DRIVER_EXTRA"] = "DB_DRIVER_EXTRA";
|
|
870
|
+
EnvironmentVariableName["DRIVER_EXTRA_ALT"] = "TYPEORM_DRIVER_EXTRA";
|
|
871
|
+
EnvironmentVariableName["SYNCHRONIZE"] = "DB_SYNCHRONIZE";
|
|
872
|
+
EnvironmentVariableName["SYNCHRONIZE_ALT"] = "TYPEORM_SYNCHRONIZE";
|
|
873
|
+
EnvironmentVariableName["MIGRATIONS"] = "DB_MIGRATIONS";
|
|
874
|
+
EnvironmentVariableName["MIGRATIONS_ALT"] = "TYPEORM_MIGRATIONS";
|
|
875
|
+
EnvironmentVariableName["MIGRATIONS_RUN"] = "DB_MIGRATIONS_RUN";
|
|
876
|
+
EnvironmentVariableName["MIGRATIONS_RUN_ALT"] = "TYPEORM_MIGRATIONS_RUN";
|
|
877
|
+
EnvironmentVariableName["MIGRATIONS_TABLE_NAME"] = "DB_MIGRATIONS_TABLE_NAME";
|
|
878
|
+
EnvironmentVariableName["MIGRATIONS_TABLE_NAME_ALT"] = "TYPEORM_MIGRATIONS_TABLE_NAME";
|
|
879
|
+
EnvironmentVariableName["ENTITIES"] = "DB_ENTITIES";
|
|
880
|
+
EnvironmentVariableName["ENTITIES_ALT"] = "TYPEORM_ENTITIES";
|
|
881
|
+
EnvironmentVariableName["ENTITY_PREFIX"] = "DB_ENTITY_PREFIX";
|
|
882
|
+
EnvironmentVariableName["ENTITY_PREFIX_ALT"] = "TYPEORM_ENTITY_PREFIX";
|
|
883
|
+
EnvironmentVariableName["METADATA_TABLE_NAME"] = "DB_METADATA_TABLE_NAME";
|
|
884
|
+
EnvironmentVariableName["METADATA_TABLE_NAME_ALT"] = "TYPEORM_METADATA_TABLE_NAME";
|
|
885
|
+
EnvironmentVariableName["SUBSCRIBERS"] = "DB_SUBSCRIBERS";
|
|
886
|
+
EnvironmentVariableName["SUBSCRIBERS_ALT"] = "TYPEORM_SUBSCRIBERS";
|
|
887
|
+
EnvironmentVariableName["LOGGING"] = "DB_LOGGING";
|
|
888
|
+
EnvironmentVariableName["LOGGING_ALT"] = "TYPEORM_LOGGING";
|
|
889
|
+
EnvironmentVariableName["LOGGER"] = "DB_LOGGER";
|
|
890
|
+
EnvironmentVariableName["LOGGER_ALT"] = "TYPEORM_LOGGER";
|
|
891
|
+
EnvironmentVariableName["MAX_QUERY_EXECUTION_TIME"] = "DB_MAX_QUERY_EXECUTION_TIME";
|
|
892
|
+
EnvironmentVariableName["MAX_QUERY_EXECUTION_TIME_ALT"] = "TYPEORM_MAX_QUERY_EXECUTION_TIME";
|
|
893
|
+
EnvironmentVariableName["DEBUG"] = "DB_DEBUG";
|
|
894
|
+
EnvironmentVariableName["DEBUG_ALT"] = "TYPEORM_DEBUG";
|
|
895
|
+
EnvironmentVariableName["UUID_EXTENSION"] = "DB_UUID_EXTENSION";
|
|
896
|
+
EnvironmentVariableName["UUID_EXTENSION_ALT"] = "TYPEORM_UUID_EXTENSION";
|
|
897
|
+
EnvironmentVariableName["CACHE"] = "DB_CACHE";
|
|
898
|
+
EnvironmentVariableName["CACHE_ALT"] = "TYPEORM_CACHE";
|
|
899
|
+
EnvironmentVariableName["CACHE_ALWAYS_ENABLED"] = "DB_CACHE_ALWAYS_ENABLED";
|
|
900
|
+
EnvironmentVariableName["CACHE_ALWAYS_ENABLED_ALT"] = "TYPEORM_CACHE_ALWAYS_ENABLED";
|
|
901
|
+
EnvironmentVariableName["CACHE_OPTIONS"] = "DB_CACHE_OPTIONS";
|
|
902
|
+
EnvironmentVariableName["CACHE_OPTIONS_ALT"] = "TYPEORM_CACHE_OPTIONS";
|
|
903
|
+
EnvironmentVariableName["CACHE_DURATION"] = "DB_CACHE_DURATION";
|
|
904
|
+
EnvironmentVariableName["CACHE_DURATION_ALT"] = "TYPEORM_CACHE_DURATION";
|
|
905
|
+
return EnvironmentVariableName;
|
|
906
|
+
}({});
|
|
907
|
+
|
|
908
|
+
function transformLogging(input) {
|
|
909
|
+
const value = envix.toBool(input);
|
|
910
|
+
if (typeof value === 'boolean') {
|
|
911
|
+
return value;
|
|
912
|
+
}
|
|
913
|
+
if (input === 'all') {
|
|
914
|
+
return 'all';
|
|
915
|
+
}
|
|
916
|
+
return envix.toArray(input) ?? [];
|
|
917
|
+
}
|
|
918
|
+
function transformCache(input) {
|
|
919
|
+
const value = envix.toBool(input);
|
|
920
|
+
if (typeof value === 'boolean') {
|
|
921
|
+
return value;
|
|
922
|
+
}
|
|
923
|
+
if (input === 'redis' || input === 'ioredis' || input === 'database' || input === 'ioredis/cluster') {
|
|
924
|
+
let options;
|
|
925
|
+
const envCacheOptions = envix.oneOf([
|
|
926
|
+
envix.read(EnvironmentVariableName.CACHE_OPTIONS),
|
|
927
|
+
envix.read(EnvironmentVariableName.CACHE_OPTIONS_ALT)
|
|
928
|
+
]);
|
|
929
|
+
if (envCacheOptions) {
|
|
930
|
+
options = JSON.parse(envCacheOptions);
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
type: input,
|
|
934
|
+
options,
|
|
935
|
+
alwaysEnabled: envix.oneOf([
|
|
936
|
+
envix.readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED),
|
|
937
|
+
envix.readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED_ALT)
|
|
938
|
+
]),
|
|
939
|
+
duration: envix.oneOf([
|
|
940
|
+
envix.readInt(EnvironmentVariableName.CACHE_DURATION),
|
|
941
|
+
envix.readInt(EnvironmentVariableName.CACHE_DURATION_ALT)
|
|
942
|
+
])
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
return undefined;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
let instance$1;
|
|
949
|
+
function useEnv(key) {
|
|
950
|
+
if (typeof instance$1 !== 'undefined') {
|
|
951
|
+
if (typeof key === 'string') {
|
|
952
|
+
return instance$1[key];
|
|
953
|
+
}
|
|
954
|
+
return instance$1;
|
|
955
|
+
}
|
|
956
|
+
const output = {
|
|
957
|
+
env: envix.read(EnvironmentVariableName.ENV, EnvironmentName.DEVELOPMENT),
|
|
958
|
+
// Seeder
|
|
959
|
+
seeds: envix.oneOf([
|
|
960
|
+
envix.readArray(EnvironmentVariableName.SEEDS),
|
|
961
|
+
envix.readArray(EnvironmentVariableName.SEEDS_ALT)
|
|
962
|
+
]) ?? [],
|
|
963
|
+
factories: envix.oneOf([
|
|
964
|
+
envix.readArray(EnvironmentVariableName.FACTORIES),
|
|
965
|
+
envix.readArray(EnvironmentVariableName.FACTORIES_ALT)
|
|
966
|
+
]) ?? [],
|
|
967
|
+
// Database
|
|
968
|
+
url: envix.oneOf([
|
|
969
|
+
envix.read(EnvironmentVariableName.URL),
|
|
970
|
+
envix.read(EnvironmentVariableName.URL_ALT)
|
|
971
|
+
]),
|
|
972
|
+
host: envix.oneOf([
|
|
973
|
+
envix.read(EnvironmentVariableName.HOST),
|
|
974
|
+
envix.read(EnvironmentVariableName.HOST_ALT)
|
|
975
|
+
]),
|
|
976
|
+
port: envix.oneOf([
|
|
977
|
+
envix.readInt(EnvironmentVariableName.PORT),
|
|
978
|
+
envix.readInt(EnvironmentVariableName.PORT_ALT)
|
|
979
|
+
]),
|
|
980
|
+
username: envix.oneOf([
|
|
981
|
+
envix.read(EnvironmentVariableName.USERNAME),
|
|
982
|
+
envix.read(EnvironmentVariableName.USERNAME_ALT)
|
|
983
|
+
]),
|
|
984
|
+
password: envix.oneOf([
|
|
985
|
+
envix.read(EnvironmentVariableName.PASSWORD),
|
|
986
|
+
envix.read(EnvironmentVariableName.PASSWORD_ALT)
|
|
987
|
+
]),
|
|
988
|
+
database: envix.oneOf([
|
|
989
|
+
envix.read(EnvironmentVariableName.DATABASE),
|
|
990
|
+
envix.read(EnvironmentVariableName.DATABASE_ALT)
|
|
991
|
+
]),
|
|
992
|
+
sid: envix.oneOf([
|
|
993
|
+
envix.read(EnvironmentVariableName.SID),
|
|
994
|
+
envix.read(EnvironmentVariableName.SID_ALT)
|
|
995
|
+
]),
|
|
996
|
+
schema: envix.oneOf([
|
|
997
|
+
envix.read(EnvironmentVariableName.SCHEMA),
|
|
998
|
+
envix.read(EnvironmentVariableName.SCHEMA_ALT)
|
|
999
|
+
]),
|
|
1000
|
+
extra: envix.oneOf([
|
|
1001
|
+
envix.read(EnvironmentVariableName.DRIVER_EXTRA),
|
|
1002
|
+
envix.read(EnvironmentVariableName.DRIVER_EXTRA_ALT)
|
|
1003
|
+
]),
|
|
1004
|
+
synchronize: envix.oneOf([
|
|
1005
|
+
envix.readBool(EnvironmentVariableName.SYNCHRONIZE),
|
|
1006
|
+
envix.readBool(EnvironmentVariableName.SYNCHRONIZE_ALT)
|
|
1007
|
+
]),
|
|
1008
|
+
schemaDrop: envix.oneOf([
|
|
1009
|
+
envix.readBool(EnvironmentVariableName.SCHEMA_DROP),
|
|
1010
|
+
envix.readBool(EnvironmentVariableName.SCHEMA_DROP_ALT)
|
|
1011
|
+
]),
|
|
1012
|
+
migrationsRun: envix.oneOf([
|
|
1013
|
+
envix.readBool(EnvironmentVariableName.MIGRATIONS_RUN),
|
|
1014
|
+
envix.readBool(EnvironmentVariableName.MIGRATIONS_RUN_ALT)
|
|
1015
|
+
]),
|
|
1016
|
+
entities: envix.oneOf([
|
|
1017
|
+
envix.readArray(EnvironmentVariableName.ENTITIES),
|
|
1018
|
+
envix.readArray(EnvironmentVariableName.ENTITIES_ALT)
|
|
1019
|
+
]) ?? [],
|
|
1020
|
+
migrations: envix.oneOf([
|
|
1021
|
+
envix.readArray(EnvironmentVariableName.MIGRATIONS),
|
|
1022
|
+
envix.readArray(EnvironmentVariableName.MIGRATIONS_ALT)
|
|
1023
|
+
]) ?? [],
|
|
1024
|
+
migrationsTableName: envix.oneOf([
|
|
1025
|
+
envix.read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME),
|
|
1026
|
+
envix.read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME_ALT)
|
|
1027
|
+
]),
|
|
1028
|
+
metadataTableName: envix.oneOf([
|
|
1029
|
+
envix.read(EnvironmentVariableName.METADATA_TABLE_NAME),
|
|
1030
|
+
envix.read(EnvironmentVariableName.METADATA_TABLE_NAME_ALT)
|
|
1031
|
+
]),
|
|
1032
|
+
subscribers: envix.oneOf([
|
|
1033
|
+
envix.readArray(EnvironmentVariableName.SUBSCRIBERS),
|
|
1034
|
+
envix.readArray(EnvironmentVariableName.SUBSCRIBERS_ALT)
|
|
1035
|
+
]) ?? [],
|
|
1036
|
+
logging: transformLogging(envix.oneOf([
|
|
1037
|
+
envix.read(EnvironmentVariableName.LOGGING),
|
|
1038
|
+
envix.read(EnvironmentVariableName.LOGGING_ALT)
|
|
1039
|
+
])),
|
|
1040
|
+
logger: envix.oneOf([
|
|
1041
|
+
envix.read(EnvironmentVariableName.LOGGER),
|
|
1042
|
+
envix.read(EnvironmentVariableName.LOGGER_ALT)
|
|
1043
|
+
]),
|
|
1044
|
+
entityPrefix: envix.oneOf([
|
|
1045
|
+
envix.read(EnvironmentVariableName.ENTITY_PREFIX),
|
|
1046
|
+
envix.read(EnvironmentVariableName.ENTITY_PREFIX_ALT)
|
|
1047
|
+
]),
|
|
1048
|
+
maxQueryExecutionTime: envix.oneOf([
|
|
1049
|
+
envix.readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME),
|
|
1050
|
+
envix.readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME_ALT)
|
|
1051
|
+
]),
|
|
1052
|
+
debug: envix.oneOf([
|
|
1053
|
+
envix.read(EnvironmentVariableName.DEBUG),
|
|
1054
|
+
envix.read(EnvironmentVariableName.DEBUG_ALT)
|
|
1055
|
+
]),
|
|
1056
|
+
cache: transformCache(envix.oneOf([
|
|
1057
|
+
envix.read(EnvironmentVariableName.CACHE),
|
|
1058
|
+
envix.read(EnvironmentVariableName.CACHE_ALT)
|
|
1059
|
+
])),
|
|
1060
|
+
uuidExtension: envix.oneOf([
|
|
1061
|
+
envix.read(EnvironmentVariableName.UUID_EXTENSION),
|
|
1062
|
+
envix.read(EnvironmentVariableName.UUID_EXTENSION_ALT)
|
|
1063
|
+
])
|
|
1064
|
+
};
|
|
1065
|
+
if (output.extra) {
|
|
1066
|
+
output.extra = JSON.parse(output.extra); // todo: ensure record<string,any> ??
|
|
1067
|
+
}
|
|
1068
|
+
let type;
|
|
1069
|
+
const envType = envix.oneOf([
|
|
1070
|
+
envix.read(EnvironmentVariableName.TYPE),
|
|
1071
|
+
envix.read(EnvironmentVariableName.TYPE_ALT)
|
|
1072
|
+
]);
|
|
1073
|
+
if (envType) {
|
|
1074
|
+
type = envType;
|
|
1075
|
+
} else {
|
|
1076
|
+
const envURL = envix.oneOf([
|
|
1077
|
+
envix.read(EnvironmentVariableName.URL),
|
|
1078
|
+
envix.read(EnvironmentVariableName.URL_ALT)
|
|
1079
|
+
]);
|
|
1080
|
+
if (envURL) {
|
|
1081
|
+
[type] = envURL.split('://');
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (type) {
|
|
1085
|
+
output.type = type; // todo: maybe validation here
|
|
1086
|
+
}
|
|
1087
|
+
instance$1 = output;
|
|
1088
|
+
if (typeof key === 'string') {
|
|
1089
|
+
return output[key];
|
|
1090
|
+
}
|
|
1091
|
+
return instance$1;
|
|
1092
|
+
}
|
|
1093
|
+
function resetEnv() {
|
|
1094
|
+
if (typeof instance$1 !== 'undefined') {
|
|
1095
|
+
instance$1 = undefined;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
const merge = smob.createMerger({
|
|
1100
|
+
strategy: (target, key, value)=>{
|
|
1101
|
+
if (typeof target[key] === 'undefined') {
|
|
1102
|
+
target[key] = value;
|
|
1103
|
+
return target;
|
|
1104
|
+
}
|
|
1105
|
+
return undefined;
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
function mergeDataSourceOptions(target, source) {
|
|
1109
|
+
if (target.type !== source.type) {
|
|
1110
|
+
return target;
|
|
1111
|
+
}
|
|
1112
|
+
return merge(target, source);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
function hasEnvDataSourceOptions() {
|
|
1116
|
+
return !!useEnv('type');
|
|
1117
|
+
}
|
|
1118
|
+
/* istanbul ignore next */ function readDataSourceOptionsFromEnv() {
|
|
1119
|
+
if (!hasEnvDataSourceOptions()) {
|
|
1120
|
+
return undefined;
|
|
1121
|
+
}
|
|
1122
|
+
// todo: include seeder options
|
|
1123
|
+
const base = {
|
|
1124
|
+
type: useEnv('type'),
|
|
1125
|
+
entities: useEnv('entities'),
|
|
1126
|
+
subscribers: useEnv('subscribers'),
|
|
1127
|
+
migrations: useEnv('migrations'),
|
|
1128
|
+
migrationsTableName: useEnv('migrationsTableName'),
|
|
1129
|
+
// migrationsTransactionMode: useEnv('migra')
|
|
1130
|
+
metadataTableName: useEnv('metadataTableName'),
|
|
1131
|
+
logging: useEnv('logging'),
|
|
1132
|
+
logger: useEnv('logger'),
|
|
1133
|
+
maxQueryExecutionTime: useEnv('maxQueryExecutionTime'),
|
|
1134
|
+
synchronize: useEnv('synchronize'),
|
|
1135
|
+
migrationsRun: useEnv('migrationsRun'),
|
|
1136
|
+
dropSchema: useEnv('schemaDrop'),
|
|
1137
|
+
entityPrefix: useEnv('entityPrefix'),
|
|
1138
|
+
extra: useEnv('extra'),
|
|
1139
|
+
cache: useEnv('cache')
|
|
1140
|
+
};
|
|
1141
|
+
const credentialOptions = {
|
|
1142
|
+
url: useEnv('url'),
|
|
1143
|
+
host: useEnv('host'),
|
|
1144
|
+
port: useEnv('port'),
|
|
1145
|
+
username: useEnv('username'),
|
|
1146
|
+
password: useEnv('password'),
|
|
1147
|
+
database: useEnv('database')
|
|
1148
|
+
};
|
|
1149
|
+
if (base.type === 'mysql' || base.type === 'mariadb') {
|
|
1150
|
+
return {
|
|
1151
|
+
...base,
|
|
1152
|
+
...credentialOptions,
|
|
1153
|
+
type: base.type
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
if (base.type === 'postgres') {
|
|
1157
|
+
return {
|
|
1158
|
+
...base,
|
|
1159
|
+
...credentialOptions,
|
|
1160
|
+
type: base.type,
|
|
1161
|
+
schema: useEnv('schema'),
|
|
1162
|
+
uuidExtension: useEnv('uuidExtension')
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
if (base.type === 'cockroachdb') {
|
|
1166
|
+
return {
|
|
1167
|
+
...base,
|
|
1168
|
+
...credentialOptions,
|
|
1169
|
+
type: base.type,
|
|
1170
|
+
schema: useEnv('schema'),
|
|
1171
|
+
timeTravelQueries: true
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
if (base.type === 'sqlite') {
|
|
1175
|
+
return {
|
|
1176
|
+
...base,
|
|
1177
|
+
type: base.type,
|
|
1178
|
+
database: useEnv('database') || 'db.sqlite'
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
if (base.type === 'better-sqlite3') {
|
|
1182
|
+
return {
|
|
1183
|
+
...base,
|
|
1184
|
+
type: base.type,
|
|
1185
|
+
database: useEnv('database') || 'db.sqlite'
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
if (base.type === 'mssql') {
|
|
1189
|
+
return {
|
|
1190
|
+
...base,
|
|
1191
|
+
...credentialOptions,
|
|
1192
|
+
type: base.type,
|
|
1193
|
+
schema: useEnv('schema')
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
if (base.type === 'oracle') {
|
|
1197
|
+
return {
|
|
1198
|
+
...base,
|
|
1199
|
+
...credentialOptions,
|
|
1200
|
+
type: base.type,
|
|
1201
|
+
sid: useEnv('sid')
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
...base,
|
|
1206
|
+
...credentialOptions
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
function mergeDataSourceOptionsWithEnv(options) {
|
|
1210
|
+
const env = readDataSourceOptionsFromEnv();
|
|
1211
|
+
if (!env) {
|
|
1212
|
+
return options;
|
|
1213
|
+
}
|
|
1214
|
+
return mergeDataSourceOptions(env, options);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* Build DataSourceOptions from DataSource or from configuration.
|
|
1219
|
+
*
|
|
1220
|
+
* @param context
|
|
1221
|
+
*/ async function buildDataSourceOptions(context = {}) {
|
|
1222
|
+
const directory = context.directory || process.cwd();
|
|
1223
|
+
let tsconfig;
|
|
1224
|
+
if (!context.preserveFilePaths) {
|
|
1225
|
+
if (locter.isObject(context.tsconfig)) {
|
|
1226
|
+
tsconfig = context.tsconfig;
|
|
1227
|
+
} else {
|
|
1228
|
+
tsconfig = await readTSConfig(context.tsconfig);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
const dataSource = await findDataSource({
|
|
1232
|
+
directory,
|
|
1233
|
+
fileName: context.dataSourceName,
|
|
1234
|
+
tsconfig
|
|
1235
|
+
});
|
|
1236
|
+
if (dataSource) {
|
|
1237
|
+
if (context.preserveFilePaths) {
|
|
1238
|
+
return mergeDataSourceOptionsWithEnv(dataSource.options);
|
|
1239
|
+
}
|
|
1240
|
+
const options = await adjustFilePaths(dataSource.options, [
|
|
1241
|
+
'entities',
|
|
1242
|
+
'migrations',
|
|
1243
|
+
'subscribers'
|
|
1244
|
+
], tsconfig);
|
|
1245
|
+
return mergeDataSourceOptionsWithEnv(options);
|
|
1246
|
+
}
|
|
1247
|
+
const options = readDataSourceOptionsFromEnv();
|
|
1248
|
+
if (options) {
|
|
1249
|
+
if (context.preserveFilePaths) {
|
|
1250
|
+
return options;
|
|
1251
|
+
}
|
|
1252
|
+
return adjustFilePaths(options, [
|
|
1253
|
+
'entities',
|
|
1254
|
+
'migrations',
|
|
1255
|
+
'subscribers'
|
|
1256
|
+
], tsconfig);
|
|
1257
|
+
}
|
|
1258
|
+
throw OptionsError.notFound();
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
const instances$1 = {};
|
|
1262
|
+
const instancePromises = {};
|
|
1263
|
+
function setDataSourceOptions(options, alias) {
|
|
1264
|
+
instances$1[alias || 'default'] = options;
|
|
1265
|
+
}
|
|
1266
|
+
function hasDataSourceOptions(alias) {
|
|
1267
|
+
return Object.prototype.hasOwnProperty.call(instances$1, alias || 'default');
|
|
1268
|
+
}
|
|
1269
|
+
async function useDataSourceOptions(alias) {
|
|
1270
|
+
alias = alias || 'default';
|
|
1271
|
+
if (Object.prototype.hasOwnProperty.call(instances$1, alias)) {
|
|
1272
|
+
return instances$1[alias];
|
|
1273
|
+
}
|
|
1274
|
+
/* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(instancePromises, alias)) {
|
|
1275
|
+
instancePromises[alias] = buildDataSourceOptions().catch((e)=>{
|
|
1276
|
+
if (alias) {
|
|
1277
|
+
delete instancePromises[alias];
|
|
1278
|
+
}
|
|
1279
|
+
throw e;
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
instances$1[alias] = await instancePromises[alias];
|
|
1283
|
+
return instances$1[alias];
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
const instances = {};
|
|
1287
|
+
const initializePromises = {};
|
|
1288
|
+
const optionsPromises = {};
|
|
1289
|
+
function setDataSource(dataSource, alias) {
|
|
1290
|
+
alias = alias || 'default';
|
|
1291
|
+
instances[alias] = dataSource;
|
|
1292
|
+
}
|
|
1293
|
+
function hasDataSource(alias) {
|
|
1294
|
+
alias = alias || 'default';
|
|
1295
|
+
return Object.prototype.hasOwnProperty.call(instances, alias);
|
|
1296
|
+
}
|
|
1297
|
+
function unsetDataSource(alias) {
|
|
1298
|
+
alias = alias || 'default';
|
|
1299
|
+
if (Object.prototype.hasOwnProperty.call(instances, alias)) {
|
|
1300
|
+
delete instances[alias];
|
|
1301
|
+
}
|
|
1302
|
+
/* istanbul ignore next */ if (Object.prototype.hasOwnProperty.call(optionsPromises, alias)) {
|
|
1303
|
+
delete optionsPromises[alias];
|
|
1304
|
+
}
|
|
1305
|
+
/* istanbul ignore next */ if (Object.prototype.hasOwnProperty.call(initializePromises, alias)) {
|
|
1306
|
+
delete initializePromises[alias];
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
async function useDataSource(alias) {
|
|
1310
|
+
alias = alias || 'default';
|
|
1311
|
+
if (Object.prototype.hasOwnProperty.call(instances, alias)) {
|
|
1312
|
+
if (!instances[alias].isInitialized) {
|
|
1313
|
+
/* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(initializePromises, alias)) {
|
|
1314
|
+
initializePromises[alias] = instances[alias].initialize().catch((e)=>{
|
|
1315
|
+
if (alias) {
|
|
1316
|
+
delete initializePromises[alias];
|
|
1317
|
+
}
|
|
1318
|
+
throw e;
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
await initializePromises[alias];
|
|
1322
|
+
}
|
|
1323
|
+
return instances[alias];
|
|
1324
|
+
}
|
|
1325
|
+
/* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(optionsPromises, alias)) {
|
|
1326
|
+
optionsPromises[alias] = useDataSourceOptions(alias).catch((e)=>{
|
|
1327
|
+
if (alias) {
|
|
1328
|
+
delete optionsPromises[alias];
|
|
1329
|
+
}
|
|
1330
|
+
throw e;
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
const options = await optionsPromises[alias];
|
|
1334
|
+
const dataSource = new typeorm.DataSource(options);
|
|
1335
|
+
/* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(initializePromises, alias)) {
|
|
1336
|
+
initializePromises[alias] = dataSource.initialize().catch((e)=>{
|
|
1337
|
+
if (alias) {
|
|
1338
|
+
delete initializePromises[alias];
|
|
1339
|
+
}
|
|
1340
|
+
throw e;
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
await initializePromises[alias];
|
|
1344
|
+
instances[alias] = dataSource;
|
|
1345
|
+
return dataSource;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Check database setup progress.
|
|
1350
|
+
*
|
|
1351
|
+
* @param context
|
|
1352
|
+
*/ async function checkDatabase(context = {}) {
|
|
1353
|
+
const result = {
|
|
1354
|
+
exists: true,
|
|
1355
|
+
schema: false,
|
|
1356
|
+
migrationsPending: []
|
|
1357
|
+
};
|
|
1358
|
+
let dataSource;
|
|
1359
|
+
let dataSourceCleanup;
|
|
1360
|
+
if (typeof context.dataSource === 'undefined' && typeof context.options === 'undefined' && hasDataSource(context.alias)) {
|
|
1361
|
+
dataSource = await useDataSource(context.alias);
|
|
1362
|
+
if (dataSource.options.synchronize || dataSource.options.migrationsRun) {
|
|
1363
|
+
dataSource = new typeorm.DataSource({
|
|
1364
|
+
...dataSource.options,
|
|
1365
|
+
synchronize: false,
|
|
1366
|
+
migrationsRun: false
|
|
1367
|
+
});
|
|
1368
|
+
dataSourceCleanup = true;
|
|
1369
|
+
} else {
|
|
1370
|
+
dataSourceCleanup = false;
|
|
1371
|
+
}
|
|
1372
|
+
} else {
|
|
1373
|
+
let dataSourceOptions;
|
|
1374
|
+
if (context.options) {
|
|
1375
|
+
dataSourceOptions = context.options;
|
|
1376
|
+
} else {
|
|
1377
|
+
dataSourceOptions = await useDataSourceOptions(context.alias);
|
|
1378
|
+
}
|
|
1379
|
+
dataSource = new typeorm.DataSource({
|
|
1380
|
+
...dataSourceOptions,
|
|
1381
|
+
synchronize: false,
|
|
1382
|
+
migrationsRun: false
|
|
1383
|
+
});
|
|
1384
|
+
dataSourceCleanup = context.dataSourceCleanup ?? true;
|
|
1385
|
+
}
|
|
1386
|
+
try {
|
|
1387
|
+
if (!dataSource.isInitialized) {
|
|
1388
|
+
await dataSource.initialize();
|
|
1389
|
+
}
|
|
1390
|
+
} catch (e) {
|
|
1391
|
+
result.exists = false;
|
|
1392
|
+
return result;
|
|
1393
|
+
}
|
|
1394
|
+
const queryRunner = dataSource.createQueryRunner();
|
|
1395
|
+
if (dataSource.migrations && dataSource.migrations.length > 0) {
|
|
1396
|
+
const migrationExecutor = new typeorm.MigrationExecutor(dataSource, queryRunner);
|
|
1397
|
+
result.migrationsPending = await migrationExecutor.getPendingMigrations();
|
|
1398
|
+
result.schema = result.migrationsPending.length === 0;
|
|
1399
|
+
} else {
|
|
1400
|
+
let schema;
|
|
1401
|
+
if (hasStringProperty(dataSource.driver.options, 'schema')) {
|
|
1402
|
+
schema = dataSource.driver.options.schema;
|
|
1403
|
+
}
|
|
1404
|
+
const migrationsTableName = dataSource.driver.buildTableName(dataSource.options.migrationsTableName || 'migrations', schema, dataSource.driver.database);
|
|
1405
|
+
const migrationsTableExists = await queryRunner.hasTable(migrationsTableName);
|
|
1406
|
+
if (migrationsTableExists) {
|
|
1407
|
+
result.schema = dataSource.entityMetadatas.length === 0;
|
|
1408
|
+
} else {
|
|
1409
|
+
const tableNames = dataSource.entityMetadatas.map((entityMetadata)=>entityMetadata.tablePath);
|
|
1410
|
+
const tables = await queryRunner.getTables(tableNames);
|
|
1411
|
+
if (tables.length === dataSource.entityMetadatas.length) {
|
|
1412
|
+
const { upQueries } = await dataSource.driver.createSchemaBuilder().log();
|
|
1413
|
+
result.schema = upQueries.length === 0;
|
|
1414
|
+
} else {
|
|
1415
|
+
result.schema = false;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
await queryRunner.release();
|
|
1420
|
+
if (dataSourceCleanup) {
|
|
1421
|
+
await dataSource.destroy();
|
|
1422
|
+
}
|
|
1423
|
+
return result;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
function getCharsetFromDataSourceOptions(options) {
|
|
1427
|
+
if (hasOwnProperty(options, 'charset') && typeof options.charset === 'string') {
|
|
1428
|
+
return options.charset;
|
|
1429
|
+
}
|
|
1430
|
+
if (typeof options?.extra?.charset === 'string') {
|
|
1431
|
+
return options.extra.charset;
|
|
1432
|
+
}
|
|
1433
|
+
return undefined;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
function getCharacterSetFromDataSourceOptions(options) {
|
|
1437
|
+
if (hasOwnProperty(options, 'characterSet') && typeof options.characterSet === 'string') {
|
|
1438
|
+
return options.characterSet;
|
|
1439
|
+
}
|
|
1440
|
+
if (typeof options?.extra?.characterSet === 'string') {
|
|
1441
|
+
return options.extra.characterSet;
|
|
1442
|
+
}
|
|
1443
|
+
return undefined;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
function buildDriverOptions(options) {
|
|
1447
|
+
let driverOptions;
|
|
1448
|
+
switch(options.type){
|
|
1449
|
+
case 'mysql':
|
|
1450
|
+
case 'mariadb':
|
|
1451
|
+
case 'postgres':
|
|
1452
|
+
case 'cockroachdb':
|
|
1453
|
+
case 'mssql':
|
|
1454
|
+
case 'oracle':
|
|
1455
|
+
driverOptions = DriverUtils.DriverUtils.buildDriverOptions(options.replication ? options.replication.master : options);
|
|
1456
|
+
break;
|
|
1457
|
+
case 'mongodb':
|
|
1458
|
+
driverOptions = DriverUtils.DriverUtils.buildMongoDBDriverOptions(options);
|
|
1459
|
+
break;
|
|
1460
|
+
default:
|
|
1461
|
+
driverOptions = DriverUtils.DriverUtils.buildDriverOptions(options);
|
|
1462
|
+
}
|
|
1463
|
+
const charset = getCharsetFromDataSourceOptions(options);
|
|
1464
|
+
const characterSet = getCharacterSetFromDataSourceOptions(options);
|
|
1465
|
+
return {
|
|
1466
|
+
host: driverOptions.host,
|
|
1467
|
+
user: driverOptions.user || driverOptions.username,
|
|
1468
|
+
password: driverOptions.password,
|
|
1469
|
+
database: driverOptions.database,
|
|
1470
|
+
port: driverOptions.port,
|
|
1471
|
+
...charset ? {
|
|
1472
|
+
charset
|
|
1473
|
+
} : {},
|
|
1474
|
+
...characterSet ? {
|
|
1475
|
+
characterSet
|
|
1476
|
+
} : {},
|
|
1477
|
+
...driverOptions.ssl ? {
|
|
1478
|
+
ssl: driverOptions.ssl
|
|
1479
|
+
} : {},
|
|
1480
|
+
...driverOptions.url ? {
|
|
1481
|
+
url: driverOptions.url
|
|
1482
|
+
} : {},
|
|
1483
|
+
...driverOptions.connectString ? {
|
|
1484
|
+
connectString: driverOptions.connectString
|
|
1485
|
+
} : {},
|
|
1486
|
+
...driverOptions.sid ? {
|
|
1487
|
+
sid: driverOptions.sid
|
|
1488
|
+
} : {},
|
|
1489
|
+
...driverOptions.serviceName ? {
|
|
1490
|
+
serviceName: driverOptions.serviceName
|
|
1491
|
+
} : {},
|
|
1492
|
+
...driverOptions.template ? {
|
|
1493
|
+
template: driverOptions.template
|
|
1494
|
+
} : {},
|
|
1495
|
+
...options.extra ? {
|
|
1496
|
+
extra: options.extra
|
|
1497
|
+
} : {},
|
|
1498
|
+
...driverOptions.domain ? {
|
|
1499
|
+
domain: driverOptions.domain
|
|
1500
|
+
} : {},
|
|
1501
|
+
...driverOptions.schema ? {
|
|
1502
|
+
schema: driverOptions.schema
|
|
1503
|
+
} : {}
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
const driversRequireDatabaseOption = [
|
|
1508
|
+
'sqlite',
|
|
1509
|
+
'better-sqlite3'
|
|
1510
|
+
];
|
|
1511
|
+
function createDriver(connectionOptions) {
|
|
1512
|
+
const fakeConnection = {
|
|
1513
|
+
options: {
|
|
1514
|
+
type: connectionOptions.type,
|
|
1515
|
+
...driversRequireDatabaseOption.indexOf(connectionOptions.type) !== -1 ? {
|
|
1516
|
+
database: connectionOptions.database
|
|
1517
|
+
} : {}
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
const driverFactory = new DriverFactory.DriverFactory();
|
|
1521
|
+
return driverFactory.create(fakeConnection);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
async function normalizeDataSourceOptions(context) {
|
|
1525
|
+
let options;
|
|
1526
|
+
if (context.options) {
|
|
1527
|
+
options = {
|
|
1528
|
+
...context.options
|
|
1529
|
+
};
|
|
1530
|
+
} else {
|
|
1531
|
+
const dataSource = await findDataSource(context.findOptions);
|
|
1532
|
+
if (dataSource) {
|
|
1533
|
+
options = {
|
|
1534
|
+
...dataSource.options
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
if (!options) {
|
|
1538
|
+
options = {
|
|
1539
|
+
...await buildDataSourceOptions()
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
return {
|
|
1544
|
+
...options,
|
|
1545
|
+
subscribers: [],
|
|
1546
|
+
synchronize: false,
|
|
1547
|
+
migrationsRun: false,
|
|
1548
|
+
dropSchema: false
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
async function buildDatabaseCreateContext(input = {}) {
|
|
1552
|
+
return {
|
|
1553
|
+
...input,
|
|
1554
|
+
options: await normalizeDataSourceOptions(input),
|
|
1555
|
+
synchronize: input.synchronize ?? true,
|
|
1556
|
+
ifNotExist: input.ifNotExist ?? true
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
async function buildDatabaseDropContext(input = {}) {
|
|
1560
|
+
return {
|
|
1561
|
+
...input,
|
|
1562
|
+
options: await normalizeDataSourceOptions(input),
|
|
1563
|
+
ifExist: input.ifExist ?? true
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
class GenerateCommand extends MigrationGenerateCommand.MigrationGenerateCommand {
|
|
1568
|
+
static prettify(query) {
|
|
1569
|
+
return this.prettifyQuery(query);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
function queryParams(parameters) {
|
|
1573
|
+
if (!parameters || !parameters.length) {
|
|
1574
|
+
return '';
|
|
1575
|
+
}
|
|
1576
|
+
return `, ${JSON.stringify(parameters)}`;
|
|
1577
|
+
}
|
|
1578
|
+
function buildTemplate(name, timestamp, upStatements, downStatements) {
|
|
1579
|
+
const migrationName = `${pascalCase.pascalCase(name)}${timestamp}`;
|
|
1580
|
+
const up = upStatements.map((statement)=>` ${statement}`);
|
|
1581
|
+
const down = downStatements.map((statement)=>` ${statement}`);
|
|
1582
|
+
return `import type { MigrationInterface, QueryRunner } from 'typeorm';
|
|
1583
|
+
|
|
1584
|
+
export class ${migrationName} implements MigrationInterface {
|
|
1585
|
+
name = '${migrationName}';
|
|
1586
|
+
|
|
1587
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
1588
|
+
${up.join(`
|
|
1589
|
+
`)}
|
|
1590
|
+
}
|
|
1591
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
1592
|
+
${down.join(`
|
|
1593
|
+
`)}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
`;
|
|
1597
|
+
}
|
|
1598
|
+
async function generateMigration(context) {
|
|
1599
|
+
context.name = context.name || 'Default';
|
|
1600
|
+
const timestamp = context.timestamp || new Date().getTime();
|
|
1601
|
+
const fileName = `${timestamp}-${context.name}.ts`;
|
|
1602
|
+
const { dataSource } = context;
|
|
1603
|
+
const up = [];
|
|
1604
|
+
const down = [];
|
|
1605
|
+
const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
|
|
1606
|
+
if (context.prettify) {
|
|
1607
|
+
sqlInMemory.upQueries.forEach((upQuery)=>{
|
|
1608
|
+
upQuery.query = GenerateCommand.prettify(upQuery.query);
|
|
1609
|
+
});
|
|
1610
|
+
sqlInMemory.downQueries.forEach((downQuery)=>{
|
|
1611
|
+
downQuery.query = GenerateCommand.prettify(downQuery.query);
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
sqlInMemory.upQueries.forEach((upQuery)=>{
|
|
1615
|
+
up.push(`await queryRunner.query(\`${upQuery.query.replace(/`/g, '\\`')}\`${queryParams(upQuery.parameters)});`);
|
|
1616
|
+
});
|
|
1617
|
+
sqlInMemory.downQueries.forEach((downQuery)=>{
|
|
1618
|
+
down.push(`await queryRunner.query(\`${downQuery.query.replace(/`/g, '\\`')}\`${queryParams(downQuery.parameters)});`);
|
|
1619
|
+
});
|
|
1620
|
+
if (up.length === 0 && down.length === 0) {
|
|
1621
|
+
return {
|
|
1622
|
+
up,
|
|
1623
|
+
down
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
const content = buildTemplate(context.name, timestamp, up, down.reverse());
|
|
1627
|
+
if (!context.preview) {
|
|
1628
|
+
let directoryPath;
|
|
1629
|
+
if (context.directoryPath) {
|
|
1630
|
+
if (!path.isAbsolute(context.directoryPath)) {
|
|
1631
|
+
directoryPath = path.join(process$1.cwd(), context.directoryPath);
|
|
1632
|
+
} else {
|
|
1633
|
+
directoryPath = context.directoryPath;
|
|
1634
|
+
}
|
|
1635
|
+
} else {
|
|
1636
|
+
directoryPath = path.join(process$1.cwd(), 'migrations');
|
|
1637
|
+
}
|
|
1638
|
+
try {
|
|
1639
|
+
await fs.promises.access(directoryPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
1640
|
+
} catch (e) {
|
|
1641
|
+
await fs.promises.mkdir(directoryPath, {
|
|
1642
|
+
recursive: true
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
await fs.promises.writeFile(path.join(directoryPath, fileName), content, {
|
|
1646
|
+
encoding: 'utf-8'
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
return {
|
|
1650
|
+
up,
|
|
1651
|
+
down,
|
|
1652
|
+
content
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/* istanbul ignore next */ function existsQuery(builder, inverse = false) {
|
|
1657
|
+
return `${inverse ? 'not ' : ''}exists (${builder.getQuery()})`;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
async function synchronizeDatabaseSchema(input) {
|
|
1661
|
+
let dataSource;
|
|
1662
|
+
let options;
|
|
1663
|
+
if (typeorm.InstanceChecker.isDataSource(input)) {
|
|
1664
|
+
dataSource = input;
|
|
1665
|
+
options = dataSource.options;
|
|
1666
|
+
} else {
|
|
1667
|
+
options = input;
|
|
1668
|
+
dataSource = new typeorm.DataSource(options);
|
|
1669
|
+
}
|
|
1670
|
+
if (!dataSource.isInitialized) {
|
|
1671
|
+
await dataSource.initialize();
|
|
1672
|
+
}
|
|
1673
|
+
let migrationsCount = 0;
|
|
1674
|
+
if (options.migrations) {
|
|
1675
|
+
migrationsCount = Array.isArray(options.migrations) ? options.migrations.length : Object.keys(options.migrations).length;
|
|
1676
|
+
}
|
|
1677
|
+
let migrations = [];
|
|
1678
|
+
if (migrationsCount > 0) {
|
|
1679
|
+
migrations = await dataSource.runMigrations({
|
|
1680
|
+
transaction: options.migrationsTransactionMode
|
|
1681
|
+
});
|
|
1682
|
+
} else {
|
|
1683
|
+
await dataSource.synchronize(false);
|
|
1684
|
+
}
|
|
1685
|
+
if (!typeorm.InstanceChecker.isDataSource(input)) {
|
|
1686
|
+
await dataSource.destroy();
|
|
1687
|
+
}
|
|
1688
|
+
return migrations;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
async function createSimplePostgresConnection(driver, options, operationContext) {
|
|
1692
|
+
/**
|
|
1693
|
+
* pg library
|
|
1694
|
+
*/ const { Client } = driver.postgres;
|
|
1695
|
+
const data = {
|
|
1696
|
+
host: options.host,
|
|
1697
|
+
port: options.port,
|
|
1698
|
+
user: options.user,
|
|
1699
|
+
password: options.password,
|
|
1700
|
+
ssl: options.ssl,
|
|
1701
|
+
schema: options.schema,
|
|
1702
|
+
...options.extra ? options.extra : {}
|
|
1703
|
+
};
|
|
1704
|
+
if (typeof operationContext.initialDatabase === 'string') {
|
|
1705
|
+
data.database = operationContext.initialDatabase;
|
|
1706
|
+
}
|
|
1707
|
+
const client = new Client(data);
|
|
1708
|
+
await client.connect();
|
|
1709
|
+
return client;
|
|
1710
|
+
}
|
|
1711
|
+
async function executeSimplePostgresQuery(connection, query, endConnection = true) {
|
|
1712
|
+
return new Promise((resolve, reject)=>{
|
|
1713
|
+
connection.query(query, (queryErr, queryResult)=>{
|
|
1714
|
+
if (endConnection) {
|
|
1715
|
+
connection.end();
|
|
1716
|
+
}
|
|
1717
|
+
if (queryErr) {
|
|
1718
|
+
reject(queryErr);
|
|
1719
|
+
}
|
|
1720
|
+
resolve(queryResult);
|
|
1721
|
+
});
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
async function createPostgresDatabase(input = {}) {
|
|
1725
|
+
const context = await buildDatabaseCreateContext(input);
|
|
1726
|
+
const options = buildDriverOptions(context.options);
|
|
1727
|
+
const driver = createDriver(context.options);
|
|
1728
|
+
const connection = await createSimplePostgresConnection(driver, options, context);
|
|
1729
|
+
if (context.ifNotExist) {
|
|
1730
|
+
const existQuery = `SELECT * FROM pg_database WHERE lower(datname) = lower('${options.database}');`;
|
|
1731
|
+
const existResult = await executeSimplePostgresQuery(connection, existQuery, false);
|
|
1732
|
+
if (locter.isObject(existResult) && hasOwnProperty(existResult, 'rows') && Array.isArray(existResult.rows) && existResult.rows.length > 0) {
|
|
1733
|
+
await connection.end();
|
|
1734
|
+
return Promise.resolve();
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/postgres/PostgresQueryRunner.ts#L326
|
|
1739
|
+
*/ let query = `CREATE DATABASE "${options.database}"`;
|
|
1740
|
+
if (typeof options.template === 'string') {
|
|
1741
|
+
query += ` TEMPLATE "${options.template}"`;
|
|
1742
|
+
} else if (typeof options.characterSet === 'string') {
|
|
1743
|
+
query += ` WITH ENCODING '${options.characterSet}'`;
|
|
1744
|
+
}
|
|
1745
|
+
const result = await executeSimplePostgresQuery(connection, query);
|
|
1746
|
+
if (typeof options.schema === 'string' && options.schema !== 'public') {
|
|
1747
|
+
const schemaConnection = await createSimplePostgresConnection(driver, options, {
|
|
1748
|
+
...context,
|
|
1749
|
+
initialDatabase: options.database
|
|
1750
|
+
});
|
|
1751
|
+
const schemaQuery = `CREATE SCHEMA IF NOT EXISTS "${options.schema}"`;
|
|
1752
|
+
await executeSimplePostgresQuery(schemaConnection, schemaQuery);
|
|
1753
|
+
}
|
|
1754
|
+
if (context.synchronize) {
|
|
1755
|
+
await synchronizeDatabaseSchema(context.options);
|
|
1756
|
+
}
|
|
1757
|
+
return result;
|
|
1758
|
+
}
|
|
1759
|
+
async function dropPostgresDatabase(input = {}) {
|
|
1760
|
+
const context = await buildDatabaseDropContext(input);
|
|
1761
|
+
const options = buildDriverOptions(context.options);
|
|
1762
|
+
const driver = createDriver(context.options);
|
|
1763
|
+
const connection = await createSimplePostgresConnection(driver, options, context);
|
|
1764
|
+
/**
|
|
1765
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/postgres/PostgresQueryRunner.ts#L343
|
|
1766
|
+
*/ const query = context.ifExist ? `DROP DATABASE IF EXISTS "${options.database}"` : `DROP DATABASE "${options.database}"`;
|
|
1767
|
+
return executeSimplePostgresQuery(connection, query);
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
async function executeSimpleCockroachDBQuery(connection, query, endConnection = true) {
|
|
1771
|
+
return new Promise((resolve, reject)=>{
|
|
1772
|
+
connection.query(query, (queryErr, queryResult)=>{
|
|
1773
|
+
if (endConnection) {
|
|
1774
|
+
connection.end();
|
|
1775
|
+
}
|
|
1776
|
+
if (queryErr) {
|
|
1777
|
+
reject(queryErr);
|
|
1778
|
+
}
|
|
1779
|
+
resolve(queryResult);
|
|
1780
|
+
});
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
async function createCockroachDBDatabase(input = {}) {
|
|
1784
|
+
const context = await buildDatabaseCreateContext(input);
|
|
1785
|
+
const options = buildDriverOptions(context.options);
|
|
1786
|
+
const driver = createDriver(context.options);
|
|
1787
|
+
const connection = await createSimplePostgresConnection(driver, options, context);
|
|
1788
|
+
/**
|
|
1789
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/cockroachdb/CockroachQueryRunner.ts#L347
|
|
1790
|
+
*/ const query = `CREATE DATABASE ${context.ifNotExist ? 'IF NOT EXISTS ' : ''} "${options.database}"`;
|
|
1791
|
+
const result = await executeSimpleCockroachDBQuery(connection, query);
|
|
1792
|
+
if (context.synchronize) {
|
|
1793
|
+
await synchronizeDatabaseSchema(context.options);
|
|
1794
|
+
}
|
|
1795
|
+
return result;
|
|
1796
|
+
}
|
|
1797
|
+
async function dropCockroachDBDatabase(input = {}) {
|
|
1798
|
+
const context = await buildDatabaseDropContext(input);
|
|
1799
|
+
const options = buildDriverOptions(context.options);
|
|
1800
|
+
const driver = createDriver(context.options);
|
|
1801
|
+
const connection = await createSimplePostgresConnection(driver, options, context);
|
|
1802
|
+
/**
|
|
1803
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/cockroachdb/CockroachQueryRunner.ts#L356
|
|
1804
|
+
*/ const query = `DROP DATABASE ${context.ifExist ? 'IF EXISTS ' : ''} "${options.database}"`;
|
|
1805
|
+
return executeSimpleCockroachDBQuery(connection, query);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
async function createSimpleMongoDBConnection(driver, options) {
|
|
1809
|
+
/**
|
|
1810
|
+
* mongodb library
|
|
1811
|
+
*/ const { MongoClient } = driver.mongodb;
|
|
1812
|
+
let url = 'mongodb://';
|
|
1813
|
+
if (options.user && options.password) {
|
|
1814
|
+
url += `${options.user}:${options.password}@`;
|
|
1815
|
+
}
|
|
1816
|
+
url += `${options.host || '127.0.0.1'}:${options.port || 27017}/${options.database}`;
|
|
1817
|
+
if (options.ssl) {
|
|
1818
|
+
url += '?tls=true';
|
|
1819
|
+
}
|
|
1820
|
+
const client = new MongoClient(url);
|
|
1821
|
+
await client.connect();
|
|
1822
|
+
return client;
|
|
1823
|
+
}
|
|
1824
|
+
async function createMongoDBDatabase(input = {}) {
|
|
1825
|
+
const context = await buildDatabaseCreateContext(input);
|
|
1826
|
+
const options = buildDriverOptions(context.options);
|
|
1827
|
+
const driver = createDriver(context.options);
|
|
1828
|
+
// connection setup, will create the database on the fly.
|
|
1829
|
+
const client = await createSimpleMongoDBConnection(driver, options);
|
|
1830
|
+
await client.close();
|
|
1831
|
+
if (context.synchronize) {
|
|
1832
|
+
await synchronizeDatabaseSchema(context.options);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
async function dropMongoDBDatabase(input = {}) {
|
|
1836
|
+
const context = await buildDatabaseDropContext(input);
|
|
1837
|
+
const options = buildDriverOptions(context.options);
|
|
1838
|
+
const driver = createDriver(context.options);
|
|
1839
|
+
const client = await createSimpleMongoDBConnection(driver, options);
|
|
1840
|
+
const result = await client.dropDatabase();
|
|
1841
|
+
await client.close();
|
|
1842
|
+
return result;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
async function createSimpleMsSQLConnection(driver, options) {
|
|
1846
|
+
const option = {
|
|
1847
|
+
user: options.user,
|
|
1848
|
+
password: options.password,
|
|
1849
|
+
server: options.host,
|
|
1850
|
+
port: options.port || 1433,
|
|
1851
|
+
...options.extra ? options.extra : {},
|
|
1852
|
+
...options.domain ? {
|
|
1853
|
+
domain: options.domain
|
|
1854
|
+
} : {}
|
|
1855
|
+
};
|
|
1856
|
+
await driver.mssql.connect(option);
|
|
1857
|
+
return driver.mssql;
|
|
1858
|
+
}
|
|
1859
|
+
async function createMsSQLDatabase(input = {}) {
|
|
1860
|
+
const context = await buildDatabaseCreateContext(input);
|
|
1861
|
+
const options = buildDriverOptions(context.options);
|
|
1862
|
+
const driver = createDriver(context.options);
|
|
1863
|
+
const connection = await createSimpleMsSQLConnection(driver, options);
|
|
1864
|
+
/**
|
|
1865
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/sqlserver/SqlServerQueryRunner.ts#L416
|
|
1866
|
+
*/ let query = context.ifNotExist ? `IF DB_ID('${options.database}') IS NULL CREATE DATABASE "${options.database}"` : `CREATE DATABASE "${options.database}"`;
|
|
1867
|
+
if (typeof options.characterSet === 'string') {
|
|
1868
|
+
query += ` CHARACTER SET ${options.characterSet}`;
|
|
1869
|
+
}
|
|
1870
|
+
const result = await connection.query(query);
|
|
1871
|
+
if (context.synchronize) {
|
|
1872
|
+
await synchronizeDatabaseSchema(context.options);
|
|
1873
|
+
}
|
|
1874
|
+
return result;
|
|
1875
|
+
}
|
|
1876
|
+
async function dropMsSQLDatabase(input = {}) {
|
|
1877
|
+
const context = await buildDatabaseDropContext(input);
|
|
1878
|
+
const options = buildDriverOptions(context.options);
|
|
1879
|
+
const driver = createDriver(context.options);
|
|
1880
|
+
const connection = await createSimpleMsSQLConnection(driver, options);
|
|
1881
|
+
/**
|
|
1882
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/sqlserver/SqlServerQueryRunner.ts#L425
|
|
1883
|
+
*/ const query = context.ifExist ? `IF DB_ID('${options.database}') IS NOT NULL DROP DATABASE "${options.database}"` : `DROP DATABASE "${options.database}"`;
|
|
1884
|
+
return connection.query(query);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
async function createSimpleMySQLConnection(driver, options) {
|
|
1888
|
+
/**
|
|
1889
|
+
* mysql|mysql2 library
|
|
1890
|
+
*/ const { createConnection } = driver.mysql;
|
|
1891
|
+
const option = {
|
|
1892
|
+
host: options.host,
|
|
1893
|
+
user: options.user,
|
|
1894
|
+
password: options.password,
|
|
1895
|
+
port: options.port,
|
|
1896
|
+
ssl: options.ssl,
|
|
1897
|
+
...options.extra ? options.extra : {}
|
|
1898
|
+
};
|
|
1899
|
+
return createConnection(option);
|
|
1900
|
+
}
|
|
1901
|
+
async function executeSimpleMysqlQuery(connection, query, endConnection = true) {
|
|
1902
|
+
return new Promise((resolve, reject)=>{
|
|
1903
|
+
connection.query(query, (queryErr, queryResult)=>{
|
|
1904
|
+
if (endConnection) connection.end();
|
|
1905
|
+
if (queryErr) {
|
|
1906
|
+
reject(queryErr);
|
|
1907
|
+
}
|
|
1908
|
+
resolve(queryResult);
|
|
1909
|
+
});
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
async function createMySQLDatabase(input = {}) {
|
|
1913
|
+
const context = await buildDatabaseCreateContext(input);
|
|
1914
|
+
const options = buildDriverOptions(context.options);
|
|
1915
|
+
const driver = createDriver(context.options);
|
|
1916
|
+
const connection = await createSimpleMySQLConnection(driver, options);
|
|
1917
|
+
/**
|
|
1918
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlQueryRunner.ts#L297
|
|
1919
|
+
*/ let query = context.ifNotExist ? `CREATE DATABASE IF NOT EXISTS \`${options.database}\`` : `CREATE DATABASE \`${options.database}\``;
|
|
1920
|
+
if (typeof options.charset === 'string') {
|
|
1921
|
+
const { charset } = options;
|
|
1922
|
+
let { characterSet } = options;
|
|
1923
|
+
if (typeof characterSet === 'undefined') {
|
|
1924
|
+
if (charset.toLowerCase().startsWith('utf8mb4')) {
|
|
1925
|
+
characterSet = 'utf8mb4';
|
|
1926
|
+
} else if (charset.toLowerCase().startsWith('utf8')) {
|
|
1927
|
+
characterSet = 'utf8';
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
if (typeof characterSet === 'string') {
|
|
1931
|
+
query += ` CHARACTER SET ${characterSet} COLLATE ${charset}`;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
const result = await executeSimpleMysqlQuery(connection, query);
|
|
1935
|
+
if (context.synchronize) {
|
|
1936
|
+
await synchronizeDatabaseSchema(context.options);
|
|
1937
|
+
}
|
|
1938
|
+
return result;
|
|
1939
|
+
}
|
|
1940
|
+
async function dropMySQLDatabase(input = {}) {
|
|
1941
|
+
const context = await buildDatabaseDropContext(input);
|
|
1942
|
+
const options = buildDriverOptions(context.options);
|
|
1943
|
+
const driver = createDriver(context.options);
|
|
1944
|
+
const connection = await createSimpleMySQLConnection(driver, options);
|
|
1945
|
+
/**
|
|
1946
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlQueryRunner.ts#L306
|
|
1947
|
+
*/ const query = context.ifExist ? `DROP DATABASE IF EXISTS \`${options.database}\`` : `DROP DATABASE \`${options.database}\``;
|
|
1948
|
+
await executeSimpleMysqlQuery(connection, 'SET FOREIGN_KEY_CHECKS=0;', false);
|
|
1949
|
+
const result = await executeSimpleMysqlQuery(connection, query, false);
|
|
1950
|
+
await executeSimpleMysqlQuery(connection, 'SET FOREIGN_KEY_CHECKS=1;');
|
|
1951
|
+
return result;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
function createSimpleOracleConnection(driver, options) {
|
|
1955
|
+
const { getConnection } = driver.oracle;
|
|
1956
|
+
if (!options.connectString) {
|
|
1957
|
+
let address = '(PROTOCOL=TCP)';
|
|
1958
|
+
if (options.host) {
|
|
1959
|
+
address += `(HOST=${options.host})`;
|
|
1960
|
+
}
|
|
1961
|
+
if (options.port) {
|
|
1962
|
+
address += `(PORT=${options.port})`;
|
|
1963
|
+
}
|
|
1964
|
+
let connectData = '(SERVER=DEDICATED)';
|
|
1965
|
+
if (options.sid) {
|
|
1966
|
+
connectData += `(SID=${options.sid})`;
|
|
1967
|
+
}
|
|
1968
|
+
if (options.serviceName) {
|
|
1969
|
+
connectData += `(SERVICE_NAME=${options.serviceName})`;
|
|
1970
|
+
}
|
|
1971
|
+
options.connectString = `(DESCRIPTION=(ADDRESS=${address})(CONNECT_DATA=${connectData}))`;
|
|
1972
|
+
}
|
|
1973
|
+
return getConnection({
|
|
1974
|
+
user: options.user,
|
|
1975
|
+
password: options.password,
|
|
1976
|
+
connectString: options.connectString || options.url,
|
|
1977
|
+
...options.extra ? options.extra : {}
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
async function createOracleDatabase(input = {}) {
|
|
1981
|
+
const context = await buildDatabaseCreateContext(input);
|
|
1982
|
+
const options = buildDriverOptions(context.options);
|
|
1983
|
+
const driver = createDriver(context.options);
|
|
1984
|
+
const connection = createSimpleOracleConnection(driver, options);
|
|
1985
|
+
/**
|
|
1986
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/oracle/OracleQueryRunner.ts#L295
|
|
1987
|
+
*/ const query = `CREATE DATABASE IF NOT EXISTS ${options.database}`;
|
|
1988
|
+
const result = await connection.execute(query);
|
|
1989
|
+
if (context.synchronize) {
|
|
1990
|
+
await synchronizeDatabaseSchema(context.options);
|
|
1991
|
+
}
|
|
1992
|
+
return result;
|
|
1993
|
+
}
|
|
1994
|
+
async function dropOracleDatabase(_context = {}) {
|
|
1995
|
+
/**
|
|
1996
|
+
* @link https://github.com/typeorm/typeorm/blob/master/src/driver/oracle/OracleQueryRunner.ts#L295
|
|
1997
|
+
*/ return Promise.resolve();
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
async function createSQLiteDatabase(input = {}) {
|
|
2001
|
+
const context = await buildDatabaseCreateContext(input);
|
|
2002
|
+
const options = buildDriverOptions(context.options);
|
|
2003
|
+
if (!options.database) {
|
|
2004
|
+
throw OptionsError.databaseNotDefined();
|
|
2005
|
+
}
|
|
2006
|
+
const filePath = path.isAbsolute(options.database) ? options.database : path.join(process.cwd(), options.database);
|
|
2007
|
+
const directoryPath = path.dirname(filePath);
|
|
2008
|
+
await fs.promises.access(directoryPath, fs.constants.W_OK);
|
|
2009
|
+
if (context.synchronize) {
|
|
2010
|
+
await synchronizeDatabaseSchema(context.options);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
async function dropSQLiteDatabase(input = {}) {
|
|
2014
|
+
const context = await buildDatabaseDropContext(input);
|
|
2015
|
+
const options = buildDriverOptions(context.options);
|
|
2016
|
+
if (!options.database) {
|
|
2017
|
+
throw OptionsError.databaseNotDefined();
|
|
2018
|
+
}
|
|
2019
|
+
const filePath = path.isAbsolute(options.database) ? options.database : path.join(process.cwd(), options.database);
|
|
2020
|
+
try {
|
|
2021
|
+
await fs.promises.access(filePath, fs.constants.F_OK | fs.constants.W_OK);
|
|
2022
|
+
if (context.ifExist) {
|
|
2023
|
+
await fs.promises.unlink(filePath);
|
|
2024
|
+
}
|
|
2025
|
+
} catch (e) {
|
|
2026
|
+
// ...
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* Create database for specified driver in ConnectionOptions.
|
|
2032
|
+
*
|
|
2033
|
+
* @throws DriverError
|
|
2034
|
+
* @throws OptionsError
|
|
2035
|
+
*
|
|
2036
|
+
* @param input
|
|
2037
|
+
*/ async function createDatabase(input = {}) {
|
|
2038
|
+
const context = await buildDatabaseCreateContext(input);
|
|
2039
|
+
let output;
|
|
2040
|
+
switch(context.options.type){
|
|
2041
|
+
case 'mongodb':
|
|
2042
|
+
output = await createMongoDBDatabase(context);
|
|
2043
|
+
break;
|
|
2044
|
+
case 'mysql':
|
|
2045
|
+
case 'mariadb':
|
|
2046
|
+
output = await createMySQLDatabase(context);
|
|
2047
|
+
break;
|
|
2048
|
+
case 'postgres':
|
|
2049
|
+
output = await createPostgresDatabase(context);
|
|
2050
|
+
break;
|
|
2051
|
+
case 'cockroachdb':
|
|
2052
|
+
output = await createCockroachDBDatabase(context);
|
|
2053
|
+
break;
|
|
2054
|
+
case 'sqlite':
|
|
2055
|
+
case 'better-sqlite3':
|
|
2056
|
+
output = await createSQLiteDatabase(context);
|
|
2057
|
+
break;
|
|
2058
|
+
case 'oracle':
|
|
2059
|
+
output = await createOracleDatabase(context);
|
|
2060
|
+
break;
|
|
2061
|
+
case 'mssql':
|
|
2062
|
+
output = await createMsSQLDatabase(context);
|
|
2063
|
+
break;
|
|
2064
|
+
default:
|
|
2065
|
+
throw DriverError.notSupported(context.options.type);
|
|
2066
|
+
}
|
|
2067
|
+
return output;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
/**
|
|
2071
|
+
* Drop database for specified driver in ConnectionOptions.
|
|
2072
|
+
*
|
|
2073
|
+
* @throws DriverError
|
|
2074
|
+
* @throws OptionsError
|
|
2075
|
+
*
|
|
2076
|
+
* @param input
|
|
2077
|
+
*/ async function dropDatabase(input = {}) {
|
|
2078
|
+
const context = await buildDatabaseDropContext(input);
|
|
2079
|
+
let output;
|
|
2080
|
+
switch(context.options.type){
|
|
2081
|
+
case 'mongodb':
|
|
2082
|
+
output = await dropMongoDBDatabase(context);
|
|
2083
|
+
break;
|
|
2084
|
+
case 'mysql':
|
|
2085
|
+
case 'mariadb':
|
|
2086
|
+
output = await dropMySQLDatabase(context);
|
|
2087
|
+
break;
|
|
2088
|
+
case 'postgres':
|
|
2089
|
+
output = await dropPostgresDatabase(context);
|
|
2090
|
+
break;
|
|
2091
|
+
case 'cockroachdb':
|
|
2092
|
+
output = await dropCockroachDBDatabase(context);
|
|
2093
|
+
break;
|
|
2094
|
+
case 'sqlite':
|
|
2095
|
+
case 'better-sqlite3':
|
|
2096
|
+
output = await dropSQLiteDatabase(context);
|
|
2097
|
+
break;
|
|
2098
|
+
case 'oracle':
|
|
2099
|
+
output = await dropOracleDatabase(context);
|
|
2100
|
+
break;
|
|
2101
|
+
case 'mssql':
|
|
2102
|
+
output = await dropMsSQLDatabase(context);
|
|
2103
|
+
break;
|
|
2104
|
+
default:
|
|
2105
|
+
throw DriverError.notSupported(context.options.type);
|
|
2106
|
+
}
|
|
2107
|
+
return output;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
class DatabaseCreateCommand {
|
|
2111
|
+
builder(args) {
|
|
2112
|
+
return args.option('preserveFilePaths', {
|
|
2113
|
+
default: false,
|
|
2114
|
+
type: 'boolean',
|
|
2115
|
+
describe: 'This option indicates if file paths should be preserved.'
|
|
2116
|
+
}).option('root', {
|
|
2117
|
+
alias: 'r',
|
|
2118
|
+
default: process.cwd(),
|
|
2119
|
+
describe: 'Root directory of the project.'
|
|
2120
|
+
}).option('tsconfig', {
|
|
2121
|
+
alias: 'tc',
|
|
2122
|
+
default: 'tsconfig.json',
|
|
2123
|
+
describe: 'Name (or relative path incl. name) of the tsconfig file.'
|
|
2124
|
+
}).option('dataSource', {
|
|
2125
|
+
alias: 'd',
|
|
2126
|
+
default: 'data-source',
|
|
2127
|
+
describe: 'Name (or relative path incl. name) of the data-source file.'
|
|
2128
|
+
}).option('synchronize', {
|
|
2129
|
+
alias: 's',
|
|
2130
|
+
default: 'yes',
|
|
2131
|
+
describe: 'Create database schema for all entities.',
|
|
2132
|
+
choices: [
|
|
2133
|
+
'yes',
|
|
2134
|
+
'no'
|
|
2135
|
+
]
|
|
2136
|
+
}).option('initialDatabase', {
|
|
2137
|
+
describe: 'Specify the initial database to connect to.'
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
async handler(raw) {
|
|
2141
|
+
const args = raw;
|
|
2142
|
+
let tsconfig;
|
|
2143
|
+
let sourcePath = resolveFilePath(args.dataSource, args.root);
|
|
2144
|
+
if (!args.preserveFilePaths) {
|
|
2145
|
+
tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig));
|
|
2146
|
+
sourcePath = await adjustFilePath(sourcePath, tsconfig);
|
|
2147
|
+
}
|
|
2148
|
+
const source = parseFilePath(sourcePath);
|
|
2149
|
+
consola.consola.info(`DataSource Directory: ${source.directory}`);
|
|
2150
|
+
consola.consola.info(`DataSource Name: ${source.name}`);
|
|
2151
|
+
const dataSourceOptions = await buildDataSourceOptions({
|
|
2152
|
+
directory: source.directory,
|
|
2153
|
+
dataSourceName: source.name,
|
|
2154
|
+
tsconfig,
|
|
2155
|
+
preserveFilePaths: args.preserveFilePaths
|
|
2156
|
+
});
|
|
2157
|
+
const context = {
|
|
2158
|
+
ifNotExist: true,
|
|
2159
|
+
options: dataSourceOptions,
|
|
2160
|
+
synchronize: args.synchronize === 'yes'
|
|
2161
|
+
};
|
|
2162
|
+
if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') {
|
|
2163
|
+
context.initialDatabase = args.initialDatabase;
|
|
2164
|
+
}
|
|
2165
|
+
try {
|
|
2166
|
+
await createDatabase(context);
|
|
2167
|
+
consola.consola.success('Created database.');
|
|
2168
|
+
process.exit(0);
|
|
2169
|
+
} catch (e) {
|
|
2170
|
+
consola.consola.warn('Failed to create database.');
|
|
2171
|
+
consola.consola.error(e);
|
|
2172
|
+
process.exit(1);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
constructor(){
|
|
2176
|
+
this.command = 'db:create';
|
|
2177
|
+
this.describe = 'Create database.';
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
class DatabaseDropCommand {
|
|
2182
|
+
builder(args) {
|
|
2183
|
+
return args.option('preserveFilePaths', {
|
|
2184
|
+
default: false,
|
|
2185
|
+
type: 'boolean',
|
|
2186
|
+
describe: 'This option indicates if file paths should be preserved.'
|
|
2187
|
+
}).option('root', {
|
|
2188
|
+
alias: 'r',
|
|
2189
|
+
default: process.cwd(),
|
|
2190
|
+
describe: 'Root directory of the project.'
|
|
2191
|
+
}).option('tsconfig', {
|
|
2192
|
+
alias: 'tc',
|
|
2193
|
+
default: 'tsconfig.json',
|
|
2194
|
+
describe: 'Name (or relative path incl. name) of the tsconfig file.'
|
|
2195
|
+
}).option('dataSource', {
|
|
2196
|
+
alias: 'd',
|
|
2197
|
+
default: 'data-source',
|
|
2198
|
+
describe: 'Name (or relative path incl. name) of the data-source file.'
|
|
2199
|
+
}).option('initialDatabase', {
|
|
2200
|
+
describe: 'Specify the initial database to connect to.'
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
async handler(raw) {
|
|
2204
|
+
const args = raw;
|
|
2205
|
+
let tsconfig;
|
|
2206
|
+
let sourcePath = resolveFilePath(args.dataSource, args.root);
|
|
2207
|
+
if (!args.preserveFilePaths) {
|
|
2208
|
+
tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig));
|
|
2209
|
+
sourcePath = await adjustFilePath(sourcePath, tsconfig);
|
|
2210
|
+
}
|
|
2211
|
+
const source = parseFilePath(sourcePath);
|
|
2212
|
+
consola.consola.info(`DataSource Directory: ${source.directory}`);
|
|
2213
|
+
consola.consola.info(`DataSource Name: ${source.name}`);
|
|
2214
|
+
const dataSourceOptions = await buildDataSourceOptions({
|
|
2215
|
+
directory: source.directory,
|
|
2216
|
+
dataSourceName: source.name,
|
|
2217
|
+
tsconfig,
|
|
2218
|
+
preserveFilePaths: args.preserveFilePaths
|
|
2219
|
+
});
|
|
2220
|
+
const context = {
|
|
2221
|
+
ifExist: true,
|
|
2222
|
+
options: dataSourceOptions
|
|
2223
|
+
};
|
|
2224
|
+
if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') {
|
|
2225
|
+
context.initialDatabase = args.initialDatabase;
|
|
2226
|
+
}
|
|
2227
|
+
try {
|
|
2228
|
+
await dropDatabase(context);
|
|
2229
|
+
consola.consola.success('Dropped database.');
|
|
2230
|
+
process.exit(0);
|
|
2231
|
+
} catch (e) {
|
|
2232
|
+
consola.consola.warn('Failed to drop database.');
|
|
2233
|
+
consola.consola.error(e);
|
|
2234
|
+
process.exit(1);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
constructor(){
|
|
2238
|
+
this.command = 'db:drop';
|
|
2239
|
+
this.describe = 'Drop database.';
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
class SeederEntity {
|
|
2244
|
+
constructor(ctx){
|
|
2245
|
+
this.id = ctx.id;
|
|
2246
|
+
this.timestamp = ctx.timestamp;
|
|
2247
|
+
this.name = ctx.name;
|
|
2248
|
+
if (ctx.constructor) {
|
|
2249
|
+
this.instance = new ctx.constructor();
|
|
2250
|
+
}
|
|
2251
|
+
this.fileName = ctx.fileName;
|
|
2252
|
+
this.filePath = ctx.filePath;
|
|
2253
|
+
}
|
|
2254
|
+
get trackExecution() {
|
|
2255
|
+
if (typeof this.instance === 'undefined') {
|
|
2256
|
+
return undefined;
|
|
2257
|
+
}
|
|
2258
|
+
return this.instance.track;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
class SeederFactory {
|
|
2263
|
+
// --------------------------------------------------------------
|
|
2264
|
+
constructor(context){
|
|
2265
|
+
this.context = context;
|
|
2266
|
+
}
|
|
2267
|
+
// --------------------------------------------------------------
|
|
2268
|
+
setMeta(value) {
|
|
2269
|
+
this.meta = value;
|
|
2270
|
+
return this;
|
|
2271
|
+
}
|
|
2272
|
+
setLocale(value) {
|
|
2273
|
+
this.faker = undefined;
|
|
2274
|
+
this.locale = Array.isArray(value) ? value : [
|
|
2275
|
+
value
|
|
2276
|
+
];
|
|
2277
|
+
}
|
|
2278
|
+
// --------------------------------------------------------------
|
|
2279
|
+
async make(params, save) {
|
|
2280
|
+
const faker = await this.useFaker();
|
|
2281
|
+
const factoryFn = this.context.factoryFn(faker, this.meta);
|
|
2282
|
+
let entity;
|
|
2283
|
+
if (isPromise(factoryFn)) {
|
|
2284
|
+
entity = await this.resolve(await factoryFn, save);
|
|
2285
|
+
} else {
|
|
2286
|
+
entity = await this.resolve(factoryFn, save);
|
|
2287
|
+
}
|
|
2288
|
+
if (params) {
|
|
2289
|
+
const keys = Object.keys(params);
|
|
2290
|
+
for(let i = 0; i < keys.length; i++){
|
|
2291
|
+
entity[keys[i]] = params[keys[i]];
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
return entity;
|
|
2295
|
+
}
|
|
2296
|
+
// --------------------------------------------------------------
|
|
2297
|
+
async save(params, options) {
|
|
2298
|
+
const dataSource = await useDataSource();
|
|
2299
|
+
const entity = await this.make(params, true);
|
|
2300
|
+
const entityManager = dataSource.getRepository(this.context.entity);
|
|
2301
|
+
return entityManager.save(entity, options);
|
|
2302
|
+
}
|
|
2303
|
+
async saveMany(amount, params, options) {
|
|
2304
|
+
const promises = [];
|
|
2305
|
+
for(let i = 0; i < amount; i++){
|
|
2306
|
+
const item = this.save(params, options);
|
|
2307
|
+
promises.push(item);
|
|
2308
|
+
}
|
|
2309
|
+
return Promise.all(promises);
|
|
2310
|
+
}
|
|
2311
|
+
// --------------------------------------------------------------
|
|
2312
|
+
async resolve(entity, save) {
|
|
2313
|
+
const keys = Object.keys(entity);
|
|
2314
|
+
for(let i = 0; i < keys.length; i++){
|
|
2315
|
+
const key = keys[i];
|
|
2316
|
+
const value = entity[key];
|
|
2317
|
+
if (!hasOwnProperty(entity, key)) {
|
|
2318
|
+
continue;
|
|
2319
|
+
}
|
|
2320
|
+
if (typeof value === 'object' && // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2321
|
+
// @ts-ignore
|
|
2322
|
+
value instanceof SeederFactory) {
|
|
2323
|
+
if (save) {
|
|
2324
|
+
entity[key] = await value.save();
|
|
2325
|
+
} else {
|
|
2326
|
+
entity[key] = await value.make();
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
if (value && hasOwnProperty(value, 'then') && typeof value.then === 'function') {
|
|
2330
|
+
entity[key] = await value;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
return entity;
|
|
2334
|
+
}
|
|
2335
|
+
async useFaker() {
|
|
2336
|
+
if (typeof this.faker !== 'undefined') {
|
|
2337
|
+
return this.faker;
|
|
2338
|
+
}
|
|
2339
|
+
const options = {
|
|
2340
|
+
locale: []
|
|
2341
|
+
};
|
|
2342
|
+
const fakerExports = await locter.load('@faker-js/faker');
|
|
2343
|
+
let names;
|
|
2344
|
+
if (this.locale) {
|
|
2345
|
+
names = Array.isArray(this.locale) ? this.locale : [
|
|
2346
|
+
this.locale
|
|
2347
|
+
];
|
|
2348
|
+
} else {
|
|
2349
|
+
names = [
|
|
2350
|
+
'en'
|
|
2351
|
+
];
|
|
2352
|
+
}
|
|
2353
|
+
for(let i = 0; i < names.length; i++){
|
|
2354
|
+
if (hasOwnProperty(fakerExports, 'default') && locter.isObject(fakerExports.default) && hasOwnProperty(fakerExports.default, names[i])) {
|
|
2355
|
+
options.locale.push(fakerExports.default[names[i]]);
|
|
2356
|
+
continue;
|
|
2357
|
+
}
|
|
2358
|
+
if (hasOwnProperty(fakerExports, names[i])) {
|
|
2359
|
+
options.locale.push(fakerExports[names[i]]);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
this.faker = new fakerExports.Faker(options);
|
|
2363
|
+
return this.faker;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
class SeederFactoryManager {
|
|
2368
|
+
set(entity, factoryFn) {
|
|
2369
|
+
const name = getEntityName(entity);
|
|
2370
|
+
this.items[name] = {
|
|
2371
|
+
factoryFn,
|
|
2372
|
+
entity
|
|
2373
|
+
};
|
|
2374
|
+
return this.items[name];
|
|
2375
|
+
}
|
|
2376
|
+
get(entity) {
|
|
2377
|
+
const name = getEntityName(entity);
|
|
2378
|
+
if (!hasOwnProperty(this.items, name)) {
|
|
2379
|
+
throw new Error(`No seeder factory is registered for the entity: ${name}`);
|
|
2380
|
+
}
|
|
2381
|
+
return new SeederFactory({
|
|
2382
|
+
factoryFn: this.items[name].factoryFn,
|
|
2383
|
+
entity,
|
|
2384
|
+
name
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
constructor(){
|
|
2388
|
+
this.items = {};
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
async function resolveFilePatterns(filesPattern, root) {
|
|
2393
|
+
return locter.locateMany(filesPattern, {
|
|
2394
|
+
...root ? {
|
|
2395
|
+
path: root
|
|
2396
|
+
} : {},
|
|
2397
|
+
ignore: [
|
|
2398
|
+
'**/*.d.ts'
|
|
2399
|
+
]
|
|
2400
|
+
}).then(buildFilePathname);
|
|
2401
|
+
}
|
|
2402
|
+
function resolveFilePaths(filePaths, root) {
|
|
2403
|
+
return filePaths.map((filePath)=>path.isAbsolute(filePath) ? filePath : path.resolve(root || process.cwd(), filePath));
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* Exported only for testing purposes
|
|
2407
|
+
*/ function buildFilePathname(files) {
|
|
2408
|
+
return(// sorting by name so that we can define the order of execution using file names
|
|
2409
|
+
files.sort((a, b)=>a.name > b.name ? 1 : -1).map((el)=>path.join(el.path, el.name + el.extension)));
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
async function prepareSeederSeeds(input, root) {
|
|
2413
|
+
const items = [];
|
|
2414
|
+
let seedFiles = [];
|
|
2415
|
+
const seedConstructors = [];
|
|
2416
|
+
for(let i = 0; i < input.length; i++){
|
|
2417
|
+
const value = input[i];
|
|
2418
|
+
if (typeof value === 'string') {
|
|
2419
|
+
seedFiles.push(value);
|
|
2420
|
+
} else {
|
|
2421
|
+
seedConstructors.push(value);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
if (seedFiles.length > 0) {
|
|
2425
|
+
seedFiles = await resolveFilePatterns(seedFiles, root);
|
|
2426
|
+
seedFiles = resolveFilePaths(seedFiles, root);
|
|
2427
|
+
for(let i = 0; i < seedFiles.length; i++){
|
|
2428
|
+
const moduleExports = await locter.load(seedFiles[i]);
|
|
2429
|
+
let clazzConstructor;
|
|
2430
|
+
const exportKeys = Object.keys(moduleExports);
|
|
2431
|
+
for(let j = 0; j < exportKeys.length; j++){
|
|
2432
|
+
const moduleExport = moduleExports[exportKeys[j]];
|
|
2433
|
+
if (typeof moduleExport === 'function' && moduleExport.prototype) {
|
|
2434
|
+
clazzConstructor = moduleExport;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
if (clazzConstructor) {
|
|
2438
|
+
const fileName = path.basename(seedFiles[i]);
|
|
2439
|
+
const filePath = seedFiles[i];
|
|
2440
|
+
const match = fileName.match(/^([0-9]{13,})-(.*)$/);
|
|
2441
|
+
let timestamp;
|
|
2442
|
+
if (match) {
|
|
2443
|
+
timestamp = parseInt(match[1], 10);
|
|
2444
|
+
}
|
|
2445
|
+
items.push({
|
|
2446
|
+
constructor: clazzConstructor,
|
|
2447
|
+
fileName,
|
|
2448
|
+
filePath,
|
|
2449
|
+
...timestamp ? {
|
|
2450
|
+
timestamp
|
|
2451
|
+
} : {}
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
if (seedConstructors.length > 0) {
|
|
2457
|
+
for(let i = 0; i < seedConstructors.length; i++){
|
|
2458
|
+
items.push({
|
|
2459
|
+
constructor: seedConstructors[i]
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
return items;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
function buildSeederFileTemplate(name, timestamp) {
|
|
2467
|
+
const className = `${pascalCase.pascalCase(name)}${timestamp}`;
|
|
2468
|
+
return `import { DataSource } from 'typeorm';
|
|
2469
|
+
import { Seeder, SeederFactoryManager } from 'typeorm-extension';
|
|
2470
|
+
|
|
2471
|
+
export class ${className} implements Seeder {
|
|
2472
|
+
track = false;
|
|
2473
|
+
|
|
2474
|
+
public async run(
|
|
2475
|
+
dataSource: DataSource,
|
|
2476
|
+
factoryManager: SeederFactoryManager
|
|
2477
|
+
): Promise<any> {
|
|
2478
|
+
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
`;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
let instance;
|
|
2485
|
+
function useSeederFactoryManager() {
|
|
2486
|
+
if (typeof instance !== 'undefined') {
|
|
2487
|
+
return instance;
|
|
2488
|
+
}
|
|
2489
|
+
instance = new SeederFactoryManager();
|
|
2490
|
+
return instance;
|
|
2491
|
+
}
|
|
2492
|
+
function setSeederFactory(entity, factoryFn) {
|
|
2493
|
+
const manager = useSeederFactoryManager();
|
|
2494
|
+
return manager.set(entity, factoryFn);
|
|
2495
|
+
}
|
|
2496
|
+
function useSeederFactory(entity) {
|
|
2497
|
+
const manager = useSeederFactoryManager();
|
|
2498
|
+
return manager.get(entity);
|
|
2499
|
+
}
|
|
2500
|
+
async function prepareSeederFactories(items, root) {
|
|
2501
|
+
let factoryFiles = [];
|
|
2502
|
+
const factoryConfigs = [];
|
|
2503
|
+
for(let i = 0; i < items.length; i++){
|
|
2504
|
+
const value = items[i];
|
|
2505
|
+
if (typeof value === 'string') {
|
|
2506
|
+
factoryFiles.push(value);
|
|
2507
|
+
} else {
|
|
2508
|
+
factoryConfigs.push(value);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
if (factoryFiles.length > 0) {
|
|
2512
|
+
factoryFiles = await resolveFilePatterns(factoryFiles, root);
|
|
2513
|
+
factoryFiles = resolveFilePaths(factoryFiles, root);
|
|
2514
|
+
for(let i = 0; i < factoryFiles.length; i++){
|
|
2515
|
+
await locter.load(factoryFiles[i]);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
if (factoryConfigs.length > 0) {
|
|
2519
|
+
const factoryManager = useSeederFactoryManager();
|
|
2520
|
+
for(let i = 0; i < factoryConfigs.length; i++){
|
|
2521
|
+
factoryManager.set(factoryConfigs[i].entity, factoryConfigs[i].factoryFn);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
class SeederExecutor {
|
|
2527
|
+
constructor(dataSource, options){
|
|
2528
|
+
this.dataSource = dataSource;
|
|
2529
|
+
this.options = options || {};
|
|
2530
|
+
setDataSource(dataSource);
|
|
2531
|
+
this.tableName = this.dataSourceOptions.seedTableName || 'seeds';
|
|
2532
|
+
}
|
|
2533
|
+
async execute(input = {}) {
|
|
2534
|
+
const options = await this.buildOptions(input);
|
|
2535
|
+
if (!options.seeds || options.seeds.length === 0) {
|
|
2536
|
+
return [];
|
|
2537
|
+
}
|
|
2538
|
+
if (options.factories) {
|
|
2539
|
+
await prepareSeederFactories(options.factories, this.options.root);
|
|
2540
|
+
}
|
|
2541
|
+
const seederElements = await prepareSeederSeeds(options.seeds, this.options.root);
|
|
2542
|
+
const all = await this.buildEntities(seederElements);
|
|
2543
|
+
let tracking = !!options.seedTracking;
|
|
2544
|
+
if (!tracking) {
|
|
2545
|
+
tracking = all.some((seed)=>!!seed.trackExecution);
|
|
2546
|
+
}
|
|
2547
|
+
let queryRunner;
|
|
2548
|
+
let existing = [];
|
|
2549
|
+
if (tracking) {
|
|
2550
|
+
queryRunner = this.dataSource.createQueryRunner();
|
|
2551
|
+
await this.createTableIfNotExist(queryRunner);
|
|
2552
|
+
existing = await this.loadExisting(queryRunner);
|
|
2553
|
+
}
|
|
2554
|
+
const isMatch = (seed)=>{
|
|
2555
|
+
if (!options.seedName) {
|
|
2556
|
+
return true;
|
|
2557
|
+
}
|
|
2558
|
+
if (seed.name === options.seedName || seed.fileName === options.seedName) {
|
|
2559
|
+
return true;
|
|
2560
|
+
}
|
|
2561
|
+
if (!seed.filePath) {
|
|
2562
|
+
return false;
|
|
2563
|
+
}
|
|
2564
|
+
if (seed.filePath === options.seedName) {
|
|
2565
|
+
return true;
|
|
2566
|
+
}
|
|
2567
|
+
return resolveFilePath(options.seedName, this.options.root) === seed.filePath;
|
|
2568
|
+
};
|
|
2569
|
+
const pending = all.filter((seed)=>{
|
|
2570
|
+
if (!isMatch(seed)) {
|
|
2571
|
+
return false;
|
|
2572
|
+
}
|
|
2573
|
+
const index = existing.findIndex((el)=>el.name === seed.name);
|
|
2574
|
+
if (index === -1) {
|
|
2575
|
+
return true;
|
|
2576
|
+
}
|
|
2577
|
+
let seedTracking;
|
|
2578
|
+
if (typeof seed.trackExecution !== 'undefined') {
|
|
2579
|
+
seedTracking = seed.trackExecution;
|
|
2580
|
+
} else {
|
|
2581
|
+
seedTracking = options.seedTracking;
|
|
2582
|
+
}
|
|
2583
|
+
return !seedTracking;
|
|
2584
|
+
});
|
|
2585
|
+
if (pending.length === 0) {
|
|
2586
|
+
if (queryRunner) {
|
|
2587
|
+
await queryRunner.release();
|
|
2588
|
+
}
|
|
2589
|
+
return [];
|
|
2590
|
+
}
|
|
2591
|
+
this.dataSource.logger.logSchemaBuild(`${existing.length} seeds are already present in the database.`);
|
|
2592
|
+
this.dataSource.logger.logSchemaBuild(`${all.length} seeds were found in the source code.`);
|
|
2593
|
+
const factoryManager = useSeederFactoryManager();
|
|
2594
|
+
const executed = [];
|
|
2595
|
+
try {
|
|
2596
|
+
for(let i = 0; i < pending.length; i++){
|
|
2597
|
+
const seeder = pending[i].instance;
|
|
2598
|
+
if (!seeder) {
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
pending[i].result = await seeder.run(this.dataSource, factoryManager);
|
|
2602
|
+
let seedTracking;
|
|
2603
|
+
if (typeof pending[i].trackExecution !== 'undefined') {
|
|
2604
|
+
seedTracking = pending[i].trackExecution;
|
|
2605
|
+
} else {
|
|
2606
|
+
seedTracking = options.seedTracking;
|
|
2607
|
+
}
|
|
2608
|
+
if (queryRunner && seedTracking) {
|
|
2609
|
+
await this.track(queryRunner, pending[i]);
|
|
2610
|
+
}
|
|
2611
|
+
this.dataSource.logger.logSchemaBuild(`Seed ${pending[i].name} has been executed successfully.`);
|
|
2612
|
+
executed.push(pending[i]);
|
|
2613
|
+
}
|
|
2614
|
+
} finally{
|
|
2615
|
+
if (queryRunner) {
|
|
2616
|
+
await queryRunner.release();
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
return executed;
|
|
2620
|
+
}
|
|
2621
|
+
async loadExisting(queryRunner) {
|
|
2622
|
+
if (this.dataSource.driver.options.type === 'mongodb') {
|
|
2623
|
+
const mongoRunner = queryRunner;
|
|
2624
|
+
return mongoRunner.cursor(this.tableName, {}).sort({
|
|
2625
|
+
_id: -1
|
|
2626
|
+
}).toArray();
|
|
2627
|
+
}
|
|
2628
|
+
const raw = await this.dataSource.manager.createQueryBuilder(queryRunner).select().orderBy(this.dataSource.driver.escape('id'), 'DESC').from(this.table, this.tableName).getRawMany();
|
|
2629
|
+
return raw.map((migrationRaw)=>new SeederEntity({
|
|
2630
|
+
id: parseInt(migrationRaw.id, 10),
|
|
2631
|
+
timestamp: parseInt(migrationRaw.timestamp, 10),
|
|
2632
|
+
name: migrationRaw.name,
|
|
2633
|
+
constructor: undefined
|
|
2634
|
+
}));
|
|
2635
|
+
}
|
|
2636
|
+
/**
|
|
2637
|
+
* Gets all migrations that setup for this connection.
|
|
2638
|
+
*/ async buildEntities(seeds) {
|
|
2639
|
+
if (!seeds) {
|
|
2640
|
+
return [];
|
|
2641
|
+
}
|
|
2642
|
+
let timestampCounter = 0;
|
|
2643
|
+
const entities = seeds.map((element)=>{
|
|
2644
|
+
const { constructor: seed, fileName, filePath } = element;
|
|
2645
|
+
let { timestamp } = element;
|
|
2646
|
+
const className = seed.name || seed.constructor.name;
|
|
2647
|
+
if (!timestamp) {
|
|
2648
|
+
timestamp = this.classNameToTimestamp(className);
|
|
2649
|
+
}
|
|
2650
|
+
const entity = new SeederEntity({
|
|
2651
|
+
fileName,
|
|
2652
|
+
filePath,
|
|
2653
|
+
timestamp: timestamp || timestampCounter,
|
|
2654
|
+
name: className,
|
|
2655
|
+
constructor: seed
|
|
2656
|
+
});
|
|
2657
|
+
timestampCounter++;
|
|
2658
|
+
return entity;
|
|
2659
|
+
});
|
|
2660
|
+
this.checkForDuplicates(entities);
|
|
2661
|
+
// sort them by file name than by timestamp
|
|
2662
|
+
return entities.sort((a, b)=>{
|
|
2663
|
+
if (typeof a.fileName !== 'undefined' && typeof b.fileName !== 'undefined') {
|
|
2664
|
+
return a.fileName > b.fileName ? 1 : -1;
|
|
2665
|
+
}
|
|
2666
|
+
return a.timestamp - b.timestamp;
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2669
|
+
checkForDuplicates(entities) {
|
|
2670
|
+
const names = entities.map((migration)=>migration.name);
|
|
2671
|
+
const duplicates = Array.from(new Set(names.filter((migrationName, index)=>names.indexOf(migrationName) < index)));
|
|
2672
|
+
if (duplicates.length > 0) {
|
|
2673
|
+
throw Error(`Duplicate seeds: ${duplicates.join(', ')}`);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
async createTableIfNotExist(queryRunner) {
|
|
2677
|
+
// If driver is mongo no need to create
|
|
2678
|
+
if (this.dataSource.driver.options.type === 'mongodb') {
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
const tableExist = await queryRunner.hasTable(this.table);
|
|
2682
|
+
if (!tableExist) {
|
|
2683
|
+
await queryRunner.createTable(new typeorm.Table({
|
|
2684
|
+
database: this.database,
|
|
2685
|
+
schema: this.schema,
|
|
2686
|
+
name: this.table,
|
|
2687
|
+
columns: [
|
|
2688
|
+
{
|
|
2689
|
+
name: 'id',
|
|
2690
|
+
type: this.dataSource.driver.normalizeType({
|
|
2691
|
+
type: this.dataSource.driver.mappedDataTypes.migrationId
|
|
2692
|
+
}),
|
|
2693
|
+
isGenerated: true,
|
|
2694
|
+
generationStrategy: 'increment',
|
|
2695
|
+
isPrimary: true,
|
|
2696
|
+
isNullable: false
|
|
2697
|
+
},
|
|
2698
|
+
{
|
|
2699
|
+
name: 'timestamp',
|
|
2700
|
+
type: this.dataSource.driver.normalizeType({
|
|
2701
|
+
type: this.dataSource.driver.mappedDataTypes.migrationTimestamp
|
|
2702
|
+
}),
|
|
2703
|
+
isPrimary: false,
|
|
2704
|
+
isNullable: false
|
|
2705
|
+
},
|
|
2706
|
+
{
|
|
2707
|
+
name: 'name',
|
|
2708
|
+
type: this.dataSource.driver.normalizeType({
|
|
2709
|
+
type: this.dataSource.driver.mappedDataTypes.migrationName
|
|
2710
|
+
}),
|
|
2711
|
+
isNullable: false
|
|
2712
|
+
}
|
|
2713
|
+
]
|
|
2714
|
+
}));
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
async track(queryRunner, seederEntity) {
|
|
2718
|
+
const values = {};
|
|
2719
|
+
if (this.dataSource.driver.options.type === 'mssql') {
|
|
2720
|
+
values.timestamp = new typeorm.MssqlParameter(seederEntity.timestamp, this.dataSource.driver.normalizeType({
|
|
2721
|
+
type: this.dataSource.driver.mappedDataTypes.migrationTimestamp
|
|
2722
|
+
}));
|
|
2723
|
+
values.name = new typeorm.MssqlParameter(seederEntity.name, this.dataSource.driver.normalizeType({
|
|
2724
|
+
type: this.dataSource.driver.mappedDataTypes.migrationName
|
|
2725
|
+
}));
|
|
2726
|
+
} else {
|
|
2727
|
+
values.timestamp = seederEntity.timestamp;
|
|
2728
|
+
values.name = seederEntity.name;
|
|
2729
|
+
}
|
|
2730
|
+
if (this.dataSource.driver.options.type === 'mongodb') {
|
|
2731
|
+
const mongoRunner = queryRunner;
|
|
2732
|
+
await mongoRunner.databaseConnection.db(this.dataSource.driver.database).collection(this.tableName).insertOne(values);
|
|
2733
|
+
} else {
|
|
2734
|
+
const qb = queryRunner.manager.createQueryBuilder();
|
|
2735
|
+
await qb.insert().into(this.table).values(values).execute();
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
get dataSourceOptions() {
|
|
2739
|
+
return this.dataSource.options;
|
|
2740
|
+
}
|
|
2741
|
+
get database() {
|
|
2742
|
+
return this.dataSource.driver.database;
|
|
2743
|
+
}
|
|
2744
|
+
get schema() {
|
|
2745
|
+
return this.dataSource.driver.schema;
|
|
2746
|
+
}
|
|
2747
|
+
get table() {
|
|
2748
|
+
return this.dataSource.driver.buildTableName(this.tableName, this.schema, this.database);
|
|
2749
|
+
}
|
|
2750
|
+
async buildOptions(input = {}) {
|
|
2751
|
+
const options = {
|
|
2752
|
+
...input,
|
|
2753
|
+
seeds: input.seeds || [],
|
|
2754
|
+
factories: input.factories || [],
|
|
2755
|
+
seedTracking: input.seedTracking ?? false
|
|
2756
|
+
};
|
|
2757
|
+
if (!options.seeds || options.seeds.length === 0) {
|
|
2758
|
+
options.seeds = this.dataSourceOptions.seeds;
|
|
2759
|
+
}
|
|
2760
|
+
if (!options.seeds || options.seeds.length === 0) {
|
|
2761
|
+
options.seeds = useEnv('seeds');
|
|
2762
|
+
}
|
|
2763
|
+
if (!options.seeds || options.seeds.length === 0) {
|
|
2764
|
+
options.seeds = [
|
|
2765
|
+
'src/database/seeds/**/*{.ts,.js}'
|
|
2766
|
+
];
|
|
2767
|
+
}
|
|
2768
|
+
if (!options.factories || options.factories.length === 0) {
|
|
2769
|
+
options.factories = this.dataSourceOptions.factories;
|
|
2770
|
+
}
|
|
2771
|
+
if (!options.factories || options.factories.length === 0) {
|
|
2772
|
+
options.factories = useEnv('factories');
|
|
2773
|
+
}
|
|
2774
|
+
if (!options.factories || options.factories.length === 0) {
|
|
2775
|
+
options.factories = [
|
|
2776
|
+
'src/database/factories/**/*{.ts,.js}'
|
|
2777
|
+
];
|
|
2778
|
+
}
|
|
2779
|
+
if (typeof options.seedTracking === 'undefined') {
|
|
2780
|
+
options.seedTracking = this.dataSourceOptions.seedTracking;
|
|
2781
|
+
}
|
|
2782
|
+
if (!this.options.preserveFilePaths) {
|
|
2783
|
+
let tsConfig;
|
|
2784
|
+
if (locter.isObject(this.options.tsconfig)) {
|
|
2785
|
+
tsConfig = this.options.tsconfig;
|
|
2786
|
+
} else {
|
|
2787
|
+
tsConfig = await readTSConfig(resolveFilePath(this.options.tsconfig || 'tsconfig.json', this.options.root));
|
|
2788
|
+
}
|
|
2789
|
+
await adjustFilePaths(options, [
|
|
2790
|
+
'seeds',
|
|
2791
|
+
'seedName',
|
|
2792
|
+
'factories'
|
|
2793
|
+
], tsConfig);
|
|
2794
|
+
}
|
|
2795
|
+
return options;
|
|
2796
|
+
}
|
|
2797
|
+
classNameToTimestamp(className) {
|
|
2798
|
+
const match = className.match(/^(.*)([0-9]{13,})$/);
|
|
2799
|
+
if (match) {
|
|
2800
|
+
return parseInt(match[2], 10);
|
|
2801
|
+
}
|
|
2802
|
+
return undefined;
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
async function runSeeder(dataSource, seeder, options = {}) {
|
|
2807
|
+
if (typeof seeder === 'string') {
|
|
2808
|
+
options.seedName = seeder;
|
|
2809
|
+
} else {
|
|
2810
|
+
options.seeds = [
|
|
2811
|
+
seeder
|
|
2812
|
+
];
|
|
2813
|
+
}
|
|
2814
|
+
const executor = new SeederExecutor(dataSource);
|
|
2815
|
+
const output = await executor.execute(options);
|
|
2816
|
+
return output.pop();
|
|
2817
|
+
}
|
|
2818
|
+
async function runSeeders(dataSource, options) {
|
|
2819
|
+
const executor = new SeederExecutor(dataSource);
|
|
2820
|
+
return executor.execute(options);
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
class SeedCreateCommand {
|
|
2824
|
+
builder(args) {
|
|
2825
|
+
return args.option('root', {
|
|
2826
|
+
alias: 'r',
|
|
2827
|
+
default: process.cwd(),
|
|
2828
|
+
describe: 'Root directory of the project.'
|
|
2829
|
+
}).option('timestamp', {
|
|
2830
|
+
alias: 't',
|
|
2831
|
+
type: 'number',
|
|
2832
|
+
describe: 'Custom timestamp for the seeder name.'
|
|
2833
|
+
}).option('javascript', {
|
|
2834
|
+
alias: 'j',
|
|
2835
|
+
type: 'boolean',
|
|
2836
|
+
default: false,
|
|
2837
|
+
describe: 'Generate a seeder file for JavaScript instead of TypeScript.'
|
|
2838
|
+
}).option('name', {
|
|
2839
|
+
alias: 'n',
|
|
2840
|
+
describe: 'Name (or relative path incl. name) of the seeder.',
|
|
2841
|
+
demandOption: true
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
async handler(raw) {
|
|
2845
|
+
const args = raw;
|
|
2846
|
+
let timestamp;
|
|
2847
|
+
if (Number.isNaN(args.timestamp) || !args.timestamp) {
|
|
2848
|
+
timestamp = Date.now();
|
|
2849
|
+
} else {
|
|
2850
|
+
timestamp = args.timestamp;
|
|
2851
|
+
}
|
|
2852
|
+
const sourcePath = parseFilePath(args.name, args.root);
|
|
2853
|
+
const dirNameIsDirectory = await isDirectory(sourcePath.directory);
|
|
2854
|
+
if (!dirNameIsDirectory) {
|
|
2855
|
+
consola.consola.warn(`The output directory ${sourcePath.directory} does not exist.`);
|
|
2856
|
+
process.exit(1);
|
|
2857
|
+
}
|
|
2858
|
+
const extension = args.javascript ? '.js' : '.ts';
|
|
2859
|
+
const nameExtension = locter.getFileNameExtension(sourcePath.name);
|
|
2860
|
+
const nameWithoutExtension = locter.removeFileNameExtension(sourcePath.name);
|
|
2861
|
+
let fileName;
|
|
2862
|
+
if (nameExtension) {
|
|
2863
|
+
fileName = `${timestamp}-${sourcePath.name}`;
|
|
2864
|
+
} else {
|
|
2865
|
+
fileName = `${timestamp}-${sourcePath.name}${extension}`;
|
|
2866
|
+
}
|
|
2867
|
+
const filePath = sourcePath.directory + path.sep + fileName;
|
|
2868
|
+
const template = buildSeederFileTemplate(nameWithoutExtension, timestamp);
|
|
2869
|
+
consola.consola.info(`Seed Directory: ${sourcePath.directory}`);
|
|
2870
|
+
consola.consola.info(`Seed FileName: ${fileName}`);
|
|
2871
|
+
consola.consola.info(`Seed Name: ${pascalCase.pascalCase(nameWithoutExtension)}`);
|
|
2872
|
+
try {
|
|
2873
|
+
await fs.promises.writeFile(filePath, template, {
|
|
2874
|
+
encoding: 'utf-8'
|
|
2875
|
+
});
|
|
2876
|
+
} catch (e) {
|
|
2877
|
+
consola.consola.warn(`The seed could not be written to the path ${filePath}.`);
|
|
2878
|
+
process.exit(1);
|
|
2879
|
+
}
|
|
2880
|
+
process.exit(0);
|
|
2881
|
+
}
|
|
2882
|
+
constructor(){
|
|
2883
|
+
this.command = 'seed:create';
|
|
2884
|
+
this.describe = 'Create a seeder file.';
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
class SeedRunCommand {
|
|
2889
|
+
builder(args) {
|
|
2890
|
+
return args.option('preserveFilePaths', {
|
|
2891
|
+
default: false,
|
|
2892
|
+
type: 'boolean',
|
|
2893
|
+
describe: 'This option indicates if file paths should be preserved.'
|
|
2894
|
+
}).option('root', {
|
|
2895
|
+
alias: 'r',
|
|
2896
|
+
default: process.cwd(),
|
|
2897
|
+
describe: 'Root directory of the project.'
|
|
2898
|
+
}).option('tsconfig', {
|
|
2899
|
+
alias: 'tc',
|
|
2900
|
+
default: 'tsconfig.json',
|
|
2901
|
+
describe: 'Name (or relative path incl. name) of the tsconfig file.'
|
|
2902
|
+
}).option('dataSource', {
|
|
2903
|
+
alias: 'd',
|
|
2904
|
+
default: 'data-source',
|
|
2905
|
+
describe: 'Name (or relative path incl. name) of the data-source file.'
|
|
2906
|
+
}).option('name', {
|
|
2907
|
+
alias: 'n',
|
|
2908
|
+
describe: 'Name (or relative path incl. name) of the seeder.'
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
async handler(raw) {
|
|
2912
|
+
const args = raw;
|
|
2913
|
+
let tsconfig;
|
|
2914
|
+
let sourcePath = resolveFilePath(args.dataSource, args.root);
|
|
2915
|
+
if (!args.preserveFilePaths) {
|
|
2916
|
+
tsconfig = await readTSConfig(args.root);
|
|
2917
|
+
sourcePath = await adjustFilePath(sourcePath, tsconfig);
|
|
2918
|
+
args.name = await adjustFilePath(args.name, tsconfig);
|
|
2919
|
+
}
|
|
2920
|
+
const source = parseFilePath(sourcePath);
|
|
2921
|
+
consola.consola.info(`DataSource Directory: ${source.directory}`);
|
|
2922
|
+
consola.consola.info(`DataSource Name: ${source.name}`);
|
|
2923
|
+
const dataSourceOptions = await buildDataSourceOptions({
|
|
2924
|
+
dataSourceName: source.name,
|
|
2925
|
+
directory: source.directory,
|
|
2926
|
+
tsconfig,
|
|
2927
|
+
preserveFilePaths: args.preserveFilePaths
|
|
2928
|
+
});
|
|
2929
|
+
setDataSourceOptions(dataSourceOptions);
|
|
2930
|
+
if (args.name) {
|
|
2931
|
+
consola.consola.info(`Seed Name: ${args.name}`);
|
|
2932
|
+
}
|
|
2933
|
+
const dataSource = await useDataSource();
|
|
2934
|
+
const executor = new SeederExecutor(dataSource, {
|
|
2935
|
+
root: args.root,
|
|
2936
|
+
tsconfig,
|
|
2937
|
+
preserveFilePaths: args.preserveFilePaths
|
|
2938
|
+
});
|
|
2939
|
+
await executor.execute({
|
|
2940
|
+
seedName: args.name
|
|
2941
|
+
});
|
|
2942
|
+
process.exit(0);
|
|
2943
|
+
}
|
|
2944
|
+
constructor(){
|
|
2945
|
+
this.command = 'seed:run';
|
|
2946
|
+
this.describe = 'Populate the database with an initial data set or generated data by a factory.';
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
class EntityRelationLookupError extends TypeormExtensionError {
|
|
2951
|
+
constructor(options){
|
|
2952
|
+
super(options.message);
|
|
2953
|
+
this.relation = options.relation;
|
|
2954
|
+
this.columns = options.columns;
|
|
2955
|
+
}
|
|
2956
|
+
static notReferenced(relation, columns) {
|
|
2957
|
+
return new EntityRelationLookupError({
|
|
2958
|
+
message: `${relation} entity is not referenced by ${columns.join(', ')}`,
|
|
2959
|
+
relation,
|
|
2960
|
+
columns
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
static notFound(relation, columns) {
|
|
2964
|
+
return new EntityRelationLookupError({
|
|
2965
|
+
message: `Can't find ${relation} entity by ${columns.join(', ')}`,
|
|
2966
|
+
relation,
|
|
2967
|
+
columns
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
/**
|
|
2973
|
+
* Receive metadata for a given repository or entity-target.
|
|
2974
|
+
*
|
|
2975
|
+
* @experimental
|
|
2976
|
+
* @param input
|
|
2977
|
+
* @param dataSource
|
|
2978
|
+
*/ async function getEntityMetadata(input, dataSource) {
|
|
2979
|
+
if (input instanceof typeorm.Repository) {
|
|
2980
|
+
return input.metadata;
|
|
2981
|
+
}
|
|
2982
|
+
dataSource = dataSource || await useDataSource();
|
|
2983
|
+
const index = dataSource.entityMetadatas.findIndex((entityMetadata)=>entityMetadata.target === input);
|
|
2984
|
+
if (index === -1) {
|
|
2985
|
+
throw new Error(`The entity ${input} is not registered.`);
|
|
2986
|
+
}
|
|
2987
|
+
return dataSource.entityMetadatas[index];
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
/**
|
|
2991
|
+
* Get (relation-) property names of a given entity.
|
|
2992
|
+
*
|
|
2993
|
+
* @experimental
|
|
2994
|
+
* @param input
|
|
2995
|
+
* @param dataSource
|
|
2996
|
+
*/ async function getEntityPropertyNames(input, dataSource) {
|
|
2997
|
+
let entityMetadata;
|
|
2998
|
+
if (input instanceof typeorm.Repository) {
|
|
2999
|
+
entityMetadata = input.metadata;
|
|
3000
|
+
} else {
|
|
3001
|
+
entityMetadata = await getEntityMetadata(input, dataSource);
|
|
3002
|
+
}
|
|
3003
|
+
const items = [];
|
|
3004
|
+
for(let i = 0; i < entityMetadata.columns.length; i++){
|
|
3005
|
+
items.push(entityMetadata.columns[i].propertyName);
|
|
3006
|
+
}
|
|
3007
|
+
for(let i = 0; i < entityMetadata.relations.length; i++){
|
|
3008
|
+
items.push(entityMetadata.relations[i].propertyName);
|
|
3009
|
+
}
|
|
3010
|
+
return items;
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
/**
|
|
3014
|
+
* Validate join columns of a given entity.
|
|
3015
|
+
* It will look up and append the referenced entities to the input entity.
|
|
3016
|
+
*
|
|
3017
|
+
* @experimental
|
|
3018
|
+
* @param entity
|
|
3019
|
+
* @param options
|
|
3020
|
+
*/ async function validateEntityJoinColumns(entity, options) {
|
|
3021
|
+
const dataSource = options.dataSource || await useDataSource();
|
|
3022
|
+
const entityMetadata = await getEntityMetadata(options.entityTarget, dataSource);
|
|
3023
|
+
const relations = {};
|
|
3024
|
+
for(let i = 0; i < entityMetadata.relations.length; i++){
|
|
3025
|
+
const relation = entityMetadata.relations[i];
|
|
3026
|
+
let skipRelation = false;
|
|
3027
|
+
const where = {};
|
|
3028
|
+
const columns = [];
|
|
3029
|
+
for(let j = 0; j < relation.joinColumns.length; j++){
|
|
3030
|
+
const joinColumn = relation.joinColumns[j];
|
|
3031
|
+
if (typeof entity[joinColumn.propertyName] === 'undefined') {
|
|
3032
|
+
continue;
|
|
3033
|
+
}
|
|
3034
|
+
if (joinColumn.isNullable && entity[joinColumn.propertyName] === null) {
|
|
3035
|
+
skipRelation = true;
|
|
3036
|
+
break;
|
|
3037
|
+
}
|
|
3038
|
+
if (joinColumn.referencedColumn) {
|
|
3039
|
+
where[joinColumn.referencedColumn.propertyName] = entity[joinColumn.propertyName];
|
|
3040
|
+
columns.push(joinColumn.propertyName);
|
|
3041
|
+
} else {
|
|
3042
|
+
throw EntityRelationLookupError.notReferenced(relation.propertyName, [
|
|
3043
|
+
joinColumn.propertyName
|
|
3044
|
+
]);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
if (skipRelation || columns.length === 0) {
|
|
3048
|
+
continue;
|
|
3049
|
+
}
|
|
3050
|
+
const repository = dataSource.getRepository(relation.type);
|
|
3051
|
+
const item = await repository.findOne({
|
|
3052
|
+
where
|
|
3053
|
+
});
|
|
3054
|
+
if (!item) {
|
|
3055
|
+
throw EntityRelationLookupError.notFound(relation.propertyName, columns);
|
|
3056
|
+
}
|
|
3057
|
+
relations[relation.propertyName] = item;
|
|
3058
|
+
}
|
|
3059
|
+
const relationKeys = Object.keys(relations);
|
|
3060
|
+
for(let i = 0; i < relationKeys.length; i++){
|
|
3061
|
+
const relationKey = relationKeys[i];
|
|
3062
|
+
entity[relationKey] = relations[relationKey];
|
|
3063
|
+
}
|
|
3064
|
+
return entity;
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
function transformUndefinedToNull(input) {
|
|
3068
|
+
if (typeof input === 'undefined') {
|
|
3069
|
+
return null;
|
|
3070
|
+
}
|
|
3071
|
+
return input;
|
|
3072
|
+
}
|
|
3073
|
+
function applyWhereExpression(qb, data, type) {
|
|
3074
|
+
const elements = [];
|
|
3075
|
+
const keys = Object.keys(data);
|
|
3076
|
+
for(let i = 0; i < keys.length; i++){
|
|
3077
|
+
elements.push({
|
|
3078
|
+
key: keys[i],
|
|
3079
|
+
value: transformUndefinedToNull(data[keys[i]]),
|
|
3080
|
+
operator: type === 'target' ? rapiq.FilterComparisonOperator.EQUAL : rapiq.FilterComparisonOperator.NOT_EQUAL
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
const queryFilters = transformParsedFilters(elements, {
|
|
3084
|
+
bindingKey (key) {
|
|
3085
|
+
if (type === 'source') {
|
|
3086
|
+
return `filter_source_${key}`;
|
|
3087
|
+
}
|
|
3088
|
+
return `filter_target_${key}`;
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
applyFiltersTransformed(qb, queryFilters);
|
|
3092
|
+
return queryFilters;
|
|
3093
|
+
}
|
|
3094
|
+
/**
|
|
3095
|
+
* Check if a given entity does not already exist.
|
|
3096
|
+
* Composite unique keys on a null column can only be present once.
|
|
3097
|
+
*
|
|
3098
|
+
* @experimental
|
|
3099
|
+
* @param options
|
|
3100
|
+
*/ async function isEntityUnique(options) {
|
|
3101
|
+
const dataSource = options.dataSource || await useDataSource();
|
|
3102
|
+
const metadata = await getEntityMetadata(options.entityTarget, dataSource);
|
|
3103
|
+
const repository = dataSource.getRepository(metadata.target);
|
|
3104
|
+
const primaryColumnNames = metadata.primaryColumns.map((c)=>c.propertyName);
|
|
3105
|
+
const columnGroups = [];
|
|
3106
|
+
if (metadata.ownUniques && metadata.ownUniques.length > 0) {
|
|
3107
|
+
for(let i = 0; i < metadata.ownUniques.length; i++){
|
|
3108
|
+
columnGroups.push(metadata.ownUniques[i].columns.map((column)=>column.propertyName));
|
|
3109
|
+
}
|
|
3110
|
+
} else {
|
|
3111
|
+
for(let i = 0; i < metadata.indices.length; i++){
|
|
3112
|
+
const index = metadata.indices[i];
|
|
3113
|
+
if (!index.isUnique || index.entityMetadata.target !== metadata.target) {
|
|
3114
|
+
continue;
|
|
3115
|
+
}
|
|
3116
|
+
columnGroups.push(index.columns.map((column)=>column.propertyName));
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
for(let i = 0; i < columnGroups.length; i++){
|
|
3120
|
+
const queryBuilder = repository.createQueryBuilder('entity');
|
|
3121
|
+
queryBuilder.where(new typeorm.Brackets((qb)=>{
|
|
3122
|
+
applyWhereExpression(qb, pickRecord(options.entity, columnGroups[i]), 'target');
|
|
3123
|
+
}));
|
|
3124
|
+
if (options.entityExisting) {
|
|
3125
|
+
queryBuilder.andWhere(new typeorm.Brackets((qb)=>{
|
|
3126
|
+
applyWhereExpression(qb, pickRecord(options.entityExisting, primaryColumnNames), 'source');
|
|
3127
|
+
}));
|
|
3128
|
+
}
|
|
3129
|
+
const entity = await queryBuilder.getOne();
|
|
3130
|
+
if (entity) {
|
|
3131
|
+
return false;
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
return true;
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
exports.CodeTransformation = CodeTransformation;
|
|
3138
|
+
exports.DatabaseCreateCommand = DatabaseCreateCommand;
|
|
3139
|
+
exports.DatabaseDropCommand = DatabaseDropCommand;
|
|
3140
|
+
exports.DriverError = DriverError;
|
|
3141
|
+
exports.EntityRelationLookupError = EntityRelationLookupError;
|
|
3142
|
+
exports.EnvironmentName = EnvironmentName;
|
|
3143
|
+
exports.EnvironmentVariableName = EnvironmentVariableName;
|
|
3144
|
+
exports.OptionsError = OptionsError;
|
|
3145
|
+
exports.SeedCreateCommand = SeedCreateCommand;
|
|
3146
|
+
exports.SeedRunCommand = SeedRunCommand;
|
|
3147
|
+
exports.SeederEntity = SeederEntity;
|
|
3148
|
+
exports.SeederExecutor = SeederExecutor;
|
|
3149
|
+
exports.SeederFactory = SeederFactory;
|
|
3150
|
+
exports.SeederFactoryManager = SeederFactoryManager;
|
|
3151
|
+
exports.TypeormExtensionError = TypeormExtensionError;
|
|
3152
|
+
exports.adjustFilePath = adjustFilePath;
|
|
3153
|
+
exports.adjustFilePaths = adjustFilePaths;
|
|
3154
|
+
exports.applyFields = applyFields;
|
|
3155
|
+
exports.applyFilters = applyFilters;
|
|
3156
|
+
exports.applyFiltersTransformed = applyFiltersTransformed;
|
|
3157
|
+
exports.applyPagination = applyPagination;
|
|
3158
|
+
exports.applyQuery = applyQuery;
|
|
3159
|
+
exports.applyQueryFields = applyQueryFields;
|
|
3160
|
+
exports.applyQueryFieldsParseOutput = applyQueryFieldsParseOutput;
|
|
3161
|
+
exports.applyQueryFilters = applyQueryFilters;
|
|
3162
|
+
exports.applyQueryFiltersParseOutput = applyQueryFiltersParseOutput;
|
|
3163
|
+
exports.applyQueryPagination = applyQueryPagination;
|
|
3164
|
+
exports.applyQueryPaginationParseOutput = applyQueryPaginationParseOutput;
|
|
3165
|
+
exports.applyQueryParseOutput = applyQueryParseOutput;
|
|
3166
|
+
exports.applyQueryRelations = applyQueryRelations;
|
|
3167
|
+
exports.applyQueryRelationsParseOutput = applyQueryRelationsParseOutput;
|
|
3168
|
+
exports.applyQuerySort = applyQuerySort;
|
|
3169
|
+
exports.applyQuerySortParseOutput = applyQuerySortParseOutput;
|
|
3170
|
+
exports.applyRelations = applyRelations;
|
|
3171
|
+
exports.applySort = applySort;
|
|
3172
|
+
exports.buildDataSourceOptions = buildDataSourceOptions;
|
|
3173
|
+
exports.buildDatabaseCreateContext = buildDatabaseCreateContext;
|
|
3174
|
+
exports.buildDatabaseDropContext = buildDatabaseDropContext;
|
|
3175
|
+
exports.buildDriverOptions = buildDriverOptions;
|
|
3176
|
+
exports.buildFilePathname = buildFilePathname;
|
|
3177
|
+
exports.buildKeyWithPrefix = buildKeyWithPrefix;
|
|
3178
|
+
exports.buildSeederFileTemplate = buildSeederFileTemplate;
|
|
3179
|
+
exports.canReplaceWindowsSeparator = canReplaceWindowsSeparator;
|
|
3180
|
+
exports.checkDatabase = checkDatabase;
|
|
3181
|
+
exports.createCockroachDBDatabase = createCockroachDBDatabase;
|
|
3182
|
+
exports.createDatabase = createDatabase;
|
|
3183
|
+
exports.createDriver = createDriver;
|
|
3184
|
+
exports.createMongoDBDatabase = createMongoDBDatabase;
|
|
3185
|
+
exports.createMsSQLDatabase = createMsSQLDatabase;
|
|
3186
|
+
exports.createMySQLDatabase = createMySQLDatabase;
|
|
3187
|
+
exports.createOracleDatabase = createOracleDatabase;
|
|
3188
|
+
exports.createPostgresDatabase = createPostgresDatabase;
|
|
3189
|
+
exports.createSQLiteDatabase = createSQLiteDatabase;
|
|
3190
|
+
exports.createSimpleMongoDBConnection = createSimpleMongoDBConnection;
|
|
3191
|
+
exports.createSimpleMsSQLConnection = createSimpleMsSQLConnection;
|
|
3192
|
+
exports.createSimpleMySQLConnection = createSimpleMySQLConnection;
|
|
3193
|
+
exports.createSimpleOracleConnection = createSimpleOracleConnection;
|
|
3194
|
+
exports.createSimplePostgresConnection = createSimplePostgresConnection;
|
|
3195
|
+
exports.detectCodeTransformation = detectCodeTransformation;
|
|
3196
|
+
exports.dropCockroachDBDatabase = dropCockroachDBDatabase;
|
|
3197
|
+
exports.dropDatabase = dropDatabase;
|
|
3198
|
+
exports.dropMongoDBDatabase = dropMongoDBDatabase;
|
|
3199
|
+
exports.dropMsSQLDatabase = dropMsSQLDatabase;
|
|
3200
|
+
exports.dropMySQLDatabase = dropMySQLDatabase;
|
|
3201
|
+
exports.dropOracleDatabase = dropOracleDatabase;
|
|
3202
|
+
exports.dropPostgresDatabase = dropPostgresDatabase;
|
|
3203
|
+
exports.dropSQLiteDatabase = dropSQLiteDatabase;
|
|
3204
|
+
exports.executeSimpleCockroachDBQuery = executeSimpleCockroachDBQuery;
|
|
3205
|
+
exports.executeSimpleMysqlQuery = executeSimpleMysqlQuery;
|
|
3206
|
+
exports.executeSimplePostgresQuery = executeSimplePostgresQuery;
|
|
3207
|
+
exports.existsQuery = existsQuery;
|
|
3208
|
+
exports.extendObject = extendObject;
|
|
3209
|
+
exports.findDataSource = findDataSource;
|
|
3210
|
+
exports.generateMigration = generateMigration;
|
|
3211
|
+
exports.getAliasForPath = getAliasForPath;
|
|
3212
|
+
exports.getCharacterSetFromDataSourceOptions = getCharacterSetFromDataSourceOptions;
|
|
3213
|
+
exports.getCharsetFromDataSourceOptions = getCharsetFromDataSourceOptions;
|
|
3214
|
+
exports.getEntityMetadata = getEntityMetadata;
|
|
3215
|
+
exports.getEntityName = getEntityName;
|
|
3216
|
+
exports.getEntityPropertyNames = getEntityPropertyNames;
|
|
3217
|
+
exports.hasDataSource = hasDataSource;
|
|
3218
|
+
exports.hasDataSourceOptions = hasDataSourceOptions;
|
|
3219
|
+
exports.hasEnvDataSourceOptions = hasEnvDataSourceOptions;
|
|
3220
|
+
exports.hasOwnProperty = hasOwnProperty;
|
|
3221
|
+
exports.hasStringProperty = hasStringProperty;
|
|
3222
|
+
exports.hasTrailingSlash = hasTrailingSlash;
|
|
3223
|
+
exports.isCodeTransformation = isCodeTransformation;
|
|
3224
|
+
exports.isDirectory = isDirectory;
|
|
3225
|
+
exports.isEntityUnique = isEntityUnique;
|
|
3226
|
+
exports.isPromise = isPromise;
|
|
3227
|
+
exports.isQueryOptionDefined = isQueryOptionDefined;
|
|
3228
|
+
exports.mergeDataSourceOptions = mergeDataSourceOptions;
|
|
3229
|
+
exports.mergeDataSourceOptionsWithEnv = mergeDataSourceOptionsWithEnv;
|
|
3230
|
+
exports.parseFilePath = parseFilePath;
|
|
3231
|
+
exports.pickRecord = pickRecord;
|
|
3232
|
+
exports.prepareSeederFactories = prepareSeederFactories;
|
|
3233
|
+
exports.prepareSeederSeeds = prepareSeederSeeds;
|
|
3234
|
+
exports.readDataSourceOptionsFromEnv = readDataSourceOptionsFromEnv;
|
|
3235
|
+
exports.readTSConfig = readTSConfig;
|
|
3236
|
+
exports.replaceWindowSeparator = replaceWindowSeparator;
|
|
3237
|
+
exports.resetEnv = resetEnv;
|
|
3238
|
+
exports.resolveFilePath = resolveFilePath;
|
|
3239
|
+
exports.resolveFilePaths = resolveFilePaths;
|
|
3240
|
+
exports.resolveFilePatterns = resolveFilePatterns;
|
|
3241
|
+
exports.runSeeder = runSeeder;
|
|
3242
|
+
exports.runSeeders = runSeeders;
|
|
3243
|
+
exports.safeReplaceWindowsSeparator = safeReplaceWindowsSeparator;
|
|
3244
|
+
exports.setDataSource = setDataSource;
|
|
3245
|
+
exports.setDataSourceOptions = setDataSourceOptions;
|
|
3246
|
+
exports.setSeederFactory = setSeederFactory;
|
|
3247
|
+
exports.synchronizeDatabaseSchema = synchronizeDatabaseSchema;
|
|
3248
|
+
exports.transformFilePath = transformFilePath;
|
|
3249
|
+
exports.transformParsedFilters = transformParsedFilters;
|
|
3250
|
+
exports.unsetDataSource = unsetDataSource;
|
|
3251
|
+
exports.useDataSource = useDataSource;
|
|
3252
|
+
exports.useDataSourceOptions = useDataSourceOptions;
|
|
3253
|
+
exports.useEnv = useEnv;
|
|
3254
|
+
exports.useSeederFactory = useSeederFactory;
|
|
3255
|
+
exports.useSeederFactoryManager = useSeederFactoryManager;
|
|
3256
|
+
exports.validateEntityJoinColumns = validateEntityJoinColumns;
|
|
3257
|
+
exports.withoutTrailingSlash = withoutTrailingSlash;
|
|
3258
|
+
//# sourceMappingURL=index.cjs.map
|