Skip to content

Commit

Permalink
Refactoring and packaging for Firefox add-ons website
Browse files Browse the repository at this point in the history
  • Loading branch information
fhemberger committed Jan 28, 2020
1 parent 07d6c51 commit 1417042
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 121 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/**
chrome-webstore-release.zip
firefox-webstore-release.zip
10 changes: 5 additions & 5 deletions extension/html/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
width: 350px;
outline: none;
font-size: 100%;
margin-bottom: 1rem;
padding: 0;
margin: 0.5em 0.5em 1rem;
}

h1 {
Expand All @@ -26,20 +27,19 @@

textarea {
width: 100%;
height: 100%;
height: 70%;
font-size: inherit;
}

footer {
p {
font-size: 85%;
margin: 0.5rem 0;
}
</style>
</head>
<body>
<h1>Paths treated as Prometheus endpoints</h1>
<textarea id="paths-to-handle"></textarea>
<footer>Separate paths by new line. Regular Expressions are supported, e.g.: ^/prometheus</footer>
<p>Separate paths by new line. Regular Expressions are supported, e.g.: ^/prometheus</p>
<script src="../js/popup.js"></script>
</body>
</html>
66 changes: 33 additions & 33 deletions extension/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,35 @@ const defaultPaths = [
'^/actuator/prometheus'
]

const formatPrometheusMetrics = (body) => body
.split(/\r?\n/)
.map(line => {
// line is a comment
if (/^#/.test(line)) {
return `<span class="comment">${line}</span>`
}

// line is a metric
// Named RegExp groups not supported by Firefox:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1362154
// const tmp = line.match(/^(?<metric>[\w_]+)(?:\{(?<tags>.*)\})?\x20(?<value>.+)/)
const tmp = line.match(/^([\w_]+)(?:\{(.*)\})?\x20(.+)/)

if (tmp && tmp.length > 1) {
let [ _, metric, tags, value ] = tmp
if (tags) {
tags = tags.replace(/([^,]+?)="(.*?)"/g, '<span class="label-key">$1</span>="<span class="label-value">$2</span>"')
tags = `{${tags}}`
}

return `<span class="metric">${metric}</span>${tags || ''} <span class="value">${value}</span>`
}

// line is something else, do nothing
return line
})
.join('<br>')

// Listen for requests from content pages wanting to set up a port
chrome.runtime.onConnect.addListener(port => {
if (port.name !== 'promformat') {
Expand All @@ -16,43 +45,14 @@ chrome.runtime.onConnect.addListener(port => {
}

port.onMessage.addListener(msg => {
if (msg.name !== 'SENDING TEXT') {
if (msg.name !== 'PROMETHEUS_METRICS_RAW_BODY') {
return
}

const html = msg.payload
.split(/\r?\n/)
.map(line => {
// line is a comment
if (/^#/.test(line)) {
return `<span class="comment">${line}</span>`
}

// line is a metric
// Named RegExp groups not supported by Firefox:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1362154
// const tmp = line.match(/^(?<metric>[\w_]+)(?:\{(?<tags>.*)\})?\x20(?<value>.+)/)
const tmp = line.match(/^([\w_]+)(?:\{(.*)\})?\x20(.+)/)

if (tmp && tmp.length > 1) {
let [ _, metric, tags, value ] = tmp
if (tags) {
tags = tags.replace(/([^,]+?)="(.*?)"/g, '<span class="label-key">$1</span>="<span class="label-value">$2</span>"')
tags = `{${tags}}`
}

return `<span class="metric">${metric}</span>${tags || ''} <span class="value">${value}</span>`
}

// line is something else, do nothing
return line
})
.join('<br>')

// Post the HTML string to the content script
// Post the HTML string back to the content script
port.postMessage({
name: 'FORMATTED',
payload: html
name: 'PROMETHEUS_METRICS_FORMATTED_BODY',
payload: formatPrometheusMetrics(msg.payload)
})

// Disconnect
Expand Down
131 changes: 64 additions & 67 deletions extension/js/content.js
Original file line number Diff line number Diff line change
@@ -1,94 +1,91 @@
/* global chrome */

const compress = (text) => text.replace(/\s+/g, '')
// Don't process HTTP response bodies over 30MB
const MAX_BODY_SIZE_BYTES = 30 * 1024 * 1024

const maxBodyLength = 3000000 // 3MB

const style = compress(`
pre {
display:none
}
#promformat {
font-family: monospace;
word-wrap: break-word;
white-space: pre-wrap;
}
.comment {
color: #6a737d;
display: inline-block;
}
br + .comment {
padding-top: 1em;
}
.comment + br + .comment {
padding-top: 0;
}
.metric { color: #000 }
.value { color: #ff20ed }
.label-key { color: blue }
.label-value { color: green }
`)

const port = chrome.runtime.connect({ name: 'promformat' })

// Add listener to receive response from BG when ready
port.onMessage.addListener(msg => {
switch (msg.name) {
case 'FORMATTED' :
// Insert CSS
const promformatStyle = document.createElement('style')
document.head.appendChild(promformatStyle)
promformatStyle.insertAdjacentHTML('beforeend', style)

// Insert HTML content
const promformatContent = document.createElement('div')
promformatContent.id = 'promformat'
document.body.appendChild(promformatContent)

promformatContent.innerHTML = msg.payload
break

default :
throw new Error(`Message not understood: ${msg.name}`)
}
})

