@itentialopensource/adapter-db_mysql 0.1.10 → 0.1.14

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.14 [08-01-2022]
3
+
4
+ * Patch/adapt 2277
5
+
6
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!8
7
+
8
+ ---
9
+
10
+ ## 0.1.13 [08-01-2022]
11
+
12
+ * Changed connection handling. Added healthcheck.
13
+
14
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!7
15
+
16
+ ---
17
+
18
+ ## 0.1.12 [08-10-2021]
19
+
20
+ * change the connection and base wait
21
+
22
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!6
23
+
24
+ ---
25
+
26
+ ## 0.1.11 [08-10-2021]
27
+
28
+ * add delays in the connect retries
29
+
30
+ See merge request itentialopensource/adapters/persistence/adapter-db_mysql!5
31
+
32
+ ---
33
+
2
34
  ## 0.1.10 [08-06-2021]
3
35
 
4
36
  * include Raj changes for connection recovery as well as other stuff
package/adapter.js CHANGED
@@ -101,7 +101,6 @@ class MySQL extends EventEmitter {
101
101
  super();
102
102
 
103
103
  this.props = properties;
104
- this.alive = false;
105
104
  this.id = prongid;
106
105
  myid = prongid;
107
106
 
@@ -117,60 +116,52 @@ class MySQL extends EventEmitter {
117
116
  // Read the action from the file system
118
117
  const errorData = JSON.parse(fs.readFileSync(errorFile, 'utf-8'));
119
118
  ({ errors } = errorData);
120
- }
121
119
 
122
- /**
123
- * Itential connect call for system green/red light
124
- *
125
- * @param {function} callback
126
- */
127
- connect() {
128
- const meth = 'adapter-connect';
129
- const origin = `${this.id}-${meth}`;
130
- log.trace(origin);
120
+ this.setConfiguration();
121
+ }
131
122
 
132
- const config = {
123
+ setConfiguration() {
124
+ this.config = {
133
125
  host: this.props.host,
134
126
  port: this.props.port,
135
127
  user: this.props.authentication.username,
136
128
  password: this.props.authentication.password,
137
129
  database: this.props.database,
138
- acquireTimeout: this.props.acquireTimout || 1000000
130
+ ...this.props.connectionOptions
139
131
  };
140
132
 
133
+ this.healthcheckType = this.props.healthcheck.type;
134
+ this.healthcheckInterval = this.props.healthcheck.frequency;
135
+
141
136
  if (this.props.ssl) {
142
137
  if (this.props.ssl.enabled === true) {
143
138
  log.info('Connecting to MySQL with SSL.');
144
139
  // validate the server's certificate
145
140
  if (this.props.ssl.accept_invalid_cert === false) {
146
141
  log.info('Certificate based SSL MySQL connections will be used.');
147
- config.ssl = {
142
+ this.config.ssl = {
148
143
  rejectUnauthorized: true
149
144
  };
150
145
  // if validation is enabled, we need to read the CA file
151
146
  if (this.props.ssl.ca_file) {
152
147
  try {
153
- config.ssl.ca = fs.readFileSync(this.props.ssl.ca_file);
148
+ this.config.ssl.ca = fs.readFileSync(this.props.ssl.ca_file);
154
149
  } catch (err) {
155
150
  log.error(`Error: Unable to load MySQL CA file: ${err}`);
156
- this.alive = false;
157
151
  this.emit('OFFLINE', {
158
152
  id: this.id
159
153
  });
160
- return;
161
154
  }
162
155
  } else {
163
156
  log.error('Error: Certificate validation'
164
157
  + 'is enabled but a CA is not specified.');
165
- this.alive = false;
166
158
  this.emit('OFFLINE', {
167
159
  id: this.id
168
160
  });
169
- return;
170
161
  }
171
162
  } else {
172
163
  log.info('SSL MySQL connection without CA certificate validation.');
173
- config.ssl = {
164
+ this.config.ssl = {
174
165
  rejectUnauthorized: false
175
166
  };
176
167
  }
@@ -180,18 +171,40 @@ class MySQL extends EventEmitter {
180
171
  } else {
181
172
  log.warn('WARNING: Connecting to MySQL without SSL.');
182
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 });
183
188
 
184
- // get connection
185
- this.connection = mysql.createConnection(config);
186
- this.attempt = 1;
187
189
  this.dbconnect();
188
190
 
189
- this.connection.on('error', (myerr) => {
190
- this.attempt += 1;
191
- log.error(`Connection to MySQL took an error: ${myerr}`);
192
- log.error(`Attampting Reconnect - attempt number ${this.attempt}`);
193
- this.dbconnect();
194
- });
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
+ }
195
208
  }
196
209
 
197
210
  /**
@@ -204,76 +217,46 @@ class MySQL extends EventEmitter {
204
217
  const origin = `${this.id}-${meth}`;
205
218
  log.trace(origin);
206
219
 
207
- if (this.attempt <= 5) {
208
- this.connection.connect((error) => {
209
- if (error) {
210
- // emit failure
211
- this.alive = false;
212
- this.emit('OFFLINE', {
213
- id: this.id
214
- });
215
- this.attempt += 1;
216
- log.error(`error connecting to MySQL: ${error}`);
217
- log.error(`Attampting Reconnect - attempt number ${this.attempt}`);
218
- this.dbconnect();
219
- } else {
220
- // emit success
221
- this.alive = true;
222
- this.emit('ONLINE', {
223
- id: this.id
224
- });
225
- this.attempt = 0;
226
- log.info(`connected as id: ${this.connection.threadId}`);
227
- }
228
- });
229
- } else {
230
- log.error('Maximum retries reached - check mysql and then restart the adapter!');
231
- }
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, connection.config));
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'));
232
231
  }
233
232
 
234
233
  /**
235
234
  * Call to run a healthcheck on the mysql database
236
235
  *
237
236
  * @function healthCheck
238
- * @param {healthCallback} callback - a callback function to return a result
239
- * healthcheck success or failure
240
237
  */
241
- healthCheck(callback) {
238
+ healthCheck() {
242
239
  const meth = 'adapter-healthCheck';
243
240
  const origin = `${this.id}-${meth}`;
244
241
  log.trace(origin);
245
242
 
246
243
  try {
247
- // verify that we are connected to MySQL
248
- if (!this.alive || !this.connection) {
244
+ if (!this.pool) {
249
245
  log.error('Error during healthcheck: Not connected to MySQL Database');
250
- return callback({
251
- id: this.id,
252
- status: 'fail'
253
- });
246
+ return this.emit('OFFLINE', { id: this.id });
254
247
  }
255
248
 
256
- this.connection.query('SELECT "test";', (error, results, fields) => {
249
+ this.pool.query('SELECT 1;', (error, results, fields) => {
257
250
  if (error) {
258
- log.error(`Error during healthcheck: ${error}`);
259
- return callback({
260
- id: this.id,
261
- status: 'fail'
262
- });
251
+ log.error(`HEALTHCHECK FAIL, ${JSON.stringify(error)}`);
252
+ return this.emit('OFFLINE', { id: this.id });
263
253
  }
264
-
265
- log.info(`result from mysql connect: ${JSON.stringify(results)}`);
266
- return callback({
267
- id: this.id,
268
- status: 'success'
269
- });
254
+ log.debug('HEALTHCHECK SUCCESSFUL');
255
+ return this.emit('ONLINE', { id: this.id });
270
256
  });
271
257
  } catch (ex) {
272
- log.error(`Exception during healthcheck: ${ex}`);
273
- return callback({
274
- id: this.id,
275
- status: 'fail'
276
- });
258
+ log.error(`HEALTHCHECK EXCEPTION: ${JSON.stringify(ex)}`);
259
+ return this.emit('OFFLINE', { id: this.id });
277
260
  }
278
261
  }
279
262
 
@@ -317,7 +300,7 @@ class MySQL extends EventEmitter {
317
300
  break;
318
301
  }
319
302
  if (myfunctions[m] !== 'connect' && myfunctions[m] !== 'healthCheck' && myfunctions[m] !== 'dbconnect'
320
- && myfunctions[m] !== 'getAllFunctions' && myfunctions[m] !== 'getWorkflowFunctions') {
303
+ && myfunctions[m] !== 'getAllFunctions' && myfunctions[m] !== 'getWorkflowFunctions' && myfunctions[m] !== 'setConfiguration') {
321
304
  wffunctions.push(myfunctions[m]);
322
305
  }
323
306
  }
@@ -336,39 +319,22 @@ class MySQL extends EventEmitter {
336
319
  const origin = `${this.id}-${meth}`;
337
320
  log.trace(origin);
338
321
  log.trace(`mysql query started with sql: ${sql}`);
339
- try {
340
- // verify the required data has been provided
341
- if (!sql) {
342
- const errorObj = formatErrorObject(origin, 'Missing Data', ['sql'], null, null, null);
343
- log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
344
- return callback(null, errorObj);
345
- }
346
322
 
347
- // if the connection is not up attempt to reconnect
348
- if (!this.alive || !this.connection) {
349
- this.connect();
350
- if (!this.alive || !this.connection) {
351
- return callback({
352
- id: this.id,
353
- status: 'fail'
354
- });
355
- }
356
- }
357
-
358
- // query connection
359
- this.connection.query(sql, (error, results, fields) => {
360
- // close connection
361
- // 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
+ }
362
328
 
329
+ try {
330
+ this.pool.query(sql, (error, results, fields) => {
363
331
  log.debug(`result from query: ${JSON.stringify(results)}`);
364
- log.debug(`result from query: ${JSON.stringify(fields)}`);
365
-
332
+ log.debug(`fields from query: ${JSON.stringify(fields)}`);
366
333
  if (error) {
367
334
  const errorObj = formatErrorObject(origin, 'Database Error', [error], null, null, null);
368
335
  log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
369
336
  return callback(null, errorObj);
370
337
  }
371
-
372
338
  return callback({
373
339
  status: 'success',
374
340
  code: 200,
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-db_mysql",
3
- "version": "0.1.10",
3
+ "version": "0.1.14",
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
  }]