Skip to content

Commit 5dd2aea

Browse files
committed
Merge branch 'dev' into Health
2 parents 23cdf7c + 68b5239 commit 5dd2aea

38 files changed

+4703
-127
lines changed

chronos_npm_package/chronos.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
const hpropagate = require('hpropagate');
2+
const postgres = require('./controllers/postgres.js');
3+
const mongo = require('./controllers/mongo.js');
4+
const MongoClientWrapper = require('./wrappers/MongoClientWrapper.js');
5+
const MongoServerWrapper = require('./wrappers/MongoServerWrapper.js');
6+
const PostgresClientWrapper = require('./wrappers/PostgresClientWrapper.js');
7+
const PostgresServerWrapper = require('./wrappers/PostgresServerWrapper.js');
8+
const { validateInput, addNotifications } = require('./controllers/helpers');
9+
10+
let userConfig = {};
11+
const chronos = {};
12+
13+
/**
14+
* **********************************
15+
* CMD CONFIG FILE SETUP
16+
*
17+
* @field microservice {string} REQUIRED
18+
* The user specified name for the microservice being tracked
19+
*
20+
* @field interval {number} DEFAULT 60000
21+
* The interval for every microservice health check in milliseconds
22+
* This defaults to 60000 ms or 1 minute
23+
*
24+
* @field database {Object}
25+
* Takes two properties
26+
* - type {string} Either PostgreSQL or MongoDB
27+
* - URI {string} Database uri
28+
*
29+
* @field notifications {array} OPTIONAL
30+
* Varies per notification method
31+
* **********************************
32+
*/
33+
chronos.use = config => {
34+
// Validate all input fields exist and setup notifications
35+
validateInput(config);
36+
addNotifications(config);
37+
38+
// Save config globally
39+
userConfig = config;
40+
};
41+
42+
/**
43+
* Places an unique x-correlating-id into the headers of each request/response.
44+
* This is used for tracking the life cycle of the request until the response
45+
*/
46+
chronos.propagate = () => {
47+
hpropagate({ propagateInResponses: true });
48+
};
49+
50+
/**
51+
* **********************************************
52+
* MAIN CONTROLLER
53+
* Only supports MongoDB and PostgreSQL for now!
54+
* **********************************************
55+
*/
56+
chronos.track = () => {
57+
const { database, dockerized } = userConfig;
58+
59+
/**
60+
* If the provided database is Mongo
61+
* - Connection is made to MongoDB via the provided URI by the user.
62+
*
63+
* - 'services' collection will be created if not already and stores every microservice
64+
* that is apart of the application.
65+
*
66+
* - Information is collected if the microservice is containerized
67+
*
68+
* - 'communications' collection will be created which creates a new document for every
69+
* endpoint that the user Request travels through (tracked with hpropograte) for express routes
70+
*/
71+
if (database.type === 'MongoDB') {
72+
mongo.connect(userConfig);
73+
mongo.services(userConfig);
74+
mongo.docker(userConfig);
75+
mongo.health(userConfig);
76+
if (database.connection === 'REST') {
77+
return mongo.communications(userConfig);
78+
}
79+
}
80+
81+
/**
82+
* If the provided database is PostgreSQL
83+
* - Connection is made to the postgres client via the provided URI by the user.
84+
*
85+
* - 'services' table will be created if not already and stores every microservice
86+
* that is apart of the application.
87+
*
88+
* - Information is collected if the microservice is containerized
89+
*
90+
* - 'communications' table will be created which creates a new row entry for every
91+
* endpoint that the user Request travels through (tracked with hpropograte)
92+
*/
93+
if (database.type === 'PostgreSQL') {
94+
postgres.connect(userConfig);
95+
postgres.services(userConfig);
96+
postgres.docker(userConfig);
97+
postgres.health(userConfig);
98+
if (database.connection === 'REST') {
99+
return postgres.communications(userConfig);
100+
}
101+
}
102+
return null;
103+
};
104+
105+
chronos.ServerWrapper = (server, proto, methods) => {
106+
const { database } = userConfig;
107+
if (database.type === 'MongoDB') {
108+
return new MongoServerWrapper(server, proto, methods, userConfig);
109+
}
110+
if (database.type === 'PostgreSQL') {
111+
return new PostgresServerWrapper(server, proto, methods, userConfig);
112+
}
113+
return null;
114+
};
115+
116+
chronos.ClientWrapper = (client, service) => {
117+
const { database } = userConfig;
118+
if (database.type === 'MongoDB') {
119+
return new MongoClientWrapper(client, service, userConfig);
120+
}
121+
if (database.type === 'PostgreSQL') {
122+
return new PostgresClientWrapper(client, service, userConfig);
123+
}
124+
return null;
125+
};
126+
127+
chronos.link = (client, server) => {
128+
client.metadata = server.metadataHolder;
129+
};
130+
131+
module.exports = chronos;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const axios = require('axios');
2+
const nodemailer = require('nodemailer');
3+
4+
const alert = {};
5+
6+
/**
7+
* Sends slack notifications to the provided slackurl with the status code
8+
* and message via an axios POST request
9+
* @param {integer} code Response status code
10+
* @param {string} message Response message
11+
* @param {Object} slackSettings User provided slack settings
12+
*/
13+
alert.sendSlack = (code, message, slackSettings) => {
14+
const { webhook } = slackSettings;
15+
16+
// Data for POST request
17+
const data = { text: `${code}, ${message}, ${Date.now()}` };
18+
19+
// Options for POST request
20+
const config = {
21+
method: 'post',
22+
headers: {
23+
'Content-Type': 'application/json',
24+
},
25+
};
26+
27+
axios
28+
.post(webhook, data, config)
29+
.then(res => console.log('Status Code >= 400...\nError message sent'))
30+
.catch(error => console.log('test------>', error.message));
31+
};
32+
33+
/**
34+
* Sends email notifications using the provided email information with the
35+
* status code and message via an axios POST request
36+
* @param {integer} code Response status code
37+
* @param {string} message Response message
38+
* @param {Object} emailSettings User provided email settings
39+
*/
40+
alert.sendEmail = (code, message, emailSettings) => {
41+
const { emails, emailHost, emailPort, user, password } = emailSettings;
42+
43+
// Message object contains receipent email list and email text body
44+
const data = {
45+
to: `${emails}`,
46+
subject: 'Error from Middleware',
47+
text: `${code}, ${message}`,
48+
};
49+
50+
// Configuartion settings for email notifications
51+
const config = {
52+
host: `${emailHost}`,
53+
port: `${emailPort}`,
54+
auth: {
55+
user: `${user}`,
56+
pass: `${password}`,
57+
},
58+
};
59+
const transport = nodemailer.createTransport(config);
60+
61+
transport.sendMail(data, function (err, info) {
62+
if (err) {
63+
console.log(err);
64+
} else {
65+
console.log(info);
66+
}
67+
});
68+
};
69+
70+
module.exports = alert;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const helpers = {
2+
/**
3+
* Helper function to validate input from user's configuration options
4+
* Throws an error on input valid data types or on missing fields
5+
* Sets the default interval to 5 seconds and dockerized to false
6+
*/
7+
validateInput(config) {
8+
let { microservice, database, interval, dockerized, connection } = config;
9+
10+
// Validate all required fields exist and are valid input types
11+
if (!microservice || typeof microservice !== 'string') {
12+
throw new Error('Invalid input "microservice": Please provide a name for your microservice');
13+
}
14+
15+
if (!database.type || typeof database.type !== 'string') {
16+
throw new Error('Invalid input "database type": Chronos supports PostgreSQL and MongoDB');
17+
}
18+
19+
if (!database.URI || typeof database.URI !== 'string') {
20+
throw new Error('Invalid input "database URI": Please provide the URI to your database');
21+
}
22+
if (!database.connection || typeof database.connection !== 'string') {
23+
throw new Error('Invalid input "database connection type: Please provide the type of connection');
24+
}
25+
26+
// Validate database type
27+
if (database.type !== 'PostgreSQL' && database.type !== 'MongoDB') {
28+
throw new Error(
29+
`Invalid input "${database.type}". Chronos only supports PostgreSQL and MongoDB.`
30+
);
31+
}
32+
if (database.connection !== 'REST' && database.connection !== 'gRPC') {
33+
throw new Error(
34+
`Invalid database connection "${database.connection}". Chronos only supports REST and gRPC.`
35+
)
36+
}
37+
38+
// Default interval to one minute
39+
if (!interval || typeof interval !== 'number') config.interval = 60000;
40+
41+
// Default dockerized to false
42+
if (dockerized === undefined || dockerized !== 'boolean') config.dockerized = false;
43+
},
44+
45+
/**
46+
* Sets up notifications depending if the user provides the options
47+
* Method adds properties to the existing userConfig object with the key
48+
* being the notification type and the value being the notification settings
49+
*/
50+
addNotifications(config) {
51+
const { notifications } = config;
52+
if (notifications) {
53+
// Current notification methods supported
54+
const features = ['slack', 'email', 'sms'];
55+
56+
// Setup notifications for user
57+
notifications.forEach(obj => {
58+
const type = obj.type;
59+
60+
// Throw errors on unsupported notification methods
61+
if (!features.includes(type)) {
62+
throw new Error(`${type} is not a supported notification method for Chronos`);
63+
} else {
64+
config[type] = obj.settings;
65+
}
66+
});
67+
}
68+
},
69+
};
70+
71+
module.exports = helpers;

0 commit comments

Comments
 (0)