-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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>
- Loading branch information
1 parent
28bee86
commit 5e40080
Showing
4 changed files
with
247 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)}"`, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters