1
0
mirror of https://github.com/badges/shields.git synced 2025-04-18 19:44:04 +03:00

migrate some services from examples to openApi part 31; affects [packagecontrol discourse] (#9858)

* migrate some services from examples to openApi

* update e2e test assertion
This commit is contained in:
chris48s 2024-01-01 10:32:38 +00:00 committed by GitHub
parent 09c83d9d46
commit a7f2396202
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 149 deletions

View File

@ -25,7 +25,7 @@ describe('Frontend', function () {
cy.contains('Build')
cy.contains('Chat').click()
cy.contains('Discourse status')
cy.contains('Discourse Status')
cy.contains('Stack Exchange questions')
})

View File

@ -1,8 +1,7 @@
import camelcase from 'camelcase'
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger, optionalUrl } from '../validators.js'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, queryParams } from '../index.js'
const schemaSingular = Joi.object({
topic_count: nonNegativeInteger,
@ -24,17 +23,19 @@ const queryParamSchema = Joi.object({
server: optionalUrl.required(),
}).required()
function singular(variant) {
return variant.slice(0, -1)
}
const params = queryParams({
name: 'server',
example: 'https://meta.discourse.org',
required: true,
})
class DiscourseBase extends BaseJsonService {
static category = 'chat'
static buildRoute(metric) {
return {
base: 'discourse',
pattern: metric,
queryParamSchema,
}
}
static defaultBadgeData = { label: 'discourse' }
async fetch({ server }) {
@ -45,58 +46,61 @@ class DiscourseBase extends BaseJsonService {
}
}
function DiscourseMetricIntegrationFactory({ metricType }) {
// We supply the singular form to more easily check against both schemas.
// But, we use the plural form as the metric name for grammatical reasons.
const metricName = `${metricType}s`
return class DiscourseMetric extends DiscourseBase {
// The space is needed so we get 'DiscourseTopics' rather than
// 'Discoursetopics'. `camelcase()` removes it.
static name = camelcase(`Discourse ${metricName}`, { pascalCase: true })
static route = this.buildRoute(metricName)
class DiscourseMetric extends DiscourseBase {
static route = {
base: 'discourse',
pattern: ':variant(topics|users|posts|likes)',
queryParamSchema,
}
static examples = [
{
title: `Discourse ${metricName}`,
namedParams: {},
queryParams: {
server: 'https://meta.discourse.org',
},
staticPreview: this.render({ stat: 100 }),
},
]
static openApi = {
'/discourse/topics': {
get: { summary: 'Discourse Topics', parameters: params },
},
'/discourse/users': {
get: { summary: 'Discourse Users', parameters: params },
},
'/discourse/posts': {
get: { summary: 'Discourse Posts', parameters: params },
},
'/discourse/likes': {
get: { summary: 'Discourse Likes', parameters: params },
},
}
static render({ stat }) {
return {
message: `${metric(stat)} ${metricName}`,
color: 'brightgreen',
}
static render({ variant, stat }) {
return {
message: `${metric(stat)} ${variant}`,
color: 'brightgreen',
}
}
async handle(_routeParams, { server }) {
const data = await this.fetch({ server })
// e.g. metricType == 'topic' --> try 'topic_count' then 'topics_count'
let stat = data[`${metricType}_count`]
if (stat === undefined) {
stat = data[`${metricType}s_count`]
}
return this.constructor.render({ stat })
async handle({ variant }, { server }) {
const data = await this.fetch({ server })
// e.g. variant == 'topics' --> try 'topic_count' then 'topics_count'
let stat = data[`${singular(variant)}_count`]
if (stat === undefined) {
stat = data[`${variant}_count`]
}
return this.constructor.render({ variant, stat })
}
}
class DiscourseStatus extends DiscourseBase {
static route = this.buildRoute('status')
static examples = [
{
title: 'Discourse status',
namedParams: {},
queryParams: {
server: 'https://meta.discourse.org',
static route = {
base: 'discourse',
pattern: 'status',
queryParamSchema,
}
static openApi = {
'/discourse/status': {
get: {
summary: 'Discourse Status',
parameters: params,
},
staticPreview: this.render(),
},
]
}
static render() {
return {
@ -113,11 +117,4 @@ class DiscourseStatus extends DiscourseBase {
}
}
const metricIntegrations = [
{ metricType: 'topic' },
{ metricType: 'user' },
{ metricType: 'post' },
{ metricType: 'like' },
].map(DiscourseMetricIntegrationFactory)
export default [...metricIntegrations, DiscourseStatus]
export default [DiscourseMetric, DiscourseStatus]

View File

@ -1,9 +1,7 @@
import Joi from 'joi'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
const keywords = ['sublime', 'sublimetext', 'packagecontrol']
import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
installs: Joi.object({
@ -20,92 +18,91 @@ const schema = Joi.object({
}).required(),
})
function DownloadsForInterval(downloadInterval) {
const { base, interval, transform, name } = {
day: {
base: 'packagecontrol/dd',
interval: 'day',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// use the downloads from yesterday
downloads += platform.totals[1]
})
return downloads
},
name: 'PackageControlDownloadsDay',
},
week: {
base: 'packagecontrol/dw',
interval: 'week',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 7 days
for (let i = 0; i < 7; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
name: 'PackageControlDownloadsWeek',
},
month: {
base: 'packagecontrol/dm',
interval: 'month',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 30 days
for (let i = 0; i < 30; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
name: 'PackageControlDownloadsMonth',
},
total: {
base: 'packagecontrol/dt',
transform: resp => resp.installs.total,
name: 'PackageControlDownloadsTotal',
},
}[downloadInterval]
return class PackageControlDownloads extends BaseJsonService {
static name = name
static category = 'downloads'
static route = { base, pattern: ':packageName' }
static examples = [
{
title: 'Package Control',
namedParams: { packageName: 'GitGutter' },
staticPreview: renderDownloadsBadge({ downloads: 12000 }),
keywords,
},
]
static defaultBadgeData = { label: 'downloads' }
async fetch({ packageName }) {
const url = `https://packagecontrol.io/packages/${packageName}.json`
return this._requestJson({ schema, url })
}
async handle({ packageName }) {
const data = await this.fetch({ packageName })
return renderDownloadsBadge({
downloads: transform(data),
interval,
const intervalMap = {
dd: {
label: 'day',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// use the downloads from yesterday
downloads += platform.totals[1]
})
}
}
return downloads
},
},
dw: {
label: 'week',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 7 days
for (let i = 0; i < 7; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
},
dm: {
label: 'month',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 30 days
for (let i = 0; i < 30; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
},
dt: {
transform: resp => resp.installs.total,
},
}
export default ['day', 'week', 'month', 'total'].map(DownloadsForInterval)
export default class PackageControlDownloads extends BaseJsonService {
static category = 'downloads'
static route = {
base: 'packagecontrol',
pattern: ':interval(dd|dw|dm|dt)/:packageName',
}
static openApi = {
'/packagecontrol/{interval}/{packageName}': {
get: {
summary: 'Package Control Downloads',
description:
'Package Control is a package registry for Sublime Text packages',
parameters: pathParams(
{
name: 'interval',
example: 'dt',
schema: { type: 'string', enum: this.getEnum('interval') },
description: 'Daily, Weekly, Monthly, or Total downloads',
},
{ name: 'packageName', example: 'GitGutter' },
),
},
},
}
static defaultBadgeData = { label: 'downloads' }
async fetch({ packageName }) {
const url = `https://packagecontrol.io/packages/${packageName}.json`
return this._requestJson({ schema, url })
}
async handle({ interval, packageName }) {
const data = await this.fetch({ packageName })
return renderDownloadsBadge({
downloads: intervalMap[interval].transform(data),
interval: intervalMap[interval].label,
})
}
}