Skip to content

Commit

Permalink
Merge pull request #7 from snyk-tech-services/feat/introduce-handlebars
Browse files Browse the repository at this point in the history
feat: introduce handlebars & views
  • Loading branch information
lili2311 authored Sep 29, 2020
2 parents f942e8b + 9884f6a commit a3e533c
Show file tree
Hide file tree
Showing 29 changed files with 521 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
12
14
package-lock=false
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,23 @@
Snyk helps you find, fix and monitor for known vulnerabilities in your dependencies, both on an ad hoc basis and as part of your CI (Build) system.

## Snyk snyk-licenses-texts
Snyk Licenses Texts
Snyk Licenses Report that provides Organization level licenses used, copyrights & dependencies data (including license texts & their urls).

## Usage
Ensure `SNYK_TOKEN` is set and has access to the Organization you want to generate the report for.

### Basic CLI commands
- `help` - show help & all available commands and their options
- `json` - generate the raw JSON licenses & dependencies data
- `generate` - generates an HTML report of licenses & dependencies data

Example usage:
`snyk-licenses-report help`
`snyk-licenses-report json --orgPublicId=<ORG_PUBLIC_ID>`
`snyk-licenses-report generate --orgPublicId=<ORG_PUBLIC_ID>`


## Development setup
- `npm i`
- `npm run test`
- `DEBUG=snyk-license* node dist/index.js generate --orgPublicId=<ORG_PUBLIC_ID>`
24 changes: 17 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
"test:unit": "jest",
"test:coverage": "npm run test:unit -- --coverage",
"test:watch": "tsc-watch --onSuccess 'npm run test:unit'",
"build": "tsc",
"build-watch": "tsc -w",
"build": "tsc && npm run copy:templates",
"build-watch": "tsc -w && npm run copy:templates",
"prepare": "npm run build",
"snyk-test": "snyk test",
"pkg-binaries": "npx pkg . -t node12-linux-x64,node12-macos-x64,node12-win-x64 --out-path ./dist/binaries"
"pkg-binaries": "npx pkg . -t node12-linux-x64,node12-macos-x64,node12-win-x64 --out-path ./dist/binaries",
"copy:templates": "cpx 'src/**/*.hbs' ./dist"
},
"bin": {
"snyk-licenses-report": "dist/index.js"
},
"types": "./dist/index.d.ts",
"repository": {
Expand All @@ -25,11 +29,12 @@
"author": "Snyk Tech Services",
"license": "Apache-2.0",
"engines": {
"node": ">=12"
"node": ">=14"
},
"files": [
"bin",
"dist"
"dist",
"src/lib/generate-output/html/templates"
],
"homepage": "https://github.com/snyk-tech-services/snyk-licenses-texts#readme",
"dependencies": {
Expand All @@ -39,14 +44,16 @@
"debug": "4.1.1",
"handlebars": "4.7.6",
"lodash": "4.17.20",
"node-fetch": "2.6.0",
"node-fetch": "2.6.1",
"snyk-api-ts-client": "1.5.0",
"snyk-config": "^3.0.0",
"source-map-support": "^0.5.16",
"tslib": "2.0.1",
"yargs": "16.0.2"
"yargs": "16.0.2",
"yargs-command-config": "1.0.5"
},
"devDependencies": {
"cpx": "1.5.0",
"@types/jest": "26.0.13",
"@types/lodash": "4.14.161",
"@types/node": "14.6.3",
Expand All @@ -64,6 +71,9 @@
"pkg": {
"scripts": [
"dist/**/*.js"
],
"assets": [
"src/**/templates/*.hbs"
]
},
"release": {
Expand Down
82 changes: 82 additions & 0 deletions src/cmds/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as debugLib from 'debug';
import * as pathLib from 'path';
import { getApiToken } from '../lib/get-api-token';
import {
LicenseReportData,
generateLicenseData,
} from '../lib/generate-org-license-report';
import {
generateHtmlReport,
generatePdfReport,
SupportedViews,
} from '../lib/generate-output';
import { writeContentsToFile } from '../lib/write-contents-to-file';
const debug = debugLib('snyk-licenses:generate');

const outputHandlers = {
[OutputFormat.HTML]: generateHtmlReport,
// [OutputFormat.PDF]: generatePdfReport
};
const enum OutputFormat {
HTML = 'html',
// TODO: support later
// PDF = 'pdf',
}

export const desc =
'Generate org licenses & dependencies report in HTML format';
export const builder = {
orgPublicId: {
required: true,
default: undefined,
desc:
'Public id of the organization in Snyk (available on organization settings)',
},
template: {
default: undefined,
desc: 'Path to custom Handelbars.js template file (*.hbs)',
},
outputFormat: {
default: OutputFormat.HTML,
desc: 'Report format',
// TODO: add also PDF when ready
options: [OutputFormat.HTML],
},
view: {
// TODO: add also dependency based view when ready
default: SupportedViews.ORG_LICENSES,
desc:
'How should the data be represented. Defaults to a license based view.',
},
};
export const aliases = ['g'];

export async function handler(argv: {
orgPublicId: string;
outputFormat: OutputFormat;
template: string;
view: SupportedViews;
}) {
try {
const { orgPublicId, outputFormat, template, view } = argv;
debug(
'ℹ️ Options: ' +
JSON.stringify({ orgPublicId, outputFormat, template, view }),
);
getApiToken();
const licenseData: LicenseReportData = await generateLicenseData(
orgPublicId,
);
const generateReportFunc = outputHandlers[outputFormat];
const res = await generateReportFunc(licenseData, template, view);
if (res) {
const outputFileName = `${orgPublicId}-${view}.html`;
const outputFile = pathLib.resolve(__dirname, outputFileName);
debug(`ℹ️ Saving generated report to ${outputFile}`);
writeContentsToFile(res, outputFile);
console.log('License report saved at ' + outputFile);
}
} catch (e) {
console.error(e);
}
}
22 changes: 19 additions & 3 deletions src/cmds/json.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import * as debugLib from 'debug';

import { getApiToken } from '../lib/get-api-token';
import { generateOrgLicensesReport } from '../lib/generate-org-license-report';
import { generateLicenseData } from '../lib/generate-org-license-report';
import { SupportedViews } from '../lib/generate-output';

const debug = debugLib('snyk-licenses:json');

export const command = 'json';
export const desc = 'Generate org licenses & dependencies data in JSON format';
Expand All @@ -8,16 +13,27 @@ export const builder = {
required: true,
default: undefined,
},
view: {
// TODO: add also dependency based view when ready
default: SupportedViews.ORG_LICENSES,
desc:
'How should the data be represented. Defaults to a license based view.',
},
};
export const aliases = ['j'];

export async function handler(argv: { orgPublicId: string }) {
export async function handler(argv: {
orgPublicId: string;
view: SupportedViews;
}) {
try {
const { orgPublicId, view } = argv;
debug('ℹ️ Options: ' + JSON.stringify({ orgPublicId, view }));
// check SNYK_TOKEN is set as the sdk uses it
getApiToken();
// TODO: define and pass options to help filter the response
// based on filters available in API
const data = await generateOrgLicensesReport(argv.orgPublicId, {});
const data = await generateLicenseData(orgPublicId, {});
console.log(JSON.stringify(data));
} catch (e) {
console.error(e);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api/org/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as snykApiSdk from 'snyk-api-ts-client';
import { getApiToken } from '../../get-api-token';
import { SortBy, Order } from './types';

const debug = debugLib('getDependenciesDataForOrg');
const debug = debugLib('snyk-licenses:getDependenciesDataForOrg');

interface GetDependenciesDataOptions {
sortBy: SortBy;
Expand Down Expand Up @@ -32,7 +32,7 @@ export async function getDependenciesDataForOrg(
);
return dependenciesData;
} catch (e) {
debug('Failed to fetch dependencies' + e);
debug('Failed to fetch dependencies' + e);
throw e;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api/org/licenses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as snykApiSdk from 'snyk-api-ts-client';
import { getApiToken } from '../../get-api-token';
import { SortBy, Order } from './types';

const debug = debugLib('getLicenseDataForOrg');
const debug = debugLib('snyk-licenses:getLicenseDataForOrg');

interface GetLicenseDataOptions {
sortBy: SortBy;
Expand All @@ -27,7 +27,7 @@ export async function getLicenseDataForOrg(
const licenseData = await getAllLicensesData(snykApiClient, body, sortBy, order);
return licenseData;
} catch (e) {
debug('Failed to fetch licenses' + e);
debug('Failed to fetch licenses' + e);
throw e;
}
}
Expand Down
37 changes: 13 additions & 24 deletions src/lib/generate-org-license-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,30 @@ import { getLicenseDataForOrg, getDependenciesDataForOrg } from './api/org';
import { fetchSpdxLicenseTextAndUrl, fetchNonSpdxLicenseTextAndUrl } from './license-text';
import { LicenseReportDataEntry, EnrichedDependency, Dependency } from './types';

const debug = debugLib('generateOrgLicensesReport');
const debug = debugLib('snyk-licenses:generateOrgLicensesReport');

interface LicenseReportData {
export interface LicenseReportData {
[licenseID: string]: LicenseReportDataEntry;
}

export async function generateOrgLicensesReport(
orgPublicId: string,
options,
): Promise<string> {
try {
const licenseData: LicenseReportData = await generateLicenseData(
orgPublicId,
options,
);
const report = licenseData;
return report as any;
} catch (e) {
debug('Failed to generate report data', e);
throw e;
}
}

export async function generateLicenseData(
orgPublicId: string,
options,
options?,
): Promise<LicenseReportData> {
debug(`ℹ️ Generating license data for Org:${orgPublicId}`);

try {
const licenseData = await getLicenseDataForOrg(orgPublicId, options);
debug(`✅ Got license API data for Org:${orgPublicId}`);
const dependenciesDataRaw = await getDependenciesDataForOrg(
orgPublicId,
options,
);
debug(`✅ Got dependencies API data for Org:${orgPublicId}`);
const licenseReportData: LicenseReportData = {};
const dependenciesData = _.groupBy(dependenciesDataRaw.results, 'id');
// TODO: what if 0?
debug(`Processing ${licenseData.total} licenses`);
debug(`Processing ${licenseData.total} licenses`);

const dependenciesAll = [];
for (const license of licenseData.results) {
Expand All @@ -68,9 +55,11 @@ export async function generateLicenseData(
licenseUrl: licenseData?.licenseUrl,
};
}
debug(`✅ Done processing ${licenseData.total} licenses`);

return licenseReportData;
} catch (e) {
debug('Failed to generate report data', e);
debug('Failed to generate report data', e);
throw e;
}
}
Expand Down Expand Up @@ -102,12 +91,12 @@ async function getLicenseTextAndUrl(
try {
return await fetchSpdxLicenseTextAndUrl(id);
} catch (e) {
debug('Failed to get license data as SPDX, trying non-SPDX');
debug(`❌ Failed to get license data for as SPDX, trying non-SPDX: ${id}`);
}
try {
return await fetchNonSpdxLicenseTextAndUrl(id);
} catch (e) {
debug('Failed to get license data as non-SPDX');
debug(`❌ Failed to get license data as non-SPDX: ${id}`);
}

return undefined;
Expand Down
Loading

0 comments on commit a3e533c

Please sign in to comment.