Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions src/common/PqSdkNugetPackageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import { PqSdkOutputChannel } from "../features/PqSdkOutputChannel";
export class PqSdkNugetPackageService {
private readonly nugetHttpService: NugetHttpService;
private readonly nugetCommandService: NugetCommandService;
private readonly nullableMaximumPqTestNugetVersion?: NugetVersions = ExtensionConstants.MaximumPqTestNugetVersion
? NugetVersions.createFromFuzzyVersionString(ExtensionConstants.MaximumPqTestNugetVersion)
: undefined;

constructor(
readonly vscExtCtx: vscode.ExtensionContext,
Expand All @@ -43,24 +46,36 @@ export class PqSdkNugetPackageService {
);
}

public async findNullableNewPqSdkVersion(): Promise<string | undefined> {
/**
* Return a list of nuget versions less or equal to `options.maximumNugetVersion` if it got specified
* Otherwise, return the whole list.
* @param options
*/
public async findNullableNewPqSdkVersion(
options: {
maximumNugetVersion?: NugetVersions;
} = {},
): Promise<string | undefined> {
let sortedNugetVersions: NugetVersions[];

// always restrain the version found beneath the MaximumPqTestNugetVersion
if (!options.maximumNugetVersion) {
options.maximumNugetVersion = this.nullableMaximumPqTestNugetVersion;
}

if (ExtensionConfigurations.nugetPath) {
sortedNugetVersions = (
await this.nugetCommandService.getPackageReleasedVersions(
ExtensionConfigurations.nugetPath,
ExtensionConfigurations.nugetFeed,
ExtensionConstants.PublicMsftPqSdkToolsNugetName,
)
).sort(NugetVersions.compare);
sortedNugetVersions = await this.nugetCommandService.getSortedPackageReleasedVersions(
ExtensionConfigurations.nugetPath,
ExtensionConfigurations.nugetFeed,
ExtensionConstants.PublicMsftPqSdkToolsNugetName,
options,
);
} else {
// we gonna use http endpoint to query the public feed
sortedNugetVersions = (
await this.nugetHttpService.getPackageReleasedVersions(ExtensionConstants.PublicMsftPqSdkToolsNugetName)
).versions
.map((releasedVersion: string) => NugetVersions.createFromReleasedVersionString(releasedVersion))
.sort(NugetVersions.compare);
sortedNugetVersions = await this.nugetHttpService.getSortedPackageReleasedVersions(
ExtensionConstants.PublicMsftPqSdkToolsNugetName,
options,
);
}

if (sortedNugetVersions.length && !sortedNugetVersions[sortedNugetVersions.length - 1].isZero()) {
Expand Down
23 changes: 23 additions & 0 deletions src/common/nuget/NugetCommandService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,29 @@ export class NugetCommandService {
);
}

public async getSortedPackageReleasedVersions(
nugetPath: string = "nuget",
nugetFeed: string | undefined = undefined,
packageName: string,
options: {
maximumNugetVersion?: NugetVersions;
} = {},
): Promise<NugetVersions[]> {
let sortedNugetVersions: NugetVersions[] = (
await this.getPackageReleasedVersions(nugetPath, nugetFeed, packageName)
).sort(NugetVersions.compare);

if (options.maximumNugetVersion) {
// filter out any version gt maximumNugetVersion in sortedNugetVersions
sortedNugetVersions = sortedNugetVersions.filter(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(one: NugetVersions) => one.compare(options.maximumNugetVersion!) <= 0,
);
}

return sortedNugetVersions;
}

public downloadAndExtractNugetPackage(
nugetPath: string = "nuget",
nugetFeed: string | undefined = undefined,
Expand Down
119 changes: 6 additions & 113 deletions src/common/nuget/NugetHttpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,131 +6,24 @@
*/

import * as path from "path";
import * as stream from "stream";
import * as StreamZip from "node-stream-zip";

import { createWriteStream, WriteStream } from "fs";
import { promisify } from "util";
import { StreamZipAsync } from "node-stream-zip";

import axios, { AxiosInstance, AxiosResponse } from "axios";
import { makeOneTmpDir } from "../../utils/osUtils";
import type { PqSdkOutputChannel } from "../../features/PqSdkOutputChannel";
import { NugetLiteHttpService } from "./NugetLiteHttpService";
import type { PqSdkOutputChannel } from "../../features/PqSdkOutputChannel"; // this pal got vscode modules imported
import { removeDirectoryRecursively } from "../../utils/files";

const streamFinished$deferred: (
stream: NodeJS.ReadStream | NodeJS.WritableStream | NodeJS.ReadWriteStream,
options?: stream.FinishedOptions | undefined,
) => Promise<void> = promisify(stream.finished);

/**
* Format
* https: into https
* http: into http
* @param urlProtocol
*/
function formatUrlProtocol(urlProtocol: string): string {
if (urlProtocol.toLowerCase().indexOf("https") > -1) {
return "https";
} else if (urlProtocol.toLowerCase().indexOf("http") > -1) {
return "http";
} else {
return urlProtocol;
}
}

export class NugetHttpService {
// public static PreReleaseIncludedVersionRegex: RegExp = /^((?:\.?[0-9]+){3,}(?:[-a-z0-9]+)?)$/;
// eslint-disable-next-line security/detect-unsafe-regex
public static ReleasedVersionRegex: RegExp = /^((?:\.?[0-9]+){3,})$/;
public static DefaultBaseUrl: string = "https://api.nuget.org";

private instance: AxiosInstance = axios.create({
baseURL: NugetHttpService.DefaultBaseUrl,
});
private errorHandler: (error: Error) => void = (error: Error) => {
export class NugetHttpService extends NugetLiteHttpService {
protected override errorHandler: (error: Error) => void = (error: Error) => {
this.outputChannel?.appendErrorLine(`Failed to request to public nuget endpoints due to ${error}`);
};
constructor(private readonly outputChannel?: PqSdkOutputChannel) {
this.updateAxiosInstance();
}

public updateAxiosInstance(
baseURL: string = NugetHttpService.DefaultBaseUrl,
nullableHttpProxy: string | undefined = undefined,
nullableHttpProxyAuthHeaderString: string | undefined = undefined,
): void {
if (nullableHttpProxy) {
const proxyUrl: URL = new URL(nullableHttpProxy);

this.instance = axios.create({
baseURL,
proxy: {
protocol: formatUrlProtocol(proxyUrl.protocol),
host: proxyUrl.hostname,
port: parseInt(proxyUrl.port, 10),
},
});

if (nullableHttpProxyAuthHeaderString) {
this.instance.defaults.headers.common["Authorization"] = nullableHttpProxyAuthHeaderString;
}
} else {
this.instance = axios.create({
baseURL,
});
}
}

public async getPackageVersions(packageName: string): Promise<{ versions: string[] }> {
try {
const response: AxiosResponse<{ versions: string[] }> = await this.instance.get(
`v3-flatcontainer/${packageName.toLowerCase()}/index.json`,
);

return response.data;
} catch (e: unknown) {
this.errorHandler(e as Error);

throw e;
}
}

public async getPackageReleasedVersions(packageName: string): Promise<{ versions: string[] }> {
const preReleasedVersionIncludeVersions: { versions: string[] } = await this.getPackageVersions(packageName);

preReleasedVersionIncludeVersions.versions = preReleasedVersionIncludeVersions.versions.filter(
(versionStr: string) => NugetHttpService.ReleasedVersionRegex.exec(versionStr),
);

return preReleasedVersionIncludeVersions;
}

public downloadNugetPackage(packageName: string, packageVersion: string, outputLocation: string): Promise<void> {
const writer: WriteStream = createWriteStream(outputLocation);
const packageNameInLowerCase: string = packageName.toLowerCase();

try {
return this.instance
.get(
`v3-flatcontainer/${packageNameInLowerCase}/${packageVersion}/${packageNameInLowerCase}.${packageVersion}.nupkg`,
{
responseType: "stream",
},
)
.then((response: AxiosResponse) => {
response.data.pipe(writer);

return streamFinished$deferred(writer);
});
} catch (e: unknown) {
this.errorHandler(e as Error);

throw e;
}
super();
}

public async downloadAndExtractNugetPackage(
public override async downloadAndExtractNugetPackage(
packageName: string,
packageVersion: string,
outputLocation: string,
Expand Down
Loading