From eca1b2823453ba353bf60eb6fa4d65747a9be415 Mon Sep 17 00:00:00 2001 From: Davide Piccinini Date: Sun, 15 Sep 2024 17:34:02 +0200 Subject: [PATCH] [Snapcraft] license (#10520) * Add snapcraft license. Update snapcraft version to inherit from snapcraft-base class. Add snapcraft base url in configurations. * Fix spec tests after making method static * remove snapcraft configurations, move into base class * Update services/snapcraft/snapcraft-base.js --------- Co-authored-by: chris48s --- services/snapcraft/snapcraft-base.js | 23 +++++++++++ .../snapcraft/snapcraft-licence.service.js | 41 +++++++++++++++++++ services/snapcraft/snapcraft-licence.spec.js | 14 +++++++ .../snapcraft/snapcraft-licence.tester.js | 14 +++++++ .../snapcraft/snapcraft-version.service.js | 36 ++++++++-------- services/snapcraft/snapcraft-version.spec.js | 8 ++-- 6 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 services/snapcraft/snapcraft-base.js create mode 100644 services/snapcraft/snapcraft-licence.service.js create mode 100644 services/snapcraft/snapcraft-licence.spec.js create mode 100644 services/snapcraft/snapcraft-licence.tester.js diff --git a/services/snapcraft/snapcraft-base.js b/services/snapcraft/snapcraft-base.js new file mode 100644 index 0000000000000..ba1a72473095e --- /dev/null +++ b/services/snapcraft/snapcraft-base.js @@ -0,0 +1,23 @@ +import { BaseJsonService, pathParam } from '../index.js' + +export const snapcraftPackageParam = pathParam({ + name: 'package', + example: 'redis', +}) + +export const snapcraftBaseParams = [snapcraftPackageParam] + +const snapcraftBaseUrl = 'https://api.snapcraft.io/v2/snaps/info' + +export default class SnapcraftBase extends BaseJsonService { + async fetch(schema, { packageName }) { + return await this._requestJson({ + schema, + url: `${snapcraftBaseUrl}/${packageName}`, + options: { + headers: { 'Snap-Device-Series': 16 }, + }, + httpErrors: { 404: 'package not found' }, + }) + } +} diff --git a/services/snapcraft/snapcraft-licence.service.js b/services/snapcraft/snapcraft-licence.service.js new file mode 100644 index 0000000000000..3be42f1a92260 --- /dev/null +++ b/services/snapcraft/snapcraft-licence.service.js @@ -0,0 +1,41 @@ +import Joi from 'joi' +import { renderLicenseBadge } from '../licenses.js' +import SnapcraftBase, { snapcraftPackageParam } from './snapcraft-base.js' + +const licenseSchema = Joi.object({ + snap: Joi.object({ + license: Joi.string().required(), + }).required(), +}).required() + +export default class SnapcraftLicense extends SnapcraftBase { + static category = 'license' + + static route = { + base: 'snapcraft/l', + pattern: ':package', + } + + static openApi = { + '/snapcraft/l/{package}': { + get: { + summary: 'Snapcraft License', + parameters: [snapcraftPackageParam], + }, + }, + } + + static render({ license }) { + return renderLicenseBadge({ license }) + } + + static transform(apiData) { + return apiData.snap.license + } + + async handle({ package: packageName }) { + const parsedData = await this.fetch(licenseSchema, { packageName }) + const license = this.constructor.transform(parsedData) + return this.constructor.render({ license }) + } +} diff --git a/services/snapcraft/snapcraft-licence.spec.js b/services/snapcraft/snapcraft-licence.spec.js new file mode 100644 index 0000000000000..91f1247f34c8c --- /dev/null +++ b/services/snapcraft/snapcraft-licence.spec.js @@ -0,0 +1,14 @@ +import { test, given } from 'sazerac' +import SnapcraftLicense from './snapcraft-licence.service.js' + +describe('SnapcraftLicense', function () { + const testApiData = { + snap: { + license: 'BSD-3-Clause', + }, + } + + test(SnapcraftLicense.transform, () => { + given(testApiData).expect('BSD-3-Clause') + }) +}) diff --git a/services/snapcraft/snapcraft-licence.tester.js b/services/snapcraft/snapcraft-licence.tester.js new file mode 100644 index 0000000000000..a2217ba5955ab --- /dev/null +++ b/services/snapcraft/snapcraft-licence.tester.js @@ -0,0 +1,14 @@ +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('Snapcraft license (valid)').get('/redis.json').expectBadge({ + label: 'license', + message: 'BSD-3-Clause', +}) + +t.create('Snapcraft license(invalid)') + .get('/this_package_doesnt_exist.json') + .expectBadge({ + label: 'license', + message: 'package not found', + }) diff --git a/services/snapcraft/snapcraft-version.service.js b/services/snapcraft/snapcraft-version.service.js index 1d18f60449bcb..da97caf98469e 100644 --- a/services/snapcraft/snapcraft-version.service.js +++ b/services/snapcraft/snapcraft-version.service.js @@ -1,6 +1,7 @@ import Joi from 'joi' -import { BaseJsonService, NotFound, pathParams, queryParam } from '../index.js' +import { NotFound, pathParams, queryParam } from '../index.js' import { renderVersionBadge } from '../version.js' +import SnapcraftBase, { snapcraftPackageParam } from './snapcraft-base.js' const queryParamSchema = Joi.object({ arch: Joi.string(), @@ -22,7 +23,7 @@ const versionSchema = Joi.object({ .required(), }).required() -export default class SnapcraftVersion extends BaseJsonService { +export default class SnapcraftVersion extends SnapcraftBase { static category = 'version' static route = { @@ -36,10 +37,10 @@ export default class SnapcraftVersion extends BaseJsonService { static openApi = { '/snapcraft/v/{package}/{track}/{risk}': { get: { - summary: 'Snapcraft version', + summary: 'Snapcraft Version', parameters: [ + snapcraftPackageParam, ...pathParams( - { name: 'package', example: 'chromium' }, { name: 'track', example: 'latest' }, { name: 'risk', example: 'stable' }, ), @@ -54,7 +55,11 @@ export default class SnapcraftVersion extends BaseJsonService { }, } - transform(apiData, track, risk, arch) { + static render({ version }) { + return renderVersionBadge({ version }) + } + + static transform(apiData, track, risk, arch) { const channelMap = apiData['channel-map'] let filteredChannelMap = channelMap.filter( ({ channel }) => channel.architecture === arch, @@ -79,20 +84,15 @@ export default class SnapcraftVersion extends BaseJsonService { } async handle({ package: packageName, track, risk }, { arch = 'amd64' }) { - const parsedData = await this._requestJson({ - schema: versionSchema, - options: { - headers: { 'Snap-Device-Series': 16 }, - }, - url: `https://api.snapcraft.io/v2/snaps/info/${packageName}`, - httpErrors: { - 404: 'package not found', - }, - }) + const parsedData = await this.fetch(versionSchema, { packageName }) // filter results by track, risk and arch - const { version } = this.transform(parsedData, track, risk, arch) - - return renderVersionBadge({ version }) + const { version } = this.constructor.transform( + parsedData, + track, + risk, + arch, + ) + return this.constructor.render({ version }) } } diff --git a/services/snapcraft/snapcraft-version.spec.js b/services/snapcraft/snapcraft-version.spec.js index a0ef7a31c7c4a..9944422eb75ee 100644 --- a/services/snapcraft/snapcraft-version.spec.js +++ b/services/snapcraft/snapcraft-version.spec.js @@ -34,7 +34,7 @@ describe('SnapcraftVersion', function () { ], } - test(SnapcraftVersion.prototype.transform, () => { + test(SnapcraftVersion.transform, () => { given( testApiData, exampleChannel.channel.track, @@ -66,7 +66,7 @@ describe('SnapcraftVersion', function () { it('throws NotFound error with missing arch', function () { expect(() => { - SnapcraftVersion.prototype.transform( + SnapcraftVersion.transform( testApiData, exampleChannel.channel.track, exampleChannel.channel.risk, @@ -78,7 +78,7 @@ describe('SnapcraftVersion', function () { }) it('throws NotFound error with missing track', function () { expect(() => { - SnapcraftVersion.prototype.transform( + SnapcraftVersion.transform( testApiData, 'missing', exampleChannel.channel.risk, @@ -90,7 +90,7 @@ describe('SnapcraftVersion', function () { }) it('throws NotFound error with missing risk', function () { expect(() => { - SnapcraftVersion.prototype.transform( + SnapcraftVersion.transform( testApiData, exampleChannel.channel.track, 'missing',