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

feat(sumologic-image-widget) : Hubot can display widget from a sumologic dashboard #8

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
980a8c4
Some test to get dashboard widget data
Mar 9, 2016
2061918
Start implement dashboard show cmd
Mar 11, 2016
e730a35
feat(package) : Set package info and dev task
julien-vidal Mar 20, 2016
fcff3f7
feat(sumologic-image-widget) : Test gist, shit and doc it
julien-vidal Mar 20, 2016
6fc5fec
feat(sumologic-image-widget) : Play with sumologic data and revertengine
julien-vidal Mar 20, 2016
83586d2
feat(sumologic-image-widget) : Push some refactor on dashboard...
Mar 21, 2016
e1e6ade
feat(sumologic-image-widget) : Start implementing show widget
julien-vidal Mar 23, 2016
d9f587c
Merge branch 'payou-playground-graph-img-generation' of https://githu…
Mar 23, 2016
d8d0e64
feat(sumologic-image-widget) : Start stackedBarFormatter
Mar 23, 2016
7df37a3
feat(sumologic-image-widget) : First working version
Mar 24, 2016
f9c01c9
feat(sumologic-image-widget) : Hubot listen for sumo command
julien-vidal Mar 24, 2016
b2fce86
feat(sumologic-image-widget) : Add Pie formatter
julien-vidal Mar 25, 2016
e5d0be9
feat(sumologic-image-widget) : Add some others chart to implement
julien-vidal Mar 25, 2016
02a531f
feat(sumologic-image-widget) : Add missing mocks for front dashboard
Mar 25, 2016
a538572
feat(sumologic-image-widget) : remove sumo token from code
julien-vidal Mar 26, 2016
3e0101e
feat(sumologic-image-widget) : Refactor chain architecture
julien-vidal Mar 26, 2016
1f1c0d9
feat(sumologic-image-widget) : Refactor fixing
julien-vidal Mar 26, 2016
ce65ee8
feat(sumologic-image-widget) : add linesChart support
julien-vidal Mar 27, 2016
ecb60fe
feat(sumologic-image-widget) : add (time)counter charts
julien-vidal Mar 27, 2016
07b7942
feat(sumologic-image-widget) : Refactor how graphBuilder getGraph
julien-vidal Mar 27, 2016
635a6db
feat(sumologic-image-widget) : Start implementing (time)counter
julien-vidal Mar 27, 2016
b926583
feat(sumologic-image-widget) : (time)counter Done
julien-vidal Mar 28, 2016
1d7292b
feat(sumologic-image-widget) : Update roadmap
julien-vidal Mar 28, 2016
1a167f5
feat(sumologic-image-widget) : Fix how counter transform data
julien-vidal Mar 28, 2016
11943d4
feat(sumologic-image-widget) : Add clean way to store graph file
Mar 29, 2016
b332496
feat(sumologic-image-widget) : Implement tmp folder and clean
julien-vidal Mar 29, 2016
14cdf05
feat(sumologic-image-widget) : Start refactor of file management
Mar 30, 2016
354316c
feat(sumologic-image-widget) : Implement fileHelper
Apr 7, 2016
b23a562
feat(sumologic-image-widget) : End FileHelper integration
Apr 8, 2016
774006f
feat(sumologic-image-widget) : Add Highcharts theme support
Apr 8, 2016
a765019
feat(sumologic-image-widget) : Implement how to learn to hubot
julien-vidal Apr 19, 2016
471223a
feat(sumologic-image-widget) : Comment dark theme
julien-vidal Apr 19, 2016
7a12d13
feat(sumologic-image-widget) : Tidy file by purposes
Apr 21, 2016
954f54e
feat(sumologic-image-widget) : Remap dependencies due to previous tidy
Apr 21, 2016
8998270
feat(sumologic-image-widget) : Add info message when hubot learn new …
Apr 21, 2016
c97938b
feat(sumologic-image-widget) : Rename method for erase sumo memory
Apr 21, 2016
3f8fced
feat(sumologic-image-widget) : Clean fileHelper
Apr 21, 2016
55fe6e9
feat(sumologic-image-widget) : Clean todo and clean
Apr 22, 2016
deae5f7
feat(sumologic-image-widget) : Add notification on hubot learnings
julien-vidal Apr 24, 2016
6a3159e
feat(sumologic-image-widget) : End error message on hubot learning fail
Apr 25, 2016
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ node_modules
.DS_Store*
.hubot_history
.idea
custom
dump.rdb
131 changes: 131 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,134 @@ Add the subdomain hubot should connect to. If you web URL looks like

You may want to get comfortable with `heroku logs` and `heroku restart` if
you're having issues.


# (Payou - Experiments) Display a widget from sumologic

## Idea

From a Sumologic dashboard widget, be able to display it as an image

## How

Step one : Target the good widget
For now we can't do it easily while the dashboard API from Sumologic is broken.
For now we should link manually id to widget :cry:

Step two : Transform widget data to graph

Thanks to jsdom and highcharts we can transform what sumologic return to a graph.
This process will be manual in a first time for testing purpose, then a bit
industrialize to display all widget possible. Sumologic seems to use highcharts
for rendering their widget, so this part shouldn't be hard...

Step three : Transform svg graph to image

For now we will use svg2png, it use phamtomjs in background for extract png from
svg. Not the nicer solution but the easiest for now. Other lib are only wrapper
of lower image libs. Maybe this latest solution is faster than the previous and
we should consider it on long term maybe. But this one is harder to implement and
depend on which OS the hubot will run.

Step Four : Host/Send the img to slack

At first sight, Hubot should send image generated.
Not the hardest part, seems slack api have what we need to do it !
We just need to implement that part and we should be done !

Could send it to a third part and just send link in a channel to.

## usefull commands

```
// Will launch the test bouzin !
coffee ./scripts/hubot-sumologic-image-widget/main.coffee

// Will launch the gist test bouzin !
node ./scripts/hubot-sumologic-image-widget/gist.js
```

## Now

### Version 1.0
- [x] Tests the all chain !
- [x] Use real data from sumologic widget
- [x] Implement some highchart adapters depending of widget
- [x] Add Hubot hear for this dev
- [x] Test on a true slack
- [x] Graph can be built on highchart or svg template
- [x] Split how work graph builder for allowing multiple implementation
- [x] Base on graph type
- [x] Use promises !
- [x] Break callback hell into promise composition
- [x] Clean generated image file
- [x] Auto find tmp file
- [x] Use it
- [x] Style graph
- [x] Learn hubot to learn
- [x] Learn from a json file (brain used)
- [x] Allow to erase memory via a command
- [x] Clean && Tidy && Refactor if needed
- [x] Clean todo's
- [x] SendToSlack (ensure work for common cases)
- [ ] Package it !
- [ ] Boulangerie repo
- [ ] Issues followings subjects
- [ ] Millestone them ?
- [ ] Readme
- [ ] Npm publish

### Learn hubot to learn
POC on fact hubot can get a json file through slack
Can be a link to file but it's less safer
Finally we can brainwash hubot if we want !
Maybe if sumologic fix their API we could use it properly
and avoid a lot of this stuff

#### Edit
Some of this feature have already been done.
But this part of this package could improved a bit more

- [] Erase brain should be possible only by some users
- [] Should list all widget available...
- [] Should add/update/remove unitary by a command

### Learn hubot to make graphs with query
Instead of using dashboard data, use true results from
a custom query. The naive way should to transform query
results into dashboard data format. Like this graphs implementation
should work as expected without no update on it.

We should use the same hubot command, but hubot have to warn user
the result could not come as quickly as a widget result

- [ ] Associate a query with a name and a graph type
- [ ] Implement logic to transform sumo data into something usable for graph
- [ ] Add hubot behavior
- [ ] (Depending of : Learn hubot to learn) Update or consider it


### Learn hubot to make beautiful graphs
- [ ] Missing infos
- [ ] Time range of current graph
- [ ] Missing graph
- [ ] Donuts
- [ ] Text percentage
- [ ] Filled line chart
- [ ] Upgrade logic behind how we format rawData from sumo (to dumb for now)
- [ ] Style should be configurable
- [ ] Apply Color rules on some of them when sumo will give us that details

