Skip to content

Commit

Permalink
feat: report API (#333)
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreDemailly authored Mar 10, 2024
1 parent ac807b2 commit 9478923
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 32 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,15 @@ The theme can be either `dark` or `light`. Themes are editable in _public/css/th

> All D3 scale-chromatic for charts can be found [here](https://github.com/d3/d3-scale-chromatic/blob/master/README.md).
## API

> [!IMPORTANT]
> The API is ESM only
### `report(reportOptions, scannerPayload): Promise<Buffer>`

Returns a PDF Buffer based on the givens report options and scanner payload.

## Contributors ✨

<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
"bin": {
"nreport": "./bin/index.js"
},
"exports": {
".": {
"import": "./src/api/index.js"
}
},
"scripts": {
"start": "node -r dotenv/config index.js",
"prepublishOnly": "pkg-ok",
Expand Down
35 changes: 28 additions & 7 deletions src/analysis/extractScannerData.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable max-depth */
// Import Node.js Dependencies
import fs from "fs/promises";
import fs from "node:fs";

// Import Third-party Dependencies
import { formatBytes } from "@nodesecure/utils";
Expand All @@ -14,8 +14,17 @@ import { getScoreColor } from "../utils.js";
// CONSTANTS
const kWantedFlags = Flags.getFlags();

export async function buildStatsFromNsecurePayloads(payloadFiles = []) {
const config = localStorage.getConfig().report;
/**
*
* @param {string[] | NodeSecure.Payload | NodeSecure.Payload[]} payloadFiles
* @param {object} options
* @param {boolean} options.isJson
* @returns
*/
export async function buildStatsFromNsecurePayloads(payloadFiles = [], options = Object.create(null)) {
const { isJson = false, reportOptions } = options;

const config = reportOptions ?? localStorage.getConfig().report;
const stats = {
size: {
all: 0, internal: 0, external: 0
Expand All @@ -38,11 +47,23 @@ export async function buildStatsFromNsecurePayloads(payloadFiles = []) {
scorecards: {}
};

for (const file of payloadFiles) {
const buf = await fs.readFile(file);
/**
* @param {string | NodeSecure.Payload} fileOrJson
* @returns {NodeSecure.Payload}
*/
function getJSONPayload(fileOrJson) {
if (isJson) {
return fileOrJson;
}

const buf = fs.readFileSync(fileOrJson);

return JSON.parse(buf.toString());
}

/** @type {NodeSecure.Payload} */
const nsecurePayload = JSON.parse(buf.toString());
const payloads = Array.isArray(payloadFiles) ? payloadFiles : [payloadFiles];
for (const fileOrJson of payloads) {
const nsecurePayload = getJSONPayload(fileOrJson);

for (const [name, descriptor] of Object.entries(nsecurePayload)) {
const { versions, metadata } = descriptor;
Expand Down
18 changes: 18 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Import Node.js Dependencies
import fs from "node:fs/promises";

// Import Internal Dependencies
import { buildStatsFromNsecurePayloads } from "../analysis/extractScannerData.js";
import { HTML, PDF } from "../reporting/index.js";
import * as CONSTANTS from "../constants.js";

export async function report(reportOptions, scannerPayload) {
await fs.mkdir(CONSTANTS.DIRS.REPORTS, { recursive: true });

const pkgStats = await buildStatsFromNsecurePayloads(scannerPayload, { isJson: true, reportOptions });
const fakeSpinner = Object.create(null);

const reportHTMLPath = await HTML({ pkgStats, repoStats: null, spinner: fakeSpinner }, reportOptions);

return PDF(reportHTMLPath, { title: reportOptions.title, saveOnDisk: false });
}
11 changes: 5 additions & 6 deletions src/reporting/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ const kAvailableThemes = new Set(
const kHTMLTemplate = readFileSync(path.join(CONSTANTS.DIRS.VIEWS, "template.html"), "utf8");
const kTemplateGenerator = compile(kHTMLTemplate);

export async function HTML(data) {
export async function HTML(data, reportOptions = null) {
const { pkgStats, repoStats, spinner } = data;

spinner.text = "Building view with zup";
const config = localStorage.getConfig().report;
const config = reportOptions ?? localStorage.getConfig().report;

const templatePayload = {
report_theme: kAvailableThemes.has(config.theme) ? config.theme : "dark",
Expand All @@ -46,7 +46,7 @@ export async function HTML(data) {
})
};

const charts = [...generateChartArray(pkgStats, repoStats)];
const charts = [...generateChartArray(pkgStats, repoStats, config)];
const HTMLReport = kTemplateGenerator(templatePayload)
.concat(`\n<script>\ndocument.addEventListener("DOMContentLoaded", () => {\n${charts.join("\n")}\n});\n</script>`);

Expand Down Expand Up @@ -82,7 +82,7 @@ export async function HTML(data) {
...imagesFiles.map((name) => fs.copyFile(
path.join(kImagesDir, name),
path.join(kOutDir, name)
)),
))
]);

return reportHTMLPath;
Expand All @@ -95,8 +95,7 @@ function toChart(baliseName, data, interpolateName, type = "bar") {
return kChartTemplate(baliseName, graphLabels, Object.values(data).join(","), interpolateName, type);
}

function* generateChartArray(pkgStats, repoStats) {
const config = localStorage.getConfig().report;
function* generateChartArray(pkgStats, repoStats, config) {
const displayableCharts = config.charts.filter((chart) => chart.display);

if (pkgStats !== null) {
Expand Down
10 changes: 5 additions & 5 deletions src/reporting/pdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as CONSTANTS from "../constants.js";
import * as utils from "../utils.js";

export async function PDF(reportHTMLPath, options) {
const { title } = options;
const { title, saveOnDisk = true } = options;

const browser = await puppeteer.launch();

Expand All @@ -21,16 +21,16 @@ export async function PDF(reportHTMLPath, options) {
timeout: 60_000
});

await page.pdf({
path: path.join(
const pdfBuffer = await page.pdf({
path: saveOnDisk ? path.join(
CONSTANTS.DIRS.REPORTS,
utils.cleanReportName(title, ".pdf")
),
) : undefined,
format: "A4",
printBackground: true
});

return path;
return saveOnDisk ? path : pdfBuffer;
}
finally {
await browser.close();
Expand Down
30 changes: 16 additions & 14 deletions views/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ <h3>Authors & Maintainers</h3>
</div>
</div>
</div>
[[ if (z.npm_stats.scorecards.length > 0) { ]]
[[ if (Object.keys(z.npm_stats.scorecards).length > 0) { ]]
<div class="box-stats-container">
<div class="box-stats">
<div class="box-title">
Expand Down Expand Up @@ -255,21 +255,23 @@ <h3>Authors & Maintainers</h3>
</div>
</div>
</div>
<div class="box-stats-container">
<div class="box-stats">
<div class="box-title">
<h3>Scorecards</h3>
[[ if (Object.keys(z.git_stats.scorecards).length > 0) { ]]
<div class="box-stats-container">
<div class="box-stats">
<div class="box-title">
<h3>Scorecards</h3>
</div>
<ul class="npm-packages-list">
[[ for (const [package, { score, color }] of Object.entries(z.git_stats.scorecards)) { ]]
<li class="scorecard-item">
<span class="package">[[=package]]</span>
<span class="score [[=color]]">[[=score]]</span>
</li>
[[ } ]]
</ul>
</div>
<ul class="scorecard-list">
[[ for (const [package, score] of Object.entries(z.scorecards)) { ]]
<li class="scorecard-item">
<span class="package">[[=package]]</span>
<span class="score">[[=score]]</span>
</li>
[[ } ]]
</ul>
</div>
</div>
[[ } ]]
[[ for (const { name, help } of z.charts) { ]]
<div class="box-stats-container charts">
<div class="box-stats">
Expand Down

0 comments on commit 9478923

Please sign in to comment.