Skip to content

Commit 0414a6a

Browse files
enhance PqSdkNugetPackageService (#144)
* create PqSdkNugetPackageService.ts and remove PqSdk relating codes out of LifecycleCommands.ts for the better readability * add unit tests for NugetHttpService.ts * add unit tests for NugetCommandService.ts * skip unit tests for NugetCommandService.ts for CI environment
1 parent b4948bb commit 0414a6a

File tree

12 files changed

+475
-227
lines changed

12 files changed

+475
-227
lines changed

.pipelines/pr-pipeline.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pr:
1111

1212
pool:
1313
vmImage: "windows-latest"
14+
variables:
15+
CI: 'true'
1416

1517
steps:
1618
- task: NodeTool@0

src/commands/LifecycleCommands.ts

Lines changed: 21 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import * as fs from "fs";
99
import * as path from "path";
10-
import * as process from "process";
1110
import * as vscode from "vscode";
1211

1312
import {
@@ -29,10 +28,7 @@ import {
2928
GenericResult,
3029
IPQTestService,
3130
} from "../common/PQTestService";
32-
import {
33-
ExtensionConfigurations,
34-
promptWarningMessageForExternalDependency,
35-
} from "../constants/PowerQuerySdkConfiguration";
31+
import { extensionI18n, resolveI18nTemplate } from "../i18n/extension";
3632
import {
3733
getAnyPqFileBeneathTheFirstWorkspace,
3834
getCurrentWorkspaceSettingPath,
@@ -44,17 +40,15 @@ import {
4440
import { InputStep, MultiStepInput } from "../common/MultiStepInput";
4541
import { PqServiceHostClient, PqServiceHostServerNotReady } from "../pqTestConnector/PqServiceHostClient";
4642
import { PqTestResultViewPanel, SimplePqTestResultViewBroker } from "../panels/PqTestResultViewPanel";
47-
48-
import { extensionI18n, resolveI18nTemplate } from "../i18n/extension";
4943
import { prettifyJson, resolveTemplateSubstitutedValues } from "../utils/strings";
44+
5045
import { debounce } from "../utils/debounce";
46+
import { ExtensionConfigurations } from "../constants/PowerQuerySdkConfiguration";
5147
import { ExtensionConstants } from "../constants/PowerQuerySdkExtension";
5248
import { getCtimeOfAFile } from "../utils/files";
5349
import { IDisposable } from "../common/Disposable";
54-
import { NugetHttpService } from "../common/NugetHttpService";
55-
import { NugetVersions } from "../utils/NugetVersions";
50+
import { PqSdkNugetPackageService } from "../common/PqSdkNugetPackageService";
5651
import { PqSdkOutputChannel } from "../features/PqSdkOutputChannel";
57-
import { SpawnedProcess } from "../common/SpawnedProcess";
5852

5953
const CommandPrefix: string = `powerquery.sdk.tools`;
6054

@@ -81,7 +75,7 @@ export class LifecycleCommands implements IDisposable {
8175
constructor(
8276
private readonly vscExtCtx: ExtensionContext,
8377
readonly globalEventBus: GlobalEventBus,
84-
private readonly nugetHttpService: NugetHttpService,
78+
private readonly pqSdkNugetPackageService: PqSdkNugetPackageService,
8579
private readonly pqTestService: IPQTestService,
8680
private readonly outputChannel: PqSdkOutputChannel,
8781
) {
@@ -490,191 +484,23 @@ export class LifecycleCommands implements IDisposable {
490484
return folder;
491485
}
492486

493-
private expectedPqTestPath(maybeNextVersion?: string): string {
494-
const baseNugetFolder: string = path.resolve(this.vscExtCtx.extensionPath, ExtensionConstants.NugetBaseFolder);
495-
496-
const pqTestSubPath: string[] = maybeNextVersion
497-
? ExtensionConstants.buildPqTestSubPath(maybeNextVersion)
498-
: ExtensionConstants.PqTestSubPath;
499-
500-
return path.resolve(baseNugetFolder, ...pqTestSubPath);
501-
}
502-
503-
private nugetPqTestExistsSync(maybeNextVersion?: string): boolean {
504-
const expectedPqTestPath: string = this.expectedPqTestPath(maybeNextVersion);
505-
506-
return fs.existsSync(expectedPqTestPath);
507-
}
508-
509-
private async doListPqTestFromNuget(): Promise<string> {
510-
await promptWarningMessageForExternalDependency(Boolean(ExtensionConfigurations.nugetPath), true, true);
511-
512-
const baseNugetFolder: string = path.resolve(this.vscExtCtx.extensionPath, ExtensionConstants.NugetBaseFolder);
513-
514-
if (!fs.existsSync(baseNugetFolder)) {
515-
fs.mkdirSync(baseNugetFolder);
516-
}
517-
518-
const args: string[] = ["list", ExtensionConstants.InternalMsftPqSdkToolsNugetName];
519-
520-
if (ExtensionConfigurations.nugetFeed) {
521-
args.push("-Source", ExtensionConfigurations.nugetFeed);
522-
}
523-
524-
const command: string = ExtensionConfigurations.nugetPath ?? "nuget";
525-
526-
this.outputChannel.appendDebugLine(`Listing nuget packages using nuget.exe`);
527-
this.outputChannel.appendInfoLine(`Running ${command} ${args.join(" ")}`);
528-
529-
const seizingProcess: SpawnedProcess = new SpawnedProcess(command, args, {
530-
cwd: baseNugetFolder,
531-
env: {
532-
...process.env,
533-
FORCE_NUGET_EXE_INTERACTIVE: "true",
534-
},
535-
});
536-
537-
await seizingProcess.deferred$;
538-
539-
return seizingProcess.stdOut;
540-
}
541-
542-
private async doUpdatePqTestFromNuget(maybeNextVersion?: string | undefined): Promise<string | undefined> {
543-
if (ExtensionConfigurations.nugetPath) {
544-
// use nuget.exe to check the configured feed location
545-
await promptWarningMessageForExternalDependency(Boolean(ExtensionConfigurations.nugetPath), true, true);
546-
547-
const baseNugetFolder: string = path.resolve(
548-
this.vscExtCtx.extensionPath,
549-
ExtensionConstants.NugetBaseFolder,
550-
);
551-
552-
const pqTestFullPath: string = this.expectedPqTestPath(maybeNextVersion);
553-
554-
if (!fs.existsSync(baseNugetFolder)) {
555-
fs.mkdirSync(baseNugetFolder);
556-
}
557-
558-
const args: string[] = [
559-
"install",
560-
ExtensionConstants.InternalMsftPqSdkToolsNugetName,
561-
"-Version",
562-
maybeNextVersion ?? ExtensionConstants.SuggestedPqTestNugetVersion,
563-
"-OutputDirectory",
564-
baseNugetFolder,
565-
];
566-
567-
if (ExtensionConfigurations.nugetFeed) {
568-
args.push("-Source", ExtensionConfigurations.nugetFeed);
569-
}
570-
571-
const command: string = ExtensionConfigurations.nugetPath ?? "nuget";
572-
573-
this.outputChannel.appendDebugLine(`Installing nuget packages using nuget.exe`);
574-
this.outputChannel.appendInfoLine(`Running ${command} ${args.join(" ")}`);
575-
576-
const seizingProcess: SpawnedProcess = new SpawnedProcess(
577-
command,
578-
args,
579-
{
580-
cwd: baseNugetFolder,
581-
env: {
582-
...process.env,
583-
FORCE_NUGET_EXE_INTERACTIVE: "true",
584-
},
585-
},
586-
{
587-
onStdOut: (data: Buffer): void => {
588-
this.outputChannel.appendInfoLine(data.toString("utf8"));
589-
},
590-
onStdErr: (data: Buffer): void => {
591-
this.outputChannel.appendErrorLine(data.toString("utf8"));
592-
},
593-
},
594-
);
595-
596-
await seizingProcess.deferred$;
597-
598-
return fs.existsSync(pqTestFullPath) ? pqTestFullPath : undefined;
599-
} else {
600-
// we gonna seize the pqSkdTools from the public feed
601-
const baseNugetFolder: string = path.resolve(
602-
this.vscExtCtx.extensionPath,
603-
ExtensionConstants.NugetBaseFolder,
604-
);
605-
606-
const pqTestFullPath: string = this.expectedPqTestPath(maybeNextVersion);
607-
608-
if (!fs.existsSync(baseNugetFolder)) {
609-
fs.mkdirSync(baseNugetFolder);
610-
}
611-
612-
if (fs.existsSync(pqTestFullPath)) return pqTestFullPath;
613-
614-
await this.nugetHttpService.downloadAndExtractNugetPackage(
615-
ExtensionConstants.PublicMsftPqSdkToolsNugetName,
616-
maybeNextVersion ?? ExtensionConstants.SuggestedPqTestNugetVersion,
617-
path.join(
618-
baseNugetFolder,
619-
`${ExtensionConstants.PublicMsftPqSdkToolsNugetName}.${
620-
maybeNextVersion ?? ExtensionConstants.SuggestedPqTestNugetVersion
621-
}`,
622-
),
623-
);
624-
625-
return fs.existsSync(pqTestFullPath) ? pqTestFullPath : undefined;
626-
}
627-
}
628-
629-
private async findMaybeNewPqSdkVersion(): Promise<string | undefined> {
630-
if (ExtensionConfigurations.nugetPath) {
631-
// use nuget.exe to check the configured feed location
632-
const pqTestLocation: string | undefined = ExtensionConfigurations.PQTestLocation;
633-
const curVersion: NugetVersions = NugetVersions.createFromPath(pqTestLocation);
634-
635-
const latestVersion: NugetVersions = NugetVersions.createFromNugetListOutput(
636-
await this.doListPqTestFromNuget(),
637-
);
638-
639-
const sortedVersions: [NugetVersions, NugetVersions] = [curVersion, latestVersion].sort(
640-
NugetVersions.compare,
641-
) as [NugetVersions, NugetVersions];
642-
643-
if (!sortedVersions[1].isZero()) {
644-
// we found a new version, thus we need to check with users first and update to the latest
645-
return sortedVersions[1].toString();
646-
} else {
647-
return undefined;
648-
}
649-
} else {
650-
// we gonna use http endpoint to query the public feed
651-
const sortedNugetVersions: NugetVersions[] = (
652-
await this.nugetHttpService.getPackageReleasedVersions(ExtensionConstants.PublicMsftPqSdkToolsNugetName)
653-
).versions
654-
.map((releasedVersion: string) => NugetVersions.createFromReleasedVersionString(releasedVersion))
655-
.sort(NugetVersions.compare);
656-
657-
if (sortedNugetVersions.length && !sortedNugetVersions[sortedNugetVersions.length - 1].isZero()) {
658-
return sortedNugetVersions[sortedNugetVersions.length - 1].toString();
659-
} else {
660-
return undefined;
661-
}
662-
}
663-
}
664-
665487
private async doCheckAndTryToUpdatePqTest(skipQueryDialog: boolean = false): Promise<string | undefined> {
666488
try {
667489
let pqTestLocation: string | undefined = ExtensionConfigurations.PQTestLocation;
668490

669-
const maybeNewVersion: string | undefined = await this.findMaybeNewPqSdkVersion();
491+
const maybeNewVersion: string | undefined =
492+
await this.pqSdkNugetPackageService.findNullableNewPqSdkVersion();
670493

671494
// we should not update to the latest unless the latest nuget doesn't exist on start
672495
// users might just want to use the previous one purposely
673496
// therefore do not try to update when, like, pqTestLocation.indexOf(maybeNewVersion) === -1
674-
if (!pqTestLocation || !this.pqTestService.pqTestReady || !this.nugetPqTestExistsSync(maybeNewVersion)) {
675-
const pqTestExecutableFullPath: string | undefined = await this.doUpdatePqTestFromNuget(
676-
maybeNewVersion,
677-
);
497+
if (
498+
!pqTestLocation ||
499+
!this.pqTestService.pqTestReady ||
500+
!this.pqSdkNugetPackageService.nugetPqSdkExistsSync(maybeNewVersion)
501+
) {
502+
const pqTestExecutableFullPath: string | undefined =
503+
await this.pqSdkNugetPackageService.updatePqSdkFromNuget(maybeNewVersion);
678504

679505
if (!pqTestExecutableFullPath && !skipQueryDialog) {
680506
const pqTestLocationUrls: Uri[] | undefined = await vscode.window.showOpenDialog({
@@ -744,24 +570,23 @@ export class LifecycleCommands implements IDisposable {
744570
public async manuallyUpdatePqTest(maybeNextVersion?: string): Promise<string | undefined> {
745571
try {
746572
if (!maybeNextVersion) {
747-
maybeNextVersion = await this.findMaybeNewPqSdkVersion();
573+
maybeNextVersion = await this.pqSdkNugetPackageService.findNullableNewPqSdkVersion();
748574
}
749575

750576
let pqTestLocation: string | undefined = ExtensionConfigurations.PQTestLocation;
751577

752578
// determine whether we should trigger to seize or not
753579
if (
754-
!this.nugetPqTestExistsSync(maybeNextVersion) ||
580+
!this.pqSdkNugetPackageService.nugetPqSdkExistsSync(maybeNextVersion) ||
755581
!pqTestLocation ||
756582
// when manually update, we should eagerly update as long as current path is not of the latest version
757583
// like,
758584
// users might want to switch back to the latest some time after
759585
// they temporarily switch back to the previous version
760586
(maybeNextVersion && pqTestLocation.indexOf(maybeNextVersion) === -1)
761587
) {
762-
const pqTestExecutableFullPath: string | undefined = await this.doUpdatePqTestFromNuget(
763-
maybeNextVersion,
764-
);
588+
const pqTestExecutableFullPath: string | undefined =
589+
await this.pqSdkNugetPackageService.updatePqSdkFromNuget(maybeNextVersion);
765590

766591
if (pqTestExecutableFullPath) {
767592
pqTestLocation = path.dirname(pqTestExecutableFullPath);
@@ -778,8 +603,9 @@ export class LifecycleCommands implements IDisposable {
778603
}
779604

780605
// check whether it got seized or not
781-
if (this.nugetPqTestExistsSync(maybeNextVersion)) {
782-
const pqTestExecutableFullPath: string = this.expectedPqTestPath(maybeNextVersion);
606+
if (this.pqSdkNugetPackageService.nugetPqSdkExistsSync(maybeNextVersion)) {
607+
const pqTestExecutableFullPath: string =
608+
this.pqSdkNugetPackageService.expectedPqSdkPath(maybeNextVersion);
783609

784610
this.outputChannel.appendInfoLine(
785611
resolveI18nTemplate("PQSdk.lifecycle.command.pqtest.seized.from", {

0 commit comments

Comments
 (0)