Add tests and optimization/compression for rendered metrics

This commit is contained in:
lowlighter
2020-10-13 14:00:48 +02:00
parent 1b738d11d2
commit 39404a9acf
10 changed files with 1055 additions and 32 deletions

View File

@@ -544,16 +544,22 @@ Below is a list of useful links :
Below is a list of primary dependencies : Below is a list of primary dependencies :
* [express/express.js](https://github.com/expressjs/express) * [express/express.js](https://github.com/expressjs/express) and [expressjs/compression](https://github.com/expressjs/compression)
* To serve, compute and render a GitHub user's metrics * To serve, compute and render a GitHub user's metrics
* [nfriedly/express-rate-limit](https://github.com/nfriedly/express-rate-limit) * [nfriedly/express-rate-limit](https://github.com/nfriedly/express-rate-limit)
* To apply rate limiting on server and avoid spams and hitting GitHub API's own rate limit * To apply rate limiting on server and avoid spams and hitting GitHub API's own rate limit
* [octokit/graphql.js](https://github.com/octokit/graphql.js/) * [octokit/graphql.js](https://github.com/octokit/graphql.js/) and [octokit/rest.js](https://github.com/octokit/rest.js)
* To perform request to GitHub GraphQL API * To perform request to GitHub GraphQL API and GitHub REST API
* [ptarjan/node-cache](https://github.com/ptarjan/node-cache) * [ptarjan/node-cache](https://github.com/ptarjan/node-cache)
* To cache generated content * To cache generated content
* [renanbastos93/image-to-base64](https://github.com/renanbastos93/image-to-base64) * [renanbastos93/image-to-base64](https://github.com/renanbastos93/image-to-base64)
* To generate base64 representation of users' avatars * To generate base64 representation of users' avatars
* [svg/svgo](https://github.com/svg/svgo)
* To optimize generated SVG
* [axios/axios](https://github.com/axios/axios)
* To make HTTP/S requests
* [actions/toolkit](https://github.com/actions/toolkit/tree/master) and [vercel/ncc](https://github.com/vercel/ncc)
* To build the GitHub Action
All icons were ripped across GitHub's site, but still remains the intellectual property of GitHub. All icons were ripped across GitHub's site, but still remains the intellectual property of GitHub.
See [GitHub Logos and Usage](https://github.com/logos) for more information. See [GitHub Logos and Usage](https://github.com/logos) for more information.

File diff suppressed because one or more lines are too long

980
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,13 @@
{ {
"name": "metrics", "name": "metrics",
"version": "1.5.0", "version": "1.6.0",
"description": "Generate an user's GitHub metrics as SVG image format to embed somewhere else", "description": "Generate an user's GitHub metrics as SVG image format to embed somewhere else",
"main": "index.mjs", "main": "index.mjs",
"scripts": { "scripts": {
"start": "node index.mjs", "start": "node index.mjs",
"build": "node utils/build.mjs", "build": "node utils/build.mjs",
"test": "echo \"Error: no test specified\" && exit 1", "test": "node tests/metrics.mjs",
"upgrade": "npm install @actions/core@latest @actions/github@latest @octokit/graphql@latest @octokit/rest@latest axios@latest express@latest express-rate-limit@latest image-to-base64@latest memory-cache@latest @vercel/ncc@latest" "upgrade": "npm install @actions/core@latest @actions/github@latest @octokit/graphql@latest @octokit/rest@latest axios@latest compression@latest express@latest express-rate-limit@latest image-to-base64@latest memory-cache@latest svgo@latest @vercel/ncc@latest libxmljs@latest"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -25,12 +25,15 @@
"@octokit/graphql": "^4.5.6", "@octokit/graphql": "^4.5.6",
"@octokit/rest": "^18.0.6", "@octokit/rest": "^18.0.6",
"axios": "^0.20.0", "axios": "^0.20.0",
"compression": "^1.7.4",
"express": "^4.17.1", "express": "^4.17.1",
"express-rate-limit": "^5.1.3", "express-rate-limit": "^5.1.3",
"image-to-base64": "^2.1.1", "image-to-base64": "^2.1.1",
"memory-cache": "^0.2.0" "memory-cache": "^0.2.0",
"svgo": "^1.3.2"
}, },
"devDependencies": { "devDependencies": {
"@vercel/ncc": "^0.24.1" "@vercel/ncc": "^0.24.1",
"libxmljs": "^0.19.7"
} }
} }

View File

@@ -7,6 +7,7 @@
import cache from "memory-cache" import cache from "memory-cache"
import ratelimit from "express-rate-limit" import ratelimit from "express-rate-limit"
import metrics from "./metrics.mjs" import metrics from "./metrics.mjs"
import compression from "compression"
//Load svg template, style and query //Load svg template, style and query
async function load() { async function load() {
@@ -28,6 +29,7 @@
//Setup server //Setup server
const app = express() const app = express()
app.use(compression())
const middlewares = [] const middlewares = []
//Rate limiter middleware //Rate limiter middleware
if (ratelimiter) { if (ratelimiter) {

View File

@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="A SVG image generator which includes activity, community and repositories metrics about your GitHub account that you can includes on your profile"> <meta name="description" content="A SVG image generator which includes activity, community and repositories metrics about your GitHub account that you can includes on your profile">
<meta name="author" content="lowlighter"> <meta name="author" content="lowlighter">
<link rel="icon" href="data:,">
</head> </head>
<body> <body>
@@ -25,6 +26,8 @@
<div class="code"> <div class="code">
![<span class="md-alt">GitHub metrics</span>](https://metrics.lecoq.io/my-github-user) ![<span class="md-alt">GitHub metrics</span>](https://metrics.lecoq.io/my-github-user)
</div> </div>
<br>
And for even more metrics, setup this <a href="https://github.com/marketplace/actions/github-metrics-as-svg-image">GitHub action</a> on your repository !
</aside> </aside>
<script> <script>
@@ -40,7 +43,7 @@
document.querySelector("#metrics .generated").style.opacity = 0 document.querySelector("#metrics .generated").style.opacity = 0
document.querySelector("aside").style.opacity = 0 document.querySelector("aside").style.opacity = 0
//Update github user //Update github user
document.querySelector(".code").innerText = `![GitHub metrics](https://metrics.lecoq.io/${user})` document.querySelector(".code").innerHTML = `![<span class="md-alt">GitHub metrics</span>](https://metrics.lecoq.io/${user})`
document.querySelector("#user-repo").href = `https://github.com/${user}/${user}` document.querySelector("#user-repo").href = `https://github.com/${user}/${user}`
document.querySelectorAll(".user").forEach(node => node.innerText = user) document.querySelectorAll(".user").forEach(node => node.innerText = user)
//Update metrics //Update metrics

View File

@@ -1,5 +1,6 @@
//Imports //Imports
import imgb64 from "image-to-base64" import imgb64 from "image-to-base64"
import SVGO from "svgo"
import Plugins from "./plugins/index.mjs" import Plugins from "./plugins/index.mjs"
//Setup //Setup
@@ -78,9 +79,19 @@
//Wait for pending promises //Wait for pending promises
await Promise.all(pending) await Promise.all(pending)
//Eval rendering and return //Eval rendering
console.debug(`metrics/metrics/${login} > computed`) console.debug(`metrics/metrics/${login} > computed`)
return eval(`\`${template}\``) const templated = eval(`\`${template}\``)
console.debug(`metrics/metrics/${login} > templated`)
//Optimize rendering
const svgo = new SVGO({plugins:[{cleanupAttrs:true}, {inlineStyles:false}]})
const {data:optimized} = await svgo.optimize(templated)
console.debug(`metrics/metrics/${login} > optimized`)
//Result
const rendered = optimized
return rendered
} }
//Internal error //Internal error
catch (error) { throw (((Array.isArray(error.errors))&&(error.errors[0].type === "NOT_FOUND")) ? new Error("user not found") : error) } catch (error) { throw (((Array.isArray(error.errors))&&(error.errors[0].type === "NOT_FOUND")) ? new Error("user not found") : error) }

26
tests/metrics.mjs Normal file
View File

@@ -0,0 +1,26 @@
//Imports
import path from "path"
import fs from "fs"
import metrics from "../src/metrics.mjs"
import octokit from "@octokit/graphql"
import OctokitRest from "@octokit/rest"
import libxmljs from "libxmljs"
//Die on unhandled rejections
process.on("unhandledRejection", error => { throw error })
//Load GitHub handlers
const token = process.argv.slice(2)[0] ?? ""
const graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
const rest = new OctokitRest.Octokit({auth:token})
//Load svg template, style and query
const [template, style, query] = await Promise.all(["template.svg", "style.css", "query.graphql"].map(async file => `${await fs.promises.readFile(path.join("src", file))}`))
//Compute render
const rendered = await metrics({login:"lowlighter", q:{}}, {template, style, query, graphql, rest, plugins:{}})
//Ensure it's a well-formed SVG image
const parsed = libxmljs.parseXml(rendered)
if (parsed.errors.length)
throw new Error(`Malformed SVG : \n${parsed.errors.join("\n")}`)