Created
November 7, 2019 00:20
-
-
Save devhawk/225f22cb7da69752353ef983ef5f515c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| '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 } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| '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