@starbemtech/star-db-query-builder 1.0.32 → 1.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -37
- package/coverage/clover.xml +2 -2
- package/coverage/lcov-report/index.html +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/src/db/initDb.d.ts +5 -2
- package/dist/src/db/initDb.js +16 -10
- package/dist/src/db/initDb.js.map +1 -1
- package/dist/src/db/mysqlClient.d.ts +2 -1
- package/dist/src/db/mysqlClient.js +69 -7
- package/dist/src/db/mysqlClient.js.map +1 -1
- package/dist/src/db/pgClient.d.ts +3 -2
- package/dist/src/db/pgClient.js +69 -4
- package/dist/src/db/pgClient.js.map +1 -1
- package/dist/src/default/types.d.ts +7 -0
- package/dist/src/monitor/monitor.d.ts +9 -0
- package/dist/src/monitor/monitor.js +14 -0
- package/dist/src/monitor/monitor.js.map +1 -0
- package/index.ts +1 -1
- package/package.json +8 -6
- package/src/db/initDb.ts +24 -12
- package/src/db/mysqlClient.ts +81 -7
- package/src/db/pgClient.ts +86 -5
- package/src/default/types.ts +8 -0
- package/src/monitor/monitor.ts +11 -0
- package/src/services/payment/index.ts +0 -31
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
7
|
+
- **Multi-Connection Support:** Simultaneously connect to multiple MySQL and PostgreSQL databases.
|
|
8
|
+
- **Automatic Retry:** Automatically retries queries in case of transient errors (e.g. connection loss or timeouts).
|
|
9
|
+
- **External Configuration:** Customize connection pool settings and retry parameters through external configuration.
|
|
10
|
+
- **Monitoring and Logging:** Emits events during the connection and query lifecycle, making it easier to integrate with your logging and monitoring systems.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -29,26 +29,57 @@ $ pnpm install star-db-query-builder
|
|
|
29
29
|
First, initialize the database with the appropriate configuration.
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
|
-
import { initDb, getDbClient } from 'star-db-query-builder';
|
|
32
|
+
import { initDb, getDbClient, PoolConfig } from 'star-db-query-builder';
|
|
33
33
|
|
|
34
34
|
// Use PostgresSQL
|
|
35
|
+
const pgPoolOptions: PoolConfig = {
|
|
36
|
+
host: process.env.PG_HOST,
|
|
37
|
+
user: process.env.PG_USER,
|
|
38
|
+
password: process.env.PG_PASS,
|
|
39
|
+
database: process.env.PG_DB,
|
|
40
|
+
// OR
|
|
41
|
+
connectionURL: 'YOUR POSTGRES CONNECTION URL'
|
|
42
|
+
max: Number(process.env.PG_POOL_MAX) || 10,
|
|
43
|
+
connectionTimeoutMillis: Number(process.env.PG_CONN_TIMEOUT) || 0,
|
|
44
|
+
// Other pool options as needed
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
initDb({
|
|
48
|
+
name: 'pg-prod',
|
|
36
49
|
type: 'pg',
|
|
37
|
-
options:
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
options: pgPoolOptions,
|
|
51
|
+
retryOptions: {
|
|
52
|
+
retries: 3,
|
|
53
|
+
factor: 2,
|
|
54
|
+
minTimeout: 1000,
|
|
55
|
+
// Other retry parameters if needed
|
|
56
|
+
}
|
|
40
57
|
});
|
|
41
58
|
|
|
42
59
|
// User MySQL
|
|
43
60
|
initDb({
|
|
61
|
+
name: 'mysql-prod',
|
|
44
62
|
type: 'mysql',
|
|
45
63
|
options: {
|
|
46
|
-
|
|
64
|
+
host: process.env.MYSQL_HOST,
|
|
65
|
+
user: process.env.MYSQL_USER,
|
|
66
|
+
password: process.env.MYSQL_PASS,
|
|
67
|
+
database: process.env.MYSQL_DB,
|
|
68
|
+
connectionLimit: Number(process.env.MYSQL_CONN_LIMIT) || 10,
|
|
69
|
+
// OR
|
|
70
|
+
url: 'YOUR MYSQL CONNECTION URL'
|
|
71
|
+
// Other pool options as needed
|
|
47
72
|
},
|
|
73
|
+
retryOptions: {
|
|
74
|
+
retries: 3,
|
|
75
|
+
factor: 2,
|
|
76
|
+
minTimeout: 1000,
|
|
77
|
+
// Other retry parameters if needed
|
|
78
|
+
}
|
|
48
79
|
});
|
|
49
80
|
|
|
50
81
|
// In your service, create an instance of getDbClient
|
|
51
|
-
const dbClient = getDbClient();
|
|
82
|
+
const dbClient = getDbClient('pg-prod');
|
|
52
83
|
```
|
|
53
84
|
|
|
54
85
|
### Methods
|
|
@@ -58,18 +89,18 @@ const dbClient = getDbClient();
|
|
|
58
89
|
Retrieve the first matching record from a table.
|
|
59
90
|
|
|
60
91
|
```typescript
|
|
61
|
-
import { findFirst } from 'star-db-query-builder'
|
|
92
|
+
import { findFirst } from 'star-db-query-builder'
|
|
62
93
|
|
|
63
94
|
const result = await findFirst({
|
|
64
95
|
tableName: 'users',
|
|
65
96
|
dbClient,
|
|
66
97
|
select: ['id', 'name', 'email'],
|
|
67
98
|
where: {
|
|
68
|
-
id: { operator: '=', value: 1 }
|
|
69
|
-
}
|
|
70
|
-
})
|
|
99
|
+
id: { operator: '=', value: 1 },
|
|
100
|
+
},
|
|
101
|
+
})
|
|
71
102
|
|
|
72
|
-
console.log(result)
|
|
103
|
+
console.log(result)
|
|
73
104
|
```
|
|
74
105
|
|
|
75
106
|
#### findMany
|
|
@@ -77,20 +108,20 @@ console.log(result);
|
|
|
77
108
|
Retrieve multiple records from a table.
|
|
78
109
|
|
|
79
110
|
```typescript
|
|
80
|
-
import { findMany } from 'star-db-query-builder'
|
|
111
|
+
import { findMany } from 'star-db-query-builder'
|
|
81
112
|
|
|
82
113
|
const results = await findMany({
|
|
83
114
|
tableName: 'users',
|
|
84
115
|
dbClient,
|
|
85
116
|
select: ['id', 'name', 'email'],
|
|
86
117
|
where: {
|
|
87
|
-
status: { operator: '= ', value: 'active' }
|
|
118
|
+
status: { operator: '= ', value: 'active' },
|
|
88
119
|
},
|
|
89
120
|
limit: 10,
|
|
90
121
|
offset: 0,
|
|
91
|
-
})
|
|
122
|
+
})
|
|
92
123
|
|
|
93
|
-
console.log(results)
|
|
124
|
+
console.log(results)
|
|
94
125
|
```
|
|
95
126
|
|
|
96
127
|
#### insert
|
|
@@ -98,18 +129,18 @@ console.log(results);
|
|
|
98
129
|
Insert a new record into a table.
|
|
99
130
|
|
|
100
131
|
```typescript
|
|
101
|
-
import { insert } from 'star-db-query-builder'
|
|
132
|
+
import { insert } from 'star-db-query-builder'
|
|
102
133
|
|
|
103
|
-
const newUser = { name: 'John Doe', email: 'john@example.com' }
|
|
134
|
+
const newUser = { name: 'John Doe', email: 'john@example.com' }
|
|
104
135
|
|
|
105
136
|
const insertedUser = await insert({
|
|
106
137
|
tableName: 'users',
|
|
107
138
|
dbClient,
|
|
108
139
|
data: newUser,
|
|
109
140
|
returning: ['id', 'name', 'email'],
|
|
110
|
-
})
|
|
141
|
+
})
|
|
111
142
|
|
|
112
|
-
console.log(insertedUser)
|
|
143
|
+
console.log(insertedUser)
|
|
113
144
|
```
|
|
114
145
|
|
|
115
146
|
#### update
|
|
@@ -117,9 +148,9 @@ console.log(insertedUser);
|
|
|
117
148
|
Update an existing record in a table.
|
|
118
149
|
|
|
119
150
|
```typescript
|
|
120
|
-
import { update } from 'star-db-query-builder'
|
|
151
|
+
import { update } from 'star-db-query-builder'
|
|
121
152
|
|
|
122
|
-
const updatedUser = { name: 'John Smith' }
|
|
153
|
+
const updatedUser = { name: 'John Smith' }
|
|
123
154
|
|
|
124
155
|
const result = await update({
|
|
125
156
|
tableName: 'users',
|
|
@@ -127,9 +158,9 @@ const result = await update({
|
|
|
127
158
|
id: 1,
|
|
128
159
|
data: updatedUser,
|
|
129
160
|
returning: ['id', 'name', 'email'],
|
|
130
|
-
})
|
|
161
|
+
})
|
|
131
162
|
|
|
132
|
-
console.log(result)
|
|
163
|
+
console.log(result)
|
|
133
164
|
```
|
|
134
165
|
|
|
135
166
|
#### deleteOne
|
|
@@ -137,16 +168,16 @@ console.log(result);
|
|
|
137
168
|
Delete a record from a table.
|
|
138
169
|
|
|
139
170
|
```typescript
|
|
140
|
-
import { deleteOne } from 'star-db-query-builder'
|
|
171
|
+
import { deleteOne } from 'star-db-query-builder'
|
|
141
172
|
|
|
142
173
|
await deleteOne({
|
|
143
174
|
tableName: 'users',
|
|
144
175
|
dbClient,
|
|
145
176
|
id: 1,
|
|
146
177
|
permanently: true,
|
|
147
|
-
})
|
|
178
|
+
})
|
|
148
179
|
|
|
149
|
-
console.log('User deleted')
|
|
180
|
+
console.log('User deleted')
|
|
150
181
|
```
|
|
151
182
|
|
|
152
183
|
#### joins
|
|
@@ -154,7 +185,7 @@ console.log('User deleted');
|
|
|
154
185
|
Execute a join query.
|
|
155
186
|
|
|
156
187
|
```typescript
|
|
157
|
-
import { joins } from 'star-db-query-builder'
|
|
188
|
+
import { joins } from 'star-db-query-builder'
|
|
158
189
|
|
|
159
190
|
const joinResults = await joins({
|
|
160
191
|
tableName: 'orders',
|
|
@@ -169,19 +200,71 @@ const joinResults = await joins({
|
|
|
169
200
|
where: {
|
|
170
201
|
JOINS: [
|
|
171
202
|
{
|
|
172
|
-
'users.id': { operator: '=', value: exist.user_id }
|
|
173
|
-
}
|
|
174
|
-
]
|
|
175
|
-
}
|
|
176
|
-
})
|
|
203
|
+
'users.id': { operator: '=', value: exist.user_id },
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
})
|
|
177
208
|
|
|
178
|
-
console.log(joinResults)
|
|
209
|
+
console.log(joinResults)
|
|
179
210
|
```
|
|
180
211
|
|
|
212
|
+
### Monitoring and Logging
|
|
213
|
+
|
|
214
|
+
The library provides a monitoring module that emits key events during the lifecycle of connections and queries. You can subscribe to these events to integrate with your logging or monitoring system.
|
|
215
|
+
|
|
216
|
+
#### Available Events
|
|
217
|
+
|
|
218
|
+
CONNECTION_CREATED: Emitted when a new connection (pool) is established.
|
|
219
|
+
QUERY_START: Emitted just before a query starts executing.
|
|
220
|
+
QUERY_END: Emitted after a query completes, including its execution time.
|
|
221
|
+
QUERY_ERROR: Emitted when an error occurs during query execution.
|
|
222
|
+
RETRY_ATTEMPT: Emitted when a query is retried due to a transient error.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { monitor, MonitorEvents } from '@starbemtech/star-db-query-builder'
|
|
226
|
+
|
|
227
|
+
monitor.on(MonitorEvents.CONNECTION_CREATED, (data) => {
|
|
228
|
+
console.log('Connection created:', data)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
monitor.on(MonitorEvents.QUERY_START, (data) => {
|
|
232
|
+
console.log('Query started:', data)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
monitor.on(MonitorEvents.QUERY_END, (data) => {
|
|
236
|
+
console.log('Query finished:', data)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
monitor.on(MonitorEvents.QUERY_ERROR, (data) => {
|
|
240
|
+
console.error('Query error:', data)
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
monitor.on(MonitorEvents.RETRY_ATTEMPT, (data) => {
|
|
244
|
+
console.warn('Retrying query:', data)
|
|
245
|
+
})
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Customizing the Retry Strategy
|
|
249
|
+
|
|
250
|
+
The automatic retry mechanism leverages the promise-retry library. You can customize the following parameters:
|
|
251
|
+
|
|
252
|
+
- retries: Number of retry attempts.
|
|
253
|
+
- factor: Exponential backoff factor.
|
|
254
|
+
- minTimeout: Minimum time (in milliseconds) to wait between retry attempts.
|
|
255
|
+
|
|
256
|
+
These parameters are passed via the retryOptions property when initializing the connection.
|
|
257
|
+
|
|
258
|
+
## Contributing
|
|
259
|
+
|
|
260
|
+
Feel free to contribute by opening pull requests or issues with improvements and bug fixes.
|
|
261
|
+
|
|
181
262
|
## License
|
|
182
263
|
|
|
183
264
|
This project is licensed under the MIT License.
|
|
184
265
|
|
|
266
|
+
This documentation explains how to set up connections with external configuration for both connection pool and retry options, retrieve clients to execute queries, and monitor events for logging and diagnostics.
|
|
267
|
+
|
|
185
268
|
---
|
|
186
269
|
|
|
187
270
|
👨💻 Happy Coding!
|
package/coverage/clover.xml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<coverage generated="
|
|
3
|
-
<project timestamp="
|
|
2
|
+
<coverage generated="1740003061166" clover="3.2.0">
|
|
3
|
+
<project timestamp="1740003061166" name="All files">
|
|
4
4
|
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0" elements="0" coveredelements="0" complexity="0" loc="0" ncloc="0" packages="0" files="0" classes="0"/>
|
|
5
5
|
</project>
|
|
6
6
|
</coverage>
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
87
87
|
Code coverage generated by
|
|
88
88
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
89
|
-
at 2025-02-
|
|
89
|
+
at 2025-02-19T22:11:01.160Z
|
|
90
90
|
</div>
|
|
91
91
|
<script src="prettify.js"></script>
|
|
92
92
|
<script>
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,4 @@ import { PoolOptions as MySqlPoolOptions } from 'mysql2';
|
|
|
3
3
|
import { Conditions as TypeConditions } from './src/default/types';
|
|
4
4
|
export * from './src/db/initDb';
|
|
5
5
|
export * from './src/default/genericRepository';
|
|
6
|
-
export * from './src/services/payment';
|
|
7
6
|
export type { PgPoolConfig, MySqlPoolOptions, TypeConditions };
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
__exportStar(require("./src/db/initDb"), exports);
|
|
19
19
|
// Generic Repository
|
|
20
20
|
__exportStar(require("./src/default/genericRepository"), exports);
|
|
21
|
-
// Services Repository
|
|
22
|
-
__exportStar(require("./src/services/payment"), exports);
|
|
23
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAIA,UAAU;AACV,kDAA+B;AAE/B,qBAAqB;AACrB,kEAA+C
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAIA,UAAU;AACV,kDAA+B;AAE/B,qBAAqB;AACrB,kEAA+C"}
|
package/dist/src/db/initDb.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { IDatabaseClient } from './IDatabaseClient';
|
|
2
|
-
import { DBClients } from '../default/types';
|
|
2
|
+
import { DBClients, RetryOptions } from '../default/types';
|
|
3
3
|
export declare const initDb: <T>(config: {
|
|
4
|
+
name: string;
|
|
4
5
|
type: DBClients;
|
|
5
6
|
options: T;
|
|
7
|
+
retryOptions?: RetryOptions;
|
|
6
8
|
}) => Promise<void>;
|
|
7
|
-
export declare const getDbClient: () => IDatabaseClient;
|
|
9
|
+
export declare const getDbClient: (name: string) => IDatabaseClient;
|
|
10
|
+
export declare const getAllDbClients: () => Record<string, IDatabaseClient>;
|
package/dist/src/db/initDb.js
CHANGED
|
@@ -1,36 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDbClient = exports.initDb = void 0;
|
|
3
|
+
exports.getAllDbClients = exports.getDbClient = exports.initDb = void 0;
|
|
4
4
|
const pg_1 = require("pg");
|
|
5
5
|
const promise_1 = require("mysql2/promise");
|
|
6
6
|
const pgClient_1 = require("./pgClient");
|
|
7
7
|
const mysqlClient_1 = require("./mysqlClient");
|
|
8
|
-
|
|
8
|
+
const dbClients = {};
|
|
9
9
|
const initDb = async (config) => {
|
|
10
10
|
if (!config.type)
|
|
11
11
|
throw new Error('Type is required. Accept values: pg | mysql');
|
|
12
12
|
if (!config.options)
|
|
13
13
|
throw new Error('Connection options is required');
|
|
14
14
|
if (config.type === 'pg') {
|
|
15
|
+
const poolConfig = config.options;
|
|
15
16
|
const pool = new pg_1.Pool(config.options);
|
|
16
|
-
|
|
17
|
-
console.log(`@starbemtech/star-db-query-builder: Postgres db client created
|
|
17
|
+
dbClients[config.name] = await (0, pgClient_1.createPgClient)(pool, config.retryOptions, poolConfig);
|
|
18
|
+
console.log(`@starbemtech/star-db-query-builder: Postgres db client "${config.name}" created successfully`);
|
|
18
19
|
}
|
|
19
20
|
else if (config.type === 'mysql') {
|
|
20
21
|
const pool = (0, promise_1.createPool)(config.options);
|
|
21
|
-
|
|
22
|
-
console.info(`@starbemtech/star-db-query-builder:
|
|
22
|
+
dbClients[config.name] = (0, mysqlClient_1.createMysqlClient)(pool, config.retryOptions);
|
|
23
|
+
console.info(`@starbemtech/star-db-query-builder: Postgres db client "${config.name}" created successfully`);
|
|
23
24
|
}
|
|
24
25
|
else {
|
|
25
26
|
throw new Error('Unsupported database type');
|
|
26
27
|
}
|
|
27
28
|
};
|
|
28
29
|
exports.initDb = initDb;
|
|
29
|
-
const getDbClient = () => {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const getDbClient = (name) => {
|
|
31
|
+
const client = dbClients[name];
|
|
32
|
+
if (!client) {
|
|
33
|
+
throw new Error(`Database client "${name}" is not initialized`);
|
|
32
34
|
}
|
|
33
|
-
return
|
|
35
|
+
return client;
|
|
34
36
|
};
|
|
35
37
|
exports.getDbClient = getDbClient;
|
|
38
|
+
const getAllDbClients = () => {
|
|
39
|
+
return dbClients;
|
|
40
|
+
};
|
|
41
|
+
exports.getAllDbClients = getAllDbClients;
|
|
36
42
|
//# sourceMappingURL=initDb.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initDb.js","sourceRoot":"","sources":["../../../src/db/initDb.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"initDb.js","sourceRoot":"","sources":["../../../src/db/initDb.ts"],"names":[],"mappings":";;;AAAA,2BAAqC;AACrC,4CAA8D;AAC9D,yCAA2C;AAC3C,+CAAiD;AAIjD,MAAM,SAAS,GAAoC,EAAE,CAAA;AAE9C,MAAM,MAAM,GAAG,KAAK,EAAK,MAK/B,EAAiB,EAAE;IAClB,IAAI,CAAC,MAAM,CAAC,IAAI;QACd,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;IAEhE,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IAEtE,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAgC,CAAA;QAC1D,MAAM,IAAI,GAAG,IAAI,SAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACrC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAA,yBAAc,EAC3C,IAAI,EACJ,MAAM,CAAC,YAAY,EACnB,UAAU,CACX,CAAA;QAED,OAAO,CAAC,GAAG,CACT,2DAA2D,MAAM,CAAC,IAAI,wBAAwB,CAC/F,CAAA;IACH,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,IAAA,oBAAe,EAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC5C,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAA,+BAAiB,EAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;QAErE,OAAO,CAAC,IAAI,CACV,2DAA2D,MAAM,CAAC,IAAI,wBAAwB,CAC/F,CAAA;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;IAC9C,CAAC;AACH,CAAC,CAAA;AAjCY,QAAA,MAAM,UAiClB;AAEM,MAAM,WAAW,GAAG,CAAC,IAAY,EAAmB,EAAE;IAC3D,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,sBAAsB,CAAC,CAAA;IACjE,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAPY,QAAA,WAAW,eAOvB;AAEM,MAAM,eAAe,GAAG,GAAoC,EAAE;IACnE,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAFY,QAAA,eAAe,mBAE3B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Pool } from 'mysql2/promise';
|
|
2
2
|
import { IDatabaseClient } from './IDatabaseClient';
|
|
3
|
-
|
|
3
|
+
import { RetryOptions } from '../default/types';
|
|
4
|
+
export declare const createMysqlClient: (pool: Pool, retryOptions?: RetryOptions) => IDatabaseClient;
|
|
@@ -1,12 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.createMysqlClient = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const promise_retry_1 = __importDefault(require("promise-retry"));
|
|
8
|
+
const monitor_1 = require("../monitor/monitor");
|
|
9
|
+
const transientErrorCodes = new Set([
|
|
10
|
+
'ECONNRESET',
|
|
11
|
+
'ETIMEDOUT',
|
|
12
|
+
'PROTOCOL_CONNECTION_LOST',
|
|
13
|
+
'ECONNREFUSED',
|
|
14
|
+
]);
|
|
15
|
+
function isTransientError(error) {
|
|
16
|
+
return error && error.code && transientErrorCodes.has(error.code);
|
|
17
|
+
}
|
|
18
|
+
const createMysqlClient = (pool, retryOptions) => {
|
|
19
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.CONNECTION_CREATED, {
|
|
20
|
+
clientType: 'mysql',
|
|
21
|
+
poolOptions: pool.config,
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
clientType: 'mysql',
|
|
25
|
+
query: async (sql, params) => {
|
|
26
|
+
return (0, promise_retry_1.default)(async (retry, attempt) => {
|
|
27
|
+
const startTime = Date.now();
|
|
28
|
+
try {
|
|
29
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.QUERY_START, {
|
|
30
|
+
clientType: 'mysql',
|
|
31
|
+
sql,
|
|
32
|
+
params,
|
|
33
|
+
attempt,
|
|
34
|
+
});
|
|
35
|
+
const [rows] = await pool.execute(sql, params);
|
|
36
|
+
const elapsedTime = Date.now() - startTime;
|
|
37
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.QUERY_END, {
|
|
38
|
+
clientType: 'mysql',
|
|
39
|
+
sql,
|
|
40
|
+
params,
|
|
41
|
+
attempt,
|
|
42
|
+
elapsedTime,
|
|
43
|
+
});
|
|
44
|
+
return rows;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const elapsedTime = Date.now() - startTime;
|
|
48
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.QUERY_ERROR, {
|
|
49
|
+
clientType: 'mysql',
|
|
50
|
+
sql,
|
|
51
|
+
params,
|
|
52
|
+
attempt,
|
|
53
|
+
elapsedTime,
|
|
54
|
+
error,
|
|
55
|
+
});
|
|
56
|
+
if (isTransientError(error)) {
|
|
57
|
+
console.warn(`MySQL query attempt ${attempt} failed, retrying...`, error);
|
|
58
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.RETRY_ATTEMPT, {
|
|
59
|
+
clientType: 'mysql',
|
|
60
|
+
sql,
|
|
61
|
+
params,
|
|
62
|
+
attempt,
|
|
63
|
+
error,
|
|
64
|
+
});
|
|
65
|
+
return retry(error);
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}, retryOptions);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
};
|
|
11
73
|
exports.createMysqlClient = createMysqlClient;
|
|
12
74
|
//# sourceMappingURL=mysqlClient.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysqlClient.js","sourceRoot":"","sources":["../../../src/db/mysqlClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mysqlClient.js","sourceRoot":"","sources":["../../../src/db/mysqlClient.ts"],"names":[],"mappings":";;;;;;AACA,kEAAwC;AAGxC,gDAA2D;AAE3D,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC;IAClC,YAAY;IACZ,WAAW;IACX,0BAA0B;IAC1B,cAAc;CACf,CAAC,CAAA;AAEF,SAAS,gBAAgB,CAAC,KAAU;IAClC,OAAO,KAAK,IAAI,KAAK,CAAC,IAAI,IAAI,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AACnE,CAAC;AAEM,MAAM,iBAAiB,GAAG,CAC/B,IAAU,EACV,YAA2B,EACV,EAAE;IACnB,iBAAO,CAAC,IAAI,CAAC,uBAAa,CAAC,kBAAkB,EAAE;QAC7C,UAAU,EAAE,OAAO;QACnB,WAAW,EAAE,IAAI,CAAC,MAAM;KACzB,CAAC,CAAA;IAEF,OAAO;QACL,UAAU,EAAE,OAAO;QACnB,KAAK,EAAE,KAAK,EAAK,GAAW,EAAE,MAAc,EAAc,EAAE;YAC1D,OAAO,IAAA,uBAAY,EAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;gBAC5B,IAAI,CAAC;oBACH,iBAAO,CAAC,IAAI,CAAC,uBAAa,CAAC,WAAW,EAAE;wBACtC,UAAU,EAAE,OAAO;wBACnB,GAAG;wBACH,MAAM;wBACN,OAAO;qBACR,CAAC,CAAA;oBAEF,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;oBAE9C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAA;oBAC1C,iBAAO,CAAC,IAAI,CAAC,uBAAa,CAAC,SAAS,EAAE;wBACpC,UAAU,EAAE,OAAO;wBACnB,GAAG;wBACH,MAAM;wBACN,OAAO;wBACP,WAAW;qBACZ,CAAC,CAAA;oBAEF,OAAO,IAAoB,CAAA;gBAC7B,CAAC;gBAAC,OAAO,KAAU,EAAE,CAAC;oBACpB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAA;oBAC1C,iBAAO,CAAC,IAAI,CAAC,uBAAa,CAAC,WAAW,EAAE;wBACtC,UAAU,EAAE,OAAO;wBACnB,GAAG;wBACH,MAAM;wBACN,OAAO;wBACP,WAAW;wBACX,KAAK;qBACN,CAAC,CAAA;oBAEF,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC5B,OAAO,CAAC,IAAI,CACV,uBAAuB,OAAO,sBAAsB,EACpD,KAAK,CACN,CAAA;wBACD,iBAAO,CAAC,IAAI,CAAC,uBAAa,CAAC,aAAa,EAAE;4BACxC,UAAU,EAAE,OAAO;4BACnB,GAAG;4BACH,MAAM;4BACN,OAAO;4BACP,KAAK;yBACN,CAAC,CAAA;wBAEF,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;oBACrB,CAAC;oBAED,MAAM,KAAK,CAAA;gBACb,CAAC;YACH,CAAC,EAAE,YAAY,CAAC,CAAA;QAClB,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAlEY,QAAA,iBAAiB,qBAkE7B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { Pool } from 'pg';
|
|
1
|
+
import { Pool, PoolConfig } from 'pg';
|
|
2
2
|
import { IDatabaseClient } from './IDatabaseClient';
|
|
3
|
-
|
|
3
|
+
import { RetryOptions } from '../default/types';
|
|
4
|
+
export declare const createPgClient: (pool: Pool, retryOptions?: RetryOptions, poolConfig?: PoolConfig) => Promise<IDatabaseClient>;
|
package/dist/src/db/pgClient.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.createPgClient = void 0;
|
|
7
|
+
const promise_retry_1 = __importDefault(require("promise-retry"));
|
|
8
|
+
const monitor_1 = require("../monitor/monitor");
|
|
9
|
+
const transientErrorCodes = new Set(['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED']);
|
|
10
|
+
function isTransientError(error) {
|
|
11
|
+
return error && error.code && transientErrorCodes.has(error.code);
|
|
12
|
+
}
|
|
4
13
|
async function ensureUnaccentExtension(pool) {
|
|
5
14
|
try {
|
|
6
15
|
const checkResult = await pool.query(`
|
|
@@ -8,21 +17,77 @@ async function ensureUnaccentExtension(pool) {
|
|
|
8
17
|
`);
|
|
9
18
|
if (checkResult.rows.length === 0) {
|
|
10
19
|
await pool.query('CREATE EXTENSION IF NOT EXISTS unaccent');
|
|
11
|
-
|
|
20
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.CONNECTION_CREATED, {
|
|
21
|
+
clientType: 'pg',
|
|
22
|
+
extension: 'unaccent',
|
|
23
|
+
status: 'installed',
|
|
24
|
+
});
|
|
25
|
+
console.info('@starbemtech/star-db-query-builder: Extensão unaccent instalada com sucesso.');
|
|
12
26
|
}
|
|
13
27
|
}
|
|
14
28
|
catch (error) {
|
|
29
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.QUERY_ERROR, {
|
|
30
|
+
clientType: 'pg',
|
|
31
|
+
action: 'install_unaccent',
|
|
32
|
+
error,
|
|
33
|
+
});
|
|
15
34
|
throw new Error(`@starbemtech/star-db-query-builder: Não foi possível instalar a extensão unaccent:
|
|
16
35
|
${error}`);
|
|
17
36
|
}
|
|
18
37
|
}
|
|
19
|
-
const createPgClient = async (pool) => {
|
|
38
|
+
const createPgClient = async (pool, retryOptions, poolConfig) => {
|
|
20
39
|
await ensureUnaccentExtension(pool);
|
|
40
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.CONNECTION_CREATED, {
|
|
41
|
+
clientType: 'pg',
|
|
42
|
+
poolOptions: poolConfig,
|
|
43
|
+
});
|
|
21
44
|
return {
|
|
22
45
|
clientType: 'pg',
|
|
23
46
|
query: async (sql, params) => {
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
return (0, promise_retry_1.default)(async (retry, attempt) => {
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
try {
|
|
50
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.QUERY_START, {
|
|
51
|
+
clientType: 'pg',
|
|
52
|
+
sql,
|
|
53
|
+
params,
|
|
54
|
+
attempt,
|
|
55
|
+
});
|
|
56
|
+
const { rows } = await pool.query(sql, params);
|
|
57
|
+
const elapsedTime = Date.now() - startTime;
|
|
58
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.QUERY_END, {
|
|
59
|
+
clientType: 'pg',
|
|
60
|
+
sql,
|
|
61
|
+
params,
|
|
62
|
+
attempt,
|
|
63
|
+
elapsedTime,
|
|
64
|
+
});
|
|
65
|
+
return rows;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const elapsedTime = Date.now() - startTime;
|
|
69
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.QUERY_ERROR, {
|
|
70
|
+
clientType: 'pg',
|
|
71
|
+
sql,
|
|
72
|
+
params,
|
|
73
|
+
attempt,
|
|
74
|
+
elapsedTime,
|
|
75
|
+
error,
|
|
76
|
+
});
|
|
77
|
+
if (isTransientError(error)) {
|
|
78
|
+
console.warn(`Postgres query attempt ${attempt} failed, retrying...`, error);
|
|
79
|
+
monitor_1.monitor.emit(monitor_1.MonitorEvents.RETRY_ATTEMPT, {
|
|
80
|
+
clientType: 'pg',
|
|
81
|
+
sql,
|
|
82
|
+
params,
|
|
83
|
+
attempt,
|
|
84
|
+
error,
|
|
85
|
+
});
|
|
86
|
+
return retry(error);
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}, retryOptions);
|
|
26
91
|
},
|
|
27
92
|
};
|
|
28
93
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pgClient.js","sourceRoot":"","sources":["../../../src/db/pgClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pgClient.js","sourceRoot":"","sources":["../../../src/db/pgClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { IDatabaseClient } from '../db/IDatabaseClient';
|
|
2
|
+
export interface RetryOptions {
|
|
3
|
+
retries?: number;
|
|
4
|
+
factor?: number;
|
|
5
|
+
minTimeout?: number;
|
|
6
|
+
maxTimeout?: number;
|
|
7
|
+
randomize?: boolean;
|
|
8
|
+
}
|
|
2
9
|
export interface QueryExec {
|
|
3
10
|
text: string;
|
|
4
11
|
values?: any[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EventEmitter } from 'stream';
|
|
2
|
+
export declare enum MonitorEvents {
|
|
3
|
+
CONNECTION_CREATED = "connection_created",
|
|
4
|
+
QUERY_START = "query_start",
|
|
5
|
+
QUERY_END = "query_end",
|
|
6
|
+
QUERY_ERROR = "query_error",
|
|
7
|
+
RETRY_ATTEMPT = "retry_attempt"
|
|
8
|
+
}
|
|
9
|
+
export declare const monitor: EventEmitter<[never]>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.monitor = exports.MonitorEvents = void 0;
|
|
4
|
+
const stream_1 = require("stream");
|
|
5
|
+
var MonitorEvents;
|
|
6
|
+
(function (MonitorEvents) {
|
|
7
|
+
MonitorEvents["CONNECTION_CREATED"] = "connection_created";
|
|
8
|
+
MonitorEvents["QUERY_START"] = "query_start";
|
|
9
|
+
MonitorEvents["QUERY_END"] = "query_end";
|
|
10
|
+
MonitorEvents["QUERY_ERROR"] = "query_error";
|
|
11
|
+
MonitorEvents["RETRY_ATTEMPT"] = "retry_attempt";
|
|
12
|
+
})(MonitorEvents || (exports.MonitorEvents = MonitorEvents = {}));
|
|
13
|
+
exports.monitor = new stream_1.EventEmitter();
|
|
14
|
+
//# sourceMappingURL=monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../../src/monitor/monitor.ts"],"names":[],"mappings":";;;AAAA,mCAAqC;AAErC,IAAY,aAMX;AAND,WAAY,aAAa;IACvB,0DAAyC,CAAA;IACzC,4CAA2B,CAAA;IAC3B,wCAAuB,CAAA;IACvB,4CAA2B,CAAA;IAC3B,gDAA+B,CAAA;AACjC,CAAC,EANW,aAAa,6BAAb,aAAa,QAMxB;AAEY,QAAA,OAAO,GAAG,IAAI,qBAAY,EAAE,CAAA"}
|
package/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * from './src/db/initDb'
|
|
|
9
9
|
export * from './src/default/genericRepository'
|
|
10
10
|
|
|
11
11
|
// Services Repository
|
|
12
|
-
export * from './src/services/payment'
|
|
12
|
+
// export * from './src/services/payment'
|
|
13
13
|
|
|
14
14
|
// Types Definition
|
|
15
15
|
export type { PgPoolConfig, MySqlPoolOptions, TypeConditions }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starbemtech/star-db-query-builder",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33",
|
|
4
4
|
"description": "A query builder to be used with mysql or postgres",
|
|
5
5
|
"author": "Julio Sousa <julio.sousa@starbem.app>",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"@types/jest": "^29.5.12",
|
|
34
34
|
"@types/mysql": "^2.15.26",
|
|
35
35
|
"@types/node": "^20.11.30",
|
|
36
|
-
"@types/pg": "^8.11.
|
|
36
|
+
"@types/pg": "^8.11.11",
|
|
37
|
+
"@types/promise-retry": "^1.1.6",
|
|
37
38
|
"@types/uuid": "^9.0.8",
|
|
38
39
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
|
39
40
|
"@typescript-eslint/parser": "^7.4.0",
|
|
@@ -46,13 +47,14 @@
|
|
|
46
47
|
"lint-staged": "^15.2.2",
|
|
47
48
|
"prettier": "^3.2.5",
|
|
48
49
|
"ts-node": "^10.9.2",
|
|
49
|
-
"typescript": "^5.
|
|
50
|
+
"typescript": "^5.8.2"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
53
|
"@starbemtech/star-db-query-builder": "link:",
|
|
53
|
-
"mysql2": "^3.
|
|
54
|
-
"pg": "^8.
|
|
55
|
-
"
|
|
54
|
+
"mysql2": "^3.13.0",
|
|
55
|
+
"pg": "^8.13.3",
|
|
56
|
+
"promise-retry": "^2.0.1",
|
|
57
|
+
"uuid": "^11.1.0"
|
|
56
58
|
},
|
|
57
59
|
"lint-staged": {
|
|
58
60
|
"*.{ts}": [
|
package/src/db/initDb.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { Pool
|
|
1
|
+
import { Pool, PoolConfig } from 'pg'
|
|
2
2
|
import { createPool as createMySqlPool } from 'mysql2/promise'
|
|
3
3
|
import { createPgClient } from './pgClient'
|
|
4
4
|
import { createMysqlClient } from './mysqlClient'
|
|
5
5
|
import { IDatabaseClient } from './IDatabaseClient'
|
|
6
|
-
import { DBClients } from '../default/types'
|
|
6
|
+
import { DBClients, RetryOptions } from '../default/types'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const dbClients: Record<string, IDatabaseClient> = {}
|
|
9
9
|
|
|
10
10
|
export const initDb = async <T>(config: {
|
|
11
|
+
name: string
|
|
11
12
|
type: DBClients
|
|
12
13
|
options: T
|
|
14
|
+
retryOptions?: RetryOptions
|
|
13
15
|
}): Promise<void> => {
|
|
14
16
|
if (!config.type)
|
|
15
17
|
throw new Error('Type is required. Accept values: pg | mysql')
|
|
@@ -17,28 +19,38 @@ export const initDb = async <T>(config: {
|
|
|
17
19
|
if (!config.options) throw new Error('Connection options is required')
|
|
18
20
|
|
|
19
21
|
if (config.type === 'pg') {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
+
const poolConfig = config.options as unknown as PoolConfig
|
|
23
|
+
const pool = new Pool(config.options)
|
|
24
|
+
dbClients[config.name] = await createPgClient(
|
|
25
|
+
pool,
|
|
26
|
+
config.retryOptions,
|
|
27
|
+
poolConfig
|
|
28
|
+
)
|
|
22
29
|
|
|
23
30
|
console.log(
|
|
24
|
-
`@starbemtech/star-db-query-builder: Postgres db client created
|
|
31
|
+
`@starbemtech/star-db-query-builder: Postgres db client "${config.name}" created successfully`
|
|
25
32
|
)
|
|
26
33
|
} else if (config.type === 'mysql') {
|
|
27
34
|
const pool = createMySqlPool(config.options)
|
|
28
|
-
|
|
35
|
+
dbClients[config.name] = createMysqlClient(pool, config.retryOptions)
|
|
29
36
|
|
|
30
37
|
console.info(
|
|
31
|
-
`@starbemtech/star-db-query-builder:
|
|
38
|
+
`@starbemtech/star-db-query-builder: Postgres db client "${config.name}" created successfully`
|
|
32
39
|
)
|
|
33
40
|
} else {
|
|
34
41
|
throw new Error('Unsupported database type')
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
export const getDbClient = (): IDatabaseClient => {
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
export const getDbClient = (name: string): IDatabaseClient => {
|
|
46
|
+
const client = dbClients[name]
|
|
47
|
+
if (!client) {
|
|
48
|
+
throw new Error(`Database client "${name}" is not initialized`)
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
return
|
|
51
|
+
return client
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const getAllDbClients = (): Record<string, IDatabaseClient> => {
|
|
55
|
+
return dbClients
|
|
44
56
|
}
|
package/src/db/mysqlClient.ts
CHANGED
|
@@ -1,10 +1,84 @@
|
|
|
1
1
|
import { Pool } from 'mysql2/promise'
|
|
2
|
+
import promiseRetry from 'promise-retry'
|
|
2
3
|
import { IDatabaseClient } from './IDatabaseClient'
|
|
4
|
+
import { RetryOptions } from '../default/types'
|
|
5
|
+
import { monitor, MonitorEvents } from '../monitor/monitor'
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const transientErrorCodes = new Set([
|
|
8
|
+
'ECONNRESET',
|
|
9
|
+
'ETIMEDOUT',
|
|
10
|
+
'PROTOCOL_CONNECTION_LOST',
|
|
11
|
+
'ECONNREFUSED',
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
function isTransientError(error: any): boolean {
|
|
15
|
+
return error && error.code && transientErrorCodes.has(error.code)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const createMysqlClient = (
|
|
19
|
+
pool: Pool,
|
|
20
|
+
retryOptions?: RetryOptions
|
|
21
|
+
): IDatabaseClient => {
|
|
22
|
+
monitor.emit(MonitorEvents.CONNECTION_CREATED, {
|
|
23
|
+
clientType: 'mysql',
|
|
24
|
+
poolOptions: pool.config,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
clientType: 'mysql',
|
|
29
|
+
query: async <T>(sql: string, params?: any[]): Promise<T> => {
|
|
30
|
+
return promiseRetry(async (retry, attempt) => {
|
|
31
|
+
const startTime = Date.now()
|
|
32
|
+
try {
|
|
33
|
+
monitor.emit(MonitorEvents.QUERY_START, {
|
|
34
|
+
clientType: 'mysql',
|
|
35
|
+
sql,
|
|
36
|
+
params,
|
|
37
|
+
attempt,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const [rows] = await pool.execute(sql, params)
|
|
41
|
+
|
|
42
|
+
const elapsedTime = Date.now() - startTime
|
|
43
|
+
monitor.emit(MonitorEvents.QUERY_END, {
|
|
44
|
+
clientType: 'mysql',
|
|
45
|
+
sql,
|
|
46
|
+
params,
|
|
47
|
+
attempt,
|
|
48
|
+
elapsedTime,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return rows as unknown as T
|
|
52
|
+
} catch (error: any) {
|
|
53
|
+
const elapsedTime = Date.now() - startTime
|
|
54
|
+
monitor.emit(MonitorEvents.QUERY_ERROR, {
|
|
55
|
+
clientType: 'mysql',
|
|
56
|
+
sql,
|
|
57
|
+
params,
|
|
58
|
+
attempt,
|
|
59
|
+
elapsedTime,
|
|
60
|
+
error,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
if (isTransientError(error)) {
|
|
64
|
+
console.warn(
|
|
65
|
+
`MySQL query attempt ${attempt} failed, retrying...`,
|
|
66
|
+
error
|
|
67
|
+
)
|
|
68
|
+
monitor.emit(MonitorEvents.RETRY_ATTEMPT, {
|
|
69
|
+
clientType: 'mysql',
|
|
70
|
+
sql,
|
|
71
|
+
params,
|
|
72
|
+
attempt,
|
|
73
|
+
error,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
return retry(error)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
throw error
|
|
80
|
+
}
|
|
81
|
+
}, retryOptions)
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/db/pgClient.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import { Pool } from 'pg'
|
|
1
|
+
import { Pool, PoolConfig } from 'pg'
|
|
2
|
+
import promiseRetry from 'promise-retry'
|
|
2
3
|
import { IDatabaseClient } from './IDatabaseClient'
|
|
4
|
+
import { RetryOptions } from '../default/types'
|
|
5
|
+
import { monitor, MonitorEvents } from '../monitor/monitor'
|
|
6
|
+
|
|
7
|
+
const transientErrorCodes = new Set(['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED'])
|
|
8
|
+
|
|
9
|
+
function isTransientError(error: any): boolean {
|
|
10
|
+
return error && error.code && transientErrorCodes.has(error.code)
|
|
11
|
+
}
|
|
3
12
|
|
|
4
13
|
async function ensureUnaccentExtension(pool: Pool): Promise<void> {
|
|
5
14
|
try {
|
|
@@ -9,11 +18,23 @@ async function ensureUnaccentExtension(pool: Pool): Promise<void> {
|
|
|
9
18
|
|
|
10
19
|
if (checkResult.rows.length === 0) {
|
|
11
20
|
await pool.query('CREATE EXTENSION IF NOT EXISTS unaccent')
|
|
12
|
-
|
|
21
|
+
|
|
22
|
+
monitor.emit(MonitorEvents.CONNECTION_CREATED, {
|
|
23
|
+
clientType: 'pg',
|
|
24
|
+
extension: 'unaccent',
|
|
25
|
+
status: 'installed',
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
console.info(
|
|
13
29
|
'@starbemtech/star-db-query-builder: Extensão unaccent instalada com sucesso.'
|
|
14
30
|
)
|
|
15
31
|
}
|
|
16
32
|
} catch (error) {
|
|
33
|
+
monitor.emit(MonitorEvents.QUERY_ERROR, {
|
|
34
|
+
clientType: 'pg',
|
|
35
|
+
action: 'install_unaccent',
|
|
36
|
+
error,
|
|
37
|
+
})
|
|
17
38
|
throw new Error(
|
|
18
39
|
`@starbemtech/star-db-query-builder: Não foi possível instalar a extensão unaccent:
|
|
19
40
|
${error}`
|
|
@@ -21,14 +42,74 @@ async function ensureUnaccentExtension(pool: Pool): Promise<void> {
|
|
|
21
42
|
}
|
|
22
43
|
}
|
|
23
44
|
|
|
24
|
-
export const createPgClient = async (
|
|
45
|
+
export const createPgClient = async (
|
|
46
|
+
pool: Pool,
|
|
47
|
+
retryOptions?: RetryOptions,
|
|
48
|
+
poolConfig?: PoolConfig
|
|
49
|
+
): Promise<IDatabaseClient> => {
|
|
25
50
|
await ensureUnaccentExtension(pool)
|
|
26
51
|
|
|
52
|
+
monitor.emit(MonitorEvents.CONNECTION_CREATED, {
|
|
53
|
+
clientType: 'pg',
|
|
54
|
+
poolOptions: poolConfig,
|
|
55
|
+
})
|
|
56
|
+
|
|
27
57
|
return {
|
|
28
58
|
clientType: 'pg',
|
|
29
59
|
query: async <T>(sql: string, params?: any[]): Promise<T> => {
|
|
30
|
-
|
|
31
|
-
|
|
60
|
+
return promiseRetry(async (retry, attempt) => {
|
|
61
|
+
const startTime = Date.now()
|
|
62
|
+
try {
|
|
63
|
+
monitor.emit(MonitorEvents.QUERY_START, {
|
|
64
|
+
clientType: 'pg',
|
|
65
|
+
sql,
|
|
66
|
+
params,
|
|
67
|
+
attempt,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const { rows } = await pool.query(sql, params)
|
|
71
|
+
|
|
72
|
+
const elapsedTime = Date.now() - startTime
|
|
73
|
+
monitor.emit(MonitorEvents.QUERY_END, {
|
|
74
|
+
clientType: 'pg',
|
|
75
|
+
sql,
|
|
76
|
+
params,
|
|
77
|
+
attempt,
|
|
78
|
+
elapsedTime,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
return rows as unknown as T
|
|
82
|
+
} catch (error) {
|
|
83
|
+
const elapsedTime = Date.now() - startTime
|
|
84
|
+
monitor.emit(MonitorEvents.QUERY_ERROR, {
|
|
85
|
+
clientType: 'pg',
|
|
86
|
+
sql,
|
|
87
|
+
params,
|
|
88
|
+
attempt,
|
|
89
|
+
elapsedTime,
|
|
90
|
+
error,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (isTransientError(error)) {
|
|
94
|
+
console.warn(
|
|
95
|
+
`Postgres query attempt ${attempt} failed, retrying...`,
|
|
96
|
+
error
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
monitor.emit(MonitorEvents.RETRY_ATTEMPT, {
|
|
100
|
+
clientType: 'pg',
|
|
101
|
+
sql,
|
|
102
|
+
params,
|
|
103
|
+
attempt,
|
|
104
|
+
error,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
return retry(error)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw error
|
|
111
|
+
}
|
|
112
|
+
}, retryOptions)
|
|
32
113
|
},
|
|
33
114
|
}
|
|
34
115
|
}
|
package/src/default/types.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { IDatabaseClient } from '../db/IDatabaseClient'
|
|
2
2
|
|
|
3
|
+
export interface RetryOptions {
|
|
4
|
+
retries?: number
|
|
5
|
+
factor?: number
|
|
6
|
+
minTimeout?: number
|
|
7
|
+
maxTimeout?: number
|
|
8
|
+
randomize?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
export interface QueryExec {
|
|
4
12
|
text: string
|
|
5
13
|
values?: any[]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EventEmitter } from 'stream'
|
|
2
|
+
|
|
3
|
+
export enum MonitorEvents {
|
|
4
|
+
CONNECTION_CREATED = 'connection_created',
|
|
5
|
+
QUERY_START = 'query_start',
|
|
6
|
+
QUERY_END = 'query_end',
|
|
7
|
+
QUERY_ERROR = 'query_error',
|
|
8
|
+
RETRY_ATTEMPT = 'retry_attempt',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const monitor = new EventEmitter()
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { createSelectFields, createWhereClause } from '../../default/utils'
|
|
2
|
-
import { QueryParams } from '../../default/types'
|
|
3
|
-
|
|
4
|
-
export const getByUserAndSubscription = async <T>({
|
|
5
|
-
tableName,
|
|
6
|
-
dbClient,
|
|
7
|
-
select,
|
|
8
|
-
where,
|
|
9
|
-
}: QueryParams<T>): Promise<T | null> => {
|
|
10
|
-
if (!tableName) throw new Error('Table name is required')
|
|
11
|
-
if (!dbClient) throw new Error('DB client is required')
|
|
12
|
-
|
|
13
|
-
const fields = createSelectFields(select, dbClient.clientType)
|
|
14
|
-
const [whereClause, params] = createWhereClause<T>(
|
|
15
|
-
where,
|
|
16
|
-
1,
|
|
17
|
-
dbClient.clientType
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
const rows = await dbClient.query<T>(
|
|
21
|
-
`SELECT
|
|
22
|
-
${fields}
|
|
23
|
-
FROM payments.plan_association_users pa
|
|
24
|
-
LEFT JOIN payments.plans ON pa.plan_id = plans.id
|
|
25
|
-
${whereClause}
|
|
26
|
-
AND pa.status IN ('active', 'cancelation_requested', 'blocked')`,
|
|
27
|
-
params
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
return rows || null
|
|
31
|
-
}
|