const parse = require('github-url-from-git'); const { XmlDocument } = require('xmldoc'); const got = require('../../util/got'); module.exports = { getQueryUrl, getPkgReleases, getDefaultFeed, }; // https://api.nuget.org/v3/index.json is a default official nuget feed const defaultNugetFeed = 'https://api.nuget.org/v3/index.json'; const cacheNamespace = 'datasource-nuget'; function getDefaultFeed() { return defaultNugetFeed; } async function getQueryUrl(url) { // https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource const resourceType = 'SearchQueryService'; const cacheKey = `${url}:${resourceType}`; const cachedResult = await renovateCache.get(cacheNamespace, cacheKey); // istanbul ignore if if (cachedResult) { return cachedResult; } try { const servicesIndexRaw = await got(url, { json: true, platform: 'nuget' }); if (servicesIndexRaw.statusCode !== 200) { logger.debug( { dependency: url, servicesIndexRaw }, `nuget registry failure: status code != 200` ); return null; } const searchQueryService = servicesIndexRaw.body.resources.find( resource => resource['@type'] === resourceType ); const searchQueryServiceId = searchQueryService['@id']; const cacheMinutes = 60; await renovateCache.set( cacheNamespace, cacheKey, searchQueryServiceId, cacheMinutes ); return searchQueryServiceId; } catch (e) { logger.debug( { e }, `nuget registry failure: can't get SearchQueryService form ${url}` ); return null; } } async function getPkgReleases(registryUrl, feedUrl, pkgName) { const queryUrl = `${feedUrl}?q=PackageId:${pkgName}&semVerLevel=2.0.0&prerelease=true`; const dep = { pkgName, }; try { const pkgUrlListRaw = await got(queryUrl, { json: true, platform: 'nuget', }); if (pkgUrlListRaw.statusCode !== 200) { logger.debug( { dependency: pkgName, pkgUrlListRaw }, `nuget registry failure: status code != 200` ); return null; } // There are no pkgName is current feed if (pkgUrlListRaw.body.totalHits === 0) { return null; } dep.releases = (pkgUrlListRaw.body.data[0].versions || []).map(item => ({ version: item.version, })); try { // For nuget.org we have a way to get nuspec file if (registryUrl.toLowerCase() === defaultNugetFeed.toLowerCase()) { const nugetOrgApi = `https://api.nuget.org/v3-flatcontainer/${pkgName.toLowerCase()}/${ [...dep.releases].pop().version }/${pkgName.toLowerCase()}.nuspec`; const result = await got(nugetOrgApi, { platform: 'nuget' }); const nuspec = new XmlDocument(result.body); if (nuspec) { const sourceUrl = parse( nuspec.valueWithPath('metadata.repository@url') ); if (sourceUrl) { dep.sourceUrl = sourceUrl; } } } else if ( Object.prototype.hasOwnProperty.call( pkgUrlListRaw.body.data[0], 'projectUrl' ) ) { dep.sourceUrl = parse(pkgUrlListRaw.body.data[0].projectUrl); } } catch (err) /* istanbul ignore next */ { logger.debug( { err, pkgName, feedUrl }, `nuget registry failure: can't parse pkg info for project url` ); } return dep; } catch (err) { logger.debug( { err, pkgName, feedUrl }, 'nuget registry failure: Unknown error' ); return null; } }