Skip to content

Commit

Permalink
Add [snapcraft] version badge (#9976)
Browse files Browse the repository at this point in the history
* Add snapstore version badge

Fixes #9103

* Add basic test for Snapcraft version

* Handle snapstore-version - package not found

* Add test for invalid snapcraft package

* Remove redundent SnapstoreBase class

not needed as there is no auth

* Remove logo from default

The project convention is using namedLogo by default only for social badges.
This commit removes the default logo usage as this badge is not in the social category.

* Rename snapstore to snapcraft

Keep 1 convention for the snapcraft badge, use only snapcraft and ditch snapstore.

* Fix SnapcraftVersion schema

* Use renderVersionBadge

Replace the costume render in SnapcraftVersion with renderVersionBadge

* Rename folder from snapstore to snapcraft

* Add track & risk path parameters for SnapcraftVersion

enhancing control and clarity

* Add architecture query parameter to SnapcraftVersion

Added architecture query parameter: The snapcraft-version.service.js file now accepts an optional arch query parameter to specify the desired architecture for the Snap package. This defaults to amd64 if not provided.

If an unsupported architecture is specified in the query parameter, a NotFound error is thrown with a specific message indicating that the requested architecture is not found.

The snapcraft-version.tester.js file is updated to include a new test case that verifies the behavior when using the arch query parameter and also includes a test case for handling an invalid architecture.

* move filter logic into a transform function

* Fix filter logic

The goal here was to filter by all conditions with logic and.
Before this fix the only the logic of the last filter is used.

* Add tests for SnapcraftVersion.transform
  • Loading branch information
jNullj committed Mar 10, 2024
1 parent 84e2944 commit c6be456
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 0 deletions.
98 changes: 98 additions & 0 deletions services/snapcraft/snapcraft-version.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import Joi from 'joi'
import { BaseJsonService, NotFound, pathParams, queryParam } from '../index.js'
import { renderVersionBadge } from '../version.js'

const queryParamSchema = Joi.object({
arch: Joi.string(),
})

const versionSchema = Joi.object({
'channel-map': Joi.array()
.items(
Joi.object({
channel: Joi.object({
architecture: Joi.string().required(),
risk: Joi.string().required(),
track: Joi.string().required(),
}),
version: Joi.string().required(),
}).required(),
)
.min(1)
.required(),
}).required()

export default class SnapcraftVersion extends BaseJsonService {
static category = 'version'

static route = {
base: 'snapcraft/v',
pattern: ':package/:track/:risk',
queryParamSchema,
}

static defaultBadgeData = { label: 'snapcraft' }

static openApi = {
'/snapcraft/v/{package}/{track}/{risk}': {
get: {
summary: 'Snapcraft version',
parameters: [
...pathParams(
{ name: 'package', example: 'chromium' },
{ name: 'track', example: 'latest' },
{ name: 'risk', example: 'stable' },
),
queryParam({
name: 'arch',
example: 'amd64',
description:
'Architecture, When not specified, this will default to `amd64`.',
}),
],
},
},
}

transform(apiData, track, risk, arch) {
const channelMap = apiData['channel-map']
let filteredChannelMap = channelMap.filter(
({ channel }) => channel.architecture === arch,
)
if (filteredChannelMap.length === 0) {
throw new NotFound({ prettyMessage: 'arch not found' })
}
filteredChannelMap = filteredChannelMap.filter(
({ channel }) => channel.track === track,
)
if (filteredChannelMap.length === 0) {
throw new NotFound({ prettyMessage: 'track not found' })
}
filteredChannelMap = filteredChannelMap.filter(
({ channel }) => channel.risk === risk,
)
if (filteredChannelMap.length === 0) {
throw new NotFound({ prettyMessage: 'risk not found' })
}

return filteredChannelMap[0]
}

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',
},
})

// filter results by track, risk and arch
const { version } = this.transform(parsedData, track, risk, arch)