function ready (data) {
const sendBodyToFormatter = (storedData) => {
// Check if it is a Prometheus plain text response
// This is quite a basic assumption, as the browser cannot access the
// 'version' part of the content type to verify.
const paths = data.paths.length ? data.paths : []

if (document.contentType !== 'text/plain') {
port.disconnect()
return
}

for (var i = 0; i < paths.length; ++i) {
if (document.location.pathname.match(paths[i])) {
format()
break
}
// Check if the current page's paths matches one of our whitelist
if (!storedData.paths.some(path => document.location.pathname.match(path))) {
port.disconnect()
return
}
}

function format () {
// Check if plain text wrapped in <pre> element exists and doesn't exceed maxBodyLength
// Check if plain text wrapped in <pre> element exists and doesn't exceed
// MAX_BODY_SIZE_BYTES
const pre = document.body.querySelector('pre')
const rawBody = pre && pre.innerText

if (!rawBody || rawBody.length > maxBodyLength) {
if (!rawBody || rawBody.length > MAX_BODY_SIZE_BYTES) {
port.disconnect()
return
}

// Post the contents of the PRE
port.postMessage({
name: 'SENDING TEXT',
name: 'PROMETHEUS_METRICS_RAW_BODY',
payload: rawBody
})
}

const renderFormattedHTML = (html) => {
const style = `
pre {
display:none
}
#promformat {
font-family: monospace;
word-wrap: break-word;
white-space: pre-wrap;
}
.comment {
color: #6a737d;
display: inline-block;
}
br + .comment {
padding-top: 1em;
}
.comment + br + .comment {
padding-top: 0;
}
.metric { color: #000 }
.value { color: #ff20ed }
.label-key { color: blue }
.label-value { color: green }
`

// Insert CSS
const promformatStyle = document.createElement('style')
document.head.appendChild(promformatStyle)
promformatStyle.insertAdjacentHTML('beforeend', style)

// Insert HTML content
const promformatContent = document.createElement('div')
promformatContent.id = 'promformat'
document.body.appendChild(promformatContent)

promformatContent.innerHTML = html
}

const port = chrome.runtime.connect({ name: 'promformat' })

// Add listener to receive response from background when ready
port.onMessage.addListener(msg => {
if (msg.name !== 'PROMETHEUS_METRICS_FORMATTED_BODY') {
return
}

renderFormattedHTML(msg.payload)
})

document.addEventListener('DOMContentLoaded', () => {
chrome.storage.sync.get({ paths: [] }, ready)
chrome.storage.sync.get({ paths: [] }, sendBodyToFormatter)
})
12 changes: 3 additions & 9 deletions extension/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Prometheus Formatter",
"version": "1.2.4",
"version": "2.0.0",
"manifest_version": 2,
"description": "Makes plain Prometheus metrics easier to read.",
"homepage_url": "https://github.com/fhemberger/prometheus-formatter",
Expand Down Expand Up @@ -37,11 +37,5 @@
"permissions": [
"activeTab",
"storage"
],
"browser_specific_settings": {
"gecko": {
"id": "[email protected]",
"strict_min_version": "72.0"
}
}
}
]
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Browser extension which makes plain Prometheus metrics easier to read.",
"scripts": {
"test": "standard js/*.js --fix",
"release": "node update-manifest.js && cd extension && zip -r ../webstore-release.zip html icons js LICENSE.txt manifest.json -x icons/.DS_Store js/.DS_Store .DS_Store"
"release": "./package/package.sh"
},
"author": "Frederic Hemberger",
"license": "MIT",
Expand Down
20 changes: 20 additions & 0 deletions package/package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -uo pipefail

readonly DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"

function package_zip () {
cd "$1" && zip -r "$2" html icons js LICENSE.txt manifest.json -x icons/.DS_Store js/.DS_Store .DS_Store
}

# Update manifest version number from package.json
"$DIR/update-manifest-version.js"

# Package Chrome webstore release
package_zip "$DIR/../extension" "$DIR/../chrome-webstore-release.zip"

# Package Firefox webstore release
cp -r "$DIR/../extension" "$DIR/../extension-firefox"
"$DIR/update-manifest-firefox.js"
package_zip "$DIR/../extension-firefox" "$DIR/../firefox-webstore-release.zip"
rm -r "$DIR/../extension-firefox"
16 changes: 16 additions & 0 deletions package/update-manifest-firefox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env node

const fs = require('fs')
const path = require('path')
const manifestPath = path.join(__dirname, '..', 'extension-firefox', 'manifest.json')
const manifest = require(manifestPath)

// Firefox needs additional keys in the manifest.json which are not allowed in Chrome
manifest.browser_specific_settings = {
gecko: {
id: '[email protected]',
strict_min_version: '72.0'
}
}

fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
10 changes: 10 additions & 0 deletions package/update-manifest-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env node

const fs = require('fs')
const path = require('path')
const pkg = require(path.join(__dirname, '..', 'package.json'))
const manifestPath = path.join(__dirname, '..', 'extension', 'manifest.json')
const manifest = require(manifestPath)

manifest.version = pkg.version
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
6 changes: 0 additions & 6 deletions update-manifest.js

This file was deleted.

0 comments on commit 1417042

Please sign in to comment.