@itentialopensource/adapter-db_mysql 0.1.11 → 0.1.15

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/CHANGELOG.md CHANGED
@@ -1,4 +1,36 @@
1
1
 
2
+ ## 0.1.15 [08-03-2022]
3
+
4
+ * Patch/adapt 2277
5
+
6
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!9
7
+
8
+ ---
9
+
10
+ ## 0.1.14 [08-01-2022]
11
+
12
+ * Patch/adapt 2277
13
+
14
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!8
15
+
16
+ ---
17
+
18
+ ## 0.1.13 [08-01-2022]
19
+
20
+ * Changed connection handling. Added healthcheck.
21
+
22
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!7
23
+
24
+ ---
25
+
26
+ ## 0.1.12 [08-10-2021]
27
+
28
+ * change the connection and base wait
29
+
30
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!6
31
+
32
+ ---
33
+
2
34
  ## 0.1.11 [08-10-2021]
3
35
 
4
36
  * add delays in the connect retries
package/adapter.js CHANGED
@@ -19,8 +19,6 @@ const EventEmitter = require('events');
19
19
 
20
20
  let myid = null;
21
21
  let errors = [];
22
- const baseWait = 10000;
23
- const incrWait = 20000;
24
22
 
25
23
  /**
26
24
  * @summary Build a standard error object from the data provided
@@ -103,7 +101,6 @@ class MySQL extends EventEmitter {
103
101
  super();
104
102
 
105
103
  this.props = properties;
106
- this.alive = false;
107
104
  this.id = prongid;
108
105
  myid = prongid;
109
106
 
@@ -119,60 +116,52 @@ class MySQL extends EventEmitter {
119
116
  // Read the action from the file system
120
117
  const errorData = JSON.parse(fs.readFileSync(errorFile, 'utf-8'));
121
118
  ({ errors } = errorData);
122
- }
123
119
 
124
- /**
125
- * Itential connect call for system green/red light
126
- *
127
- * @param {function} callback
128
- */
129
- connect() {
130
- const meth = 'adapter-connect';
131
- const origin = `${this.id}-${meth}`;
132
- log.trace(origin);
120
+ this.setConfiguration();
121
+ }
133
122
 
