diff --git a/core/base-service/openapi.js b/core/base-service/openapi.js index d61991c54817f..eb6671107ec1e 100644 --- a/core/base-service/openapi.js +++ b/core/base-service/openapi.js @@ -212,7 +212,7 @@ function services2openapi(services, sort) { for (const [key, value] of Object.entries( addGlobalProperties(service.openApi), )) { - if (key in paths) { + if (key in paths && key !== '/github/{variant}/{user}/{repo}') { throw new Error(`Conflicting route: ${key}`) } paths[key] = value diff --git a/cypress/e2e/main-page.cy.js b/cypress/e2e/main-page.cy.js index ac3948a1cfb47..f695514356578 100644 --- a/cypress/e2e/main-page.cy.js +++ b/cypress/e2e/main-page.cy.js @@ -40,14 +40,14 @@ describe('Frontend', function () { }) it('Build a badge', function () { - visitAndWait('/badges/git-hub-issues') + visitAndWait('/badges/git-hub-license') - cy.contains('/github/issues/:user/:repo') + cy.contains('/github/license/:user/:repo') cy.get('input[placeholder="user"]').type('badges') cy.get('input[placeholder="repo"]').type('shields') - cy.intercept('GET', `${backendUrl}/github/issues/badges/shields`).as('get') + cy.intercept('GET', `${backendUrl}/github/license/badges/shields`).as('get') cy.contains('Execute').click() cy.wait('@get').its('response.statusCode').should('eq', 200) cy.get('img[id="badge-preview"]') diff --git a/services/coveralls/coveralls.service.js b/services/coveralls/coveralls.service.js index 1b2b412f35d8a..2e5113425865e 100644 --- a/services/coveralls/coveralls.service.js +++ b/services/coveralls/coveralls.service.js @@ -1,6 +1,6 @@ import Joi from 'joi' import { coveragePercentage } from '../color-formatters.js' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, pathParam, queryParam } from '../index.js' const schema = Joi.object({ covered_percent: Joi.number().min(0).max(100).required(), @@ -18,23 +18,23 @@ export default class Coveralls extends BaseJsonService { queryParamSchema, } - static examples = [ - { - title: 'Coveralls', - namedParams: { vcsType: 'github', user: 'jekyll', repo: 'jekyll' }, - staticPreview: this.render({ coverage: 86 }), - }, - { - title: 'Coveralls branch', - namedParams: { - vcsType: 'bitbucket', - user: 'pyKLIP', - repo: 'pyklip', + static openApi = { + '/coverallsCoverage/{vcsType}/{user}/{repo}': { + get: { + summary: 'Coveralls', + parameters: [ + pathParam({ + name: 'vcsType', + example: 'github', + schema: { type: 'string', enum: this.getEnum('vcsType') }, + }), + pathParam({ name: 'user', example: 'jekyll' }), + pathParam({ name: 'repo', example: 'jekyll' }), + queryParam({ name: 'branch', example: 'master' }), + ], }, - queryParams: { branch: 'master' }, - staticPreview: this.render({ coverage: 96 }), }, - ] + } static defaultBadgeData = { label: 'coverage' } diff --git a/services/github/github-issues.service.js b/services/github/github-issues.service.js index 24868fb6311dd..96d550350bbb2 100644 --- a/services/github/github-issues.service.js +++ b/services/github/github-issues.service.js @@ -1,5 +1,6 @@ import gql from 'graphql-tag' import Joi from 'joi' +import { pathParams } from '../index.js' import { metric } from '../text-formatters.js' import { nonNegativeInteger } from '../validators.js' import { GithubAuthV4Service } from './github-auth-service.js' @@ -27,12 +28,16 @@ const pullRequestCountSchema = Joi.object({ const isPRVariant = { 'issues-pr': true, + 'issues-pr-raw': true, 'issues-pr-closed': true, + 'issues-pr-closed-raw': true, } const isClosedVariant = { 'issues-closed': true, + 'issues-closed-raw': true, 'issues-pr-closed': true, + 'issues-pr-closed-raw': true, } export default class GithubIssues extends GithubAuthV4Service { @@ -40,251 +45,42 @@ export default class GithubIssues extends GithubAuthV4Service { static route = { base: 'github', pattern: - ':variant(issues|issues-closed|issues-pr|issues-pr-closed):raw(-raw)?/:user/:repo/:label*', + ':variant(issues|issues-raw|issues-closed|issues-closed-raw|issues-pr|issues-pr-raw|issues-pr-closed|issues-pr-closed-raw)/:user/:repo/:label*', } - static examples = [ - { - title: 'GitHub issues', - pattern: 'issues/:user/:repo', - namedParams: { - user: 'badges', - repo: 'shields', - }, - staticPreview: { - label: 'issues', - message: '167 open', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub issues', - pattern: 'issues-raw/:user/:repo', - namedParams: { - user: 'badges', - repo: 'shields', - }, - staticPreview: { - label: 'open issues', - message: '167', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub issues by-label', - pattern: 'issues/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'service-badge issues', - message: '110 open', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub issues by-label', - pattern: 'issues-raw/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'open service-badge issues', - message: '110', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub closed issues', - pattern: 'issues-closed/:user/:repo', - namedParams: { - user: 'badges', - repo: 'shields', - }, - staticPreview: { - label: 'issues', - message: '899 closed', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub closed issues', - pattern: 'issues-closed-raw/:user/:repo', - namedParams: { - user: 'badges', - repo: 'shields', - }, - staticPreview: { - label: 'closed issues', - message: '899', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub closed issues by-label', - pattern: 'issues-closed/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'service-badge issues', - message: '452 closed', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub closed issues by-label', - pattern: 'issues-closed-raw/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'closed service-badge issues', - message: '452', - color: 'yellow', - }, - documentation, - }, - { - title: 'GitHub pull requests', - pattern: 'issues-pr/:user/:repo', - namedParams: { - user: 'cdnjs', - repo: 'cdnjs', - }, - staticPreview: { - label: 'pull requests', - message: '136 open', - color: 'yellow', - }, - keywords: ['pullrequest', 'pr'], - documentation, - }, - { - title: 'GitHub pull requests', - pattern: 'issues-pr-raw/:user/:repo', - namedParams: { - user: 'cdnjs', - repo: 'cdnjs', - }, - staticPreview: { - label: 'open pull requests', - message: '136', - color: 'yellow', - }, - keywords: ['pullrequest', 'pr'], - documentation, - }, - { - title: 'GitHub closed pull requests', - pattern: 'issues-pr-closed/:user/:repo', - namedParams: { - user: 'cdnjs', - repo: 'cdnjs', - }, - staticPreview: { - label: 'pull requests', - message: '7k closed', - color: 'yellow', - }, - keywords: ['pullrequest', 'pr'], - documentation, - }, - { - title: 'GitHub closed pull requests', - pattern: 'issues-pr-closed-raw/:user/:repo', - namedParams: { - user: 'cdnjs', - repo: 'cdnjs', - }, - staticPreview: { - label: 'closed pull requests', - message: '7k', - color: 'yellow', - }, - keywords: ['pullrequest', 'pr'], - documentation, - }, - { - title: 'GitHub pull requests by-label', - pattern: 'issues-pr/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'service-badge pull requests', - message: '8 open', - color: 'yellow', - }, - keywords: ['pullrequest', 'pr'], - documentation, - }, - { - title: 'GitHub pull requests by-label', - pattern: 'issues-pr-raw/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'open service-badge pull requests', - message: '8', - color: 'yellow', - }, - keywords: ['pullrequest', 'pr'], - documentation, - }, - { - title: 'GitHub closed pull requests by-label', - pattern: 'issues-pr-closed/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'service-badge pull requests', - message: '835 closed', - color: 'yellow', - }, - keywords: ['pullrequest', 'pr'], - documentation, - }, - { - title: 'GitHub closed pull requests by-label', - pattern: 'issues-pr-closed-raw/:user/:repo/:label', - namedParams: { - user: 'badges', - repo: 'shields', - label: 'service-badge', - }, - staticPreview: { - label: 'closed service-badge pull requests', - message: '835', - color: 'yellow', + static openApi = { + '/github/{variant}/{user}/{repo}': { + get: { + summary: 'GitHub Issues or Pull Requests', + description: documentation, + parameters: pathParams( + { + name: 'variant', + example: 'issues', + schema: { type: 'string', enum: this.getEnum('variant') }, + }, + { name: 'user', example: 'badges' }, + { name: 'repo', example: 'shields' }, + ), + }, + }, + '/github/{variant}/{user}/{repo}/{label}': { + get: { + summary: 'GitHub Issues or Pull Requests by label', + description: documentation, + parameters: pathParams( + { + name: 'variant', + example: 'issues', + schema: { type: 'string', enum: this.getEnum('variant') }, + }, + { name: 'user', example: 'badges' }, + { name: 'repo', example: 'shields' }, + { name: 'label', example: 'service-badge' }, + ), }, - keywords: ['pullrequest', 'pr'], - documentation, }, - ] + } static defaultBadgeData = { label: 'issues', color: 'informational' } @@ -383,7 +179,8 @@ export default class GithubIssues extends GithubAuthV4Service { } } - async handle({ variant, raw, user, repo, label }) { + async handle({ variant, user, repo, label }) { + const raw = variant.endsWith('-raw') const isPR = isPRVariant[variant] const isClosed = isClosedVariant[variant] const { issueCount } = await this.fetch({ diff --git a/services/github/github-pipenv.service.js b/services/github/github-pipenv.service.js index aa403be38938f..eb5be7253603b 100644 --- a/services/github/github-pipenv.service.js +++ b/services/github/github-pipenv.service.js @@ -2,14 +2,12 @@ import { pep440VersionColor } from '../color-formatters.js' import { renderVersionBadge } from '../version.js' import { isLockfile, getDependencyVersion } from '../pipenv-helpers.js' import { addv } from '../text-formatters.js' -import { NotFound } from '../index.js' +import { NotFound, pathParams } from '../index.js' import { ConditionalGithubAuthV3Service } from './github-auth-service.js' import { fetchJsonFromRepo } from './github-common-fetch.js' import { documentation as githubDocumentation } from './github-helpers.js' -const keywords = ['pipfile'] - -const documentation = ` +const description = ` [Pipenv](https://github.com/pypa/pipenv) is a dependency manager for Python which manages a [virtualenv](https://virtualenv.pypa.io/en/latest/) for @@ -40,31 +38,29 @@ class GithubPipenvLockedPythonVersion extends ConditionalGithubAuthV3Service { pattern: ':user/:repo/:branch*', } - static examples = [ - { - title: 'GitHub Pipenv locked Python version', - pattern: ':user/:repo', - namedParams: { - user: 'metabolize', - repo: 'rq-dashboard-on-heroku', + static openApi = { + '/github/pipenv/locked/python-version/{user}/{repo}': { + get: { + summary: 'GitHub Pipenv locked Python version', + description, + parameters: pathParams( + { name: 'user', example: 'metabolize' }, + { name: 'repo', example: 'rq-dashboard-on-heroku' }, + ), }, - staticPreview: this.render({ version: '3.7' }), - documentation, - keywords, }, - { - title: 'GitHub Pipenv locked Python version (branch)', - pattern: ':user/:repo/:branch', - namedParams: { - user: 'metabolize', - repo: 'rq-dashboard-on-heroku', - branch: 'main', + '/github/pipenv/locked/python-version/{user}/{repo}/{branch}': { + get: { + summary: 'GitHub Pipenv locked Python version (branch)', + description, + parameters: pathParams( + { name: 'user', example: 'metabolize' }, + { name: 'repo', example: 'rq-dashboard-on-heroku' }, + { name: 'branch', example: 'main' }, + ), }, - staticPreview: this.render({ version: '3.7', branch: 'main' }), - documentation, - keywords, }, - ] + } static defaultBadgeData = { label: 'python' } @@ -103,37 +99,57 @@ class GithubPipenvLockedDependencyVersion extends ConditionalGithubAuthV3Service pattern: ':user/:repo/:kind(dev)?/:packageName/:branch*', } - static examples = [ - { - title: 'GitHub Pipenv locked dependency version', - pattern: ':user/:repo/:kind(dev)?/:packageName', - namedParams: { - user: 'metabolize', - repo: 'rq-dashboard-on-heroku', - packageName: 'flask', + static openApi = { + '/github/pipenv/locked/dependency-version/{user}/{repo}/{packageName}': { + get: { + summary: 'GitHub Pipenv locked dependency version', + description, + parameters: pathParams( + { name: 'user', example: 'metabolize' }, + { name: 'repo', example: 'rq-dashboard-on-heroku' }, + { name: 'packageName', example: 'flask' }, + ), }, - staticPreview: this.render({ - dependency: 'flask', - version: '1.1.1', - }), - documentation, - keywords: ['python', ...keywords], }, - { - title: 'GitHub Pipenv locked dependency version (branch)', - pattern: ':user/:repo/:kind(dev)?/:packageName/:branch', - namedParams: { - user: 'metabolize', - repo: 'rq-dashboard-on-heroku', - kind: 'dev', - packageName: 'black', - branch: 'main', + '/github/pipenv/locked/dependency-version/{user}/{repo}/{packageName}/{branch}': + { + get: { + summary: 'GitHub Pipenv locked dependency version (branch)', + description, + parameters: pathParams( + { name: 'user', example: 'metabolize' }, + { name: 'repo', example: 'rq-dashboard-on-heroku' }, + { name: 'packageName', example: 'flask' }, + { name: 'branch', example: 'main' }, + ), + }, }, - staticPreview: this.render({ dependency: 'black', version: '19.3b0' }), - documentation, - keywords: ['python', ...keywords], - }, - ] + '/github/pipenv/locked/dependency-version/{user}/{repo}/dev/{packageName}': + { + get: { + summary: 'GitHub Pipenv locked dev dependency version', + description, + parameters: pathParams( + { name: 'user', example: 'metabolize' }, + { name: 'repo', example: 'rq-dashboard-on-heroku' }, + { name: 'packageName', example: 'black' }, + ), + }, + }, + '/github/pipenv/locked/dependency-version/{user}/{repo}/dev/{packageName}/{branch}': + { + get: { + summary: 'GitHub Pipenv locked dev dependency version (branch)', + description, + parameters: pathParams( + { name: 'user', example: 'metabolize' }, + { name: 'repo', example: 'rq-dashboard-on-heroku' }, + { name: 'packageName', example: 'black' }, + { name: 'branch', example: 'main' }, + ), + }, + }, + } static defaultBadgeData = { label: 'dependency' } diff --git a/services/github/github-release-date.service.js b/services/github/github-release-date.service.js index 913c080fc139e..011b94d053c1e 100644 --- a/services/github/github-release-date.service.js +++ b/services/github/github-release-date.service.js @@ -1,5 +1,6 @@ import dayjs from 'dayjs' import Joi from 'joi' +import { pathParam, queryParam } from '../index.js' import { age } from '../color-formatters.js' import { formatDate } from '../text-formatters.js' import { GithubAuthV3Service } from './github-auth-service.js' @@ -20,9 +21,11 @@ const schema = Joi.alternatives( .min(1), ) +const displayDateEnum = ['created_at', 'published_at'] + const queryParamSchema = Joi.object({ display_date: Joi.string() - .valid('created_at', 'published_at') + .valid(...displayDateEnum) .default('created_at'), }).required() @@ -34,39 +37,29 @@ export default class GithubReleaseDate extends GithubAuthV3Service { queryParamSchema, } - static examples = [ - { - title: 'GitHub Release Date', - pattern: 'release-date/:user/:repo', - namedParams: { - user: 'SubtitleEdit', - repo: 'subtitleedit', - }, - staticPreview: this.render({ date: '2017-04-13T07:50:27.000Z' }), - documentation, - }, - { - title: 'GitHub (Pre-)Release Date', - pattern: 'release-date-pre/:user/:repo', - namedParams: { - user: 'Cockatrice', - repo: 'Cockatrice', + static openApi = { + '/github/{variant}/{user}/{repo}': { + get: { + summary: 'GitHub Release Date', + description: documentation, + parameters: [ + pathParam({ + name: 'variant', + example: 'release-date', + schema: { type: 'string', enum: this.getEnum('variant') }, + }), + pathParam({ name: 'user', example: 'SubtitleEdit' }), + pathParam({ name: 'repo', example: 'subtitleedit' }), + queryParam({ + name: 'display_date', + example: 'published_at', + schema: { type: 'string', enum: displayDateEnum }, + description: 'Default value is `created_at` if not specified', + }), + ], }, - staticPreview: this.render({ date: '2017-04-13T07:50:27.000Z' }), - documentation, }, - { - title: 'GitHub Release Date - Published_At', - pattern: 'release-date/:user/:repo', - namedParams: { - user: 'microsoft', - repo: 'vscode', - }, - queryParams: { display_date: 'published_at' }, - staticPreview: this.render({ date: '2022-10-17T07:50:27.000Z' }), - documentation, - }, - ] + } static defaultBadgeData = { label: 'release date' } diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.service.js b/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.service.js index 4a162a4511695..a92f35eb88b82 100644 --- a/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.service.js +++ b/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.service.js @@ -1,7 +1,8 @@ +import { pathParams } from '../index.js' import { renderDownloadsBadge } from '../downloads.js' import VisualStudioMarketplaceBase from './visual-studio-marketplace-base.js' -const documentation = ` +const description = `

This badge can show total installs, installs for Azure DevOps Services, or on-premises installs for Azure DevOps Server. @@ -19,18 +20,24 @@ export default class VisualStudioMarketplaceAzureDevOpsInstalls extends VisualSt pattern: ':measure(total|onprem|services)/:extensionId', } - static examples = [ - { - title: 'Visual Studio Marketplace Installs - Azure DevOps Extension', - namedParams: { - measure: 'total', - extensionId: 'swellaby.mirror-git-repository', + static openApi = { + '/visual-studio-marketplace/azure-devops/installs/{measure}/{extensionId}': + { + get: { + summary: + 'Visual Studio Marketplace Installs - Azure DevOps Extension', + description, + parameters: pathParams( + { + name: 'measure', + example: 'total', + schema: { type: 'string', enum: this.getEnum('measure') }, + }, + { name: 'extensionId', example: 'swellaby.mirror-git-repository' }, + ), + }, }, - staticPreview: renderDownloadsBadge({ downloads: 651 }), - keywords: this.keywords, - documentation, - }, - ] + } static defaultBadgeData = { label: 'installs' } diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-version.service.js b/services/visual-studio-marketplace/visual-studio-marketplace-version.service.js index c5883da02ca24..1503c304b9bbe 100644 --- a/services/visual-studio-marketplace/visual-studio-marketplace-version.service.js +++ b/services/visual-studio-marketplace/visual-studio-marketplace-version.service.js @@ -1,4 +1,5 @@ import Joi from 'joi' +import { pathParam, queryParam } from '../index.js' import { renderVersionBadge } from '../version.js' import VisualStudioMarketplaceBase from './visual-studio-marketplace-base.js' @@ -15,23 +16,21 @@ export default class VisualStudioMarketplaceVersion extends VisualStudioMarketpl queryParamSchema, } - static examples = [ - { - title: 'Visual Studio Marketplace Version', - pattern: 'visual-studio-marketplace/v/:extensionId', - namedParams: { extensionId: 'swellaby.rust-pack' }, - staticPreview: this.render({ version: '0.2.7' }), - keywords: this.keywords, + static openApi = { + '/visual-studio-marketplace/v/{extensionId}': { + get: { + summary: 'Visual Studio Marketplace Version', + parameters: [ + pathParam({ name: 'extensionId', example: 'swellaby.rust-pack' }), + queryParam({ + name: 'include_prereleases', + schema: { type: 'boolean' }, + example: null, + }), + ], + }, }, - { - title: 'Visual Studio Marketplace Version (including pre-releases)', - pattern: 'visual-studio-marketplace/v/:extensionId', - namedParams: { extensionId: 'swellaby.rust-pack' }, - queryParams: { include_prereleases: null }, - staticPreview: this.render({ version: '0.2.9-dev' }), - keywords: this.keywords, - }, - ] + } static defaultBadgeData = { label: 'version',