diff --git a/src/cmds/generate.ts b/src/cmds/generate.ts index 8b54a54..d893d6c 100644 --- a/src/cmds/generate.ts +++ b/src/cmds/generate.ts @@ -41,7 +41,7 @@ export const builder = { choices: [OutputFormat.HTML, OutputFormat.PDF], }, view: { - // TODO: add also dependency based view when ready + choices: [SupportedViews.ORG_LICENSES, SupportedViews.PROJECT_DEPENDENCIES], default: SupportedViews.ORG_LICENSES, desc: 'How should the data be represented. Defaults to a license based view.', diff --git a/src/cmds/json.ts b/src/cmds/json.ts index 53b5de5..cb01982 100644 --- a/src/cmds/json.ts +++ b/src/cmds/json.ts @@ -12,13 +12,7 @@ export const builder = { orgPublicId: { 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']; diff --git a/src/lib/generate-org-license-report.ts b/src/lib/generate-org-license-report.ts index 0bfe51a..3ccaa61 100644 --- a/src/lib/generate-org-license-report.ts +++ b/src/lib/generate-org-license-report.ts @@ -5,7 +5,7 @@ export * from './license-text'; export * from './get-api-token'; import { getLicenseDataForOrg, getDependenciesDataForOrg } from './api/org'; import { fetchSpdxLicenseTextAndUrl, fetchNonSpdxLicenseTextAndUrl } from './license-text'; -import { LicenseReportDataEntry, EnrichedDependency, Dependency } from './types'; +import { LicenseReportDataEntry, EnrichedDependency, Dependency, DependencyData } from './types'; const debug = debugLib('snyk-licenses:generateOrgLicensesReport'); @@ -29,7 +29,11 @@ export async function generateLicenseData( debug(`✅ Got dependencies API data for Org:${orgPublicId}`); const licenseReportData: LicenseReportData = {}; const dependenciesData = _.groupBy(dependenciesDataRaw.results, 'id'); - // TODO: what if 0? + + if (!licenseData.total) { + debug(`ℹ️ Detected 0 licenses`); + return licenseReportData; + } debug(`⏳ Processing ${licenseData.total} licenses`); const dependenciesAll = []; @@ -69,9 +73,9 @@ function enrichDependencies( dependencies: Dependency[], dependenciesData, ): EnrichedDependency[] { - const enrichDependencies = []; + const enrichDependencies: EnrichedDependency [] = []; for (const dependency of dependencies) { - const dep = dependenciesData[dependency.id]; + const dep: DependencyData[] = dependenciesData[dependency.id]; if (dep && dep[0]) { enrichDependencies.push({ ...dependency, diff --git a/src/lib/generate-report/index.ts b/src/lib/generate-report/index.ts index 7f7f6af..4bf1b29 100644 --- a/src/lib/generate-report/index.ts +++ b/src/lib/generate-report/index.ts @@ -2,19 +2,18 @@ import * as Handlebars from 'handlebars'; import * as path from 'path'; import * as fs from 'fs'; import * as debugLib from 'debug'; +import * as _ from 'lodash'; import { LicenseReportData } from '../generate-org-license-report'; import { OrgData } from '../get-org-data'; -import { generateReportName } from '../generate-report-name'; -import { SupportedViews } from '../types'; +import { SupportedViews, LicenseReportDataEntry } from '../types'; const debug = debugLib('snyk-licenses:generateHtmlReport'); const DEFAULT_TEMPLATE = './templates/licenses-view.hbs'; const transformDataFunc = { [SupportedViews.ORG_LICENSES]: transformDataForLicenseView, - // TODO: support later - // [SupportedViews.PROJECT_DEPENDENCIES]: transformDataForDependencyView, + [SupportedViews.PROJECT_DEPENDENCIES]: transformDataForDependencyView, }; export async function generateHtmlReport( @@ -50,18 +49,62 @@ function transformDataForLicenseView( return { licenses: data, orgPublicId, orgData }; } -// TODO: support later -// function transformDataForDependencyView(data: LicenseReportData) { -// return data; -// } +interface ProjectsReportData { + [projectId: string]: { + projectName: string; + projectIndex: number; + licenses: { + [licenseId: string]: LicenseReportDataEntry; + }; + }; +} + +function transformDataForDependencyView( + orgPublicId: string, + data: LicenseReportData, + orgData: OrgData, +): { + projects: ProjectsReportData; + orgPublicId: string; + orgData: OrgData; + totalProjects: number; +} { + const projectData: ProjectsReportData = {}; + let totalProjects = 0; + + for (const licenseId of Object.keys(data)) { + const licenseData = data[licenseId]; + + for (const project of licenseData.projects) { + if (!projectData[project.id]) { + totalProjects += 1; + projectData[project.id] = { + projectIndex: totalProjects, + projectName: project.name, + licenses: { + [licenseId]: licenseData, + }, + }; + } else { + projectData[project.id].licenses[licenseId] = licenseData; + } + } + } + + return { + projects: projectData, + orgPublicId, + orgData, + totalProjects, + }; +} function selectTemplate(view: SupportedViews, templateOverride?): string { switch (view) { case SupportedViews.ORG_LICENSES: return templateOverride || DEFAULT_TEMPLATE; - // TODO: support later - // case SupportedViews.PROJECT_DEPENDENCIES: - // return templateOverride || '../templates/project-dependencies-view.hbs'; + case SupportedViews.PROJECT_DEPENDENCIES: + return templateOverride || './templates/project-dependencies-view.hbs'; default: return DEFAULT_TEMPLATE; } diff --git a/src/lib/generate-report/templates/licenses-view.hbs b/src/lib/generate-report/templates/licenses-view.hbs index 7fb7f3f..6f578e3 100644 --- a/src/lib/generate-report/templates/licenses-view.hbs +++ b/src/lib/generate-report/templates/licenses-view.hbs @@ -143,7 +143,7 @@ Snyk Licenses Attribution Report - Org: {{orgData.name}} + Organization: {{orgData.name}} {{#each licenses}} diff --git a/src/lib/generate-report/templates/project-dependencies-view.hbs b/src/lib/generate-report/templates/project-dependencies-view.hbs index e69de29..af5cf17 100644 --- a/src/lib/generate-report/templates/project-dependencies-view.hbs +++ b/src/lib/generate-report/templates/project-dependencies-view.hbs @@ -0,0 +1,212 @@ + + + + + + + + + Snyk Licenses Report + + + + + + + {{!-- {{> header }} --}} + + + + + + Snyk - Open Source Security + + + + + + + + + + + + + + Snyk Licenses Attribution Report + Organization: {{orgData.name}} + {{#each projects}} + + + Project ({{projectIndex}}/{{../totalProjects}}): {{projectName}} + + {{#each licenses}} + + + {{id}} + + Severity: {{severity}} + {{#if instructions}} + Legal Instructions: {{instructions}} + {{/if}} + + + + + + + + Dependencies + + + Copyrights + + + + + {{#each dependencies}} + + + {{name}}@{{version}} + + + {{#each copyright}} + + {{this}} + + {{/each}} + + + + {{/each}} + + + + + + License Text + + {{{licenseText}}} + + + + {{/each}} + + {{/each}} + + + + diff --git a/src/lib/types.ts b/src/lib/types.ts index 9b1b218..0a3a1c7 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -4,11 +4,10 @@ export interface Dependency { version: string; packageManager: string; } -//TODO: finish the type -export interface EnrichedDependency extends Dependency {} + +export type EnrichedDependency = Dependency & DependencyData; export interface LicenseReportDataEntry { - // TODO: what if it is a dual license? /** * The text of the license in HTML format */ @@ -133,6 +132,5 @@ export interface DependencyData { export const enum SupportedViews { ORG_LICENSES = 'org-licenses', - // TODO: support later - // PROJECT_DEPENDENCIES = 'project-dependencies', + PROJECT_DEPENDENCIES = 'project-dependencies', } diff --git a/test/lib/__snapshots__/generate-html-report.test.ts.snap b/test/lib/__snapshots__/generate-html-report.test.ts.snap index e3d2e06..e0f0cd8 100644 --- a/test/lib/__snapshots__/generate-html-report.test.ts.snap +++ b/test/lib/__snapshots__/generate-html-report.test.ts.snap @@ -145,7 +145,7 @@ exports[`Generate HTML report License HTML Report is generated as expected 1`] = Snyk Licenses Attribution Report - Org: org + Organization: org BSD-2-Clause @@ -716,7 +716,7 @@ exports[`Generate HTML report License HTML Report is generated as expected with Custom Template Licenses Attribution Report - Org: org + Organization: org BSD-2-Clause @@ -741,3 +741,564 @@ exports[`Generate HTML report License HTML Report is generated as expected with