return renderVersionBadge({ version })
}
}
103 changes: 103 additions & 0 deletions services/snapcraft/snapcraft-version.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { expect } from 'chai'
import { test, given } from 'sazerac'
import _ from 'lodash'
import { NotFound } from '../index.js'
import SnapcraftVersion from './snapcraft-version.service.js'

describe('SnapcraftVersion', function () {
const exampleChannel = {
channel: {
architecture: 'amd64',
risk: 'stable',
track: 'latest',
},
version: '1.2.3',
}
const exampleArchChange = _.merge(_.cloneDeep(exampleChannel), {
channel: { architecture: 'arm64' },
version: '2.3.4',
})
const exampleTrackChange = _.merge(_.cloneDeep(exampleChannel), {
channel: { track: 'beta' },
version: '3.4.5',
})
const exampleRiskChange = _.merge(_.cloneDeep(exampleChannel), {
channel: { risk: 'edge' },
version: '5.4.6',
})
const testApiData = {
'channel-map': [
exampleChannel,
exampleArchChange,
exampleTrackChange,
exampleRiskChange,
],
}

test(SnapcraftVersion.prototype.transform, () => {
given(
testApiData,
exampleChannel.channel.track,
exampleChannel.channel.risk,
exampleChannel.channel.architecture,
).expect(exampleChannel)
// change arch
given(
testApiData,
exampleChannel.channel.track,
exampleChannel.channel.risk,
exampleArchChange.channel.architecture,
).expect(exampleArchChange)
// change track
given(
testApiData,
exampleTrackChange.channel.track,
exampleChannel.channel.risk,
exampleChannel.channel.architecture,
).expect(exampleTrackChange)
// change risk
given(
testApiData,
exampleChannel.channel.track,
exampleRiskChange.channel.risk,
exampleChannel.channel.architecture,
).expect(exampleRiskChange)
})

it('throws NotFound error with missing arch', function () {
expect(() => {
SnapcraftVersion.prototype.transform(
testApiData,
exampleChannel.channel.track,
exampleChannel.channel.risk,
'missing',
)
})
.to.throw(NotFound)
.with.property('prettyMessage', 'arch not found')
})
it('throws NotFound error with missing track', function () {
expect(() => {
SnapcraftVersion.prototype.transform(
testApiData,
'missing',
exampleChannel.channel.risk,
exampleChannel.channel.architecture,
)
})
.to.throw(NotFound)
.with.property('prettyMessage', 'track not found')
})
it('throws NotFound error with missing risk', function () {
expect(() => {
SnapcraftVersion.prototype.transform(
testApiData,
exampleChannel.channel.track,
'missing',
exampleChannel.channel.architecture,
)
})
.to.throw(NotFound)
.with.property('prettyMessage', 'risk not found')
})
})
45 changes: 45 additions & 0 deletions services/snapcraft/snapcraft-version.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { isSemver } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()

t.create('Snapcraft Version for redis')
.get('/redis/latest/stable.json')
.expectBadge({
label: 'snapcraft',
message: isSemver,
})

t.create('Snapcraft Version for redis (query param arch=arm64)')
.get('/redis/latest/stable.json?arch=arm64')
.expectBadge({
label: 'snapcraft',
message: isSemver,
})

t.create('Snapcraft Version for redis (invalid package)')
.get('/this_package_doesnt_exist/fake/fake.json')
.expectBadge({
label: 'snapcraft',
message: 'package not found',
})

t.create('Snapcraft Version for redis (invalid track)')
.get('/redis/notfound/stable.json')
.expectBadge({
label: 'snapcraft',
message: 'track not found',
})

t.create('Snapcraft Version for redis (invalid risk)')
.get('/redis/latest/notfound.json')
.expectBadge({
label: 'snapcraft',
message: 'risk not found',
})

t.create('Snapcraft Version for redis (invalid arch)')
.get('/redis/latest/stable.json?arch=fake')
.expectBadge({
label: 'snapcraft',
message: 'arch not found',
})

0 comments on commit c6be456

Please sign in to comment.