Commit 5880a933 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '37271-remove-unused-utils-ee' into 'master'

Remove unused utility/support code in License Management

See merge request gitlab-org/gitlab!24200
parents c5103527 5a45f0ed
......@@ -4,10 +4,6 @@ export const LICENSE_APPROVAL_STATUS = {
BLACKLISTED: 'blacklisted',
};
export const VERSION_1_0 = '1.0';
export const VERSION_1_1 = '1.1';
export const VERSION_2_0 = '2.0';
export const KNOWN_LICENSES = [
'AGPL-1.0',
'AGPL-3.0',
......
import V2Report from './v2_report';
const DEFAULT_VERSION = '1';
export default class ReportMapper {
constructor() {
this.mappers = {
'1': report => report,
'2': report => new V2Report(report).toV1Schema(),
};
}
mapFrom(reportArtifact) {
const majorVersion = ReportMapper.majorVersionFor(reportArtifact);
return this.mapperFor(majorVersion)(reportArtifact);
}
mapperFor(majorVersion) {
return this.mappers[majorVersion];
}
static majorVersionFor(report) {
if (report && report.version) {
const [majorVersion] = report.version.split('.');
return majorVersion;
}
return DEFAULT_VERSION;
}
}
import { LICENSE_APPROVAL_STATUS } from 'ee/vue_shared/license_management/constants';
import ReportMapper from 'ee/vue_shared/license_management/report_mapper';
import { n__, sprintf } from '~/locale';
import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
const toLowerCase = name => name.toLowerCase();
/**
*
* Converts the snake case in license objects to camel case
......@@ -20,28 +18,6 @@ export const normalizeLicense = license => {
};
};
/**
*
* Comparator function for sorting licenses by name
*
* @param a {Object} License Object a
* @param b {Object} License Object b
* @returns {number}
*
* @example
*
* arrayOfLicenses.sort(byLicenseNameComparator)
*
*/
export const byLicenseNameComparator = (a, b) => {
const x = toLowerCase(a.name || '');
const y = toLowerCase(b.name || '');
if (x === y) {
return 0;
}
return x > y ? 1 : -1;
};
export const getIssueStatusFromLicenseStatus = approvalStatus => {
if (approvalStatus === LICENSE_APPROVAL_STATUS.APPROVED) {
return STATUS_SUCCESS;
......@@ -51,81 +27,6 @@ export const getIssueStatusFromLicenseStatus = approvalStatus => {
return STATUS_NEUTRAL;
};
const caseInsensitiveMatch = (name, otherName) => toLowerCase(name) === toLowerCase(otherName);
const getLicenseStatusByName = (managedLicenses = [], licenseName) =>
managedLicenses.find(license => caseInsensitiveMatch(license.name, licenseName)) || {};
const getDependenciesByLicenseName = (dependencies = [], licenseName) =>
dependencies.filter(dependencyItem => {
const licenses = dependencyItem.licenses || [dependencyItem.license];
return licenses.find(license => caseInsensitiveMatch(license.name, licenseName));
});
/**
*
* Prepares a license report of the format:
*
* [
* {
* name: 'MIT',
* count: 1,
* url: 'https://spdx.org/MIT',
* packages: [{name: 'vue'}],
* approvalStatus: 'approved',
* id: 4,
* }
* ]
*
* @param headMetrics {Object}
* License scanning report on head. Contains all found licenses and dependencies.
* @param baseMetrics {Object}
* License scanning report on base. Contains all found licenses and dependencies.
* @param managedLicenses {Array} List of licenses currently managed. (Approval Status)
* @returns {Array}
*/
export const parseLicenseReportMetrics = (headMetrics, baseMetrics, managedLicenses) => {
if (!headMetrics && !baseMetrics) {
return [];
}
const reportMapper = new ReportMapper();
const headReport = reportMapper.mapFrom(headMetrics);
const baseReport = reportMapper.mapFrom(baseMetrics);
const headLicenses = headReport.licenses || [];
const headDependencies = headReport.dependencies || [];
const baseLicenses = baseReport.licenses || [];
const managedLicenseList = managedLicenses || [];
if (!headLicenses.length && !headDependencies.length) return [];
const knownLicenses = baseLicenses.map(license => toLowerCase(license.name));
const identityMap = license => knownLicenses.includes(toLowerCase(license.name));
const mapper = license => {
const { name, count } = license;
const { id, approvalStatus } = getLicenseStatusByName(managedLicenseList, name);
const dependencies = getDependenciesByLicenseName(headDependencies, name);
const url =
license.url ||
(dependencies && dependencies[0] && dependencies[0].license && dependencies[0].license.url) ||
'';
return {
name,
count,
url,
packages: dependencies.map(dependencyItem => dependencyItem.dependency),
status: getIssueStatusFromLicenseStatus(approvalStatus),
approvalStatus,
id,
};
};
return headLicenses
.filter(license => !identityMap(license))
.map(mapper)
.sort(byLicenseNameComparator);
};
export const getPackagesString = (packages, truncate, maxPackages) => {
const translatedMessage = n__(
'ciReport|Used by %{packagesString}',
......
import { byLicenseNameComparator } from './store/utils';
import { VERSION_1_1 } from './constants';
export default class V2Report {
constructor(report) {
this.report = report;
this.licenseMap = V2Report.createLicenseMap(report.licenses);
this.licenses = report.licenses.sort(byLicenseNameComparator).map(V2Report.mapFromLicense);
}
toV1Schema() {
return {
version: VERSION_1_1,
licenses: this.licenses,
dependencies: this.report.dependencies.map(v2Dependency =>
this.mapFromDependency(v2Dependency),
),
};
}
combine(licenses, visitor) {
const reducer = (memo, licenseId) => {
const license = this.licenseMap[licenseId];
visitor(license);
if (memo) return { name: `${memo.name}, ${license.name}`, url: '' };
return { name: license.name, url: license.url };
};
return licenses.reduce(reducer, null);
}
incrementCountFor(licenseName) {
const matchingLicense = this.licenses.find(license => license.name === licenseName);
if (matchingLicense) matchingLicense.count += 1;
}
mapFromDependency({ name, description, url, licenses }) {
const convertedLicenses = [];
const combinedLicense = this.combine(licenses, license => {
this.incrementCountFor(license.name);
convertedLicenses.push(license);
});
return {
license: combinedLicense,
licenses: convertedLicenses,
dependency: { name, url, description },
};
}
static mapFromLicense({ name, url = '', count = 0 }) {
return { name, url, count };
}
static createLicenseMap(licenses) {
const identityMap = {};
licenses.forEach(item => {
identityMap[item.id] = {
name: item.name,
url: item.url,
};
});
return identityMap;
}
}
import {
LICENSE_APPROVAL_STATUS,
VERSION_1_0,
VERSION_1_1,
VERSION_2_0,
} from 'ee/vue_shared/license_management/constants';
const urlFor = ({ scheme = 'https', host = 'www.example.org', path = '/' }) =>
`${scheme}://${host}${path}`;
const licenseUrlFor = name =>
urlFor({ host: 'opensource.org', path: `/licenses/${name.split(' ')[0]}` });
const dependencyUrlFor = name => urlFor({ path: `/${name}` });
const normalizeV1License = ({ name, url = licenseUrlFor(name) }) => ({ name, url });
const V1 = {
template: () => ({ licenses: [], dependencies: [] }),
normalizeLicenseSummary: ({ name, url = licenseUrlFor(name), count = 0 }) => ({
name,
url,
count,
}),
normalizeDependency: ({
name,
url = dependencyUrlFor(name),
description = name.toUpperCase(),
license = {},
}) => ({ dependency: { name, url, description }, license: normalizeV1License(license) }),
};
const V1_1 = Object.assign(V1, {
template: () => ({ version: VERSION_1_1, licenses: [], dependencies: [] }),
normalizeDependency: ({
name,
url = dependencyUrlFor(name),
description = name.toUpperCase(),
license = {},
licenses = [normalizeV1License(license)],
}) => ({
dependency: { name, url, description },
license: normalizeV1License(license),
licenses,
}),
});
const V2 = {
template: () => ({ version: VERSION_2_0, licenses: [], dependencies: [] }),
normalizeLicenseSummary: ({ id, name, url = licenseUrlFor(id) }) => ({ id, name, url }),
normalizeDependency: ({
name,
url = dependencyUrlFor(name),
description = name.toUpperCase(),
licenses = [],
}) => ({ name, url, licenses, description }),
};
export class Builder {
static for(version) {
switch (version) {
case VERSION_1_0:
return new Builder(V1);
case VERSION_1_1:
return new Builder(V1_1);
case VERSION_2_0:
return new Builder(V2);
default:
return new Builder(V1);
}
}
static forV1(minor = '0') {
return this.for(`1.${minor}`);
}
static forV2(minor = '0') {
return this.for(`2.${minor}`);
}
constructor(version) {
this.report = version.template();
this.version = version;
}
addLicense(license) {
this.report.licenses.push(this.version.normalizeLicenseSummary(license));
return this;
}
addDependency(dependency) {
this.report.dependencies.push(this.version.normalizeDependency(dependency));
return this;
}
build(override = {}) {
return Object.assign(this.report, override);
}
}
import { LICENSE_APPROVAL_STATUS } from 'ee/vue_shared/license_management/constants';
export const approvedLicense = {
id: 5,
......@@ -104,47 +12,6 @@ export const blacklistedLicense = {
approvalStatus: LICENSE_APPROVAL_STATUS.BLACKLISTED,
};
export const licenseBaseIssues = Builder.forV1()
.addLicense({ name: 'MIT', count: 1 })
.addDependency({
name: 'bundler',
url: 'http://bundler.io',
description: "The best way to manage your application's dependencies",
license: { name: 'MIT', url: 'http://opensource.org/licenses/mit-license' },
})
.build();
export const licenseHeadIssues = Builder.forV1()
.addLicense({ name: 'New BSD', count: 3 })
.addLicense({ name: 'MIT', count: 1 })
.addDependency({
name: 'pg',
url: 'https://bitbucket.org/ged/ruby-pg',
description: 'Pg is the Ruby interface to the {PostgreSQL RDBMS}[http://www.postgresql.org/]',
license: { name: 'New BSD', url: 'http://opensource.org/licenses/BSD-3-Clause' },
})
.addDependency({
name: 'puma',
url: 'http://puma.io',
description:
'Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications',
license: { name: 'New BSD', url: 'http://opensource.org/licenses/BSD-3-Clause' },
})
.addDependency({
name: 'foo',
url: 'http://foo.io',
description:
'Foo is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications',
license: { name: 'New BSD', url: 'http://opensource.org/licenses/BSD-3-Clause' },
})
.addDependency({
name: 'execjs',
url: 'https://github.com/rails/execjs',
description: 'Run JavaScript code from Ruby',
license: { name: 'MIT', url: 'http://opensource.org/licenses/mit-license' },
})
.build();
export const licenseReport = [
{
name: 'New BSD',
......
import { Builder } from '../../license_management/mock_data';
describe('build', () => {
it('creates a v1 report', () => {
const result = Builder.forV1()
.addLicense({ name: 'MIT License' })
.addDependency({ name: 'rails', license: { name: 'MIT License' } })
.build();
expect(result).toMatchObject({
licenses: [{ name: 'MIT License', url: 'https://opensource.org/licenses/MIT', count: 0 }],
dependencies: [
{
license: { name: 'MIT License', url: 'https://opensource.org/licenses/MIT' },
dependency: { name: 'rails', description: 'RAILS', url: 'https://www.example.org/rails' },
},
],
});
});
it('creates a v1.1 report', () => {
const result = Builder.forV1('1')
.addLicense({ name: 'MIT License' })
.addDependency({ name: 'rails', license: { name: 'MIT License' } })
.build();
expect(result).toMatchObject({
version: '1.1',
licenses: [{ name: 'MIT License', url: 'https://opensource.org/licenses/MIT', count: 0 }],
dependencies: [
{
license: { name: 'MIT License', url: 'https://opensource.org/licenses/MIT' },
licenses: [{ name: 'MIT License', url: 'https://opensource.org/licenses/MIT' }],
dependency: { name: 'rails', description: 'RAILS', url: 'https://www.example.org/rails' },
},
],
});
});
it('creates a v2 report', () => {
const result = Builder.forV2()
.addLicense({ id: 'MIT', name: 'MIT License' })
.addDependency({ name: 'rails', licenses: ['MIT'] })
.build();
expect(result).toMatchObject({
version: '2.0',
licenses: [{ id: 'MIT', name: 'MIT License', url: 'https://opensource.org/licenses/MIT' }],
dependencies: [{ name: 'rails', description: 'RAILS', licenses: ['MIT'] }],
});
});
});
import ReportMapper from 'ee/vue_shared/license_management/report_mapper';
import { Builder } from '../../license_management/mock_data';
describe('mapFrom', () => {
let subject = null;
beforeEach(() => {
subject = new ReportMapper(true);
});
it('converts a v2 schema report to v1.1', () => {
const report = Builder.forV2()
.addLicense({ id: 'MIT', name: 'MIT License' })
.addLicense({ id: 'BSD', name: 'BSD License' })
.addDependency({ name: 'x', licenses: ['MIT'] })
.addDependency({ name: 'y', licenses: ['BSD'] })
.addDependency({ name: 'z', licenses: ['BSD', 'MIT'] })
.build();
const result = subject.mapFrom(report);
expect(result).toMatchObject(
Builder.forV1('1')
.addLicense({ name: 'BSD License', count: 2 })
.addLicense({ name: 'MIT License', count: 2 })
.addDependency({ name: 'x', license: { name: 'MIT License' } })
.addDependency({ name: 'y', license: { name: 'BSD License' } })
.addDependency({
name: 'z',
license: { name: 'BSD License, MIT License', url: '' },
licenses: [{ name: 'BSD License' }, { name: 'MIT License' }],
})
.build(),
);
});
it('returns a v1 schema report', () => {
const report = Builder.forV1().build();
expect(subject.mapFrom(report)).toBe(report);
});
it('returns a v1.1 schema report', () => {
const report = Builder.forV1().build({ version: '1.1' });
expect(subject.mapFrom(report)).toBe(report);
});
it('ignores undefined versions', () => {
const report = {};
expect(subject.mapFrom(report)).toBe(report);
});
it('ignores undefined reports', () => {
const report = undefined;
expect(subject.mapFrom(report)).toBe(report);
});
it('ignores null reports', () => {
const report = null;
expect(subject.mapFrom(report)).toBe(report);
});
});
import {
parseLicenseReportMetrics,
byLicenseNameComparator,
normalizeLicense,
getPackagesString,
getIssueStatusFromLicenseStatus,
convertToOldReportFormat,
} from 'ee/vue_shared/license_management/store/utils';
import { LICENSE_APPROVAL_STATUS } from 'ee/vue_shared/license_management/constants';
import {
Builder,
approvedLicense,
blacklistedLicense,
licenseHeadIssues,
licenseBaseIssues,
licenseReport,
} from 'ee_spec/license_management/mock_data';
import { licenseReport } from 'ee_spec/license_management/mock_data';
import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
describe('utils', () => {
describe('parseLicenseReportMetrics', () => {
it('should return empty result, if no parameters are given', () => {
const result = parseLicenseReportMetrics();
expect(result).toEqual(jasmine.any(Array));
expect(result.length).toEqual(0);
});
it('should return empty result, if license head report is empty', () => {
const result = parseLicenseReportMetrics({ licenses: [] }, licenseBaseIssues);
expect(result).toEqual(jasmine.any(Array));
expect(result.length).toEqual(0);
});
it('should parse the received issues', () => {
const result = parseLicenseReportMetrics(licenseHeadIssues, licenseBaseIssues);
expect(result[0].name).toBe(licenseHeadIssues.licenses[0].name);
expect(result[0].url).toBe(licenseHeadIssues.licenses[0].url);
});
it('should omit issues from base report', () => {
const knownLicenseName = licenseBaseIssues.licenses[0].name;
const result = parseLicenseReportMetrics(licenseHeadIssues, licenseBaseIssues);
expect(result.length).toBe(licenseHeadIssues.licenses.length - 1);
expect(result[0].packages.length).toBe(licenseHeadIssues.dependencies.length - 1);
result.forEach(license => {
expect(license.name).not.toBe(knownLicenseName);
});
});
it('should enrich the report with information from managed licenses report', () => {
const result = parseLicenseReportMetrics(licenseHeadIssues, {}, [
approvedLicense,
blacklistedLicense,
]);
expect(result.length).toBe(2);
expect(result[0].approvalStatus).toBe(approvedLicense.approvalStatus);
expect(result[0].id).toBe(approvedLicense.id);
expect(result[1].approvalStatus).toBe(blacklistedLicense.approvalStatus);
expect(result[1].id).toBe(blacklistedLicense.id);
});
it('compares a v2 report with a v2 report', () => {
const policies = [{ id: 100, name: 'BSD License', approvalStatus: 'blacklisted' }];
const baseReport = Builder.forV2()
.addLicense({ id: 'MIT', name: 'MIT License' })
.addDependency({ name: 'x', licenses: ['MIT'] })
.build();
const headReport = Builder.forV2()
.addLicense({ id: 'MIT', name: 'MIT License' })
.addLicense({ id: 'BSD', name: 'BSD License' })
.addDependency({ name: 'x', licenses: ['MIT'] })
.addDependency({ name: 'y', licenses: ['BSD'] })
.addDependency({ name: 'z', licenses: ['BSD', 'MIT'] })
.build();
const result = parseLicenseReportMetrics(headReport, baseReport, policies);
expect(result.length).toBe(1);
expect(result[0]).toEqual(
jasmine.objectContaining({
id: 100,
approvalStatus: 'blacklisted',
count: 2,
status: 'failed',
name: 'BSD License',
url: 'https://opensource.org/licenses/BSD',
packages: [
{ name: 'y', url: 'https://www.example.org/y', description: 'Y' },
{ name: 'z', url: 'https://www.example.org/z', description: 'Z' },
],
}),
);
});
it('compares a v1 report with a v2 report', () => {
const policies = [{ id: 101, name: 'BSD License', approvalStatus: 'blacklisted' }];
const baseReport = Builder.forV1()
.addLicense({ name: 'MIT License' })
.addDependency({
name: 'x',
license: { name: 'MIT License', url: 'https://opensource.org/licenses/MIT' },
})
.build();
const headReport = Builder.forV2()
.addLicense({ id: 'MIT', name: 'MIT License' })
.addLicense({ id: 'BSD', name: 'BSD License' })
.addLicense({ id: 'MPL-1.1', name: 'Mozilla Public License 1.1' })
.addDependency({ name: 'x', licenses: ['MIT'] })
.addDependency({ name: 'y', licenses: ['BSD'] })
.addDependency({ name: 'z', licenses: ['BSD', 'MIT', 'MPL-1.1'] })
.build();
const result = parseLicenseReportMetrics(headReport, baseReport, policies);
expect(result.length).toBe(2);
expect(result[0]).toEqual(
jasmine.objectContaining({
id: 101,
approvalStatus: 'blacklisted',
count: 2,
status: 'failed',
name: 'BSD License',
url: 'https://opensource.org/licenses/BSD',
packages: [
{ name: 'y', url: 'https://www.example.org/y', description: 'Y' },
{ name: 'z', url: 'https://www.example.org/z', description: 'Z' },
],
}),
);
expect(result[1]).toEqual(
jasmine.objectContaining({
id: undefined,
approvalStatus: undefined,
count: 1,
status: 'neutral',
name: 'Mozilla Public License 1.1',
url: 'https://opensource.org/licenses/MPL-1.1',
packages: [{ name: 'z', url: 'https://www.example.org/z', description: 'Z' }],
}),
);
});
it('matches using a case insensitive match on license name', () => {
const headReport = { licenses: [{ count: 1, name: 'BSD' }], dependencies: [] };
const baseReport = { licenses: [{ count: 1, name: 'bsd' }], dependencies: [] };
const result = parseLicenseReportMetrics(headReport, baseReport, []);
expect(result.length).toBe(0);
});
it('applies the correct approval status', () => {
const policies = [{ id: 1, name: 'LGPL', approvalStatus: 'blacklisted' }];
const dependency = {
license: { name: 'lgpl', url: 'http://example.org' },
dependency: { name: 'geoip' },
};
const headReport = {
licenses: [{ count: 1, name: 'BSD' }, { count: 1, name: 'lgpl' }],
dependencies: [dependency],
};
const baseReport = { licenses: [{ count: 1, name: 'bsd' }], dependencies: [] };
const result = parseLicenseReportMetrics(headReport, baseReport, policies);
expect(result.length).toBe(1);
expect(result[0]).toEqual(
jasmine.objectContaining({
approvalStatus: 'blacklisted',
count: 1,
status: 'failed',
name: 'lgpl',
packages: [{ name: 'geoip' }],
}),
);
});
});
describe('byLicenseNameComparator', () => {
it('should Array sorted by of licenses by name', () => {
const licenses = [
{ name: 'MIT' },
{ name: 'New BSD' },
{ name: 'BSD-3-Clause' },
{ name: null },
];
const result = licenses.sort(byLicenseNameComparator).map(({ name }) => name);
expect(result).toEqual([null, 'BSD-3-Clause', 'MIT', 'New BSD']);
});
});
describe('normalizeLicense', () => {
it('should convert `approval_status` to `approvalStatus`', () => {
const src = { name: 'Foo', approval_status: 'approved', id: 3 };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment