@mimik/mongooser 1.2.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc CHANGED
@@ -1,17 +1,34 @@
1
- // Use this file as a starting point for your project's .eslintrc.
2
- // Copy this file, and add rule overrides as needed.
3
1
  {
2
+ "plugins": [
3
+ "@mimik/document-env",
4
+ "@mimik/dependencies"
5
+ ],
4
6
  "env": {
5
7
  "node": true
6
8
  },
9
+ "parserOptions": {
10
+ "ecmaVersion": 2020
11
+ },
7
12
  "extends": "airbnb",
8
13
  "rules": {
14
+ "import/no-extraneous-dependencies": ["error", {"devDependencies": true}],
9
15
  "brace-style": [1, "stroustrup", {"allowSingleLine": true}],
10
16
  "no-confusing-arrow": [0], // arrow isnt confusing
11
17
  "max-len": [1, 180, { "ignoreComments": true }],
12
18
  "linebreak-style": 0,
13
19
  "quotes": [1, "single"],
14
- "semi": [1, "always"]
20
+ "semi": [1, "always"],
21
+ "no-process-env": ["error"],
22
+ "@mimik/document-env/validate-document-env": 2,
23
+ "@mimik/dependencies/case-sensitive": 2,
24
+ "@mimik/dependencies/no-cycles": 2,
25
+ "@mimik/dependencies/no-unresolved": 2,
26
+ "@mimik/dependencies/require-json-ext": 2
27
+ },
28
+ "settings":{
29
+ "react": {
30
+ "version": "latest"
31
+ }
15
32
  },
16
33
  "globals": {
17
34
  "module": true,
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npm run commit-ready
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npm run test
package/.nycrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "exclude": ["gulpfile.js"],
3
+ "reporter": ["lcov", "text"]
4
+ }
package/Gulpfile.js CHANGED
@@ -1,29 +1,33 @@
1
- /* eslint-disable import/no-extraneous-dependencies */
1
+ //* eslint-disable import/no-extraneous-dependencies */
2
+ const fs = require('fs');
2
3
  const log = require('fancy-log');
3
4
  const gulp = require('gulp');
4
5
  const eslint = require('gulp-eslint');
5
6
  const git = require('gulp-git');
6
- const gulpJsdoc2md = require('gulp-jsdoc-to-markdown');
7
- const rename = require('gulp-rename');
7
+ const jsdoc2md = require('jsdoc-to-markdown');
8
8
 
9
9
  const files = [
10
10
  'index.js',
11
11
  'Gulpfile.js',
12
- 'test/*.spec.js',
12
+ 'test/**.js',
13
+ 'lib/**.js',
13
14
  ];
14
15
 
15
- gulp.task('docs', () => gulp.src('index.js')
16
- .pipe(gulpJsdoc2md())
17
- .on('error', (err) => {
18
- log.error('jsdoc2md failed', err.message);
19
- })
20
- .pipe(rename('README.md'))
21
- .pipe(gulp.dest('.')));
16
+ const createDocs = (done) => {
17
+ jsdoc2md.render({ files: 'index.js' })
18
+ .then((output) => fs.writeFileSync('README.md', output))
19
+ .catch((err) => log.error('docs creation failed:', err.message))
20
+ .finally(() => done());
21
+ };
22
22
 
23
- gulp.task('lint', () => gulp.src(files)
23
+ const lint = () => gulp.src(files)
24
24
  .pipe(eslint({}))
25
- .pipe(eslint.format())
26
- .pipe(eslint.failOnError()));
25
+ .pipe(eslint.format());
27
26
 
28
- gulp.task('add', () => gulp.src('README.md')
29
- .pipe(git.add()));
27
+ const add = () => gulp.src('README.md')
28
+ .pipe(git.add({ quiet: true }));
29
+
30
+ const docs = gulp.series(createDocs, add);
31
+
32
+ gulp.task('docs', docs);
33
+ gulp.task('lint', lint);
package/README.md CHANGED
@@ -10,7 +10,8 @@ const mongodb = require('@mimik/mongooser');
10
10
  * _async_
11
11
  * [~validate()](#module_mongooser..validate) ⇒ <code>Promise</code>
12
12
  * _sync_
13
- * [~initializeSync()](#module_mongooser..initializeSync) ⇒ <code>object</code>
13
+ * [~getKMSProviders()](#module_mongooser..getKMSProviders) ⇒ <code>object</code>
14
+ * [~initializeSync(autoEncryption)](#module_mongooser..initializeSync) ⇒ <code>object</code>
14
15
  * [~replicat()](#module_mongooser..replicat) ⇒ <code>boolean</code>
15
16
 
16
17
  <a name="module_mongooser..validate"></a>
@@ -18,7 +19,7 @@ const mongodb = require('@mimik/mongooser');
18
19
  ### mongooser~validate() ⇒ <code>Promise</code>
19
20
  Database connection validation.
20
21
 
21
- **Kind**: inner method of <code>[mongooser](#module_mongooser)</code>
22
+ **Kind**: inner method of [<code>mongooser</code>](#module_mongooser)
22
23
  **Returns**: <code>Promise</code> - .
23
24
  **Category**: async
24
25
  **Throws**:
@@ -29,22 +30,49 @@ Will exit 1 if the connection request times out and the connection state is not
29
30
 
30
31
  **Requires**: <code>module:@mimik/sumologic-winston-logger</code>
31
32
  **Fulfil**: Return `null`.
33
+ <a name="module_mongooser..getKMSProviders"></a>
34
+
35
+ ### mongooser~getKMSProviders() ⇒ <code>object</code>
36
+ AutoEncryption KMSProvider.
37
+
38
+ **Kind**: inner method of [<code>mongooser</code>](#module_mongooser)
39
+ **Returns**: <code>object</code> - The KMSProviders object.
40
+
41
+ Will return the kmsProvider settings for the service.
42
+ **Category**: sync
32
43
  <a name="module_mongooser..initializeSync"></a>
33
44
 
34
- ### mongooser~initializeSync() ⇒ <code>object</code>
45
+ ### mongooser~initializeSync(autoEncryption) ⇒ <code>object</code>
35
46
  Database initialization.
36
47
 
37
- **Kind**: inner method of <code>[mongooser](#module_mongooser)</code>
48
+ **Kind**: inner method of [<code>mongooser</code>](#module_mongooser)
38
49
  **Returns**: <code>object</code> - The database object.
39
50
 
51
+ The autoEncryption has the following strucuture:
52
+ ``` javascript
53
+ {
54
+ "keyVaultClient" : <object>,
55
+ "keyVaultNamespace": <string>, // namespace for the keyvault collection
56
+ "kmsProviders": <object>, // KeyManager service settings
57
+ "schemaMap": <object>, // json object for defining encryption schema
58
+ "bypassAutoEncryption": <boolean>
59
+ }
60
+ ```
61
+ Check MongoDB documentation for more details. https://docs.mongodb.com/manual/reference/method/Mongo/#clientsidefieldlevelencryptionoptions
62
+
40
63
  Will exit 1 if the connection request generates an error or the connection state is `disconnected` or `disconnecting`.
41
64
  **Category**: sync
42
65
  **Requires**: <code>module:sumologic-winston-logger</code>
66
+
67
+ | Param | Type | Description |
68
+ | --- | --- | --- |
69
+ | autoEncryption | <code>object</code> | Encryption settings for mongo connection. |
70
+
43
71
  <a name="module_mongooser..replicat"></a>
44
72
 
45
73
  ### mongooser~replicat() ⇒ <code>boolean</code>
46
74
  Indicates if the replica is set.
47
75
 
48
- **Kind**: inner method of <code>[mongooser](#module_mongooser)</code>
76
+ **Kind**: inner method of [<code>mongooser</code>](#module_mongooser)
49
77
  **Returns**: <code>boolean</code> - `True` if replica is set, `False` if not.
50
78
  **Category**: sync
package/index.js CHANGED
@@ -1,6 +1,11 @@
1
1
  const mongoose = require('mongoose');
2
2
  const Promise = require('bluebird');
3
+ const _ = require('lodash');
4
+
5
+ const { getCorrelationId } = require('@mimik/request-helper');
3
6
  const logger = require('@mimik/sumologic-winston-logger');
7
+ const { isProd, MASK } = require('@mimik/lib-filters');
8
+
4
9
  /**
5
10
  * @module mongooser
6
11
  * @example
@@ -9,8 +14,8 @@ const logger = require('@mimik/sumologic-winston-logger');
9
14
 
10
15
  mongoose.Promise = Promise;
11
16
 
12
- const correlationId = 'database-start';
13
17
  const type = 'mongo';
18
+ const correlationId = getCorrelationId(`${type}-database-start`);
14
19
 
15
20
  const VALIDATION_CHECK = 100; // in ms
16
21
  const DISCONNECTED = 0;
@@ -18,25 +23,79 @@ const CONNECTED = 1;
18
23
  const CONNECTING = 2;
19
24
  const DISCONNECTING = 3;
20
25
 
26
+ let fatal = false;
27
+
21
28
  module.exports = (config) => {
29
+ const { mongoSettings, encryption } = config;
30
+ const { options } = mongoSettings;
31
+
32
+ /**
33
+ * AutoEncryption KMSProvider.
34
+ *
35
+ * @function getKMSProviders
36
+ * @category sync
37
+ * @return {object} The KMSProviders object.
38
+ *
39
+ * Will return the kmsProvider settings for the service.
40
+ */
41
+ const getKMSProviders = () => {
42
+ const kmsProviders = {};
43
+ const provider = encryption.kmsProvider.toLowerCase();
44
+
45
+ if (provider === 'local') {
46
+ kmsProviders.local = { key: Buffer.from(encryption.localMasterKey, 'base64') };
47
+ }
48
+ else {
49
+ kmsProviders.aws = encryption.aws;
50
+ }
51
+ return kmsProviders;
52
+ };
53
+
22
54
  /**
23
55
  * Database initialization.
24
56
  *
25
57
  * @requires sumologic-winston-logger
26
58
  *
27
- * @function initializeSync
59
+ * @param {object} autoEncryption - Encryption settings for mongo connection.
28
60
  * @category sync
29
61
  * @return {object} The database object.
30
62
  *
63
+ * The autoEncryption has the following strucuture:
64
+ * ``` javascript
65
+ * {
66
+ * "keyVaultClient" : <object>,
67
+ * "keyVaultNamespace": <string>, // namespace for the keyvault collection
68
+ * "kmsProviders": <object>, // KeyManager service settings
69
+ * "schemaMap": <object>, // json object for defining encryption schema
70
+ * "bypassAutoEncryption": <boolean>
71
+ * }
72
+ *```
73
+ * Check MongoDB documentation for more details. https://docs.mongodb.com/manual/reference/method/Mongo/#clientsidefieldlevelencryptionoptions
74
+ *
31
75
  * Will exit 1 if the connection request generates an error or the connection state is `disconnected` or `disconnecting`.
32
76
  */
33
77
  const initializeSync = () => {
34
- let interval = {};
78
+ let interval;
79
+
80
+ if (encryption.set === 'on') {
81
+ options.autoEncryption = {
82
+ keyVaultNamespace: `${encryption.database}.${encryption.keyVaultTable}`,
83
+ kmsProviders: getKMSProviders(),
84
+ };
85
+ }
86
+
87
+ if (mongoose.connection.readyState === DISCONNECTED && !fatal) {
88
+ if (isProd(config.nodeEnvironment) && mongoSettings.password) {
89
+ const mongoSettingsClone = { ...mongoSettings };
35
90
 
36
- if (mongoose.connection.readyState === 0) {
37
- logger.info('creating a database connection', { type, settings: config.mongoSettings }, correlationId);
38
- mongoose.connect(config.mongoSettings.url, config.mongoSettings.options).then(() => mongoose).catch((error) => {
91
+ mongoSettingsClone.url = _.replace(mongoSettingsClone.url, mongoSettings.password, MASK);
92
+ logger.info('creating a database connection', { type, settings: mongoSettingsClone }, correlationId);
93
+ }
94
+ else logger.info('creating a database connection', { type, settings: mongoSettings }, correlationId);
95
+ mongoose.connect(mongoSettings.url, options).then(() => mongoose).catch((error) => {
96
+ fatal = true;
39
97
  logger.error('Fatal error: Could not connect to database', { type, error }, correlationId);
98
+ if (interval) clearInterval(interval);
40
99
  logger.flushAndExit(1);
41
100
  });
42
101
  }
@@ -45,14 +104,16 @@ module.exports = (config) => {
45
104
  interval = setInterval(() => {
46
105
  const state = mongoose.connection.readyState;
47
106
 
48
- if (state === DISCONNECTED || state === DISCONNECTING) {
107
+ if ((state === DISCONNECTED || state === DISCONNECTING) && !fatal) {
108
+ fatal = true;
49
109
  logger.error('Fatal error: Could not connect to database', { type, error: state }, correlationId);
110
+ clearInterval(interval);
50
111
  logger.flushAndExit(1);
51
112
  }
52
- }, (config.mongoSettings.options.reconnectTries * config.mongoSettings.options.reconnectInterval));
113
+ }, (options.heartbeatFrequencyMS + (mongoSettings.reconnectOffset * 1000)));
53
114
  });
54
115
  mongoose.connection.on('connected', () => {
55
- clearInterval(interval);
116
+ if (interval) clearInterval(interval);
56
117
  });
57
118
  return mongoose;
58
119
  };
@@ -75,13 +136,15 @@ module.exports = (config) => {
75
136
  const interval = setInterval(() => {
76
137
  const state = mongoose.connection.readyState;
77
138
 
78
- if (state !== CONNECTED) {
139
+ if (state !== CONNECTED && !fatal) {
140
+ fatal = true;
79
141
  logger.error('Fatal error: Timeout in connecting to database', {
80
- type, error: state, timeout: config.mongoSettings.connectTimeout,
142
+ type, error: state, timeout: mongoSettings.connectTimeout,
81
143
  }, correlationId);
144
+ clearInterval(interval);
82
145
  logger.flushAndExit(1);
83
146
  }
84
- }, config.mongoSettings.connectTimeout * 1000); // convert in seconds
147
+ }, mongoSettings.connectTimeout * 1000); // convert in seconds
85
148
 
86
149
  return Promise.delay(VALIDATION_CHECK).then(() => {
87
150
  const state = mongoose.connection.readyState;
@@ -109,11 +172,12 @@ module.exports = (config) => {
109
172
  * @category sync
110
173
  * @return {boolean} `True` if replica is set, `False` if not.
111
174
  */
112
- const replicat = () => config.mongoSettings.replicat;
175
+ const replicat = () => mongoSettings.replicat;
113
176
 
114
177
  return {
115
178
  initializeSync,
116
179
  validate,
117
180
  replicat,
181
+ getKMSProviders,
118
182
  };
119
183
  };
package/package.json CHANGED
@@ -1,45 +1,54 @@
1
1
  {
2
2
  "name": "@mimik/mongooser",
3
- "version": "1.2.4",
3
+ "version": "1.7.0",
4
4
  "description": "Helper for setting up mongodb using mongoose for mimik microservices",
5
+ "main": "index.js",
5
6
  "scripts": {
6
7
  "lint": "gulp lint",
7
8
  "docs": "gulp docs",
8
- "test": "exit 0"
9
+ "test": "exit 0",
10
+ "prepublishOnly": "gulp docs; gulp lint; npm run test",
11
+ "commit-ready": "gulp docs; gulp lint; npm run test",
12
+ "prepare": "husky install"
9
13
  },
10
14
  "husky": {
11
15
  "hooks": {
12
- "pre-commit": "npm run lint; npm run docs; gulp add; npm run test",
13
- "pre-push": "npm run lint; npm run test"
16
+ "pre-commit": "npm run commit-ready",
17
+ "pre-push": "npm run test"
14
18
  }
15
19
  },
16
20
  "keywords": [
17
21
  "mimik",
18
22
  "microservice"
19
23
  ],
20
- "author": "mimik",
24
+ "author": "mimik technology inc <support@mimik.com> (https://developer.mimik.com/)",
21
25
  "license": "Apache-2.0",
22
26
  "repository": {
23
27
  "type": "git",
24
28
  "url": "https://bitbucket.org/mimiktech/mongooser"
25
29
  },
26
30
  "dependencies": {
27
- "@mimik/sumologic-winston-logger": "1.1.1",
28
- "bluebird": "3.5.4",
29
- "mongoose": "5.5.5"
31
+ "@mimik/lib-filters": "^1.4.3",
32
+ "@mimik/request-helper": "^1.7.3",
33
+ "@mimik/sumologic-winston-logger": "^1.6.6",
34
+ "bluebird": "3.7.2",
35
+ "lodash": "4.17.21",
36
+ "mongoose": "5.13.7"
30
37
  },
31
38
  "devDependencies": {
32
- "eslint": "5.16.0",
33
- "eslint-config-airbnb": "17.1.0",
34
- "eslint-plugin-import": "2.17.2",
35
- "eslint-plugin-jsx-a11y": "6.2.1",
36
- "eslint-plugin-react": "7.12.4",
39
+ "@mimik/eslint-plugin-dependencies": "^2.4.1",
40
+ "@mimik/eslint-plugin-document-env": "^1.0.0",
41
+ "eslint": "8.4.1",
42
+ "eslint-config-airbnb": "18.2.1",
43
+ "eslint-plugin-import": "2.25.3",
44
+ "eslint-plugin-jsx-a11y": "6.5.1",
45
+ "eslint-plugin-react": "7.27.1",
46
+ "eslint-plugin-react-hooks": "4.3.0",
37
47
  "fancy-log": "1.3.3",
38
- "gulp": "4.0.1",
39
- "gulp-eslint": "5.0.0",
40
- "gulp-git": "2.9.0",
41
- "gulp-jsdoc-to-markdown": "1.2.2",
42
- "gulp-rename": "1.4.0",
43
- "husky": "2.2.0"
48
+ "gulp": "4.0.2",
49
+ "gulp-eslint": "6.0.0",
50
+ "gulp-git": "2.10.1",
51
+ "husky": "7.0.4",
52
+ "jsdoc-to-markdown": "7.1.0"
44
53
  }
45
54
  }