### Learn hubot to leave rocks for find his bullshit !
- [ ] Implement a true logger to see what hubot do

### Improve filesystem management
Instead of using promise of folder, use it or flag, this could avoid problem
with promise. Other solution should be to create it at module install

- [ ] Spec it :)

### Other points
- [ ] Find a way to delete what hubot send to slack
- [ ] Make this part of hubot more customisable via brain of something (theme ?)

18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"name": "hubot",
"name": "hubot-mill",
"version": "0.0.0",
"description": "A simple helpful robot for your Company",
"description": "Our cool dear hubot which help us to make good things at Boulangerie",
"scripts": {
"dev": "./bin/hubot",
"payou-dev": "coffee ./scripts/hubot-sumologic-image-widget/main.coffee"
},
"dependencies": {
"gitio2": "^3.1.0",
"hubot": "^2.18.0",
Expand All @@ -26,5 +30,15 @@
"engines": {
"node": "0.10.x",
"npm": "2.14.7"
},
"devDependencies": {
"bluebird": "^3.3.4",
"form-data": "^0.2.0",
"highcharts": "^4.2.3",
"jsdom": "^8.1.0",
"moment": "^2.12.0",
"numeral": "^1.5.3",
"pn": "^1.0.0",
"svg2png": "^3.0.0"
}
}
47 changes: 42 additions & 5 deletions scripts/dashboard.coffee
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
Util = require "util"
_ = require "lodash"
getRandomItem = (items) => items[Math.floor(Math.random()*items.length)]

# Description:
# Example scripts for you to examine and try out.
#
Expand All @@ -10,7 +14,7 @@

class Dashboard
# Static
@SumologicToken: process.env.SUMOLOGIC_TOKEN || "ZnJvbnRlbmRAdGVhZHMudHY6RzhMZGNKdHVSTTh5IQ=="
@SumologicToken: process.env.SUMOLOGIC_TOKEN
@sumoId: '140742254'
@getSumoURLById: (id) -> 'https://service.sumologic.com/ui/dashboard.html?f=' + id + '&t=d'

Expand All @@ -36,10 +40,32 @@ class Dashboard
@robot.brain.data.dashboard.sumologic.dashboards = dashboards
done(@robot.brain.data.dashboard.sumologic.dashboards)

getSumologicDashboardData: (dashId) ->
@robot.http("https://api.sumologic.com/api/v1/dashboards/"+dashId+"/data")
.header('Authorization', 'Basic ' + Dashboard.SumologicToken)
.get() (err, res, body) =>
dashboardData = JSON.parse(body).dashboardMonitorDatas
console.log JSON.stringify(dashboardData, null, 4)

getSumologicDashboardInfo: (dashId) ->
@robot.http("https://api.sumologic.com/api/v1/dashboards/"+dashId)
.header('Authorization', 'Basic ' + Dashboard.SumologicToken)
.get() (err, res, body) =>
dashboardInfo = JSON.parse(body).dashboard
console.log JSON.stringify(dashboardInfo, null, 4)


class WidgetDisplay
constructor: (@robot, widgetKey) ->
[@dashboardId, @widgetId] = @widgetKey.split('-')

show: () ->
console.log "This is the widget #{@widgetId} from dashboard #{@dashboardId}"

fetch: () ->
console.log "I will fetch something from sumo"


Util = require "util"
_ = require "lodash"
getRandomItem = (items) => items[Math.floor(Math.random()*items.length)]

module.exports = (robot) ->

Expand All @@ -49,6 +75,7 @@ module.exports = (robot) ->
Dashboard URL manager
`!dashboard set <url>` : Switch to given URL
`!dashboard sumo <dashboardId>` : Switch to the dashboardId URL
`!dashboard show <dashboardId>-<widgetId>` : Get picture of a widget

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my mind, dashboard means the TV Dashboard :)
Maybe use a sumologic cmd ? :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is just for testing, will disappear when finished ^^

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for sure, feel free it's a dashboard TV manager, I don't have TV

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should buy one 😜
Really under construction lol, I think I will move my dev in an other place at the end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure I should buy one.
Waiting our office keys 🍭

