mirror of
https://github.com/badges/shields.git
synced 2025-04-18 19:44:04 +03:00
[Scoop] Added scoop-license badge. (#10627)
* scoop-license service is added. * refactored scoop badges - added scoop base * refactor - description changed to base class. * refactor - description changed to base class. * Update services/scoop/scoop-license.tester.js Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com> * refactor - buckets variable is moved to base class, also updated tester with createTestService. * moved queryParamSchema to base class. --------- Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com>
This commit is contained in:
parent
28bee8681a
commit
5e40080099
81
services/scoop/scoop-base.js
Normal file
81
services/scoop/scoop-base.js
Normal file
@ -0,0 +1,81 @@
|
||||
import Joi from 'joi'
|
||||
import { ConditionalGithubAuthV3Service } from '../github/github-auth-service.js'
|
||||
import { fetchJsonFromRepo } from '../github/github-common-fetch.js'
|
||||
import { NotFound } from '../index.js'
|
||||
|
||||
const gitHubRepoRegExp =
|
||||
/https:\/\/github.com\/(?<user>.*?)\/(?<repo>.*?)(\/|$)/
|
||||
|
||||
const bucketsSchema = Joi.object()
|
||||
.pattern(/.+/, Joi.string().pattern(gitHubRepoRegExp).required())
|
||||
.required()
|
||||
|
||||
export const queryParamSchema = Joi.object({
|
||||
bucket: Joi.string(),
|
||||
})
|
||||
|
||||
export class ScoopBase extends ConditionalGithubAuthV3Service {
|
||||
// The buckets file (https://github.com/lukesampson/scoop/blob/master/buckets.json) changes very rarely.
|
||||
// Cache it for the lifetime of the current Node.js process.
|
||||
buckets = null
|
||||
|
||||
async fetch({ app, schema }, queryParams) {
|
||||
if (!this.buckets) {
|
||||
this.buckets = await fetchJsonFromRepo(this, {
|
||||
schema: bucketsSchema,
|
||||
user: 'ScoopInstaller',
|
||||
repo: 'Scoop',
|
||||
branch: 'master',
|
||||
filename: 'buckets.json',
|
||||
})
|
||||
}
|
||||
const bucket = queryParams.bucket || 'main'
|
||||
let bucketUrl = this.buckets[bucket]
|
||||
if (!bucketUrl) {
|
||||
// Parsing URL here will throw an error if the url is invalid
|
||||
try {
|
||||
const url = new URL(decodeURIComponent(bucket))
|
||||
|
||||
// Throw errors to go to jump to catch statement
|
||||
// The error messages here are purely for code readability, and will never reach the user.
|
||||
if (url.hostname !== 'github.com') {
|
||||
throw new Error('Not a GitHub URL')
|
||||
}
|
||||
const path = url.pathname.split('/').filter(value => value !== '')
|
||||
|
||||
if (path.length !== 2) {
|
||||
throw new Error('Not a valid GitHub Repo')
|
||||
}
|
||||
|
||||
const [user, repo] = path
|
||||
|
||||
// Reconstructing the url here ensures that the url will match the regex
|
||||
bucketUrl = `https://github.com/${user}/${repo}`
|
||||
} catch (e) {
|
||||
throw new NotFound({ prettyMessage: `bucket "${bucket}" not found` })
|
||||
}
|
||||
}
|
||||
const {
|
||||
groups: { user, repo },
|
||||
} = gitHubRepoRegExp.exec(bucketUrl)
|
||||
try {
|
||||
return await fetchJsonFromRepo(this, {
|
||||
schema,
|
||||
user,
|
||||
repo,
|
||||
branch: 'master',
|
||||
filename: `bucket/${app}.json`,
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof NotFound) {
|
||||
throw new NotFound({
|
||||
prettyMessage: `${app} not found in bucket "${bucket}"`,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const description =
|
||||
'[Scoop](https://scoop.sh/) is a command-line installer for Windows'
|
63
services/scoop/scoop-license.service.js
Normal file
63
services/scoop/scoop-license.service.js
Normal file
@ -0,0 +1,63 @@
|
||||
import Joi from 'joi'
|
||||
import { pathParam, queryParam } from '../index.js'
|
||||
import { renderLicenseBadge } from '../licenses.js'
|
||||
import toArray from '../../core/base-service/to-array.js'
|
||||
import { queryParamSchema, description, ScoopBase } from './scoop-base.js'
|
||||
|
||||
const scoopLicenseSchema = Joi.object({
|
||||
license: Joi.alternatives()
|
||||
.try(
|
||||
Joi.string().required(),
|
||||
Joi.object({
|
||||
identifier: Joi.string().required(),
|
||||
}),
|
||||
)
|
||||
.required(),
|
||||
}).required()
|
||||
|
||||
export default class ScoopLicense extends ScoopBase {
|
||||
static category = 'license'
|
||||
|
||||
static route = {
|
||||
base: 'scoop/l',
|
||||
pattern: ':app',
|
||||
queryParamSchema,
|
||||
}
|
||||
|
||||
static openApi = {
|
||||
'/scoop/l/{app}': {
|
||||
get: {
|
||||
summary: 'Scoop License',
|
||||
description,
|
||||
parameters: [
|
||||
pathParam({ name: 'app', example: 'ngrok' }),
|
||||
queryParam({
|
||||
name: 'bucket',
|
||||
description:
|
||||
"App's containing bucket. Can either be a name (e.g `extras`) or a URL to a GitHub Repo (e.g `https://github.com/jewlexx/personal-scoop`)",
|
||||
example: 'extras',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
static defaultBadgeData = { label: 'license' }
|
||||
|
||||
static render({ licenses }) {
|
||||
return renderLicenseBadge({ licenses })
|
||||
}
|
||||
|
||||
async handle({ app }, queryParams) {
|
||||
const { license } = await this.fetch(
|
||||
{ app, schema: scoopLicenseSchema },
|
||||
queryParams,
|
||||
)
|
||||
|
||||
const licenses = toArray(license).map(license =>
|
||||
typeof license === 'string' ? license : license.identifier,
|
||||
)
|
||||
|
||||
return this.constructor.render({ licenses })
|
||||
}
|
||||
}
|
94
services/scoop/scoop-license.tester.js
Normal file
94
services/scoop/scoop-license.tester.js
Normal file
@ -0,0 +1,94 @@
|
||||
import { createServiceTester } from '../tester.js'
|
||||
|
||||
export const t = await createServiceTester()
|
||||
|
||||
t.create('License (valid) - with nested response')
|
||||
.get('/ngrok.json')
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: 'Shareware',
|
||||
})
|
||||
|
||||
t.create('License (valid) - with string response')
|
||||
.get('/nvs.json')
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: 'MIT',
|
||||
})
|
||||
|
||||
t.create('License (invalid)').get('/not-a-real-app.json').expectBadge({
|
||||
label: 'license',
|
||||
message: 'not-a-real-app not found in bucket "main"',
|
||||
})
|
||||
|
||||
t.create('License (valid custom bucket)')
|
||||
.get('/atom.json?bucket=extras')
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: 'MIT',
|
||||
})
|
||||
|
||||
t.create('license (not found in custom bucket)')
|
||||
.get('/not-a-real-app.json?bucket=extras')
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: 'not-a-real-app not found in bucket "extras"',
|
||||
})
|
||||
|
||||
t.create('license (wrong bucket)')
|
||||
.get('/not-a-real-app.json?bucket=not-a-real-bucket')
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: 'bucket "not-a-real-bucket" not found',
|
||||
})
|
||||
|
||||
// version (bucket url)
|
||||
const validBucketUrl = encodeURIComponent(
|
||||
'https://github.com/jewlexx/personal-scoop',
|
||||
)
|
||||
|
||||
t.create('license (valid bucket url)')
|
||||
.get(`/sfsu.json?bucket=${validBucketUrl}`)
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: 'Apache-2.0',
|
||||
})
|
||||
|
||||
const validBucketUrlTrailingSlash = encodeURIComponent(
|
||||
'https://github.com/jewlexx/personal-scoop/',
|
||||
)
|
||||
|
||||
t.create('license (valid bucket url)')
|
||||
.get(`/sfsu.json?bucket=${validBucketUrlTrailingSlash}`)
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: 'Apache-2.0',
|
||||
})
|
||||
|
||||
t.create('license (not found in custom bucket)')
|
||||
.get(`/not-a-real-app.json?bucket=${validBucketUrl}`)
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: `not-a-real-app not found in bucket "${decodeURIComponent(validBucketUrl)}"`,
|
||||
})
|
||||
|
||||
const nonGithubUrl = encodeURIComponent('https://example.com/')
|
||||
|
||||
t.create('license (non-github url)')
|
||||
.get(`/not-a-real-app.json?bucket=${nonGithubUrl}`)
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
message: `bucket "${decodeURIComponent(nonGithubUrl)}" not found`,
|
||||
})
|
||||
|
||||
const nonBucketRepo = encodeURIComponent('https://github.com/jewlexx/sfsu')
|
||||
|
||||
t.create('version (non-bucket repo)')
|
||||
.get(`/sfsu.json?bucket=${nonBucketRepo}`)
|
||||
.expectBadge({
|
||||
label: 'license',
|
||||
// !!! Important note here
|
||||
// It is hard to tell if a repo is actually a scoop bucket, without getting the contents
|
||||
// As such, a helpful error message here, which would require testing if the url is a valid scoop bucket, is difficult.
|
||||
message: `sfsu not found in bucket "${decodeURIComponent(nonBucketRepo)}"`,
|
||||
})
|
@ -1,27 +1,13 @@
|
||||
import { URL } from 'url'
|
||||
import Joi from 'joi'
|
||||
import { NotFound, pathParam, queryParam } from '../index.js'
|
||||
import { ConditionalGithubAuthV3Service } from '../github/github-auth-service.js'
|
||||
import { fetchJsonFromRepo } from '../github/github-common-fetch.js'
|
||||
import { pathParam, queryParam } from '../index.js'
|
||||
import { renderVersionBadge } from '../version.js'
|
||||
import { queryParamSchema, description, ScoopBase } from './scoop-base.js'
|
||||
|
||||
const gitHubRepoRegExp =
|
||||
/https:\/\/github.com\/(?<user>.*?)\/(?<repo>.*?)(\/|$)/
|
||||
const bucketsSchema = Joi.object()
|
||||
.pattern(/.+/, Joi.string().pattern(gitHubRepoRegExp).required())
|
||||
.required()
|
||||
const scoopSchema = Joi.object({
|
||||
version: Joi.string().required(),
|
||||
}).required()
|
||||
const queryParamSchema = Joi.object({
|
||||
bucket: Joi.string(),
|
||||
})
|
||||
|
||||
export default class ScoopVersion extends ConditionalGithubAuthV3Service {
|
||||
// The buckets file (https://github.com/lukesampson/scoop/blob/master/buckets.json) changes very rarely.
|
||||
// Cache it for the lifetime of the current Node.js process.
|
||||
buckets = null
|
||||
|
||||
export default class ScoopVersion extends ScoopBase {
|
||||
static category = 'version'
|
||||
|
||||
static route = {
|
||||
@ -34,8 +20,7 @@ export default class ScoopVersion extends ConditionalGithubAuthV3Service {
|
||||
'/scoop/v/{app}': {
|
||||
get: {
|
||||
summary: 'Scoop Version',
|
||||
description:
|
||||
'[Scoop](https://scoop.sh/) is a command-line installer for Windows',
|
||||
description,
|
||||
parameters: [
|
||||
pathParam({ name: 'app', example: 'ngrok' }),
|
||||
queryParam({
|
||||
@ -56,60 +41,11 @@ export default class ScoopVersion extends ConditionalGithubAuthV3Service {
|
||||
}
|
||||
|
||||
async handle({ app }, queryParams) {
|
||||
if (!this.buckets) {
|
||||
this.buckets = await fetchJsonFromRepo(this, {
|
||||
schema: bucketsSchema,
|
||||
user: 'ScoopInstaller',
|
||||
repo: 'Scoop',
|
||||
branch: 'master',
|
||||
filename: 'buckets.json',
|
||||
})
|
||||
}
|
||||
const bucket = queryParams.bucket || 'main'
|
||||
let bucketUrl = this.buckets[bucket]
|
||||
if (!bucketUrl) {
|
||||
// Parsing URL here will throw an error if the url is invalid
|
||||
try {
|
||||
const url = new URL(decodeURIComponent(bucket))
|
||||
const { version } = await this.fetch(
|
||||
{ app, schema: scoopSchema },
|
||||
queryParams,
|
||||
)
|
||||
|
||||
// Throw errors to go to jump to catch statement
|
||||
// The error messages here are purely for code readability, and will never reach the user.
|
||||
if (url.hostname !== 'github.com') {
|
||||
throw new Error('Not a GitHub URL')
|
||||
}
|
||||
const path = url.pathname.split('/').filter(value => value !== '')
|
||||
|
||||
if (path.length !== 2) {
|
||||
throw new Error('Not a valid GitHub Repo')
|
||||
}
|
||||
|
||||
const [user, repo] = path
|
||||
|
||||
// Reconstructing the url here ensures that the url will match the regex
|
||||
bucketUrl = `https://github.com/${user}/${repo}`
|
||||
} catch (e) {
|
||||
throw new NotFound({ prettyMessage: `bucket "${bucket}" not found` })
|
||||
}
|
||||
}
|
||||
const {
|
||||
groups: { user, repo },
|
||||
} = gitHubRepoRegExp.exec(bucketUrl)
|
||||
try {
|
||||
const { version } = await fetchJsonFromRepo(this, {
|
||||
schema: scoopSchema,
|
||||
user,
|
||||
repo,
|
||||
branch: 'master',
|
||||
filename: `bucket/${app}.json`,
|
||||
})
|
||||
return this.constructor.render({ version })
|
||||
} catch (error) {
|
||||
if (error instanceof NotFound) {
|
||||
throw new NotFound({
|
||||
prettyMessage: `${app} not found in bucket "${bucket}"`,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
return this.constructor.render({ version })
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user