Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split to a Grafana Service #180

Merged
merged 44 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
9921260
Refactor the creation of the Grafana Client to the Grafan Client Fact…
KeesCBakker Apr 22, 2024
c2e3f04
Remove only
KeesCBakker Apr 22, 2024
d3cc152
Make sure we forbid only on commit and push
KeesCBakker Apr 22, 2024
8b5b6ed
Refactor response to be easier to read. Fail fast.
KeesCBakker Apr 22, 2024
07f89f3
Small refactoring to make code more readable.
KeesCBakker Apr 22, 2024
f618e1d
refactored pausing / resuming of alerts to the service.
KeesCBakker Apr 23, 2024
fefe1e4
refactor single alert
KeesCBakker Apr 23, 2024
f278bef
refactor alert querying into the service
KeesCBakker Apr 23, 2024
b4e0686
remove send alerts, as it is refactored.
KeesCBakker Apr 23, 2024
c9be884
refactor search out
KeesCBakker Apr 23, 2024
b224ff7
refactor get dashboard out
KeesCBakker Apr 23, 2024
2686582
Version bump & make failing tests execute faster.
KeesCBakker Apr 23, 2024
c27cb7e
Add prettier support to dev container.
KeesCBakker Apr 23, 2024
d978eb5
formatting
KeesCBakker Apr 23, 2024
db18212
harmonize with the rest of the code
KeesCBakker Apr 23, 2024
831190f
add missing ';'
KeesCBakker Apr 23, 2024
de84625
Move object around and refactor parseToGrafanaDashboardRequest and ge…
KeesCBakker Apr 23, 2024
d34989c
Harmonize code
KeesCBakker Apr 23, 2024
3cc3d22
Fix fetch testing.
KeesCBakker Apr 23, 2024
6150b96
Was missing co-pilot in the dev env.
KeesCBakker Apr 23, 2024
2dbf2c7
Implementation of the bot.
KeesCBakker Apr 23, 2024
8dfa58a
Refactor types.
KeesCBakker Apr 23, 2024
d8eb15e
Merge branch 'main' into feature/grafana-service
KeesCBakker Apr 23, 2024
af150e7
Combine the processing of the string to Grafana dashboards.
KeesCBakker Apr 23, 2024
4a8d96e
Fix return type.
KeesCBakker Apr 23, 2024
3ff9209
Improve documentation with types. Move `nock.cleanAll` to the right `…
KeesCBakker Apr 23, 2024
eca3d68
Add tests for pausing and unpausing all alerts. Improves code coverage.
KeesCBakker Apr 23, 2024
76caff3
Add tests for Grafana service processing and response handling. Impro…
KeesCBakker Apr 23, 2024
bc88859
The "should respond with a png graph in the default s3 region" test t…
KeesCBakker Apr 23, 2024
46fc436
Fix bug with template values not showing up in the title. Improved te…
KeesCBakker Apr 24, 2024
49cfa68
Add getUidBySlug for the Grafana Service.
KeesCBakker Apr 24, 2024
53bc40e
Fix typing.
KeesCBakker Apr 24, 2024
6dbb8a0
Rename `DashboardResponse` to `DashboardChart` so it does not look li…
KeesCBakker Apr 24, 2024
b3aba0c
These defaults work better with TypeScript.
KeesCBakker Apr 24, 2024
0e0795c
Make it possible to change the output by overriding the system with a…
KeesCBakker Apr 24, 2024
7924c2c
Naming.
KeesCBakker Apr 24, 2024
cf89bd3
No need for the GrafanaClientFactory now that we have the Bot.
KeesCBakker Apr 25, 2024
9ba7fa7
Rename bot.js to Bot.js
KeesCBakker Apr 25, 2024
0040f01
Restore naming
KeesCBakker Apr 25, 2024
b43a36d
Restore strict
KeesCBakker Apr 25, 2024
edbe9f0
Restore http
KeesCBakker Apr 25, 2024
c878ba7
Restore http
KeesCBakker Apr 25, 2024
fcfd11a
Change formatting
KeesCBakker Apr 25, 2024
0905561
ship types.d.ts as well
KeesCBakker Apr 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@
"mounts": [
"source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume"
],
"postCreateCommand": "sudo chown node node_modules"
"postCreateCommand": "sudo chown node node_modules",
"customizations": {
"vscode": {
"extensions": [
"esbenp.prettier-vscode",
"GitHub.copilot"
]
}
}
}
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm test
npm test -- --forbid-only --forbid-pending
KeesCBakker marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm test
npm test -- --forbid-only --forbid-pending
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "hubot-grafana",
"description": "Query Grafana dashboards",
"version": "5.1.2",
"version": "6.0.0",
KeesCBakker marked this conversation as resolved.
Show resolved Hide resolved
"author": "Stephen Yeargin <[email protected]>",
"license": "MIT",
"keywords": [
Expand Down Expand Up @@ -39,7 +39,7 @@
},
"main": "index.js",
"scripts": {
"test": "mocha \"test/**/*.js\" --reporter spec",
"test": "mocha \"test/**/*.js\" --reporter spec --no-experiemental-fetch --timeout 200",
"test-with-coverage": "nyc --reporter=text mocha \"test/**/*.js\" --reporter spec",
"bootstrap": "script/bootstrap",
"prepare": "husky install",
Expand All @@ -49,7 +49,8 @@
"src/**/*.js",
"CONTRIBUTING.md",
"LICENSE",
"index.js"
"index.js",
"types.d.ts"
],
"volta": {
"node": "18.19.0"
Expand Down
101 changes: 101 additions & 0 deletions src/Bot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const { Adapter } = require('./adapters/Adapter');
const { GrafanaService } = require('./service/GrafanaService');
const { GrafanaClient } = require('./grafana-client');

/**
* The bot brings the Adapter and the Grafana Service together.
* It can be used for uploading charts and sending responses out.
*/
class Bot {
/**
* Represents the Bot class.
* @constructor
* @param {Hubot.Robot} robot - The robot instance.
*/
constructor(robot) {
/** @type {Adapter} */
this.adapter = new Adapter(robot);

/** @type {Hubot.Log} */
this.logger = robot.logger;
}

/**
* Creates a new Grafana service based on the provided message.
* @param {Hubot.Response} context - The context object.
* @returns {GrafanaService|null} - The created Grafana service or null if the client is not available.
*/
createService(context) {

const robot = context.robot;
let host = process.env.HUBOT_GRAFANA_HOST;
let apiKey = process.env.HUBOT_GRAFANA_API_KEY;

if (process.env.HUBOT_GRAFANA_PER_ROOM === '1') {
const room = this.getRoom(context);
host = robot.brain.get(`grafana_host_${room}`);
apiKey = robot.brain.get(`grafana_api_key_${room}`);
}

if (host == null) {
this.sendError('No Grafana endpoint configured.', context);
return null;
}

let client = new GrafanaClient(robot.http, robot.logger, host, apiKey);
return new GrafanaService(client);
}

/**
* Sends a dashboard chart.
*
* @param {Hubot.Response} context - The context object.
* @param {DashboardChart} dashboard - The dashboard object.
* @returns {Promise<void>} - A promise that resolves when the chart is sent.
*/
async sendDashboardChart(context, dashboard) {
if (!this.adapter.isUploadSupported()) {
this.adapter.responder.send(context, dashboard.title, dashboard.imageUrl, dashboard.grafanaChartLink);
return;
}

const service = this.createService(context);
if (service == null) return;

/** @type {DownloadedFile|null} */
let file = null;

try {
file = await service.client.download(dashboard.imageUrl);
} catch (err) {
this.sendError(err, context);
return;
}

this.logger.debug(`Uploading file: ${file.body.length} bytes, content-type[${file.contentType}]`);
this.adapter.uploader.upload(context, dashboard.title || 'Image', file, dashboard.grafanaChartLink);
}

/**
* *Sends an error message.
* @param {string} message the error message.
* @param {Hubot.Response} context The context.
*/
sendError(message, context) {
context.robot.logger.error(message);
context.send(message);
}

/**
* Gets the room from the context.
* @param {Hubot.Response} context The context.
* @returns {string}
*/
getRoom(context) {
// placeholder for further adapter support (i.e. MS Teams) as then room also
// contains thread conversation id
return context.envelope.room;
}
}

exports.Bot = Bot;
31 changes: 29 additions & 2 deletions src/adapters/Adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ const { RocketChatUploader } = require('./implementations/RocketChatUploader');
const { TelegramUploader } = require('./implementations/TelegramUploader');
const { SlackUploader } = require('./implementations/SlackUploader');

/**
* The override responder is used to override the default responder.
* This can be used to inject a custom responder to influence the message formatting.
* @type {Responder|null}
*/
let overrideResponder = null;

/**
* The Adapter will hide away platform specific details for file upload and
* response messages. When an S3 bucket is configured, it will always take
Expand Down Expand Up @@ -53,6 +60,11 @@ class Adapter {
*/
/** @type {Responder} */
get responder() {

if(overrideResponder){
return overrideResponder;
}

if (/slack/i.test(this.robot.adapterName)) {
return new SlackResponder();
}
Expand All @@ -66,7 +78,7 @@ class Adapter {
return new Responder();
}

/**
/**
* The responder is responsible for doing a (platform specific) upload.
* If an upload is not supported, the method will throw an error.
*/
Expand All @@ -80,7 +92,7 @@ class Adapter {
case 'slack':
return new SlackUploader(this.robot, this.robot.logger);
case 'telegram':
return new TelegramUploader()
return new TelegramUploader();
KeesCBakker marked this conversation as resolved.
Show resolved Hide resolved
}

throw new Error(`Upload not supported for '${this.robot.adapterName}'`);
Expand All @@ -95,4 +107,19 @@ class Adapter {
}
}

/**
* Overrides the responder.
* @param {Responder} responder The responder to use.
*/
exports.setResponder = function(responder) {
overrideResponder = responder;
}

/**
* Clears the override responder.
*/
exports.clearResponder = function() {
overrideResponder = null;
}

exports.Adapter = Adapter;
Loading
Loading