`!dashboard [-c|--current]` : Display current URL
`!dashboard [-l|--list]` : List sumologic dashboard
`!dashboard [-d|--default]` : Set default dashboard
Expand Down Expand Up @@ -82,6 +109,15 @@ module.exports = (robot) ->
else if key in ["set"]
msg.send(dashboard.setCurrentUrl(value))

else if key in ["info"]
dashboard.getSumologicDashboardInfo(value)

else if key in ["data"]
dashboard.getSumologicDashboardData(value)

else if key in ["show"]
new WidgetDisplay(value).show()

else if key in ["sumo"]
dashboard.setCurrentUrl(Dashboard.getSumoURLById(value))
msg.send('Current url is now ' + dashboard.getCurrentUrl())
Expand Down Expand Up @@ -117,4 +153,5 @@ module.exports = (robot) ->
res.json({
url: dashboard.getCurrentUrl(),
history: dashboard.getHistory()
})
})

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"unit": "milliseconds",
"format": "ms"
},
{
"unit": "seconds",
"format": "s"
},
{
"unit": "minutes",
"format": "m"
},
{
"unit": "hours",
"format": "h"
}
]
33 changes: 33 additions & 0 deletions scripts/hubot-sumologic-image-widget/graphs/counterChart.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
_ = require 'lodash'
numeral = require 'numeral'
counterTpl = require './svgTpl/counter.tpl'
SvgHelper = require '../helpers/graphs/svgHelper'

class CounterChart
constructor: (@name, @config, @rawData) ->
@template = _.template(counterTpl)
@variables = SvgHelper.getDimensions(@config)
@svg = @template(_.merge(@variables, @format(@rawData)))

getSvg: () ->
@svg

format: (rawData) ->
fieldName = @extractFieldName(_.get(rawData, 'fields', []))
value = _.parseInt(_.get(rawData, "records.0.map.#{fieldName}"))
numberFormatted = numeral(value).format('0a').match(/([0-9]*)([a-zA-Z])?$/)
if _.isUndefined(numberFormatted[2])
numberFormatted[2] = ''
return {value : numberFormatted[1], metric: numberFormatted[2]}

extractFieldName: (fields) ->
allowedFieldTypes = ['int', 'double']
field = _.filter(fields, (item) ->
_.includes(allowedFieldTypes, item.fieldType)
)
if field.length is 1
return _.first(field).name
else
throw "Counter Graph : Don't find an eligible field, in JSON after #{JSON.stringify(fields, null, 4)}"

module.exports = CounterChart
66 changes: 66 additions & 0 deletions scripts/hubot-sumologic-image-widget/graphs/linesChart.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
_ = require 'lodash'
HighchartsHelper = require '../helpers/highcharts/highchartsHelper'

class LinesChart
constructor: (@name, @config, @rawData) ->
@svg = HighchartsHelper.getNewPlayground(@format(@rawData))

getSvg: () ->
@svg

format: (rawData) ->
@data = {
chart:
forExport: true
width: 800
height: 600
title:
text: @name
xAxis:
type: 'datetime'
min: rawData.resolvedTimeRange.startMillis
max: rawData.resolvedTimeRange.endMillis
plotOptions:
series:
marker:
enabled: false
series: @formatSeries(rawData)
}

#rawData is like raw.stackedbar.json
#Function goal format it like highchart.stackedbar.js (series)
formatSeries: (rawData) ->
flatData = _.mapValues(rawData.records, 'map')

seriesKeys = _(flatData)
.mapValues(_.keys)
.toArray()
.flattenDeep()
.uniq()
.zipObject()
.mapValues( (v, k) ->
return k
)
.value()

createSerie = (v, k) ->
return {name : k, data:[]}

xKeyFinder = _.partial(_.startsWith, _, '_')
xKey = _(seriesKeys).chain().pickBy(xKeyFinder).values().first().value()
series = _(seriesKeys).chain().omitBy(xKeyFinder).mapValues(createSerie).value()


_.forEach(flatData, (record) ->
_.forIn(record, (value, key, item) ->
if (key isnt xKey)
series[key].data.push({
x: _.parseInt(item[xKey])
y: _.parseInt(value)
})
)
)
return _.toArray(series);


module.exports = LinesChart
Loading