Skip to content

Commit

Permalink
refactor(datasource/nuget): move v2/v3 API logic to classes (#28117)
Browse files Browse the repository at this point in the history
  • Loading branch information
fgreinacher authored Mar 27, 2024
1 parent fde2dff commit 87bba9d
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 256 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { sortNugetVersions } from './v3';
import { sortNugetVersions } from './common';

describe('modules/datasource/nuget/v3', () => {
describe('modules/datasource/nuget/common', () => {
it.each<{ version: string; other: string; result: number }>`
version | other | result
${'invalid1'} | ${'invalid2'} | ${0}
Expand Down
21 changes: 21 additions & 0 deletions lib/modules/datasource/nuget/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { logger } from '../../../logger';
import { regEx } from '../../../util/regex';
import { parseUrl } from '../../../util/url';
import { api as versioning } from '../../versioning/nuget';
import type { ParsedRegistryUrl } from './types';

const buildMetaRe = regEx(/\+.+$/g);
Expand Down Expand Up @@ -47,3 +48,23 @@ export function parseRegistryUrl(registryUrl: string): ParsedRegistryUrl {
const feedUrl = parsedUrl.href;
return { feedUrl, protocolVersion };
}

/**
* Compare two versions. Return:
* - `1` if `a > b` or `b` is invalid
* - `-1` if `a < b` or `a` is invalid
* - `0` if `a == b` or both `a` and `b` are invalid
*/
export function sortNugetVersions(a: string, b: string): number {
if (versioning.isValid(a)) {
if (versioning.isValid(b)) {
return versioning.sortVersions(a, b);
} else {
return 1;
}
} else if (versioning.isValid(b)) {
return -1;
} else {
return 0;
}
}
19 changes: 14 additions & 5 deletions lib/modules/datasource/nuget/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as nugetVersioning from '../../versioning/nuget';
import { Datasource } from '../datasource';
import type { GetReleasesConfig, ReleaseResult } from '../types';
import { parseRegistryUrl } from './common';
import * as v2 from './v2';
import * as v3 from './v3';
import { NugetV2Api } from './v2';
import { NugetV3Api } from './v3';

// https://api.nuget.org/v3/index.json is a default official nuget feed
export const nugetOrg = 'https://api.nuget.org/v3/index.json';
Expand All @@ -18,6 +18,10 @@ export class NugetDatasource extends Datasource {

override readonly registryStrategy = 'merge';

readonly v2Api = new NugetV2Api();

readonly v3Api = new NugetV3Api();

constructor() {
super(NugetDatasource.id);
}
Expand All @@ -33,12 +37,17 @@ export class NugetDatasource extends Datasource {
}
const { feedUrl, protocolVersion } = parseRegistryUrl(registryUrl);
if (protocolVersion === 2) {
return v2.getReleases(this.http, feedUrl, packageName);
return this.v2Api.getReleases(this.http, feedUrl, packageName);
}
if (protocolVersion === 3) {
const queryUrl = await v3.getResourceUrl(this.http, feedUrl);
const queryUrl = await this.v3Api.getResourceUrl(this.http, feedUrl);
if (queryUrl) {
return v3.getReleases(this.http, feedUrl, queryUrl, packageName);
return this.v3Api.getReleases(
this.http,
feedUrl,
queryUrl,
packageName,
);
}
}
return null;
Expand Down
108 changes: 56 additions & 52 deletions lib/modules/datasource/nuget/v2.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,74 @@
import { XmlDocument, XmlElement } from 'xmldoc';
import { logger } from '../../../logger';
import type { Http } from '../../../util/http';
import type { HttpResponse } from '../../../util/http/types';
import { regEx } from '../../../util/regex';
import type { ReleaseResult } from '../types';
import { massageUrl, removeBuildMeta } from './common';

function getPkgProp(pkgInfo: XmlElement, propName: string): string | undefined {
return pkgInfo.childNamed('m:properties')?.childNamed(`d:${propName}`)?.val;
}
export class NugetV2Api {
getPkgProp(pkgInfo: XmlElement, propName: string): string | undefined {
return pkgInfo.childNamed('m:properties')?.childNamed(`d:${propName}`)?.val;
}

export async function getReleases(
http: Http,
feedUrl: string,
pkgName: string,
): Promise<ReleaseResult | null> {
const dep: ReleaseResult = {
releases: [],
};
let pkgUrlList: string | null = `${feedUrl.replace(
regEx(/\/+$/),
'',
)}/FindPackagesById()?id=%27${pkgName}%27&$select=Version,IsLatestVersion,ProjectUrl,Published`;
while (pkgUrlList !== null) {
// typescript issue
const pkgVersionsListRaw: HttpResponse<string> = await http.get(pkgUrlList);
const pkgVersionsListDoc = new XmlDocument(pkgVersionsListRaw.body);
async getReleases(
http: Http,
feedUrl: string,
pkgName: string,
): Promise<ReleaseResult | null> {
const dep: ReleaseResult = {
releases: [],
};
let pkgUrlList: string | null = `${feedUrl.replace(
regEx(/\/+$/),
'',
)}/FindPackagesById()?id=%27${pkgName}%27&$select=Version,IsLatestVersion,ProjectUrl,Published`;
while (pkgUrlList !== null) {
// typescript issue
const pkgVersionsListRaw = await http.get(pkgUrlList);
const pkgVersionsListDoc = new XmlDocument(pkgVersionsListRaw.body);

const pkgInfoList = pkgVersionsListDoc.childrenNamed('entry');
const pkgInfoList = pkgVersionsListDoc.childrenNamed('entry');

for (const pkgInfo of pkgInfoList) {
const version = getPkgProp(pkgInfo, 'Version');
const releaseTimestamp = getPkgProp(pkgInfo, 'Published');
dep.releases.push({
// TODO: types (#22198)
version: removeBuildMeta(`${version}`),
releaseTimestamp,
});
try {
const pkgIsLatestVersion = getPkgProp(pkgInfo, 'IsLatestVersion');
if (pkgIsLatestVersion === 'true') {
dep['tags'] = { latest: removeBuildMeta(`${version}`) };
const projectUrl = getPkgProp(pkgInfo, 'ProjectUrl');
if (projectUrl) {
dep.sourceUrl = massageUrl(projectUrl);
for (const pkgInfo of pkgInfoList) {
const version = this.getPkgProp(pkgInfo, 'Version');
const releaseTimestamp = this.getPkgProp(pkgInfo, 'Published');
dep.releases.push({
// TODO: types (#22198)
version: removeBuildMeta(`${version}`),
releaseTimestamp,
});
try {
const pkgIsLatestVersion = this.getPkgProp(
pkgInfo,
'IsLatestVersion',
);
if (pkgIsLatestVersion === 'true') {
dep['tags'] = { latest: removeBuildMeta(`${version}`) };
const projectUrl = this.getPkgProp(pkgInfo, 'ProjectUrl');
if (projectUrl) {
dep.sourceUrl = massageUrl(projectUrl);
}
}
} catch (err) /* istanbul ignore next */ {
logger.debug(
{ err, pkgName, feedUrl },
`nuget registry failure: can't parse pkg info for project url`,
);
}
} catch (err) /* istanbul ignore next */ {
logger.debug(
{ err, pkgName, feedUrl },
`nuget registry failure: can't parse pkg info for project url`,
);
}
}

const nextPkgUrlListLink = pkgVersionsListDoc
.childrenNamed('link')
.find((node) => node.attr.rel === 'next');
const nextPkgUrlListLink = pkgVersionsListDoc
.childrenNamed('link')
.find((node) => node.attr.rel === 'next');

pkgUrlList = nextPkgUrlListLink ? nextPkgUrlListLink.attr.href : null;
}
pkgUrlList = nextPkgUrlListLink ? nextPkgUrlListLink.attr.href : null;
}

// dep not found if no release, so we can try next registry
if (dep.releases.length === 0) {
return null;
}
// dep not found if no release, so we can try next registry
if (dep.releases.length === 0) {
return null;
}

return dep;
return dep;
}
}
Loading

0 comments on commit 87bba9d

Please sign in to comment.