From 7edaea304aff8db8add0e07ad24a143647d67abf Mon Sep 17 00:00:00 2001 From: profgrammer Date: Thu, 6 May 2021 12:30:44 +0530 Subject: [PATCH 1/6] add optional distribution flag to GemVersion --- services/gem/gem-version.service.js | 42 ++++++++++++++++++++++++----- services/gem/gem-version.tester.js | 16 ++++++++++- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/services/gem/gem-version.service.js b/services/gem/gem-version.service.js index ba8c7bc62a3b1..86cf1d197885b 100644 --- a/services/gem/gem-version.service.js +++ b/services/gem/gem-version.service.js @@ -1,8 +1,9 @@ 'use strict' const Joi = require('joi') -const { renderVersionBadge } = require('../version') -const { BaseJsonService } = require('..') +const { renderVersionBadge, latest } = require('../version') +const { BaseJsonService, NotFound } = require('..') +const { nonNegativeInteger } = require('../validators') const schema = Joi.object({ // In most cases `version` will be a SemVer but the registry doesn't @@ -10,9 +11,22 @@ const schema = Joi.object({ version: Joi.string().required(), }).required() +const versionSchema = Joi.array() + .items( + Joi.object({ + prerelease: Joi.boolean().required(), + number: Joi.string().required(), + downloads_count: nonNegativeInteger, + }) + ) + .min(1) + .required() + +const defaultDistribution = 'stable' + module.exports = class GemVersion extends BaseJsonService { static category = 'version' - static route = { base: 'gem/v', pattern: ':gem' } + static route = { base: 'gem/v', pattern: ':gem/:distribution?' } static examples = [ { title: 'Gem', @@ -35,8 +49,24 @@ module.exports = class GemVersion extends BaseJsonService { }) } - async handle({ gem }) { - const { version } = await this.fetch({ gem }) - return this.constructor.render({ version }) + async fetchLatest({ gem }) { + return this._requestJson({ + schema: versionSchema, + url: `https://rubygems.org/api/v1/versions/${gem}.json`, + }) + } + + async handle({ gem, distribution = defaultDistribution }) { + if (distribution === defaultDistribution) { + const { version } = await this.fetch({ gem }) + return this.constructor.render({ version }) + } else { + const data = await this.fetchLatest({ gem }) + if (!Array.isArray(data) || data.length === 0) { + throw new NotFound() + } + const versions = data.map(version => version.number) + return this.constructor.render({ version: latest(versions) }) + } } } diff --git a/services/gem/gem-version.tester.js b/services/gem/gem-version.tester.js index 53a610a457947..076ae91e92120 100644 --- a/services/gem/gem-version.tester.js +++ b/services/gem/gem-version.tester.js @@ -1,6 +1,9 @@ 'use strict' -const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') +const { + isVPlusDottedVersionAtLeastOne, + isVPlusDottedVersionNClausesWithOptionalSuffix, +} = require('../test-validators') const t = (module.exports = require('../tester').createServiceTester()) t.create('version (valid)').get('/formatador.json').expectBadge({ @@ -11,3 +14,14 @@ t.create('version (valid)').get('/formatador.json').expectBadge({ t.create('version (not found)') .get('/not-a-package.json') .expectBadge({ label: 'gem', message: 'not found' }) + +t.create('latest version (valid)') + .get('/flame/latest.json') + .expectBadge({ + label: 'gem', + message: isVPlusDottedVersionNClausesWithOptionalSuffix, + }) + +t.create('latest version (not found)') + .get('/not-a-package/latest.json') + .expectBadge({ label: 'gem', message: 'not found' }) From 4c9412539f205811ae7467b8481678f2f05e031b Mon Sep 17 00:00:00 2001 From: profgrammer Date: Thu, 6 May 2021 18:09:38 +0530 Subject: [PATCH 2/6] add regex for rc versions --- services/gem/gem-version.tester.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/services/gem/gem-version.tester.js b/services/gem/gem-version.tester.js index 076ae91e92120..d3a525f5c8168 100644 --- a/services/gem/gem-version.tester.js +++ b/services/gem/gem-version.tester.js @@ -2,7 +2,7 @@ const { isVPlusDottedVersionAtLeastOne, - isVPlusDottedVersionNClausesWithOptionalSuffix, + withRegex, } = require('../test-validators') const t = (module.exports = require('../tester').createServiceTester()) @@ -15,12 +15,15 @@ t.create('version (not found)') .get('/not-a-package.json') .expectBadge({ label: 'gem', message: 'not found' }) -t.create('latest version (valid)') - .get('/flame/latest.json') - .expectBadge({ - label: 'gem', - message: isVPlusDottedVersionNClausesWithOptionalSuffix, - }) +// this is the same as isVPlusDottedVersionNClausesWithOptionalSuffix from test-validators.js +// except that it also accepts regexes like 5.0.0.rc5 - the . before the rc5 is not accepted in the original +const isVPlusDottedVersionNClausesWithOptionalSuffix = withRegex( + /^v\d+(\.\d+)*([-+~.].*)?$/ +) +t.create('latest version (valid)').get('/flame/latest.json').expectBadge({ + label: 'gem', + message: isVPlusDottedVersionNClausesWithOptionalSuffix, +}) t.create('latest version (not found)') .get('/not-a-package/latest.json') From c4fcd9cf4946a940f673c02fcfe110c03959288a Mon Sep 17 00:00:00 2001 From: profgrammer Date: Thu, 6 May 2021 18:10:03 +0530 Subject: [PATCH 3/6] add example for latest version --- services/gem/gem-version.service.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/gem/gem-version.service.js b/services/gem/gem-version.service.js index 86cf1d197885b..5e4071d04b4ee 100644 --- a/services/gem/gem-version.service.js +++ b/services/gem/gem-version.service.js @@ -34,6 +34,12 @@ module.exports = class GemVersion extends BaseJsonService { staticPreview: this.render({ version: '2.1.0' }), keywords: ['ruby'], }, + { + title: 'Gem', + namedParams: { gem: 'flame', distribution: 'latest' }, + staticPreview: this.render({ version: '5.0.0.rc6' }), + keywords: ['ruby'], + }, ] static defaultBadgeData = { label: 'gem' } From 10b27f557a524f883d9604769ae6b2243a83d5a9 Mon Sep 17 00:00:00 2001 From: profgrammer Date: Fri, 7 May 2021 15:27:05 +0530 Subject: [PATCH 4/6] change distribution path param to latest query param --- services/gem/gem-version.service.js | 26 ++++++++++++++------------ services/gem/gem-version.tester.js | 4 ++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/services/gem/gem-version.service.js b/services/gem/gem-version.service.js index 5e4071d04b4ee..b8a07bb736ea9 100644 --- a/services/gem/gem-version.service.js +++ b/services/gem/gem-version.service.js @@ -3,7 +3,6 @@ const Joi = require('joi') const { renderVersionBadge, latest } = require('../version') const { BaseJsonService, NotFound } = require('..') -const { nonNegativeInteger } = require('../validators') const schema = Joi.object({ // In most cases `version` will be a SemVer but the registry doesn't @@ -14,19 +13,19 @@ const schema = Joi.object({ const versionSchema = Joi.array() .items( Joi.object({ - prerelease: Joi.boolean().required(), number: Joi.string().required(), - downloads_count: nonNegativeInteger, }) ) .min(1) .required() -const defaultDistribution = 'stable' +const queryParamSchema = Joi.object({ + latest: Joi.equal(''), +}).required() module.exports = class GemVersion extends BaseJsonService { static category = 'version' - static route = { base: 'gem/v', pattern: ':gem/:distribution?' } + static route = { base: 'gem/v', pattern: ':gem', queryParamSchema } static examples = [ { title: 'Gem', @@ -35,8 +34,11 @@ module.exports = class GemVersion extends BaseJsonService { keywords: ['ruby'], }, { - title: 'Gem', - namedParams: { gem: 'flame', distribution: 'latest' }, + title: 'Gem (latest)', + namedParams: { gem: 'flame' }, + queryParams: { + latest: null, + }, staticPreview: this.render({ version: '5.0.0.rc6' }), keywords: ['ruby'], }, @@ -62,17 +64,17 @@ module.exports = class GemVersion extends BaseJsonService { }) } - async handle({ gem, distribution = defaultDistribution }) { - if (distribution === defaultDistribution) { - const { version } = await this.fetch({ gem }) - return this.constructor.render({ version }) - } else { + async handle({ gem }, queryParams) { + if (queryParams && typeof queryParams.latest !== 'undefined') { const data = await this.fetchLatest({ gem }) if (!Array.isArray(data) || data.length === 0) { throw new NotFound() } const versions = data.map(version => version.number) return this.constructor.render({ version: latest(versions) }) + } else { + const { version } = await this.fetch({ gem }) + return this.constructor.render({ version }) } } } diff --git a/services/gem/gem-version.tester.js b/services/gem/gem-version.tester.js index d3a525f5c8168..c919e87b8523a 100644 --- a/services/gem/gem-version.tester.js +++ b/services/gem/gem-version.tester.js @@ -20,11 +20,11 @@ t.create('version (not found)') const isVPlusDottedVersionNClausesWithOptionalSuffix = withRegex( /^v\d+(\.\d+)*([-+~.].*)?$/ ) -t.create('latest version (valid)').get('/flame/latest.json').expectBadge({ +t.create('latest version (valid)').get('/flame.json?latest').expectBadge({ label: 'gem', message: isVPlusDottedVersionNClausesWithOptionalSuffix, }) t.create('latest version (not found)') - .get('/not-a-package/latest.json') + .get('/not-a-package.json?latest') .expectBadge({ label: 'gem', message: 'not found' }) From 786da3971f6309d14678591cb72204184e71ae6c Mon Sep 17 00:00:00 2001 From: profgrammer Date: Sat, 8 May 2021 11:59:23 +0530 Subject: [PATCH 5/6] rename latest to include_prereleases --- services/gem/gem-version.service.js | 8 ++++---- services/gem/gem-version.tester.js | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/services/gem/gem-version.service.js b/services/gem/gem-version.service.js index b8a07bb736ea9..28309d6607f58 100644 --- a/services/gem/gem-version.service.js +++ b/services/gem/gem-version.service.js @@ -20,7 +20,7 @@ const versionSchema = Joi.array() .required() const queryParamSchema = Joi.object({ - latest: Joi.equal(''), + include_prereleases: Joi.equal(''), }).required() module.exports = class GemVersion extends BaseJsonService { @@ -34,10 +34,10 @@ module.exports = class GemVersion extends BaseJsonService { keywords: ['ruby'], }, { - title: 'Gem (latest)', + title: 'Gem (including prereleases)', namedParams: { gem: 'flame' }, queryParams: { - latest: null, + include_prereleases: null, }, staticPreview: this.render({ version: '5.0.0.rc6' }), keywords: ['ruby'], @@ -65,7 +65,7 @@ module.exports = class GemVersion extends BaseJsonService { } async handle({ gem }, queryParams) { - if (queryParams && typeof queryParams.latest !== 'undefined') { + if (queryParams && typeof queryParams.include_prereleases !== 'undefined') { const data = await this.fetchLatest({ gem }) if (!Array.isArray(data) || data.length === 0) { throw new NotFound() diff --git a/services/gem/gem-version.tester.js b/services/gem/gem-version.tester.js index c919e87b8523a..6877777e14c33 100644 --- a/services/gem/gem-version.tester.js +++ b/services/gem/gem-version.tester.js @@ -20,11 +20,13 @@ t.create('version (not found)') const isVPlusDottedVersionNClausesWithOptionalSuffix = withRegex( /^v\d+(\.\d+)*([-+~.].*)?$/ ) -t.create('latest version (valid)').get('/flame.json?latest').expectBadge({ - label: 'gem', - message: isVPlusDottedVersionNClausesWithOptionalSuffix, -}) +t.create('version including prereleases (valid)') + .get('/flame.json?include_prereleases') + .expectBadge({ + label: 'gem', + message: isVPlusDottedVersionNClausesWithOptionalSuffix, + }) -t.create('latest version (not found)') - .get('/not-a-package.json?latest') +t.create('version including prereleases (not found)') + .get('/not-a-package.json?include_prereleases') .expectBadge({ label: 'gem', message: 'not found' }) From 8e63a858084e359b56fdff1f68c980770b441ec2 Mon Sep 17 00:00:00 2001 From: profgrammer Date: Sat, 8 May 2021 14:14:43 +0530 Subject: [PATCH 6/6] remove redundant data validation --- services/gem/gem-version.service.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/gem/gem-version.service.js b/services/gem/gem-version.service.js index 28309d6607f58..fba13f7d933d4 100644 --- a/services/gem/gem-version.service.js +++ b/services/gem/gem-version.service.js @@ -2,7 +2,7 @@ const Joi = require('joi') const { renderVersionBadge, latest } = require('../version') -const { BaseJsonService, NotFound } = require('..') +const { BaseJsonService } = require('..') const schema = Joi.object({ // In most cases `version` will be a SemVer but the registry doesn't @@ -67,9 +67,6 @@ module.exports = class GemVersion extends BaseJsonService { async handle({ gem }, queryParams) { if (queryParams && typeof queryParams.include_prereleases !== 'undefined') { const data = await this.fetchLatest({ gem }) - if (!Array.isArray(data) || data.length === 0) { - throw new NotFound() - } const versions = data.map(version => version.number) return this.constructor.render({ version: latest(versions) }) } else {