diff --git a/services/sourceforge/sourceforge-base.js b/services/sourceforge/sourceforge-base.js new file mode 100644 index 0000000000000..c8e84ed0dfa1e --- /dev/null +++ b/services/sourceforge/sourceforge-base.js @@ -0,0 +1,13 @@ +import { BaseJsonService } from '../index.js' + +export default class BaseSourceForgeService extends BaseJsonService { + async fetch({ project, schema }) { + return this._requestJson({ + url: `https://sourceforge.net/rest/p/${project}/`, + schema, + errorMessages: { + 404: 'project not found', + }, + }) + } +} diff --git a/services/sourceforge/sourceforge-commit-count.service.js b/services/sourceforge/sourceforge-commit-count.service.js new file mode 100644 index 0000000000000..581f4384c22cd --- /dev/null +++ b/services/sourceforge/sourceforge-commit-count.service.js @@ -0,0 +1,54 @@ +import Joi from 'joi' +import { BaseJsonService } from '../index.js' +import { metric } from '../text-formatters.js' + +const schema = Joi.object({ + commit_count: Joi.number().required(), +}).required() + +export default class SourceforgeCommitCount extends BaseJsonService { + static category = 'activity' + + static route = { + base: 'sourceforge/commit-count', + pattern: ':project', + } + + static examples = [ + { + title: 'SourceForge commit count', + namedParams: { + project: 'guitarix', + }, + staticPreview: this.render({ + commitCount: 1365, + }), + }, + ] + + static defaultBadgeData = { label: 'commit count' } + + static render({ commitCount }) { + return { + message: metric(commitCount), + color: 'blue', + } + } + + async fetch({ project }) { + return this._requestJson({ + url: `https://sourceforge.net/rest/p/${project}/git`, + schema, + errorMessages: { + 404: 'project not found', + }, + }) + } + + async handle({ project }) { + const body = await this.fetch({ project }) + return this.constructor.render({ + commitCount: body.commit_count, + }) + } +} diff --git a/services/sourceforge/sourceforge-commit-count.tester.js b/services/sourceforge/sourceforge-commit-count.tester.js new file mode 100644 index 0000000000000..f7a7e3f990e20 --- /dev/null +++ b/services/sourceforge/sourceforge-commit-count.tester.js @@ -0,0 +1,11 @@ +import { isMetric } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('commit count') + .get('/guitarix.json') + .expectBadge({ label: 'commit count', message: isMetric }) + +t.create('commit count (project not found)') + .get('/that-doesnt-exist.json') + .expectBadge({ label: 'commit count', message: 'project not found' }) diff --git a/services/sourceforge/sourceforge-contributors.service.js b/services/sourceforge/sourceforge-contributors.service.js new file mode 100644 index 0000000000000..498bf865d0450 --- /dev/null +++ b/services/sourceforge/sourceforge-contributors.service.js @@ -0,0 +1,43 @@ +import Joi from 'joi' +import { renderContributorBadge } from '../contributor-count.js' +import BaseSourceForgeService from './sourceforge-base.js' + +const schema = Joi.object({ + developers: Joi.array().required(), +}).required() + +export default class SourceforgeContributors extends BaseSourceForgeService { + static category = 'activity' + + static route = { + base: 'sourceforge/contributors', + pattern: ':project', + } + + static examples = [ + { + title: 'SourceForge contributors', + namedParams: { + project: 'guitarix', + }, + staticPreview: this.render({ + contributorCount: 9, + }), + }, + ] + + static defaultBadgeData = { label: 'contributors' } + + static render({ contributorCount }) { + return renderContributorBadge({ + contributorCount, + }) + } + + async handle({ project }) { + const body = await this.fetch({ project, schema }) + return this.constructor.render({ + contributorCount: body.developers.length, + }) + } +} diff --git a/services/sourceforge/sourceforge-contributors.tester.js b/services/sourceforge/sourceforge-contributors.tester.js new file mode 100644 index 0000000000000..b0fa07216963d --- /dev/null +++ b/services/sourceforge/sourceforge-contributors.tester.js @@ -0,0 +1,11 @@ +import { isMetric } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('contributors') + .get('/guitarix.json') + .expectBadge({ label: 'contributors', message: isMetric }) + +t.create('contributors (project not found)') + .get('/that-doesnt-exist.json') + .expectBadge({ label: 'contributors', message: 'project not found' }) diff --git a/services/sourceforge/sourceforge.service.js b/services/sourceforge/sourceforge-downloads.service.js similarity index 92% rename from services/sourceforge/sourceforge.service.js rename to services/sourceforge/sourceforge-downloads.service.js index ebbe1e46e595c..6201c38d11146 100644 --- a/services/sourceforge/sourceforge.service.js +++ b/services/sourceforge/sourceforge-downloads.service.js @@ -27,17 +27,17 @@ const intervalMap = { }, } -export default class Sourceforge extends BaseJsonService { +export default class SourceforgeDownloads extends BaseJsonService { static category = 'downloads' static route = { - base: 'sourceforge', + base: 'sourceforge/downloads', pattern: ':interval(dt|dm|dw|dd)/:project/:folder*', } static examples = [ { - title: 'SourceForge', + title: 'SourceForge Downloads', pattern: ':interval(dt|dm|dw|dd)/:project', namedParams: { interval: 'dm', @@ -49,7 +49,7 @@ export default class Sourceforge extends BaseJsonService { }), }, { - title: 'SourceForge', + title: 'SourceForge Downloads (folder)', pattern: ':interval(dt|dm|dw|dd)/:project/:folder', namedParams: { interval: 'dm', diff --git a/services/sourceforge/sourceforge.tester.js b/services/sourceforge/sourceforge-downloads.tester.js similarity index 100% rename from services/sourceforge/sourceforge.tester.js rename to services/sourceforge/sourceforge-downloads.tester.js diff --git a/services/sourceforge/sourceforge-languages.service.js b/services/sourceforge/sourceforge-languages.service.js new file mode 100644 index 0000000000000..682133ef12e35 --- /dev/null +++ b/services/sourceforge/sourceforge-languages.service.js @@ -0,0 +1,42 @@ +import Joi from 'joi' +import { metric } from '../text-formatters.js' +import BaseSourceForgeService from './sourceforge-base.js' + +const schema = Joi.object({ + categories: Joi.object({ + language: Joi.array().required(), + }).required(), +}).required() + +export default class SourceforgeLanguages extends BaseSourceForgeService { + static category = 'analysis' + + static route = { + base: 'sourceforge/languages', + pattern: ':project', + } + + static examples = [ + { + title: 'SourceForge languages', + namedParams: { + project: 'mingw', + }, + staticPreview: this.render(6), + }, + ] + + static defaultBadgeData = { label: 'languages' } + + static render(languages) { + return { + message: metric(languages), + color: 'blue', + } + } + + async handle({ project }) { + const body = await this.fetch({ project, schema }) + return this.constructor.render(body.categories.language.length) + } +} diff --git a/services/sourceforge/sourceforge-languages.tester.js b/services/sourceforge/sourceforge-languages.tester.js new file mode 100644 index 0000000000000..f79186e205e22 --- /dev/null +++ b/services/sourceforge/sourceforge-languages.tester.js @@ -0,0 +1,11 @@ +import { isMetric } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('languages') + .get('/guitarix.json') + .expectBadge({ label: 'languages', message: isMetric }) + +t.create('languages (project not found)') + .get('/that-doesnt-exist.json') + .expectBadge({ label: 'languages', message: 'project not found' }) diff --git a/services/sourceforge/sourceforge-last-commit.service.js b/services/sourceforge/sourceforge-last-commit.service.js new file mode 100644 index 0000000000000..9e6acfb38f32a --- /dev/null +++ b/services/sourceforge/sourceforge-last-commit.service.js @@ -0,0 +1,61 @@ +import Joi from 'joi' +import { BaseJsonService } from '../index.js' +import { formatDate } from '../text-formatters.js' +import { age as ageColor } from '../color-formatters.js' + +const schema = Joi.object({ + commits: Joi.array() + .items( + Joi.object({ + committed_date: Joi.string().required(), + }).required() + ) + .required(), +}).required() + +export default class SourceforgeLastCommit extends BaseJsonService { + static category = 'activity' + + static route = { + base: 'sourceforge/last-commit', + pattern: ':project', + } + + static examples = [ + { + title: 'SourceForge last commit', + namedParams: { + project: 'guitarix', + }, + staticPreview: this.render({ + commitDate: 1653556285, + }), + }, + ] + + static defaultBadgeData = { label: 'last commit' } + + static render({ commitDate }) { + return { + message: formatDate(new Date(commitDate)), + color: ageColor(new Date(commitDate)), + } + } + + async fetch({ project }) { + return this._requestJson({ + url: `https://sourceforge.net/rest/p/${project}/git/commits`, + schema, + errorMessages: { + 404: 'project not found', + }, + }) + } + + async handle({ project }) { + const body = await this.fetch({ project }) + return this.constructor.render({ + commitDate: body.commits[0].committed_date, + }) + } +} diff --git a/services/sourceforge/sourceforge-last-commit.tester.js b/services/sourceforge/sourceforge-last-commit.tester.js new file mode 100644 index 0000000000000..8a4487c6e37cf --- /dev/null +++ b/services/sourceforge/sourceforge-last-commit.tester.js @@ -0,0 +1,11 @@ +import { isFormattedDate } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('last commit') + .get('/guitarix.json') + .expectBadge({ label: 'last commit', message: isFormattedDate }) + +t.create('last commit (project not found)') + .get('/that-doesnt-exist.json') + .expectBadge({ label: 'last commit', message: 'project not found' }) diff --git a/services/sourceforge/sourceforge-platform.service.js b/services/sourceforge/sourceforge-platform.service.js new file mode 100644 index 0000000000000..207d5cbe767a4 --- /dev/null +++ b/services/sourceforge/sourceforge-platform.service.js @@ -0,0 +1,48 @@ +import Joi from 'joi' +import BaseSourceForgeService from './sourceforge-base.js' + +const schema = Joi.object({ + categories: Joi.object({ + os: Joi.array() + .items({ + fullname: Joi.string().required(), + }) + .required(), + }).required(), +}).required() + +export default class SourceforgePlatform extends BaseSourceForgeService { + static category = 'platform-support' + + static route = { + base: 'sourceforge/platform', + pattern: ':project', + } + + static examples = [ + { + title: 'SourceForge Platform', + namedParams: { + project: 'guitarix', + }, + staticPreview: this.render({ + platforms: ['linux', 'bsd'], + }), + }, + ] + + static defaultBadgeData = { label: 'platform' } + + static render({ platforms }) { + return { + message: platforms.join(' | '), + } + } + + async handle({ project }) { + const body = await this.fetch({ project, schema }) + return this.constructor.render({ + platforms: body.categories.os.map(obj => obj.fullname), + }) + } +} diff --git a/services/sourceforge/sourceforge-platform.tester.js b/services/sourceforge/sourceforge-platform.tester.js new file mode 100644 index 0000000000000..836fa86754433 --- /dev/null +++ b/services/sourceforge/sourceforge-platform.tester.js @@ -0,0 +1,11 @@ +import Joi from 'joi' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('platform') + .get('/guitarix.json') + .expectBadge({ label: 'platform', message: Joi.string().required() }) + +t.create('platform (project not found)') + .get('/that-doesnt-exist.json') + .expectBadge({ label: 'platform', message: 'project not found' }) diff --git a/services/sourceforge/sourceforge-translations.service.js b/services/sourceforge/sourceforge-translations.service.js new file mode 100644 index 0000000000000..ad64137530cb1 --- /dev/null +++ b/services/sourceforge/sourceforge-translations.service.js @@ -0,0 +1,46 @@ +import Joi from 'joi' +import { metric } from '../text-formatters.js' +import BaseSourceForgeService from './sourceforge-base.js' + +const schema = Joi.object({ + categories: Joi.object({ + translation: Joi.array().required(), + }).required(), +}).required() + +export default class SourceforgeTranslations extends BaseSourceForgeService { + static category = 'activity' + + static route = { + base: 'sourceforge/translations', + pattern: ':project', + } + + static examples = [ + { + title: 'SourceForge Translations', + namedParams: { + project: 'guitarix', + }, + staticPreview: this.render({ + translationCount: 4, + }), + }, + ] + + static defaultBadgeData = { label: 'translations' } + + static render({ translationCount }) { + return { + message: metric(translationCount), + color: 'blue', + } + } + + async handle({ project }) { + const body = await this.fetch({ project, schema }) + return this.constructor.render({ + translationCount: body.categories.translation.length, + }) + } +} diff --git a/services/sourceforge/sourceforge-translations.tester.js b/services/sourceforge/sourceforge-translations.tester.js new file mode 100644 index 0000000000000..b66947190d93a --- /dev/null +++ b/services/sourceforge/sourceforge-translations.tester.js @@ -0,0 +1,11 @@ +import { isMetric } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('translations') + .get('/guitarix.json') + .expectBadge({ label: 'translations', message: isMetric }) + +t.create('translations (project not found)') + .get('/that-doesnt-exist.json') + .expectBadge({ label: 'translations', message: 'project not found' })