From 8133c761ed3d62cfad7344f6297a124ebc0f139d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Fri, 29 Jul 2022 10:09:59 -0300 Subject: [PATCH 1/6] [pypi] Add Framework Version Badges support --- services/pypi/pypi-django-versions.service.js | 57 ++----- services/pypi/pypi-django-versions.tester.js | 34 ++-- .../pypi/pypi-framework-versions.service.js | 111 ++++++++++++ .../pypi/pypi-framework-versions.tester.js | 161 ++++++++++++++++++ services/pypi/pypi-helpers.js | 20 +-- services/pypi/pypi-helpers.spec.js | 8 +- 6 files changed, 308 insertions(+), 83 deletions(-) create mode 100644 services/pypi/pypi-framework-versions.service.js create mode 100644 services/pypi/pypi-framework-versions.tester.js diff --git a/services/pypi/pypi-django-versions.service.js b/services/pypi/pypi-django-versions.service.js index ffe0fbf1bf37f..d74a4b238a105 100644 --- a/services/pypi/pypi-django-versions.service.js +++ b/services/pypi/pypi-django-versions.service.js @@ -1,45 +1,12 @@ -import PypiBase from './pypi-base.js' -import { sortDjangoVersions, parseClassifiers } from './pypi-helpers.js' - -export default class PypiDjangoVersions extends PypiBase { - static category = 'platform-support' - - static route = this.buildRoute('pypi/djversions') - - static examples = [ - { - title: 'PyPI - Django Version', - pattern: ':packageName', - namedParams: { packageName: 'djangorestframework' }, - staticPreview: this.render({ versions: ['1.11', '2.0', '2.1'] }), - keywords: ['python'], - }, - ] - - static defaultBadgeData = { label: 'django versions' } - - static render({ versions }) { - if (versions.length > 0) { - return { - message: sortDjangoVersions(versions).join(' | '), - color: 'blue', - } - } else { - return { - message: 'missing', - color: 'red', - } - } - } - - async handle({ egg }) { - const packageData = await this.fetch({ egg }) - - const versions = parseClassifiers( - packageData, - /^Framework :: Django :: ([\d.]+)$/ - ) - - return this.constructor.render({ versions }) - } -} +import { redirector } from '../index.js' + +export default redirector({ + category: 'platform-support', + route: { + base: 'pypi/djversions', + pattern: ':packageName*', + }, + transformPath: ({ packageName }) => + `/pypi/frameworkversions/Django/${packageName}`, + dateAdded: new Date('2022-07-28'), +}) diff --git a/services/pypi/pypi-django-versions.tester.js b/services/pypi/pypi-django-versions.tester.js index 83fdca15ec086..9afec1127f086 100644 --- a/services/pypi/pypi-django-versions.tester.js +++ b/services/pypi/pypi-django-versions.tester.js @@ -1,32 +1,24 @@ -import Joi from 'joi' import { createServiceTester } from '../tester.js' export const t = await createServiceTester() -const isPipeSeparatedDjangoVersions = Joi.string().regex( - /^([1-9]\.[0-9]+(?: \| )?)+$/ +t.create( + 'redirect supported django versions (valid, package version in request)' ) - -t.create('supported django versions (valid, package version in request)') .get('/djangorestframework/3.7.3.json') - .expectBadge({ - label: 'django versions', - message: isPipeSeparatedDjangoVersions, - }) + .expectRedirect( + '/pypi/frameworkversions/Django/djangorestframework/3.7.3.json' + ) -t.create('supported django versions (valid, no package version specified)') +t.create( + 'redirect supported django versions (valid, no package version specified)' +) .get('/djangorestframework.json') - .expectBadge({ - label: 'django versions', - message: isPipeSeparatedDjangoVersions, - }) + .expectRedirect('/pypi/frameworkversions/Django/djangorestframework.json') -t.create('supported django versions (no versions specified)') +t.create('redirect supported django versions (no versions specified)') .get('/django/1.11.json') - .expectBadge({ label: 'django versions', message: 'missing' }) + .expectRedirect('/pypi/frameworkversions/Django/django/1.11.json') -t.create('supported django versions (invalid)') +t.create('redirect supported django versions (invalid)') .get('/not-a-package.json') - .expectBadge({ - label: 'django versions', - message: 'package or version not found', - }) + .expectRedirect('/pypi/frameworkversions/Django/not-a-package.json') diff --git a/services/pypi/pypi-framework-versions.service.js b/services/pypi/pypi-framework-versions.service.js new file mode 100644 index 0000000000000..90a6c7cbbdbb1 --- /dev/null +++ b/services/pypi/pypi-framework-versions.service.js @@ -0,0 +1,111 @@ +import PypiBase from './pypi-base.js' +import { sortPypiVersions, parseClassifiers } from './pypi-helpers.js' + +const documentation = ` +

+ This service currently support the following Frameworks:
+ AWS CDK, + Django, + Django CMS, + JupyterLab, + Odoo, + Plone, + Wagtail, + Zope. +

+` + +const frameworkNameMap = { + awscdk: { + name: 'AWS CDK', + classifier: 'AWS CDK', + }, + django: { + name: 'Django', + classifier: 'Django', + }, + djangocms: { + name: 'Django CMS', + classifier: 'Django CMS', + }, + jupyterlab: { + name: 'JupyterLab', + classifier: 'Jupyter :: JupyterLab', + }, + odoo: { + name: 'Odoo', + classifier: 'Odoo', + }, + plone: { + name: 'Plone', + classifier: 'Plone', + }, + wagtail: { + name: 'Wagtail', + classifier: 'Wagtail', + }, + zope: { + name: 'Zope', + classifier: 'Zope', + }, +} + +export default class PypiFrameworkVersion extends PypiBase { + static category = 'platform-support' + + static route = { + base: 'pypi/frameworkversions', + pattern: + ':frameworkName(awscdk|django|djangocms|jupyterlab|odoo|plone|wagtail|zope)/:packageName*', + } + + static examples = [ + { + title: 'PyPI - Framework Version', + namedParams: { + frameworkName: 'Plone', + packageName: 'plone.volto', + }, + staticPreview: this.render({ + frameworkName: 'Plone', + versions: ['5.2', '6.0'], + }), + keywords: ['python'], + documentation, + }, + ] + + static defaultBadgeData = { label: 'versions' } + + static render({ name, versions }) { + name = name ? name.toLowerCase() : '' + const label = `${name} versions` + if (versions.length > 0) { + return { + label, + message: sortPypiVersions(versions).join(' | '), + color: 'blue', + } + } else { + return { + label, + message: 'missing', + color: 'red', + } + } + } + + async handle({ frameworkName, packageName }) { + const classifier = frameworkNameMap[frameworkName] + ? frameworkNameMap[frameworkName].classifier + : frameworkName + const name = frameworkNameMap[frameworkName] + ? frameworkNameMap[frameworkName].name + : frameworkName + const regex = new RegExp(`^Framework :: ${classifier} :: ([\\d.]+)$`) + const packageData = await this.fetch({ egg: packageName }) + const versions = parseClassifiers(packageData, regex) + + return this.constructor.render({ name, versions }) + } +} diff --git a/services/pypi/pypi-framework-versions.tester.js b/services/pypi/pypi-framework-versions.tester.js new file mode 100644 index 0000000000000..a763eae5a745f --- /dev/null +++ b/services/pypi/pypi-framework-versions.tester.js @@ -0,0 +1,161 @@ +import Joi from 'joi' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +const isPipeSeparatedFrameworkVersions = Joi.string().regex( + /^([1-9]+(\.[0-9]+)?(?: \| )?)+$/ +) + +t.create('supported django versions (valid, package version in request)') + .get('/django/djangorestframework/3.7.3.json') + .expectBadge({ + label: 'django versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported django versions (valid, no package version specified)') + .get('/django/djangorestframework.json') + .expectBadge({ + label: 'django versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported django versions (no versions specified)') + .get('/django/django/1.11.json') + .expectBadge({ label: 'django versions', message: 'missing' }) + +t.create('supported django versions (invalid)') + .get('/django/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) + +t.create('supported plone versions (valid, package version in request)') + .get('/plone/plone.rest/1.6.2.json') + .expectBadge({ label: 'plone versions', message: '4.3 | 5.0 | 5.1 | 5.2' }) + +t.create('supported plone versions (valid, no package version specified)') + .get('/plone/plone.rest.json') + .expectBadge({ + label: 'plone versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported plone versions (invalid)') + .get('/plone/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) + +t.create('supported zope versions (valid, package version in request)') + .get('/zope/plone/5.2.9.json') + .expectBadge({ label: 'zope versions', message: '4' }) + +t.create('supported zope versions (valid, no package version specified)') + .get('/zope/Plone.json') + .expectBadge({ + label: 'zope versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported zope versions (invalid)') + .get('/zope/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) + +t.create('supported wagtail versions (valid, package version in request)') + .get('/wagtail/wagtail-headless-preview/0.3.0.json') + .expectBadge({ label: 'wagtail versions', message: '2 | 3' }) + +t.create('supported wagtail versions (valid, no package version specified)') + .get('/wagtail/wagtail-headless-preview.json') + .expectBadge({ + label: 'wagtail versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported wagtail versions (invalid)') + .get('/wagtail/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) + +t.create('supported django cms versions (valid, package version in request)') + .get('/djangocms/djangocms-ads/1.1.0.json') + .expectBadge({ + label: 'django cms versions', + message: '3.7 | 3.8 | 3.9 | 3.10', + }) + +t.create('supported django cms versions (valid, no package version specified)') + .get('/djangocms/djangocms-ads.json') + .expectBadge({ + label: 'django cms versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported django cms versions (invalid)') + .get('/djangocms/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) + +t.create('supported odoo versions (valid, package version in request)') + .get('/odoo/odoo-addon-sale-tier-validation/15.0.1.0.0.6.json') + .expectBadge({ label: 'odoo versions', message: '15.0' }) + +t.create('supported odoo versions (valid, no package version specified)') + .get('/odoo/odoo-addon-sale-tier-validation.json') + .expectBadge({ + label: 'odoo versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported odoo versions (invalid)') + .get('/odoo/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) + +t.create('supported aws cdk versions (valid, package version in request)') + .get('/awscdk/aws-cdk.aws-glue-alpha/2.34.0a0.json') + .expectBadge({ label: 'aws cdk versions', message: '2' }) + +t.create('supported aws cdk versions (valid, no package version specified)') + .get('/awscdk/aws-cdk.aws-glue-alpha.json') + .expectBadge({ + label: 'aws cdk versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported aws cdk versions (invalid)') + .get('/awscdk/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) + +t.create('supported jupyterlab versions (valid, package version in request)') + .get('/jupyterlab/structured-text/0.0.2.json') + .expectBadge({ label: 'jupyterlab versions', message: '3' }) + +t.create('supported jupyterlab versions (valid, no package version specified)') + .get('/jupyterlab/structured-text.json') + .expectBadge({ + label: 'jupyterlab versions', + message: isPipeSeparatedFrameworkVersions, + }) + +t.create('supported jupyterlab versions (invalid)') + .get('/jupyterlab/not-a-package.json') + .expectBadge({ + label: 'versions', + message: 'package or version not found', + }) diff --git a/services/pypi/pypi-helpers.js b/services/pypi/pypi-helpers.js index 0e16534d8bd89..706955b4f969b 100644 --- a/services/pypi/pypi-helpers.js +++ b/services/pypi/pypi-helpers.js @@ -6,7 +6,7 @@ our own functions to parse and sort django versions */ -function parseDjangoVersionString(str) { +function parsePypiVersionString(str) { if (typeof str !== 'string') { return false } @@ -20,18 +20,12 @@ function parseDjangoVersionString(str) { } // Sort an array of django versions low to high. -function sortDjangoVersions(versions) { +function sortPypiVersions(versions) { return versions.sort((a, b) => { - if ( - parseDjangoVersionString(a).major === parseDjangoVersionString(b).major - ) { - return ( - parseDjangoVersionString(a).minor - parseDjangoVersionString(b).minor - ) + if (parsePypiVersionString(a).major === parsePypiVersionString(b).major) { + return parsePypiVersionString(a).minor - parsePypiVersionString(b).minor } else { - return ( - parseDjangoVersionString(a).major - parseDjangoVersionString(b).major - ) + return parsePypiVersionString(a).major - parsePypiVersionString(b).major } }) } @@ -101,8 +95,8 @@ function getPackageFormats(packageData) { export { parseClassifiers, - parseDjangoVersionString, - sortDjangoVersions, + parsePypiVersionString, + sortPypiVersions, getLicenses, getPackageFormats, } diff --git a/services/pypi/pypi-helpers.spec.js b/services/pypi/pypi-helpers.spec.js index d5a54dd5f505f..17e42b3b4fd5a 100644 --- a/services/pypi/pypi-helpers.spec.js +++ b/services/pypi/pypi-helpers.spec.js @@ -1,8 +1,8 @@ import { test, given, forCases } from 'sazerac' import { parseClassifiers, - parseDjangoVersionString, - sortDjangoVersions, + parsePypiVersionString, + sortPypiVersions, getLicenses, getPackageFormats, } from './pypi-helpers.js' @@ -60,7 +60,7 @@ describe('PyPI helpers', function () { given(classifiersFixture, /^(?!.*)*$/).expect([]) }) - test(parseDjangoVersionString, function () { + test(parsePypiVersionString, function () { given('1').expect({ major: 1, minor: 0 }) given('1.0').expect({ major: 1, minor: 0 }) given('7.2').expect({ major: 7, minor: 2 }) @@ -69,7 +69,7 @@ describe('PyPI helpers', function () { given('foo').expect({ major: 0, minor: 0 }) }) - test(sortDjangoVersions, function () { + test(sortPypiVersions, function () { // Each of these includes a different variant: 2.0, 2, and 2.0rc1. given(['2.0', '1.9', '10', '1.11', '2.1', '2.11']).expect([ '1.9', From 687243b6885b96c88f4ac354a7e3402063a5bae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 1 Aug 2022 11:21:05 -0300 Subject: [PATCH 2/6] Fix redirect from Django versions --- services/pypi/pypi-django-versions.service.js | 2 +- services/pypi/pypi-django-versions.tester.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/pypi/pypi-django-versions.service.js b/services/pypi/pypi-django-versions.service.js index d74a4b238a105..da07cf8f0cf9c 100644 --- a/services/pypi/pypi-django-versions.service.js +++ b/services/pypi/pypi-django-versions.service.js @@ -7,6 +7,6 @@ export default redirector({ pattern: ':packageName*', }, transformPath: ({ packageName }) => - `/pypi/frameworkversions/Django/${packageName}`, + `/pypi/frameworkversions/django/${packageName}`, dateAdded: new Date('2022-07-28'), }) diff --git a/services/pypi/pypi-django-versions.tester.js b/services/pypi/pypi-django-versions.tester.js index 9afec1127f086..adba0a5e2b892 100644 --- a/services/pypi/pypi-django-versions.tester.js +++ b/services/pypi/pypi-django-versions.tester.js @@ -6,19 +6,19 @@ t.create( ) .get('/djangorestframework/3.7.3.json') .expectRedirect( - '/pypi/frameworkversions/Django/djangorestframework/3.7.3.json' + '/pypi/frameworkversions/django/djangorestframework/3.7.3.json' ) t.create( 'redirect supported django versions (valid, no package version specified)' ) .get('/djangorestframework.json') - .expectRedirect('/pypi/frameworkversions/Django/djangorestframework.json') + .expectRedirect('/pypi/frameworkversions/django/djangorestframework.json') t.create('redirect supported django versions (no versions specified)') .get('/django/1.11.json') - .expectRedirect('/pypi/frameworkversions/Django/django/1.11.json') + .expectRedirect('/pypi/frameworkversions/django/django/1.11.json') t.create('redirect supported django versions (invalid)') .get('/not-a-package.json') - .expectRedirect('/pypi/frameworkversions/Django/not-a-package.json') + .expectRedirect('/pypi/frameworkversions/django/not-a-package.json') From 4f51b8dd9e3ff65c8abd8c087d635b95a7efeb74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 1 Aug 2022 11:22:09 -0300 Subject: [PATCH 3/6] Fix staticPreview --- services/pypi/pypi-framework-versions.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/pypi/pypi-framework-versions.service.js b/services/pypi/pypi-framework-versions.service.js index 90a6c7cbbdbb1..b9fe026444096 100644 --- a/services/pypi/pypi-framework-versions.service.js +++ b/services/pypi/pypi-framework-versions.service.js @@ -67,7 +67,7 @@ export default class PypiFrameworkVersion extends PypiBase { packageName: 'plone.volto', }, staticPreview: this.render({ - frameworkName: 'Plone', + name: 'Plone', versions: ['5.2', '6.0'], }), keywords: ['python'], From 90da717771fc1c0c4ed0df3a586222bffef0d471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 1 Aug 2022 11:32:55 -0300 Subject: [PATCH 4/6] Refactor service to remove duplication --- .../pypi/pypi-framework-versions.service.js | 29 +++++++------------ .../pypi/pypi-framework-versions.tester.js | 12 ++++---- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/services/pypi/pypi-framework-versions.service.js b/services/pypi/pypi-framework-versions.service.js index b9fe026444096..ccf8507b35858 100644 --- a/services/pypi/pypi-framework-versions.service.js +++ b/services/pypi/pypi-framework-versions.service.js @@ -1,22 +1,8 @@ import PypiBase from './pypi-base.js' import { sortPypiVersions, parseClassifiers } from './pypi-helpers.js' -const documentation = ` -

- This service currently support the following Frameworks:
- AWS CDK, - Django, - Django CMS, - JupyterLab, - Odoo, - Plone, - Wagtail, - Zope. -

-` - const frameworkNameMap = { - awscdk: { + 'aws-cdk': { name: 'AWS CDK', classifier: 'AWS CDK', }, @@ -24,7 +10,7 @@ const frameworkNameMap = { name: 'Django', classifier: 'Django', }, - djangocms: { + 'django-cms': { name: 'Django CMS', classifier: 'Django CMS', }, @@ -50,13 +36,20 @@ const frameworkNameMap = { }, } +const documentation = ` +

+ This service currently support the following Frameworks:
+ ${Object.values(frameworkNameMap).map(obj => `${obj.name}`)} +

+` export default class PypiFrameworkVersion extends PypiBase { static category = 'platform-support' static route = { base: 'pypi/frameworkversions', - pattern: - ':frameworkName(awscdk|django|djangocms|jupyterlab|odoo|plone|wagtail|zope)/:packageName*', + pattern: `:frameworkName(${Object.keys(frameworkNameMap).join( + '|' + )})/:packageName*`, } static examples = [ diff --git a/services/pypi/pypi-framework-versions.tester.js b/services/pypi/pypi-framework-versions.tester.js index a763eae5a745f..e04cd1e689cd4 100644 --- a/services/pypi/pypi-framework-versions.tester.js +++ b/services/pypi/pypi-framework-versions.tester.js @@ -86,21 +86,21 @@ t.create('supported wagtail versions (invalid)') }) t.create('supported django cms versions (valid, package version in request)') - .get('/djangocms/djangocms-ads/1.1.0.json') + .get('/django-cms/djangocms-ads/1.1.0.json') .expectBadge({ label: 'django cms versions', message: '3.7 | 3.8 | 3.9 | 3.10', }) t.create('supported django cms versions (valid, no package version specified)') - .get('/djangocms/djangocms-ads.json') + .get('/django-cms/djangocms-ads.json') .expectBadge({ label: 'django cms versions', message: isPipeSeparatedFrameworkVersions, }) t.create('supported django cms versions (invalid)') - .get('/djangocms/not-a-package.json') + .get('/django-cms/not-a-package.json') .expectBadge({ label: 'versions', message: 'package or version not found', @@ -125,18 +125,18 @@ t.create('supported odoo versions (invalid)') }) t.create('supported aws cdk versions (valid, package version in request)') - .get('/awscdk/aws-cdk.aws-glue-alpha/2.34.0a0.json') + .get('/aws-cdk/aws-cdk.aws-glue-alpha/2.34.0a0.json') .expectBadge({ label: 'aws cdk versions', message: '2' }) t.create('supported aws cdk versions (valid, no package version specified)') - .get('/awscdk/aws-cdk.aws-glue-alpha.json') + .get('/aws-cdk/aws-cdk.aws-glue-alpha.json') .expectBadge({ label: 'aws cdk versions', message: isPipeSeparatedFrameworkVersions, }) t.create('supported aws cdk versions (invalid)') - .get('/awscdk/not-a-package.json') + .get('/aws-cdk/not-a-package.json') .expectBadge({ label: 'versions', message: 'package or version not found', From 57f17cfceb8164702efd972c6006a8584f4e2882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 1 Aug 2022 11:34:09 -0300 Subject: [PATCH 5/6] Rename to Versions from Framework Classifiers --- services/pypi/pypi-framework-versions.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/pypi/pypi-framework-versions.service.js b/services/pypi/pypi-framework-versions.service.js index ccf8507b35858..130d1fbde0c69 100644 --- a/services/pypi/pypi-framework-versions.service.js +++ b/services/pypi/pypi-framework-versions.service.js @@ -54,7 +54,7 @@ export default class PypiFrameworkVersion extends PypiBase { static examples = [ { - title: 'PyPI - Framework Version', + title: 'PyPI - Versions from Framework Classifiers', namedParams: { frameworkName: 'Plone', packageName: 'plone.volto', From 10d027be7236ddf6e3e3e47d473f881af3fda5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 1 Aug 2022 11:45:33 -0300 Subject: [PATCH 6/6] Refactor render and handle to thrown an exception --- .../pypi/pypi-framework-versions.service.js | 23 +++++++++---------- .../pypi/pypi-framework-versions.tester.js | 5 +++- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/services/pypi/pypi-framework-versions.service.js b/services/pypi/pypi-framework-versions.service.js index 130d1fbde0c69..1da6edb1e8750 100644 --- a/services/pypi/pypi-framework-versions.service.js +++ b/services/pypi/pypi-framework-versions.service.js @@ -1,3 +1,4 @@ +import { InvalidResponse } from '../index.js' import PypiBase from './pypi-base.js' import { sortPypiVersions, parseClassifiers } from './pypi-helpers.js' @@ -73,18 +74,10 @@ export default class PypiFrameworkVersion extends PypiBase { static render({ name, versions }) { name = name ? name.toLowerCase() : '' const label = `${name} versions` - if (versions.length > 0) { - return { - label, - message: sortPypiVersions(versions).join(' | '), - color: 'blue', - } - } else { - return { - label, - message: 'missing', - color: 'red', - } + return { + label, + message: sortPypiVersions(versions).join(' | '), + color: 'blue', } } @@ -99,6 +92,12 @@ export default class PypiFrameworkVersion extends PypiBase { const packageData = await this.fetch({ egg: packageName }) const versions = parseClassifiers(packageData, regex) + if (versions.length === 0) { + throw new InvalidResponse({ + prettyMessage: `${name} versions are missing for ${packageName}`, + }) + } + return this.constructor.render({ name, versions }) } } diff --git a/services/pypi/pypi-framework-versions.tester.js b/services/pypi/pypi-framework-versions.tester.js index e04cd1e689cd4..89d2f9821b7a7 100644 --- a/services/pypi/pypi-framework-versions.tester.js +++ b/services/pypi/pypi-framework-versions.tester.js @@ -22,7 +22,10 @@ t.create('supported django versions (valid, no package version specified)') t.create('supported django versions (no versions specified)') .get('/django/django/1.11.json') - .expectBadge({ label: 'django versions', message: 'missing' }) + .expectBadge({ + label: 'versions', + message: 'Django versions are missing for django/1.11', + }) t.create('supported django versions (invalid)') .get('/django/not-a-package.json')