From 1f6de9bb6e2290c2ff39f816fe5c046a28e20295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Fri, 15 Dec 2023 09:55:15 +0100 Subject: [PATCH 1/2] feat(nuget/v3): Added support for new `source` query parameter --- services/nuget/nuget-v3-service-family.js | 48 +++++++++++++++++------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/services/nuget/nuget-v3-service-family.js b/services/nuget/nuget-v3-service-family.js index 3a82e7b41d1d7..c557033f20c06 100644 --- a/services/nuget/nuget-v3-service-family.js +++ b/services/nuget/nuget-v3-service-family.js @@ -1,6 +1,7 @@ import Joi from 'joi' import RouteBuilder from '../route-builder.js' import { BaseJsonService, NotFound } from '../index.js' +import { optionalUrl } from '../validators.js' import { renderVersionBadge, renderDownloadBadge, @@ -89,6 +90,10 @@ async function fetch( }) } +const queryParamSchema = Joi.object({ + source: optionalUrl, +}).required() + /* * Create a version and download service for a NuGet v2 API. Return an object * containing both services. @@ -113,13 +118,24 @@ function createServiceFamily({ apiBaseUrl, withFeed = true, }) { + /** + * Extract source parameters + */ + + function unpackParams({ source = apiBaseUrl }) { + return { source: `${source}/v3` } + } + class NugetVersionService extends BaseJsonService { static category = 'version' - static route = buildRoute({ serviceBaseUrl, withTenant, withFeed }) - .push('(v|vpre)', 'which') - .push('(.+?)', 'packageName') - .toObject() + static route = { + ...buildRoute({ serviceBaseUrl, withTenant, withFeed }) + .push('(v|vpre)', 'which') + .push('(.+?)', 'packageName') + .toObject(), + queryParamSchema, + } static examples = [] @@ -146,11 +162,14 @@ function createServiceFamily({ } } - async handle({ tenant, feed, which, packageName }) { + async handle({ tenant, feed, which, packageName }, queryParams) { const includePrereleases = which === 'vpre' + + const { source } = unpackParams(queryParams) + const baseUrl = apiUrl({ withTenant, - apiBaseUrl, + apiBaseUrl: source, apiDomain, tenant, withFeed, @@ -165,10 +184,13 @@ function createServiceFamily({ class NugetDownloadService extends BaseJsonService { static category = 'downloads' - static route = buildRoute({ serviceBaseUrl, withTenant, withFeed }) - .push('dt') - .push('(.+?)', 'packageName') - .toObject() + static route = { + ...buildRoute({ serviceBaseUrl, withTenant, withFeed }) + .push('dt') + .push('(.+?)', 'packageName') + .toObject(), + queryParamSchema, + } static examples = [] @@ -190,10 +212,12 @@ function createServiceFamily({ } } - async handle({ tenant, feed, which, packageName }) { + async handle({ tenant, feed, which, packageName }, queryParams) { + const { source } = unpackParams(queryParams) + const baseUrl = apiUrl({ withTenant, - apiBaseUrl, + apiBaseUrl: source, apiDomain, tenant, withFeed, From bb2682ff3e36e13e24d60332f86e0efecac5ee45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Fri, 15 Dec 2023 10:22:15 +0100 Subject: [PATCH 2/2] feat(baget): Added version & download count badges --- services/baget/baget.service.js | 41 +++++++++++++++++ services/nuget/nuget-v3-service-family.js | 56 +++++++++++++++-------- 2 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 services/baget/baget.service.js diff --git a/services/baget/baget.service.js b/services/baget/baget.service.js new file mode 100644 index 0000000000000..b4ed482cb386a --- /dev/null +++ b/services/baget/baget.service.js @@ -0,0 +1,41 @@ +import { createServiceFamily } from '../nuget/nuget-v3-service-family.js' + +const { NugetVersionService: Version, NugetDownloadService: Downloads } = + createServiceFamily({ + defaultLabel: 'baget', + serviceBaseUrl: 'baget', + withTenant: false, + withFeed: false, + withQueryNamedParams: false, + packageDataIncludesVersion: true, + }) + +class BagetVersionService extends Version { + static examples = [ + { + title: 'Baget', + pattern: 'v/:packageName', + namedParams: { packageName: 'Microsoft.AspNet.Mvc' }, + staticPreview: this.render({ version: '5.2.4' }), + }, + { + title: 'Baget (with prereleases)', + pattern: 'vpre/:packageName', + namedParams: { packageName: 'Microsoft.AspNet.Mvc' }, + staticPreview: this.render({ version: '5.2.5-preview1' }), + }, + ] +} + +class BagetDownloadService extends Downloads { + static examples = [ + { + title: 'Baget', + pattern: 'dt/:packageName', + namedParams: { packageName: 'Microsoft.AspNet.Mvc' }, + staticPreview: this.render({ downloads: 49e6 }), + }, + ] +} + +export { BagetVersionService, BagetDownloadService } diff --git a/services/nuget/nuget-v3-service-family.js b/services/nuget/nuget-v3-service-family.js index c557033f20c06..5258f9b8a2b8f 100644 --- a/services/nuget/nuget-v3-service-family.js +++ b/services/nuget/nuget-v3-service-family.js @@ -62,6 +62,7 @@ const schema = Joi.object({ .default([]), totalDownloads: Joi.number().integer(), totaldownloads: Joi.number().integer(), + version: Joi.string().optional(), // Available in BaGet server }), ) .max(1) @@ -73,16 +74,18 @@ const schema = Joi.object({ */ async function fetch( serviceInstance, - { baseUrl, packageName, includePrereleases = false }, + { baseUrl, packageName, includePrereleases = true, queryNamedParams }, ) { return serviceInstance._requestJson({ schema, url: await searchServiceUrl(baseUrl, 'SearchQueryService'), options: { searchParams: { - q: `packageid:${encodeURIComponent(packageName.toLowerCase())}`, + q: queryNamedParams + ? `packageid:${encodeURIComponent(packageName.toLowerCase())}` + : encodeURIComponent(packageName.toLowerCase()), // Include prerelease versions. - prerelease: 'true', + prerelease: includePrereleases, // Include packages with SemVer 2 version numbers. semVerLevel: '2', }, @@ -117,12 +120,15 @@ function createServiceFamily({ apiDomain, apiBaseUrl, withFeed = true, + withQueryNamedParams = true, + packageDataIncludesVersion = false, }) { /** * Extract source parameters */ function unpackParams({ source = apiBaseUrl }) { + if (source.includes('v3')) return { source } return { source: `${source}/v3` } } @@ -151,15 +157,17 @@ function createServiceFamily({ * Extract version information from the raw package info. */ transform({ json, includePrereleases }) { - if (json.data.length === 1 && json.data[0].versions.length > 0) { - const { versions: packageVersions } = json.data[0] - const versions = packageVersions.map(item => - stripBuildMetadata(item.version), - ) - return selectVersion(versions, includePrereleases) - } else { + if (json.data.length !== 1 || json.data[0].versions.length <= 0) throw new NotFound({ prettyMessage: 'package not found' }) - } + + // Baget server includes latest package version in the data response + if (packageDataIncludesVersion) return json.data[0].version + + const { versions: packageVersions } = json.data[0] + const versions = packageVersions.map(item => + stripBuildMetadata(item.version), + ) + return selectVersion(versions, includePrereleases) } async handle({ tenant, feed, which, packageName }, queryParams) { @@ -175,7 +183,12 @@ function createServiceFamily({ withFeed, feed, }) - const json = await fetch(this, { baseUrl, packageName }) + const json = await fetch(this, { + baseUrl, + packageName, + includePrereleases, + queryNamedParams: withQueryNamedParams, + }) const version = this.transform({ json, includePrereleases }) return this.constructor.render({ version, feed }) } @@ -202,14 +215,13 @@ function createServiceFamily({ * Extract download count from the raw package. */ transform({ json }) { - if (json.data.length === 1) { - const packageInfo = json.data[0] - // Official NuGet server uses "totalDownloads" whereas MyGet uses - // "totaldownloads" (lowercase D). Ugh. - return packageInfo.totalDownloads || packageInfo.totaldownloads || 0 - } else { + if (json.data.length !== 1) throw new NotFound({ prettyMessage: 'package not found' }) - } + + const packageInfo = json.data[0] + // Official NuGet server uses "totalDownloads" whereas MyGet uses + // "totaldownloads" (lowercase D). Ugh. + return packageInfo.totalDownloads || packageInfo.totaldownloads || 0 } async handle({ tenant, feed, which, packageName }, queryParams) { @@ -223,7 +235,11 @@ function createServiceFamily({ withFeed, feed, }) - const json = await fetch(this, { baseUrl, packageName }) + const json = await fetch(this, { + baseUrl, + packageName, + queryNamedParams: withQueryNamedParams, + }) const downloads = this.transform({ json }) return this.constructor.render({ downloads }) }