diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json
index 8ca1cb0a..64b615f9 100644
--- a/l10n/bundle.l10n.json
+++ b/l10n/bundle.l10n.json
@@ -182,6 +182,7 @@
"Inactive Sites": "Inactive Sites",
"No sites found": "No sites found",
"No environments found": "No environments found",
+ "Active Environment is expired or deleted. Please select a new environment.": "Active Environment is expired or deleted. Please select a new environment.",
"PAC Telemetry enabled": "PAC Telemetry enabled",
"Failed to enable PAC telemetry.": "Failed to enable PAC telemetry.",
"PAC Telemetry disabled": "PAC Telemetry disabled",
diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf
index 62c771cb..9c6d83c9 100644
--- a/loc/translations-export/vscode-powerplatform.xlf
+++ b/loc/translations-export/vscode-powerplatform.xlf
@@ -22,6 +22,9 @@
AI-generated content can contain mistakes
+
+ Active Environment is expired or deleted. Please select a new environment.
+
Active Sites
@@ -693,6 +696,9 @@ The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.
Preview Site
+
+ Refresh
+
Refresh
diff --git a/package.json b/package.json
index 8058db7b..29532a4d 100644
--- a/package.json
+++ b/package.json
@@ -375,6 +375,11 @@
"command": "microsoft.powerplatform.pages.preview-site",
"category": "Powerpages",
"title": "%powerplatform.pages.previewSite.title%"
+ },
+ {
+ "command": "powerpages.actionsHub.refresh",
+ "title": "%powerpages.actionsHub.refresh.title%",
+ "icon": "$(refresh)"
}
],
"configuration": {
@@ -850,6 +855,11 @@
"command": "powerpages.copilot.clearConversation",
"when": "view == powerpages.copilot",
"group": "navigation"
+ },
+ {
+ "command": "powerpages.actionsHub.refresh",
+ "when": "view == powerpages.actionsHub",
+ "group": "navigation"
}
],
"view/item/context": [
diff --git a/package.nls.json b/package.nls.json
index cba41a2f..7b76055f 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -99,5 +99,6 @@
},
"microsoft-powerplatform-portals.navigation-loop.powerPagesFileExplorer.title": "POWER PAGES ACTIONS",
"powerplatform.pages.previewSite.title": "Preview Site",
- "microsoft.powerplatform.pages.actionsHub.title": "POWER PAGES ACTIONS"
+ "microsoft.powerplatform.pages.actionsHub.title": "POWER PAGES ACTIONS",
+ "powerpages.actionsHub.refresh.title": "Refresh"
}
diff --git a/src/client/OrgChangeNotifier.ts b/src/client/OrgChangeNotifier.ts
index ef6dc9eb..39f217b1 100644
--- a/src/client/OrgChangeNotifier.ts
+++ b/src/client/OrgChangeNotifier.ts
@@ -50,6 +50,7 @@ export class OrgChangeNotifier {
this._orgDetails = pacActiveOrg.Results;
orgChangeEventEmitter.fire(this._orgDetails);
} else {
+ //If org/env is expired or deleted, this event will be fired
orgChangeErrorEventEmitter.fire();
}
}
diff --git a/src/client/extension.ts b/src/client/extension.ts
index 744ab694..4aab7099 100644
--- a/src/client/extension.ts
+++ b/src/client/extension.ts
@@ -34,7 +34,7 @@ import { CopilotNotificationShown } from "../common/copilot/telemetry/telemetryC
import { copilotNotificationPanel, disposeNotificationPanel } from "../common/copilot/welcome-notification/CopilotNotificationPanel";
import { COPILOT_NOTIFICATION_DISABLED, EXTENSION_VERSION_KEY } from "../common/copilot/constants";
import { oneDSLoggerWrapper } from "../common/OneDSLoggerTelemetry/oneDSLoggerWrapper";
-import { OrgChangeNotifier, orgChangeEvent } from "./OrgChangeNotifier";
+import { OrgChangeNotifier, orgChangeErrorEvent, orgChangeEvent } from "./OrgChangeNotifier";
import { ActiveOrgOutput } from "./pac/PacTypes";
import { desktopTelemetryEventNames } from "../common/OneDSLoggerTelemetry/client/desktopExtensionTelemetryEventNames";
import { ArtemisService } from "../common/services/ArtemisService";
@@ -48,6 +48,10 @@ import { getECSOrgLocationValue, getWorkspaceFolders } from "../common/utilities
import { CliAcquisitionContext } from "./lib/CliAcquisitionContext";
import { PreviewSite } from "./power-pages/preview-site/PreviewSite";
import { ActionsHub } from "./power-pages/actions-hub/ActionsHub";
+import { pacAuthManager } from "./pac/PacAuthManager";
+import { showErrorDialog } from "../common/utilities/errorHandlerUtil";
+import { ENVIRONMENT_EXPIRED } from "./power-pages/actions-hub/Constants";
+import { extractAuthInfo } from "./power-pages/commonUtility";
let client: LanguageClient;
let _context: vscode.ExtensionContext;
@@ -205,6 +209,8 @@ export async function activate(
AadIdObject = pacActiveAuth.Results?.filter(obj => obj.Key === AadIdKey);
EnvID = pacActiveAuth.Results?.filter(obj => obj.Key === EnvIdKey);
TenantID = pacActiveAuth.Results?.filter(obj => obj.Key === TenantIdKey);
+ const authInfo = extractAuthInfo(pacActiveAuth.Results);
+ pacAuthManager.setAuthInfo(authInfo);
}
if (EnvID?.[0]?.Value && TenantID?.[0]?.Value && AadIdObject?.[0]?.Value) {
@@ -251,6 +257,10 @@ export async function activate(
await PreviewSite.initialize(artemisResponse, workspaceFolders, orgDetails, pacTerminal, context, _telemetry);
await ActionsHub.initialize(context, pacTerminal);
+ }),
+
+ orgChangeErrorEvent(() => {
+ showErrorDialog(ENVIRONMENT_EXPIRED);
})
);
diff --git a/src/client/pac/PacAuthManager.ts b/src/client/pac/PacAuthManager.ts
index a612001c..f9931338 100644
--- a/src/client/pac/PacAuthManager.ts
+++ b/src/client/pac/PacAuthManager.ts
@@ -3,13 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
+import { EventEmitter, Event } from "vscode";
import { AuthInfo } from "./PacTypes";
-
-
class PacAuthManager {
private static instance: PacAuthManager;
private authInfo: AuthInfo | null = null;
+ private _onDidChangeEnvironment: EventEmitter = new EventEmitter();
+ public readonly onDidChangeEnvironment: Event = this._onDidChangeEnvironment.event;
public static getInstance(): PacAuthManager {
if (!PacAuthManager.instance) {
@@ -20,6 +21,7 @@ class PacAuthManager {
public setAuthInfo(authInfo: AuthInfo): void {
this.authInfo = authInfo;
+ this._onDidChangeEnvironment.fire();
}
public getAuthInfo(): AuthInfo | null {
diff --git a/src/client/power-pages/actions-hub/ActionsHub.ts b/src/client/power-pages/actions-hub/ActionsHub.ts
index ff625cf6..21643c8e 100644
--- a/src/client/power-pages/actions-hub/ActionsHub.ts
+++ b/src/client/power-pages/actions-hub/ActionsHub.ts
@@ -9,11 +9,6 @@ import { EnableActionsHub } from "../../../common/ecs-features/ecsFeatureGates";
import { ActionsHubTreeDataProvider } from "./ActionsHubTreeDataProvider";
import { oneDSLoggerWrapper } from "../../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper";
import { PacTerminal } from "../../lib/PacTerminal";
-import { SUCCESS } from "../../../common/constants";
-import { extractAuthInfo } from "../commonUtility";
-import { pacAuthManager } from "../../pac/PacAuthManager";
-import { Constants } from "./Constants";
-
export class ActionsHub {
static isEnabled(): boolean {
const enableActionsHub = ECSFeaturesClient.getConfig(EnableActionsHub).enableActionsHub
@@ -38,16 +33,6 @@ export class ActionsHub {
return;
}
- try {
- const pacActiveAuth = await pacTerminal.getWrapper()?.activeAuth();
- if (pacActiveAuth && pacActiveAuth.Status === SUCCESS) {
- const authInfo = extractAuthInfo(pacActiveAuth.Results);
- pacAuthManager.setAuthInfo(authInfo);
- }
-
- ActionsHubTreeDataProvider.initialize(context);
- } catch (error) {
- oneDSLoggerWrapper.getLogger().traceError(Constants.EventNames.ACTIONS_HUB_INITIALIZATION_FAILED, error as string, error as Error, { methodName: ActionsHub.initialize.name }, {});
- }
+ ActionsHubTreeDataProvider.initialize(context, pacTerminal);
}
}
diff --git a/src/client/power-pages/actions-hub/ActionsHubTreeDataProvider.ts b/src/client/power-pages/actions-hub/ActionsHubTreeDataProvider.ts
index 15bb606a..40d72fa6 100644
--- a/src/client/power-pages/actions-hub/ActionsHubTreeDataProvider.ts
+++ b/src/client/power-pages/actions-hub/ActionsHubTreeDataProvider.ts
@@ -11,21 +11,31 @@ import { oneDSLoggerWrapper } from "../../../common/OneDSLoggerTelemetry/oneDSLo
import { EnvironmentGroupTreeItem } from "./tree-items/EnvironmentGroupTreeItem";
import { IEnvironmentInfo } from "./models/IEnvironmentInfo";
import { pacAuthManager } from "../../pac/PacAuthManager";
+import { SUCCESS } from "../../../common/constants";
+import { extractAuthInfo } from "../commonUtility";
+import { PacTerminal } from "../../lib/PacTerminal";
export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider {
private readonly _disposables: vscode.Disposable[] = [];
private readonly _context: vscode.ExtensionContext;
+ private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter();
+ readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event;
- private constructor(context: vscode.ExtensionContext) {
+
+ private constructor(context: vscode.ExtensionContext, private readonly _pacTerminal: PacTerminal) {
this._disposables.push(
vscode.window.registerTreeDataProvider("powerpages.actionsHub", this)
);
this._context = context;
+
+ // Register an event listener for environment changes
+ pacAuthManager.onDidChangeEnvironment(() => this.refresh());
+ this._disposables.push(...this.registerPanel(this._pacTerminal));
}
- public static initialize(context: vscode.ExtensionContext): ActionsHubTreeDataProvider {
- const actionsHubTreeDataProvider = new ActionsHubTreeDataProvider(context);
+ public static initialize(context: vscode.ExtensionContext, pacTerminal: PacTerminal): ActionsHubTreeDataProvider {
+ const actionsHubTreeDataProvider = new ActionsHubTreeDataProvider(context, pacTerminal);
oneDSLoggerWrapper.getLogger().traceInfo(Constants.EventNames.ACTIONS_HUB_INITIALIZED);
return actionsHubTreeDataProvider;
@@ -61,7 +71,28 @@ export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider d.dispose());
}
+
+ private registerPanel(pacTerminal: PacTerminal): vscode.Disposable[] {
+ const pacWrapper = pacTerminal.getWrapper();
+ return [
+ vscode.commands.registerCommand("powerpages.actionsHub.refresh", async () => {
+ try {
+ const pacActiveAuth = await pacWrapper.activeAuth();
+ if (pacActiveAuth && pacActiveAuth.Status === SUCCESS) {
+ const authInfo = extractAuthInfo(pacActiveAuth.Results);
+ pacAuthManager.setAuthInfo(authInfo);
+ }
+ } catch (error) {
+ oneDSLoggerWrapper.getLogger().traceError(Constants.EventNames.ACTIONS_HUB_REFRESH_FAILED, error as string, error as Error, { methodName: this.refresh.name }, {});
+ }
+ })
+ ];
+ }
}
diff --git a/src/client/power-pages/actions-hub/Constants.ts b/src/client/power-pages/actions-hub/Constants.ts
index 8f40165a..dedd9910 100644
--- a/src/client/power-pages/actions-hub/Constants.ts
+++ b/src/client/power-pages/actions-hub/Constants.ts
@@ -32,6 +32,8 @@ export const Constants = {
ACTIONS_HUB_INITIALIZED: "actionsHubInitialized",
ACTIONS_HUB_INITIALIZATION_FAILED: "actionsHubInitializationFailed",
ACTIONS_HUB_CURRENT_ENV_FETCH_FAILED: "actionsHubCurrentEnvFetchFailed",
+ ACTIONS_HUB_REFRESH_FAILED: "actionsHubRefreshFailed"
}
};
+export const ENVIRONMENT_EXPIRED = vscode.l10n.t("Active Environment is expired or deleted. Please select a new environment.")
diff --git a/src/client/test/Integration/power-pages/actions-hub/ActionsHub.test.ts b/src/client/test/Integration/power-pages/actions-hub/ActionsHub.test.ts
index 95072ac5..02459fa1 100644
--- a/src/client/test/Integration/power-pages/actions-hub/ActionsHub.test.ts
+++ b/src/client/test/Integration/power-pages/actions-hub/ActionsHub.test.ts
@@ -6,17 +6,12 @@
import { expect } from "chai";
import * as sinon from "sinon";
import * as vscode from "vscode";
-import * as CommonUtils from "../../../../power-pages/commonUtility";
import { ActionsHub } from "../../../../power-pages/actions-hub/ActionsHub";
import { PacTerminal } from "../../../../lib/PacTerminal";
import { ECSFeaturesClient } from "../../../../../common/ecs-features/ecsFeatureClient";
import { oneDSLoggerWrapper } from "../../../../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper";
import { ActionsHubTreeDataProvider } from "../../../../power-pages/actions-hub/ActionsHubTreeDataProvider";
-import { SUCCESS } from "../../../../../common/constants";
-import { AUTH_KEYS } from "../../../../../common/OneDSLoggerTelemetry/telemetryConstants";
import { PacWrapper } from "../../../../pac/PacWrapper";
-import { AuthInfo, PacAuthWhoOutput } from "../../../../pac/PacTypes";
-import { pacAuthManager } from "../../../../pac/PacAuthManager";
describe("ActionsHub", () => {
let getConfigStub: sinon.SinonStub;
@@ -72,31 +67,21 @@ describe("ActionsHub", () => {
expect(executeCommandStub.calledWith("setContext", "microsoft.powerplatform.pages.actionsHubEnabled", true)).to.be.true;
});
- it("should set Auth Info", async () => {
- getConfigStub.returns({ enableActionsHub: true });
- const data = { organizationFriendlyName: 'Foo' } as AuthInfo;
- sinon.stub(CommonUtils, 'extractAuthInfo').returns(data);
- const pacAuthSetInfoStub = sinon.stub(pacAuthManager, 'setAuthInfo');
- fakePacWrapper.activeAuth.returns(Promise.resolve({ Status: SUCCESS, Results: [{ Key: AUTH_KEYS.ORGANIZATION_FRIENDLY_NAME, Value: 'Foo' }] }) as Promise);
-
- await ActionsHub.initialize(fakeContext, fakePacTerminal);
- expect(pacAuthSetInfoStub.calledWith(data)).to.be.true;
- });
-
it("should initialize ActionsHubTreeDataProvider", async () => {
getConfigStub.returns({ enableActionsHub: true });
const actionsHubTreeDataProviderStub = sinon.stub(ActionsHubTreeDataProvider, 'initialize');
- fakePacWrapper.activeAuth.returns(Promise.resolve({ Status: SUCCESS, Results: [{ Key: AUTH_KEYS.ORGANIZATION_FRIENDLY_NAME, Value: 'Foo' }] }) as Promise);
await ActionsHub.initialize(fakeContext, fakePacTerminal);
expect(actionsHubTreeDataProviderStub.calledWith(fakeContext)).to.be.true;
+
+ actionsHubTreeDataProviderStub.restore();
});
});
describe("when ActionsHub is disabled", () => {
- it("should set context to false and return early if isEnabled returns false", () => {
+ it("should set context to false and return early if isEnabled returns false", async () => {
getConfigStub.returns({ enableActionsHub: false });
- ActionsHub.initialize(fakeContext, fakePacTerminal);
+ await ActionsHub.initialize(fakeContext, fakePacTerminal);
expect(traceInfoStub.calledWith("EnableActionsHub", { isEnabled: "false" })).to.be.true;
expect(executeCommandStub.calledWith("setContext", "microsoft.powerplatform.pages.actionsHubEnabled", false)).to.be.true;
});
@@ -104,6 +89,7 @@ describe("ActionsHub", () => {
it("should not initialize ActionsHubTreeDataProvider", () => {
getConfigStub.returns({ enableActionsHub: false });
const actionsHubTreeDataProviderStub = sinon.stub(ActionsHubTreeDataProvider, 'initialize');
+
ActionsHub.initialize(fakeContext, fakePacTerminal);
expect(actionsHubTreeDataProviderStub.called).to.be.false;
});
diff --git a/src/client/test/Integration/power-pages/actions-hub/ActionsHubTreeDataProvider.test.ts b/src/client/test/Integration/power-pages/actions-hub/ActionsHubTreeDataProvider.test.ts
index 51109ec5..a164fdeb 100644
--- a/src/client/test/Integration/power-pages/actions-hub/ActionsHubTreeDataProvider.test.ts
+++ b/src/client/test/Integration/power-pages/actions-hub/ActionsHubTreeDataProvider.test.ts
@@ -14,14 +14,21 @@ import { EnvironmentGroupTreeItem } from "../../../../power-pages/actions-hub/tr
import { OtherSitesGroupTreeItem } from "../../../../power-pages/actions-hub/tree-items/OtherSitesGroupTreeItem";
import { ActionsHubTreeItem } from "../../../../power-pages/actions-hub/tree-items/ActionsHubTreeItem";
import { pacAuthManager } from "../../../../pac/PacAuthManager";
+import { PacTerminal } from "../../../../lib/PacTerminal";
+import { PacWrapper } from "../../../../pac/PacWrapper";
+import { SUCCESS } from "../../../../../common/constants";
describe("ActionsHubTreeDataProvider", () => {
let context: vscode.ExtensionContext;
let traceInfoStub: sinon.SinonStub;
let traceErrorStub: sinon.SinonStub;
let authInfoStub: sinon.SinonStub;
+ let pacTerminal: PacTerminal;
+ let pacWrapperStub: sinon.SinonStubbedInstance;
+ let registerCommandStub: sinon.SinonStub;
beforeEach(() => {
+ registerCommandStub = sinon.stub(vscode.commands, "registerCommand");
context = {
extensionUri: vscode.Uri.parse("https://localhost:3000")
} as vscode.ExtensionContext;
@@ -34,6 +41,10 @@ describe("ActionsHubTreeDataProvider", () => {
featureUsage: sinon.stub()
});
authInfoStub = sinon.stub(pacAuthManager, "getAuthInfo");
+ pacTerminal = sinon.createStubInstance(PacTerminal);
+ pacWrapperStub = sinon.createStubInstance(PacWrapper);
+ pacWrapperStub.activeAuth.resolves({ Status: SUCCESS, Results: [], Errors: [], Information: [] });
+ (pacTerminal.getWrapper as sinon.SinonStub).returns(pacWrapperStub);
});
afterEach(() => {
@@ -42,15 +53,24 @@ describe("ActionsHubTreeDataProvider", () => {
describe('initialize', () => {
it("should initialize and log initialization event", () => {
- ActionsHubTreeDataProvider.initialize(context);
+ ActionsHubTreeDataProvider.initialize(context, pacTerminal);
expect(traceInfoStub.calledWith(Constants.EventNames.ACTIONS_HUB_INITIALIZED)).to.be.true;
});
+
+ it("should register refresh command", () => {
+ // Initialize
+ const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
+ actionsHubTreeDataProvider["registerPanel"](pacTerminal);
+
+ // Assert that the command was registered
+ expect(registerCommandStub.calledWith("powerpages.actionsHub.refresh")).to.be.true;
+ });
});
describe('getTreeItem', () => {
it("should return the element in getTreeItem", () => {
const element = {} as ActionsHubTreeItem;
- const result = ActionsHubTreeDataProvider.initialize(context).getTreeItem(element);
+ const result = ActionsHubTreeDataProvider.initialize(context, pacTerminal).getTreeItem(element);
expect(result).to.equal(element);
});
});
@@ -75,7 +95,7 @@ describe("ActionsHubTreeDataProvider", () => {
organizationId: "",
organizationUniqueName: ""
});
- const provider = ActionsHubTreeDataProvider.initialize(context);
+ const provider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
const result = await provider.getChildren();
expect(result).to.not.be.null;
@@ -87,7 +107,7 @@ describe("ActionsHubTreeDataProvider", () => {
it("should return environment group tree item with default name when no auth info is available", async () => {
authInfoStub.returns(null);
- const provider = ActionsHubTreeDataProvider.initialize(context);
+ const provider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
const result = await provider.getChildren();
expect(result).to.not.be.null;
@@ -101,7 +121,7 @@ describe("ActionsHubTreeDataProvider", () => {
it("should return null in getChildren when an error occurs", async () => {
authInfoStub.throws(new Error("Test Error"));
- const provider = ActionsHubTreeDataProvider.initialize(context);
+ const provider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
const result = await provider.getChildren();
expect(result).to.be.null;
@@ -112,23 +132,11 @@ describe("ActionsHubTreeDataProvider", () => {
it("should return an empty array in getChildren when an element is passed", async () => {
const element = {} as ActionsHubTreeItem;
- const provider = ActionsHubTreeDataProvider.initialize(context);
+ const provider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
const result = await provider.getChildren(element);
expect(result).to.be.an("array").that.is.empty;
});
});
- describe('dispose', () => {
- it("should dispose all disposables", () => {
- const disposable1 = { dispose: sinon.spy() };
- const disposable2 = { dispose: sinon.spy() };
- const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context);
- actionsHubTreeDataProvider["_disposables"].push(disposable1 as vscode.Disposable, disposable2 as vscode.Disposable);
-
- actionsHubTreeDataProvider.dispose();
- expect(disposable1.dispose.calledOnce).to.be.true;
- expect(disposable2.dispose.calledOnce).to.be.true;
- });
- });
});