Skip to content

Instantly share code, notes, and snippets.

@devhawk
Created November 7, 2019 00:20
Show Gist options
  • Select an option

  • Save devhawk/225f22cb7da69752353ef983ef5f515c to your computer and use it in GitHub Desktop.

Select an option

Save devhawk/225f22cb7da69752353ef983ef5f515c to your computer and use it in GitHub Desktop.
'use strict'
const { fetchPackage, getLatestVersion } = require('../nuget/nuget-v3-helpers')
const { renderVersionBadge } = require('../nuget/nuget-helpers')
const { BaseJsonService } = require('..')
const { NotFound } = require('..')
class AzureArtifactsNugetVersionService extends BaseJsonService {
static get category() {
return 'version'
}
static get route() {
return {
base: 'azure-devops',
pattern: ':which(v|vpre)/:organization/:project/:feed/:view(all|local|prerelease|release)?/:packageName'
}
}
static get examples() {
return []
}
static get defaultBadgeData() {
return {
label: 'nuget',
}
}
static render(props) {
return renderVersionBadge(props)
}
async handle({ which, organization, project, feed, view, packageName }) {
function getViewName() {
if (!view)
return '';
switch (view) {
case 'all':
return '';
case 'local':
return '%40Local'
case 'prerelease':
return '%40Prerelease'
case 'release':
return '%40Release'
default:
throw new NotFound({ prettyMessage: 'invalid view name: ' + view })
}
}
const viewName = getViewName();
const baseUrl = `https://pkgs.dev.azure.com/${organization}/${project}/_packaging/${feed}${viewName}/nuget/v3`
const { versions } = await fetchPackage(this, { baseUrl, packageName })
const { version } = getLatestVersion(versions, which === 'vpre')
return this.constructor.render({ version })
}
}
module.exports = { AzureArtifactsNugetVersionService }
'use strict'
const { promisify } = require('util')
const Joi = require('@hapi/joi')
const { regularUpdate } = require('../../core/legacy/regular-update')
const { NotFound } = require('..')
const semver = require('semver')
function randomElementFrom(items) {
const index = Math.floor(Math.random() * items.length)
return items[index]
}
/*
* Hit the service index endpoint and return a SearchQueryService URL, chosen
* at random. Cache the responses, but return a different random URL each time.
*/
async function searchQueryServiceUrl(baseUrl) {
// Should we really be caching all these NuGet feeds in memory?
const searchQueryServices = await promisify(regularUpdate)({
url: `${baseUrl}/index.json`,
// The endpoint changes once per year (ie, a period of n = 1 year).
// We minimize the users' waiting time for information.
// With l = latency to fetch the endpoint and x = endpoint update period
// both in years, the yearly number of queries for the endpoint are 1/x,
// and when the endpoint changes, we wait for up to x years to get the
// right endpoint.
// So the waiting time within n years is n*l/x + x years, for which a
// derivation yields an optimum at x = sqrt(n*l), roughly 42 minutes.
intervalMillis: 42 * 60 * 1000,
json: true,
scraper: json =>
json.resources.filter(
resource => resource['@type'] === 'SearchQueryService/3.0.0-beta'
),
})
return randomElementFrom(searchQueryServices)['@id']
}
const schema = Joi.object({
data: Joi.array()
.items(
Joi.object({
id: Joi.string().required(),
versions: Joi.array()
.items(
Joi.object({
version: Joi.string().required(),
})
)
.default([]),
totalDownloads: Joi.number().integer(),
totaldownloads: Joi.number().integer(),
})
)
// .max(1)
.default([]),
}).required()
/*
* Get information about a single package.
*/
async function fetchPackage(
serviceInstance,
{ baseUrl, packageName, includePrereleases = false }
) {
const url = await searchQueryServiceUrl(baseUrl)
const json = await serviceInstance._requestJson({
schema,
url: url,
options: {
qs: {
q: `${encodeURIComponent(packageName.toLowerCase())}`,
// Include prerelease versions.
prerelease: 'true',
// Include packages with SemVer 2 version numbers.
semVerLevel: '2',
},
},
})
for (let d of json.data) {
if (packageName.localeCompare(d.id, undefined, { sensitivity: 'base' }) === 0) {
return d
}
}
throw new NotFound({ prettyMessage: 'package not found' })
}
function getLatestVersion(versions, includePrereleases = false) {
if (!includePrereleases) {
versions = versions.filter(item => !item.version.includes('-'))
}
versions.sort((a, b) => semver.rcompare(a.version, b.version));
return versions[0];
}
module.exports = {
fetchPackage, getLatestVersion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment