serverless-simple-middleware 0.0.74 → 0.0.75
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/.prettierignore +2 -2
- package/README.md +3 -3
- package/dist/middleware/buildWebSocket.d.ts +5 -0
- package/dist/middleware/buildWebSocket.js +127 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.js +3 -0
- package/dist/middleware/websocketBase.d.ts +32 -0
- package/dist/middleware/websocketBase.js +65 -0
- package/jest.config.js +7 -7
- package/package.json +70 -70
- package/src/aws/config.ts +46 -46
- package/src/aws/define.ts +10 -10
- package/src/aws/index.ts +3 -3
- package/src/aws/simple.ts +705 -705
- package/src/index.ts +3 -3
- package/src/internal/AwsError.ts +13 -13
- package/src/internal/oncePromise.ts +29 -29
- package/src/internal/s3.ts +75 -75
- package/src/middleware/aws.ts +78 -78
- package/src/middleware/base.ts +206 -206
- package/src/middleware/build.ts +273 -273
- package/src/middleware/buildWebSocket.ts +199 -0
- package/src/middleware/database/connectionProxy.ts +294 -294
- package/src/middleware/database/sqlClient.ts +208 -208
- package/src/middleware/index.ts +24 -21
- package/src/middleware/logger.ts +28 -28
- package/src/middleware/mysql.ts +62 -62
- package/src/middleware/trace.ts +265 -265
- package/src/middleware/websocketBase.ts +92 -0
- package/src/utils/index.ts +2 -2
- package/src/utils/logger.ts +94 -94
- package/src/utils/misc.ts +20 -20
- package/src/utils/secretsManager.ts +86 -86
- package/tsconfig.json +16 -16
- package/tslint.json +12 -12
|
@@ -1,294 +1,294 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createConnection,
|
|
3
|
-
type Connection,
|
|
4
|
-
type ConnectionOptions,
|
|
5
|
-
type FieldPacket,
|
|
6
|
-
type QueryError,
|
|
7
|
-
type QueryResult,
|
|
8
|
-
} from 'mysql2';
|
|
9
|
-
import { OncePromise } from '../../internal/oncePromise';
|
|
10
|
-
import { getLogger } from '../../utils';
|
|
11
|
-
import { SecretsManagerCache } from '../../utils/secretsManager';
|
|
12
|
-
import { MySQLPluginOptions } from '../mysql';
|
|
13
|
-
|
|
14
|
-
const logger = getLogger(__filename);
|
|
15
|
-
|
|
16
|
-
export class ConnectionProxy {
|
|
17
|
-
private connection?: Connection;
|
|
18
|
-
private connectionConfig: ConnectionOptions;
|
|
19
|
-
private secretsCache: SecretsManagerCache;
|
|
20
|
-
private configInitOnce = new OncePromise<void>();
|
|
21
|
-
private connectionInitOnce = new OncePromise<Connection>();
|
|
22
|
-
|
|
23
|
-
private initialized: boolean;
|
|
24
|
-
private dbName?: string;
|
|
25
|
-
|
|
26
|
-
private readonly MAX_RETRIES: number = 1;
|
|
27
|
-
|
|
28
|
-
public constructor(private readonly options: MySQLPluginOptions) {
|
|
29
|
-
if (options.schema && options.schema.database) {
|
|
30
|
-
this.dbName = options.config.database;
|
|
31
|
-
options.config.database = undefined;
|
|
32
|
-
}
|
|
33
|
-
this.secretsCache = SecretsManagerCache.getInstance();
|
|
34
|
-
if (options.secretsManagerConfig) {
|
|
35
|
-
this.secretsCache.configure(options.secretsManagerConfig);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
public query = <T>(sql: string, params?: any[]) =>
|
|
40
|
-
this.prepareConnection().then((connection) =>
|
|
41
|
-
this.tryToInitializeSchema(false).then(
|
|
42
|
-
() =>
|
|
43
|
-
new Promise<T | undefined>((resolve, reject) => {
|
|
44
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
45
|
-
logger.silly(`Execute query[${sql}] with params[${params}]`);
|
|
46
|
-
}
|
|
47
|
-
connection.query(
|
|
48
|
-
sql,
|
|
49
|
-
params,
|
|
50
|
-
(err: QueryError, result: QueryResult, _fields?: FieldPacket[]) => {
|
|
51
|
-
if (err) {
|
|
52
|
-
logger.error(
|
|
53
|
-
`error occurred in database query=${sql}, error=${err}`,
|
|
54
|
-
);
|
|
55
|
-
reject(err);
|
|
56
|
-
} else {
|
|
57
|
-
resolve(result as T);
|
|
58
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
59
|
-
logger.silly(`DB result is ${JSON.stringify(result)}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
);
|
|
64
|
-
}),
|
|
65
|
-
),
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
public fetch = <T>(sql: string, params?: any[]) =>
|
|
69
|
-
this.query<T[]>(sql, params).then((res) => res || []);
|
|
70
|
-
|
|
71
|
-
public fetchOne = <T>(sql: string, params?: any[], defaultValue?: T) =>
|
|
72
|
-
this.fetch<T>(sql, params).then((res) => {
|
|
73
|
-
if (res === undefined || res[0] === undefined) {
|
|
74
|
-
// Makes it as non-null result.
|
|
75
|
-
return defaultValue || ({} as any as T);
|
|
76
|
-
}
|
|
77
|
-
return res[0];
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
public beginTransaction = () =>
|
|
81
|
-
this.prepareConnection().then((connection) =>
|
|
82
|
-
this.tryToInitializeSchema(false).then(
|
|
83
|
-
() =>
|
|
84
|
-
new Promise<void>((resolve, reject) => {
|
|
85
|
-
connection.beginTransaction((err: QueryError) => {
|
|
86
|
-
if (err) {
|
|
87
|
-
reject(err);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
resolve();
|
|
91
|
-
});
|
|
92
|
-
}),
|
|
93
|
-
),
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
public commit = () =>
|
|
97
|
-
this.prepareConnection().then((connection) =>
|
|
98
|
-
this.tryToInitializeSchema(false).then(
|
|
99
|
-
() =>
|
|
100
|
-
new Promise<void>((resolve, reject) => {
|
|
101
|
-
connection.commit((err: QueryError) => {
|
|
102
|
-
if (err) {
|
|
103
|
-
reject(err);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
resolve();
|
|
107
|
-
});
|
|
108
|
-
}),
|
|
109
|
-
),
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
public rollback = () =>
|
|
113
|
-
this.prepareConnection().then((connection) =>
|
|
114
|
-
this.tryToInitializeSchema(false).then(
|
|
115
|
-
() =>
|
|
116
|
-
new Promise<void>((resolve, reject) => {
|
|
117
|
-
connection.rollback((err: QueryError) => {
|
|
118
|
-
if (err) {
|
|
119
|
-
reject(err);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
resolve();
|
|
123
|
-
});
|
|
124
|
-
}),
|
|
125
|
-
),
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
public clearConnection = () => {
|
|
129
|
-
const conn = this.connection;
|
|
130
|
-
this.connection = undefined;
|
|
131
|
-
this.connectionInitOnce.reset();
|
|
132
|
-
|
|
133
|
-
if (conn) {
|
|
134
|
-
try {
|
|
135
|
-
conn.end();
|
|
136
|
-
} catch (error) {
|
|
137
|
-
logger.warn(`Error occurred while ending connection: ${error}`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Destroy the connection socket immediately. No further events or callbacks will be triggered.
|
|
144
|
-
* This should be used only for special use cases!
|
|
145
|
-
*/
|
|
146
|
-
public destroyConnection = () => {
|
|
147
|
-
const conn = this.connection;
|
|
148
|
-
this.connection = undefined;
|
|
149
|
-
this.connectionInitOnce.reset();
|
|
150
|
-
|
|
151
|
-
if (conn) {
|
|
152
|
-
try {
|
|
153
|
-
conn.destroy();
|
|
154
|
-
} catch (error) {
|
|
155
|
-
logger.warn(`Error occurred while destroying connection: ${error}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
public onPluginCreated = async () => this.tryToInitializeSchema(true);
|
|
161
|
-
|
|
162
|
-
private prepareConnection = async (): Promise<Connection> => {
|
|
163
|
-
if (this.connection) {
|
|
164
|
-
return this.connection;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return await this.connectionInitOnce.run(async () => {
|
|
168
|
-
await this.ensureConnectionConfig();
|
|
169
|
-
this.connection = await this.createConnection(this.MAX_RETRIES);
|
|
170
|
-
return this.connection;
|
|
171
|
-
});
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
private createConnection = async (
|
|
175
|
-
remainingRetries: number,
|
|
176
|
-
): Promise<Connection> => {
|
|
177
|
-
const conn = createConnection(this.connectionConfig);
|
|
178
|
-
|
|
179
|
-
return new Promise((resolve, reject) => {
|
|
180
|
-
conn.on('error', (err) => {
|
|
181
|
-
logger.error(`Connection error event: ${err.message}`);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
conn.connect((err) => {
|
|
185
|
-
if (err) {
|
|
186
|
-
logger.error(`Failed to connect to database: ${err.message}`);
|
|
187
|
-
conn.destroy();
|
|
188
|
-
|
|
189
|
-
if (remainingRetries > 0) {
|
|
190
|
-
logger.warn(
|
|
191
|
-
`Retrying database connection... (${remainingRetries} attempt(s) remaining)`,
|
|
192
|
-
);
|
|
193
|
-
setTimeout(() => {
|
|
194
|
-
this.createConnection(remainingRetries - 1)
|
|
195
|
-
.then(resolve)
|
|
196
|
-
.catch(reject);
|
|
197
|
-
}, 100);
|
|
198
|
-
} else {
|
|
199
|
-
logger.error('Database connection failed after all retries. Giving up.');
|
|
200
|
-
reject(err);
|
|
201
|
-
}
|
|
202
|
-
} else {
|
|
203
|
-
logger.verbose('Database connection established successfully.');
|
|
204
|
-
resolve(conn);
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
private ensureConnectionConfig = async (): Promise<void> => {
|
|
211
|
-
if (this.connectionConfig) {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
await this.configInitOnce.run(async () => {
|
|
216
|
-
const baseConfig = this.options.config;
|
|
217
|
-
if (!this.options.secretId) {
|
|
218
|
-
this.connectionConfig = baseConfig;
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
const credentials = await this.secretsCache.getDatabaseCredentials(
|
|
222
|
-
this.options.secretId,
|
|
223
|
-
);
|
|
224
|
-
this.connectionConfig = {
|
|
225
|
-
...baseConfig,
|
|
226
|
-
user: credentials.username,
|
|
227
|
-
password: credentials.password,
|
|
228
|
-
};
|
|
229
|
-
});
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
private changeDatabase = async (dbName: string) =>
|
|
233
|
-
new Promise<void>((resolve, reject) =>
|
|
234
|
-
this.prepareConnection()
|
|
235
|
-
.then((connection) =>
|
|
236
|
-
connection.changeUser(
|
|
237
|
-
{
|
|
238
|
-
database: dbName,
|
|
239
|
-
},
|
|
240
|
-
(err) => (err ? reject(err) : resolve(undefined)),
|
|
241
|
-
),
|
|
242
|
-
)
|
|
243
|
-
.catch(reject),
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
private tryToInitializeSchema = async (initial: boolean) => {
|
|
247
|
-
const {
|
|
248
|
-
eager = false,
|
|
249
|
-
ignoreError = false,
|
|
250
|
-
database = '',
|
|
251
|
-
tables = {},
|
|
252
|
-
} = this.options.schema || {};
|
|
253
|
-
if (initial && !eager) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// This method can be called twice when eager option is on,
|
|
258
|
-
// so this flag should be set and checked at first.
|
|
259
|
-
if (this.initialized) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
this.initialized = true;
|
|
263
|
-
|
|
264
|
-
try {
|
|
265
|
-
if (database) {
|
|
266
|
-
logger.debug(`Prepare a database[${this.dbName}]`);
|
|
267
|
-
logger.stupid(this.dbName!, database);
|
|
268
|
-
const result = await this.query(database);
|
|
269
|
-
logger.debug(
|
|
270
|
-
`Database[${this.dbName}] is initialized: ${JSON.stringify(result)}`,
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
if (this.dbName) {
|
|
274
|
-
await this.changeDatabase(this.dbName);
|
|
275
|
-
logger.verbose(`Database[${this.dbName}] is connected.`);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
for (const [name, query] of Object.entries(tables)) {
|
|
279
|
-
logger.debug(`Prepare a table[${name}]`);
|
|
280
|
-
logger.stupid(name, query);
|
|
281
|
-
const result = await this.query(query);
|
|
282
|
-
logger.debug(
|
|
283
|
-
`Table[${name}] is initialized: ${JSON.stringify(result)}`,
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
logger.verbose(`Database schema is initialized.`);
|
|
287
|
-
} catch (error) {
|
|
288
|
-
logger.warn(error);
|
|
289
|
-
if (!ignoreError) {
|
|
290
|
-
throw error;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
createConnection,
|
|
3
|
+
type Connection,
|
|
4
|
+
type ConnectionOptions,
|
|
5
|
+
type FieldPacket,
|
|
6
|
+
type QueryError,
|
|
7
|
+
type QueryResult,
|
|
8
|
+
} from 'mysql2';
|
|
9
|
+
import { OncePromise } from '../../internal/oncePromise';
|
|
10
|
+
import { getLogger } from '../../utils';
|
|
11
|
+
import { SecretsManagerCache } from '../../utils/secretsManager';
|
|
12
|
+
import { MySQLPluginOptions } from '../mysql';
|
|
13
|
+
|
|
14
|
+
const logger = getLogger(__filename);
|
|
15
|
+
|
|
16
|
+
export class ConnectionProxy {
|
|
17
|
+
private connection?: Connection;
|
|
18
|
+
private connectionConfig: ConnectionOptions;
|
|
19
|
+
private secretsCache: SecretsManagerCache;
|
|
20
|
+
private configInitOnce = new OncePromise<void>();
|
|
21
|
+
private connectionInitOnce = new OncePromise<Connection>();
|
|
22
|
+
|
|
23
|
+
private initialized: boolean;
|
|
24
|
+
private dbName?: string;
|
|
25
|
+
|
|
26
|
+
private readonly MAX_RETRIES: number = 1;
|
|
27
|
+
|
|
28
|
+
public constructor(private readonly options: MySQLPluginOptions) {
|
|
29
|
+
if (options.schema && options.schema.database) {
|
|
30
|
+
this.dbName = options.config.database;
|
|
31
|
+
options.config.database = undefined;
|
|
32
|
+
}
|
|
33
|
+
this.secretsCache = SecretsManagerCache.getInstance();
|
|
34
|
+
if (options.secretsManagerConfig) {
|
|
35
|
+
this.secretsCache.configure(options.secretsManagerConfig);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public query = <T>(sql: string, params?: any[]) =>
|
|
40
|
+
this.prepareConnection().then((connection) =>
|
|
41
|
+
this.tryToInitializeSchema(false).then(
|
|
42
|
+
() =>
|
|
43
|
+
new Promise<T | undefined>((resolve, reject) => {
|
|
44
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
45
|
+
logger.silly(`Execute query[${sql}] with params[${params}]`);
|
|
46
|
+
}
|
|
47
|
+
connection.query(
|
|
48
|
+
sql,
|
|
49
|
+
params,
|
|
50
|
+
(err: QueryError, result: QueryResult, _fields?: FieldPacket[]) => {
|
|
51
|
+
if (err) {
|
|
52
|
+
logger.error(
|
|
53
|
+
`error occurred in database query=${sql}, error=${err}`,
|
|
54
|
+
);
|
|
55
|
+
reject(err);
|
|
56
|
+
} else {
|
|
57
|
+
resolve(result as T);
|
|
58
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
59
|
+
logger.silly(`DB result is ${JSON.stringify(result)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
}),
|
|
65
|
+
),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
public fetch = <T>(sql: string, params?: any[]) =>
|
|
69
|
+
this.query<T[]>(sql, params).then((res) => res || []);
|
|
70
|
+
|
|
71
|
+
public fetchOne = <T>(sql: string, params?: any[], defaultValue?: T) =>
|
|
72
|
+
this.fetch<T>(sql, params).then((res) => {
|
|
73
|
+
if (res === undefined || res[0] === undefined) {
|
|
74
|
+
// Makes it as non-null result.
|
|
75
|
+
return defaultValue || ({} as any as T);
|
|
76
|
+
}
|
|
77
|
+
return res[0];
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
public beginTransaction = () =>
|
|
81
|
+
this.prepareConnection().then((connection) =>
|
|
82
|
+
this.tryToInitializeSchema(false).then(
|
|
83
|
+
() =>
|
|
84
|
+
new Promise<void>((resolve, reject) => {
|
|
85
|
+
connection.beginTransaction((err: QueryError) => {
|
|
86
|
+
if (err) {
|
|
87
|
+
reject(err);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
resolve();
|
|
91
|
+
});
|
|
92
|
+
}),
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
public commit = () =>
|
|
97
|
+
this.prepareConnection().then((connection) =>
|
|
98
|
+
this.tryToInitializeSchema(false).then(
|
|
99
|
+
() =>
|
|
100
|
+
new Promise<void>((resolve, reject) => {
|
|
101
|
+
connection.commit((err: QueryError) => {
|
|
102
|
+
if (err) {
|
|
103
|
+
reject(err);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
resolve();
|
|
107
|
+
});
|
|
108
|
+
}),
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
public rollback = () =>
|
|
113
|
+
this.prepareConnection().then((connection) =>
|
|
114
|
+
this.tryToInitializeSchema(false).then(
|
|
115
|
+
() =>
|
|
116
|
+
new Promise<void>((resolve, reject) => {
|
|
117
|
+
connection.rollback((err: QueryError) => {
|
|
118
|
+
if (err) {
|
|
119
|
+
reject(err);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
resolve();
|
|
123
|
+
});
|
|
124
|
+
}),
|
|
125
|
+
),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
public clearConnection = () => {
|
|
129
|
+
const conn = this.connection;
|
|
130
|
+
this.connection = undefined;
|
|
131
|
+
this.connectionInitOnce.reset();
|
|
132
|
+
|
|
133
|
+
if (conn) {
|
|
134
|
+
try {
|
|
135
|
+
conn.end();
|
|
136
|
+
} catch (error) {
|
|
137
|
+
logger.warn(`Error occurred while ending connection: ${error}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Destroy the connection socket immediately. No further events or callbacks will be triggered.
|
|
144
|
+
* This should be used only for special use cases!
|
|
145
|
+
*/
|
|
146
|
+
public destroyConnection = () => {
|
|
147
|
+
const conn = this.connection;
|
|
148
|
+
this.connection = undefined;
|
|
149
|
+
this.connectionInitOnce.reset();
|
|
150
|
+
|
|
151
|
+
if (conn) {
|
|
152
|
+
try {
|
|
153
|
+
conn.destroy();
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.warn(`Error occurred while destroying connection: ${error}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
public onPluginCreated = async () => this.tryToInitializeSchema(true);
|
|
161
|
+
|
|
162
|
+
private prepareConnection = async (): Promise<Connection> => {
|
|
163
|
+
if (this.connection) {
|
|
164
|
+
return this.connection;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return await this.connectionInitOnce.run(async () => {
|
|
168
|
+
await this.ensureConnectionConfig();
|
|
169
|
+
this.connection = await this.createConnection(this.MAX_RETRIES);
|
|
170
|
+
return this.connection;
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
private createConnection = async (
|
|
175
|
+
remainingRetries: number,
|
|
176
|
+
): Promise<Connection> => {
|
|
177
|
+
const conn = createConnection(this.connectionConfig);
|
|
178
|
+
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
conn.on('error', (err) => {
|
|
181
|
+
logger.error(`Connection error event: ${err.message}`);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
conn.connect((err) => {
|
|
185
|
+
if (err) {
|
|
186
|
+
logger.error(`Failed to connect to database: ${err.message}`);
|
|
187
|
+
conn.destroy();
|
|
188
|
+
|
|
189
|
+
if (remainingRetries > 0) {
|
|
190
|
+
logger.warn(
|
|
191
|
+
`Retrying database connection... (${remainingRetries} attempt(s) remaining)`,
|
|
192
|
+
);
|
|
193
|
+
setTimeout(() => {
|
|
194
|
+
this.createConnection(remainingRetries - 1)
|
|
195
|
+
.then(resolve)
|
|
196
|
+
.catch(reject);
|
|
197
|
+
}, 100);
|
|
198
|
+
} else {
|
|
199
|
+
logger.error('Database connection failed after all retries. Giving up.');
|
|
200
|
+
reject(err);
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
logger.verbose('Database connection established successfully.');
|
|
204
|
+
resolve(conn);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
private ensureConnectionConfig = async (): Promise<void> => {
|
|
211
|
+
if (this.connectionConfig) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
await this.configInitOnce.run(async () => {
|
|
216
|
+
const baseConfig = this.options.config;
|
|
217
|
+
if (!this.options.secretId) {
|
|
218
|
+
this.connectionConfig = baseConfig;
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const credentials = await this.secretsCache.getDatabaseCredentials(
|
|
222
|
+
this.options.secretId,
|
|
223
|
+
);
|
|
224
|
+
this.connectionConfig = {
|
|
225
|
+
...baseConfig,
|
|
226
|
+
user: credentials.username,
|
|
227
|
+
password: credentials.password,
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
private changeDatabase = async (dbName: string) =>
|
|
233
|
+
new Promise<void>((resolve, reject) =>
|
|
234
|
+
this.prepareConnection()
|
|
235
|
+
.then((connection) =>
|
|
236
|
+
connection.changeUser(
|
|
237
|
+
{
|
|
238
|
+
database: dbName,
|
|
239
|
+
},
|
|
240
|
+
(err) => (err ? reject(err) : resolve(undefined)),
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
.catch(reject),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
private tryToInitializeSchema = async (initial: boolean) => {
|
|
247
|
+
const {
|
|
248
|
+
eager = false,
|
|
249
|
+
ignoreError = false,
|
|
250
|
+
database = '',
|
|
251
|
+
tables = {},
|
|
252
|
+
} = this.options.schema || {};
|
|
253
|
+
if (initial && !eager) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// This method can be called twice when eager option is on,
|
|
258
|
+
// so this flag should be set and checked at first.
|
|
259
|
+
if (this.initialized) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
this.initialized = true;
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
if (database) {
|
|
266
|
+
logger.debug(`Prepare a database[${this.dbName}]`);
|
|
267
|
+
logger.stupid(this.dbName!, database);
|
|
268
|
+
const result = await this.query(database);
|
|
269
|
+
logger.debug(
|
|
270
|
+
`Database[${this.dbName}] is initialized: ${JSON.stringify(result)}`,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
if (this.dbName) {
|
|
274
|
+
await this.changeDatabase(this.dbName);
|
|
275
|
+
logger.verbose(`Database[${this.dbName}] is connected.`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
for (const [name, query] of Object.entries(tables)) {
|
|
279
|
+
logger.debug(`Prepare a table[${name}]`);
|
|
280
|
+
logger.stupid(name, query);
|
|
281
|
+
const result = await this.query(query);
|
|
282
|
+
logger.debug(
|
|
283
|
+
`Table[${name}] is initialized: ${JSON.stringify(result)}`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
logger.verbose(`Database schema is initialized.`);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
logger.warn(error);
|
|
289
|
+
if (!ignoreError) {
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|