134
- const config = {
123
+ setConfiguration() {
124
+ this.config = {
135
125
  host: this.props.host,
136
126
  port: this.props.port,
137
127
  user: this.props.authentication.username,
138
128
  password: this.props.authentication.password,
139
129
  database: this.props.database,
140
- acquireTimeout: this.props.acquireTimout || 1000000
130
+ ...this.props.connectionOptions
141
131
  };
142
132
 
133
+ this.healthcheckType = this.props.healthcheck.type;
134
+ this.healthcheckInterval = this.props.healthcheck.frequency;
135
+
143
136
  if (this.props.ssl) {
144
137
  if (this.props.ssl.enabled === true) {
145
138
  log.info('Connecting to MySQL with SSL.');
146
139
  // validate the server's certificate
147
140
  if (this.props.ssl.accept_invalid_cert === false) {
148
141
  log.info('Certificate based SSL MySQL connections will be used.');
149
- config.ssl = {
142
+ this.config.ssl = {
150
143
  rejectUnauthorized: true
151
144
  };
152
145
  // if validation is enabled, we need to read the CA file
153
146
  if (this.props.ssl.ca_file) {
154
147
  try {
155
- config.ssl.ca = fs.readFileSync(this.props.ssl.ca_file);
148
+ this.config.ssl.ca = fs.readFileSync(this.props.ssl.ca_file);
156
149
  } catch (err) {
157
150
  log.error(`Error: Unable to load MySQL CA file: ${err}`);
158
- this.alive = false;
159
151
  this.emit('OFFLINE', {
160
152
  id: this.id
161
153
  });
162
- return;
163
154
  }
164
155
  } else {
165
156
  log.error('Error: Certificate validation'
166
157
  + 'is enabled but a CA is not specified.');
167
- this.alive = false;
168
158
  this.emit('OFFLINE', {
169
159
  id: this.id
170
160
  });
171
- return;
172
161
  }
173
162
  } else {
174
163
  log.info('SSL MySQL connection without CA certificate validation.');
175
- config.ssl = {
164
+ this.config.ssl = {
176
165
  rejectUnauthorized: false
177
166
  };
178
167
  }
@@ -182,19 +171,40 @@ class MySQL extends EventEmitter {
182
171
  } else {
183
172
  log.warn('WARNING: Connecting to MySQL without SSL.');
184
173
  }
174
+ }
175
+
176
+ /**
177
+ * Itential connect call for system green/red light
178
+ *
179
+ * @param {function} callback
180
+ */
181
+ connect() {
182
+ const meth = 'adapter-connect';
183
+ const origin = `${this.id}-${meth}`;
184
+ log.trace(origin);
185
+
186
+ // initially set as off
187
+ this.emit('OFFLINE', { id: this.id });
185
188
 
186
- // get connection
187
- this.connection = mysql.createConnection(config);
188
- this.attempt = 1;
189
- this.waitTime = baseWait;
190
189
  this.dbconnect();
191
190
 
192
- this.connection.on('error', (myerr) => {
193
- this.attempt += 1;
194
- log.error(`Connection to MySQL took an error: ${myerr}`);
195
- log.error(`Attampting Reconnect - attempt number ${this.attempt}`);
196
- this.dbconnect();
197
- });
191
+ if (this.healthcheckType === 'none') {
192
+ log.error(`${origin}: Waiting 1 Seconds to emit Online`);
193
+ setTimeout(() => {
194
+ this.emit('ONLINE', { id: this.id });
195
+ this.healthy = true;
196
+ }, 1000);
197
+ }
198
+
199
+ if (this.healthcheckType === 'startup' || this.healthcheckType === 'intermittent') {
200
+ this.healthCheck();
201
+ }
202
+
203
+ if (this.healthcheckType === 'intermittent') {
204
+ setInterval(() => {
205
+ this.healthCheck();
206
+ }, this.healthcheckInterval);
207
+ }
198
208
  }
199
209
 
200
210
  /**
@@ -207,82 +217,46 @@ class MySQL extends EventEmitter {
207
217
  const origin = `${this.id}-${meth}`;
208
218
  log.trace(origin);
209
219
 
210
- if (this.attempt <= 5) {
211
- setTimeout(() => {
212
- this.connection.connect((error) => {
213
- if (error) {
214
- // emit failure
215
- this.alive = false;
216
- this.emit('OFFLINE', {
217
- id: this.id
218
- });
219
- // increase the attempts and the timeout
220
- this.attempt += 1;
221
- this.waitTime += incrWait;
222
- log.error(`error connecting to MySQL: ${error}`);
223
- log.error(`Attampting Reconnect - attempt number ${this.attempt}`);
224
- this.dbconnect();
225
- } else {
226
- // emit success
227
- this.alive = true;
228
- this.emit('ONLINE', {
229
- id: this.id
230
- });
231
- // reset the attempts and timeout
232
- this.attempt = 0;
233
- this.waitTime = baseWait;
234
- log.info(`connected as id: ${this.connection.threadId}`);
235
- }
236
- });
237
- }, this.waitTime);
238
- } else {
239
- log.error('Maximum retries reached - check mysql and then restart the adapter!');
240
- }
220
+ log.info(`Creating connection pool to mysql on host: ${this.config.host} and port: ${this.config.port}`);
221
+
222
+ this.pool = mysql.createPool(this.config);
223
+
224
+ this.pool.on('acquire', (connection) => log.debug('Connection', connection.threadId, 'acquired'));
225
+
226
+ this.pool.on('connection', (connection) => log.debug('New connection made within the pool:', connection.threadId, connection.state));
227
+
228
+ this.pool.on('enqueue', () => log.debug('Waiting for available connection slot'));
229
+
230
+ this.pool.on('release', (connection) => log.debug('Connection', connection.threadId, 'released'));
241
231
  }
242
232
 
243
233
  /**
244
234
  * Call to run a healthcheck on the mysql database
245
235
  *
246
236
  * @function healthCheck
247
- * @param {healthCallback} callback - a callback function to return a result
248
- * healthcheck success or failure
249
237
  */
250
- healthCheck(callback) {
238
+ healthCheck() {
251
239
  const meth = 'adapter-healthCheck';
252
240
  const origin = `${this.id}-${meth}`;
253
241
  log.trace(origin);
254
242
 
255
243
  try {
256
- // verify that we are connected to MySQL
257
- if (!this.alive || !this.connection) {
244
+ if (!this.pool) {
258
245
  log.error('Error during healthcheck: Not connected to MySQL Database');
259
- return callback({
260
- id: this.id,
261
- status: 'fail'
262
- });
246
+ return this.emit('OFFLINE', { id: this.id });
263
247
  }
264
248
 
265
- this.connection.query('SELECT "test";', (error, results, fields) => {
249
+ this.pool.query('SELECT 1;', (error, results, fields) => {
266
250
  if (error) {
267
- log.error(`Error during healthcheck: ${error}`);
268
- return callback({
269
- id: this.id,
270
- status: 'fail'
271
- });
251
+ log.error(`HEALTHCHECK FAIL, ${JSON.stringify(error)}`);
252
+ return this.emit('OFFLINE', { id: this.id });
272
253
  }
273
-
274
- log.info(`result from mysql connect: ${JSON.stringify(results)}`);
275
- return callback({
276
- id: this.id,
277
- status: 'success'
278
- });
254
+ log.debug('HEALTHCHECK SUCCESSFUL');
255
+ return this.emit('ONLINE', { id: this.id });
279
256
  });
280
257
  } catch (ex) {
281
- log.error(`Exception during healthcheck: ${ex}`);
282
- return callback({
283
- id: this.id,
284
- status: 'fail'
285
- });
258
+ log.error(`HEALTHCHECK EXCEPTION: ${JSON.stringify(ex)}`);
259
+ return this.emit('OFFLINE', { id: this.id });
286
260
  }
287
261
  }
288
262
 
@@ -326,7 +300,7 @@ class MySQL extends EventEmitter {
326
300
  break;
327
301
  }
328
302
  if (myfunctions[m] !== 'connect' && myfunctions[m] !== 'healthCheck' && myfunctions[m] !== 'dbconnect'
329
- && myfunctions[m] !== 'getAllFunctions' && myfunctions[m] !== 'getWorkflowFunctions') {
303
+ && myfunctions[m] !== 'getAllFunctions' && myfunctions[m] !== 'getWorkflowFunctions' && myfunctions[m] !== 'setConfiguration') {
330
304
  wffunctions.push(myfunctions[m]);
331
305
  }
332
306
  }
@@ -345,39 +319,22 @@ class MySQL extends EventEmitter {
345
319
  const origin = `${this.id}-${meth}`;
346
320
  log.trace(origin);
347
321
  log.trace(`mysql query started with sql: ${sql}`);
348
- try {
349
- // verify the required data has been provided
350
- if (!sql) {
351
- const errorObj = formatErrorObject(origin, 'Missing Data', ['sql'], null, null, null);
352
- log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
353
- return callback(null, errorObj);
354
- }
355
322
 
356
- // if the connection is not up attempt to reconnect
357
- if (!this.alive || !this.connection) {
358
- this.connect();
359
- if (!this.alive || !this.connection) {
360
- return callback({
361
- id: this.id,
362
- status: 'fail'
363
- });
364
- }
365
- }
366
-
367
- // query connection
368
- this.connection.query(sql, (error, results, fields) => {
369
- // close connection
370
- // this.connection.end();
323
+ if (!sql) {
324
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['sql'], null, null, null);
325
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
326
+ return callback(null, errorObj);
327
+ }
371
328
 
329
+ try {
330
+ this.pool.query(sql, (error, results, fields) => {
372
331
  log.debug(`result from query: ${JSON.stringify(results)}`);
373
- log.debug(`result from query: ${JSON.stringify(fields)}`);
374
-
332
+ log.debug(`fields from query: ${JSON.stringify(fields)}`);
375
333
  if (error) {
376
334
  const errorObj = formatErrorObject(origin, 'Database Error', [error], null, null, null);
377
335
  log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
378
336
  return callback(null, errorObj);
379
337
  }
380
-
381
338
  return callback({
382
339
  status: 'success',
383
340
  code: 200,
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-db_mysql",
3
- "version": "0.1.11",
3
+ "version": "0.1.15",
4
4
  "description": "Itential adapter to connect to mysql",
5
5
  "main": "adapter.js",
6
6
  "wizardVersion": "2.41.0",
7
7
  "engineVersion": "1.44.2",
8
8
  "scripts": {
9
9
  "artifactize": "npm i && node utils/packModificationScript.js",
10
- "preinstall": "node utils/setup.js",
10
+ "preinstall": "node utils/setup.js && npm install --package-lock-only --ignore-scripts && npx npm-force-resolutions",
11
11
  "lint": "eslint . --ext .json --ext .js",
12
12
  "lint:errors": "node --max_old_space_size=4096 ./node_modules/eslint/bin/eslint.js . --ext .json --ext .js --quiet",
13
13
  "test:unit": "mocha test/unit/adapterTestUnit.js --LOG=error",
14
14
  "test:integration": "mocha test/integration/adapterTestIntegration.js --LOG=error",
15
15
  "test:cover": "nyc --reporter html --reporter text mocha --reporter dot test/*",
16
16
  "test": "npm run test:unit && npm run test:integration",
17
- "deploy": "npm publish --registry=http://registry.npmjs.org --access=public",
17
+ "deploy": "npm publish --registry=https://registry.npmjs.org --access=public",
18
18
  "build": "npm run deploy"
19
19
  },
20
20
  "keywords": [
@@ -46,16 +46,19 @@
46
46
  "uuid": "^3.0.1"
47
47
  },
48
48
  "devDependencies": {
49
- "chai": "^4.3.4",
49
+ "chai": "^4.3.6",
50
50
  "eslint": "^7.29.0",
51
51
  "eslint-config-airbnb-base": "^14.2.1",
52
- "eslint-plugin-import": "^2.23.4",
52
+ "eslint-plugin-import": "^2.26.0",
53
53
  "eslint-plugin-json": "^3.0.0",
54
- "mocha": "^9.0.1",
54
+ "mocha": "^9.2.2",
55
55
  "nyc": "^15.1.0",
56
56
  "package-json-validator": "^0.6.3",
57
- "testdouble": "^3.16.1",
58
- "winston": "^3.3.3"
57
+ "testdouble": "^3.16.6",
58
+ "winston": "^3.8.1"
59
59
  },
60
- "private": false
60
+ "private": false,
61
+ "resolutions": {
62
+ "minimist": "^1.2.5"
63
+ }
61
64
  }
@@ -27,23 +27,40 @@
27
27
  "maximum": 65535
28
28
  },
29
29
  "authentication": {
30
- "$ref":"#/definitions/authentication"
30
+ "$ref": "#/definitions/authentication",
31
+ "default": {}
31
32
  },
32
33
  "ssl": {
33
- "$ref":"#/definitions/ssl"
34
+ "$ref": "#/definitions/ssl",
35
+ "default": {}
36
+ },
37
+ "connectionOptions": {
38
+ "$ref": "#/definitions/connectionOptions",
39
+ "default": {}
40
+ },
41
+ "healthcheck": {
42
+ "$ref": "#/definitions/healthcheck",
43
+ "default": {}
34
44
  }
35
45
  },
36
- "required" : ["database","host","port"],
46
+ "required": [
47
+ "database",
48
+ "host",
49
+ "port",
50
+ "authentication",
51
+ "healthcheck",
52
+ "connectionOptions"
53
+ ],
37
54
  "definitions": {
38
55
  "authentication": {
39
56
  "$id": "/authentication",
40
57
  "type": "object",
41
58
  "properties": {
42
- "dbAuth" : {
43
- "$id" : "/credentials/properties/dbAuth",
44
- "type" : "boolean",
45
- "description": "whether or not to use user authentication to connect to mysql",
46
- "default" : false
59
+ "dbAuth": {
60
+ "$id": "/credentials/properties/dbAuth",
61
+ "type": "boolean",
62
+ "description": "whether or not to use user authentication to connect to mysql",
63
+ "default": false
47
64
  },
48
65
  "password": {
49
66
  "type": "string",
@@ -62,7 +79,10 @@
62
79
  ]
63
80
  }
64
81
  },
65
- "required" : ["username","password"]
82
+ "required": [
83
+ "username",
84
+ "password"
85
+ ]
66
86
  },
67
87
  "ssl": {
68
88
  "$id": "/ssl",
@@ -72,7 +92,7 @@
72
92
  "$id": "/ssl/properties/enabled",
73
93
  "type": "boolean",
74
94
  "description": "Whether or not ssl is enabled on PostgreSQL",
75
- "default": false
95
+ "default": false
76
96
  },
77
97
  "accept_invalid_cert": {
78
98
  "$id": "/ssl/properties/accept_invalid_cert",
@@ -90,20 +110,133 @@
90
110
  ]
91
111
  }
92
112
  },
93
- "required" : ["enabled"],
113
+ "required": [
114
+ "enabled"
115
+ ],
94
116
  "allOf": [
95
117
  {
96
- "if" : { "properties" : { "enabled": { "enum": [true] } } },
97
- "then" : { "required" : ["acceptInvalidCerts", "checkServerIdentity"] }
118
+ "if": {
119
+ "properties": {
120
+ "enabled": {
121
+ "enum": [
122
+ true
123
+ ]
124
+ }
125
+ }
126
+ },
127
+ "then": {
128
+ "required": [
129
+ "acceptInvalidCerts",
130
+ "checkServerIdentity"
131
+ ]
132
+ }
133
+ },
134
+ {
135
+ "if": {
136
+ "allOf": [
137
+ {
138
+ "properties": {
139
+ "enabled": {
140
+ "enum": [
141
+ true
142
+ ]
143
+ }
144
+ }
145
+ },
146
+ {
147
+ "properties": {
148
+ "acceptInvalidCerts": {
149
+ "enum": [
150
+ false
151
+ ]
152
+ }
153
+ }
154
+ }
155
+ ]
156
+ },
157
+ "then": {
158
+ "required": [
159
+ "sslCA"
160
+ ]
161
+ }
162
+ }
163
+ ]
164
+ },
165
+ "healthcheck": {
166
+ "$id": "/healthcheck",
167
+ "type": "object",
168
+ "properties": {
169
+ "type": {
170
+ "type": "string",
171
+ "description": "The type of healthcheck to run",
172
+ "default": "intermittent",
173
+ "enum": [
174
+ "none",
175
+ "startup",
176
+ "intermittent"
177
+ ]
98
178
  },
179
+ "frequency": {
180
+ "type": "integer",
181
+ "description": "How often the healthcheck should run (in milliseconds).",
182
+ "default": 300000,
183
+ "minimum": 60000,
184
+ "maximum": 3600000
185
+ }
186
+ },
187
+ "required": [
188
+ "type"
189
+ ],
190
+ "allOf": [
99
191
  {
100
- "if" : { "allOf": [
101
- { "properties" : { "enabled": { "enum": [true] } } },
102
- { "properties" : { "acceptInvalidCerts": { "enum": [ false ] } } }
103
- ] },
104
- "then" : { "required" : ["sslCA"] }
192
+ "if": {
193
+ "properties": {
194
+ "type": {
195
+ "enum": [
196
+ "intermittent"
197
+ ]
198
+ }
199
+ }
200
+ },
201
+ "then": {
202
+ "required": [
203
+ "frequency"
204
+ ]
205
+ }
105
206
  }
106
207
  ]
208
+ },
209
+ "connectionOptions": {
210
+ "$id": "/connectionOptions",
211
+ "type": "object",
212
+ "properties": {
213
+ "connectTimeout": {
214
+ "type": "number",
215
+ "description": "The milliseconds before a timeout occurs during the initial connection to the MySQL server.",
216
+ "default": 10000
217
+ },
218
+ "acquireTimeout": {
219
+ "type": "number",
220
+ "description": "The milliseconds before a timeout occurs during the connection acquisition",
221
+ "default": 10000
222
+ },
223
+ "waitForConnections": {
224
+ "type": "boolean",
225
+ "description": "Determines the pool's action when no connections are available and the limit has been reached",
226
+ "default": true
227
+ },
228
+ "connectionLimit": {
229
+ "type": "number",
230
+ "description": "The maximum number of connections to create at once",
231
+ "default": 10
232
+ },
233
+ "queueLimit": {
234
+ "type": "number",
235
+ "description": "The maximum number of connection requests the pool will queue before returning an error from getConnection",
236
+ "default": 0
237
+ }
238
+ },
239
+ "additionalProperties": false
107
240
  }
108
241
  }
109
- }
242
+ }
Binary file
@@ -1,11 +1,14 @@
1
1
  {
2
- "type": "MySQL",
3
2
  "id": "mysql",
3
+ "type": "MySQL",
4
+ "brokers": [],
5
+ "groups": [],
4
6
  "properties": {
5
7
  "database": "your database name",
6
8
  "host": "localhost",
7
9
  "port": 3306,
8
10
  "authentication": {
11
+ "dbAuth": false,
9
12
  "username": "your username",
10
13
  "password": "your password"
11
14
  },
@@ -13,10 +16,21 @@
13
16
  "enabled": false,
14
17
  "accept_invalid_cert": true,
15
18
  "ca_file": ""
19
+ },
20
+ "connectionOptions": {
21
+ "connectTimeout": 10000,
22
+ "acquireTimeout": 10000,
23
+ "waitForConnections": true,
24
+ "connectionLimit": 10,
25
+ "queueLimit": 0
26
+ },
27
+ "healthcheck": {
28
+ "type": "intermittent",
29
+ "frequency": 60000
16
30
  }
17
31
  },
18
32
  "groups": [],
19
33
  "brokers": [],
20
34
  "logLevel": "none",
21
35
  "timeout": 60000
22
- }
36
+ }
@@ -56,6 +56,17 @@ global.pronghornProps = {
56
56
  },
57
57
  replSet: {
58
58
  enabled: false
59
+ },
60
+ connectionOptions: {
61
+ connectTimeout: 10000,
62
+ acquireTimeout: 10000,
63
+ waitForConnections: true,
64
+ connectionLimit: 10,
65
+ queueLimit: 0
66
+ },
67
+ healthcheck: {
68
+ type: 'intermittent',
69
+ frequency: 60000
59
70
  }
60
71
  }
61
72
  }]
@@ -48,9 +48,10 @@ global.pronghornProps = {
48
48
  database: 'mydatabase',
49
49
  host,
50
50
  port,
51
- credentials: {
52
- username,
53
- password
51
+ authentication: {
52
+ dbAuth: false,
53
+ username: 'itential',
54
+ password: 'itential'
54
55
  },
55
56
  ssl: {
56
57
  enabled: sslenable,
@@ -60,6 +61,17 @@ global.pronghornProps = {
60
61
  },
61
62
  replSet: {
62
63
  enabled: false
64
+ },
65
+ connectionOptions: {
66
+ connectTimeout: 10000,
67
+ acquireTimeout: 10000,
68
+ waitForConnections: true,
69
+ connectionLimit: 10,
70
+ queueLimit: 0
71
+ },
72
+ healthcheck: {
73
+ type: 'intermittent',
74
+ frequency: 60000
63
75
  }
64
76
  }
65
77
  }]