Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Actions Hub with environment retrieval and refresh functionality #1111

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1f0d7f5
Enhance Actions Hub: Add support for environment retrieval and locali…
Jan 24, 2025
7a2f296
Add unit tests for ActionsHubTreeDataProvider functionality
Jan 24, 2025
2856e6c
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
Jan 24, 2025
fa5a7d1
Add AuthManager class and AuthInfo interface for authentication handling
Jan 27, 2025
a94ac27
Enhance authentication handling: Add auth info extraction and update …
Jan 27, 2025
47acab7
Add refresh command and event handling for Actions Hub environment ch…
Jan 29, 2025
eea2bb8
Refactor ActionsHubTreeDataProvider: Manual refresh should trigger Pac
Feb 3, 2025
6bdd46e
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
Feb 3, 2025
638652e
Remove AuthManager class and related authentication logic
Feb 3, 2025
7423e29
Add error handling to refresh command in ActionsHubTreeDataProvider
Feb 4, 2025
19dca91
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
Feb 4, 2025
84178b7
RemoveDependencyOnOldTelemetryCluster
Feb 4, 2025
854157e
Refactor ActionsHubTreeDataProvider tests to include PacTerminal in i…
Feb 4, 2025
8d06ba2
Remove telemetry dependency from intelligence API endpoint retrieval
Feb 4, 2025
dcc317d
Refactor telemetry handling by removing ITelemetry parameter from sen…
Feb 4, 2025
91b17ae
Refactor: Remove telemetry parameter from getEndpoint and related fun…
Feb 4, 2025
22edc08
Refactor: Remove telemetry parameter from authentication functions an…
Feb 4, 2025
c6bc170
Refactor telemetry event assertion in AuthenticationProvider tests
Feb 4, 2025
0631e39
Refactor authentication functions to remove telemetry parameter and u…
Feb 4, 2025
f49c9c3
Refactor: Remove telemetry parameter from getArtemisResponse call in …
Feb 4, 2025
1afbcf0
Merge branch 'users/amitjoshi/removeOldTelemetryClusterDependencyInCo…
Feb 4, 2025
adcbed0
Refactor: Remove telemetry parameter from various authentication and …
Feb 4, 2025
d1c5159
Refactor: Rename AuthManager to PacAuthManager for consistency
Feb 4, 2025
38e09b1
Merge branch 'users/amitjoshi/removeOldTelemetryClusterDependencyInCo…
Feb 4, 2025
5a2424d
Refactor: Use pacWrapper for activeAuth retrieval in ActionsHubTreeDa…
Feb 4, 2025
4866efd
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
Feb 5, 2025
1726fbc
Add ACTIONS_HUB_REFRESH_FAILED constant and update error logging in A…
Feb 5, 2025
86fa5b9
Remove unused AuthInfo interface from Constants.ts
Feb 5, 2025
a14ff62
Refactor ActionsHub initialization by removing authentication handlin…
Feb 5, 2025
dd04b24
Add localization for expired environment message and update refresh t…
Feb 5, 2025
5e80fbd
Merge branch 'main' into users/amitjoshi/actionsHubRefreshEnv
amitjoshi438 Feb 6, 2025
5057d2d
Refactor ActionsHub tests to use ECS feature flags and improve error …
Feb 6, 2025
fe78e41
Merge branch 'users/amitjoshi/actionsHubRefreshEnv' of https://github…
Feb 6, 2025
0bacb1a
Refactor ActionsHub tests to use ECS feature flags and improve error …
Feb 6, 2025
aa6633c
Merge branch 'users/amitjoshi/actionsHubRefreshEnv' of https://github…
Feb 6, 2025
3175764
Fix logger and terminal stubs in ActionsHub tests to resolve merge co…
Feb 6, 2025
a577c7a
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
Feb 6, 2025
3635f27
Add command registration stub to ActionsHubTreeDataProvider tests
Feb 6, 2025
ae6cfff
Fix logger stub assertions in ActionsHub tests to correctly check tra…
Feb 6, 2025
068ac9c
Refactor logger assertions in ActionsHub tests to simplify traceInfo …
Feb 6, 2025
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
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@
"command": "microsoft.powerplatform.pages.preview-site",
"category": "Powerpages",
"title": "%powerplatform.pages.previewSite.title%"
},
{
"command": "powerpages.actionsHub.refresh",
"title": "Refresh",
amitjoshi438 marked this conversation as resolved.
Show resolved Hide resolved
"icon": "$(refresh)"
}
],
"configuration": {
Expand Down Expand Up @@ -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": [
Expand Down
1 change: 1 addition & 0 deletions src/client/OrgChangeNotifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand Down
12 changes: 11 additions & 1 deletion src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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);
amitjoshi438 marked this conversation as resolved.
Show resolved Hide resolved
}

if (EnvID?.[0]?.Value && TenantID?.[0]?.Value && AadIdObject?.[0]?.Value) {
Expand Down Expand Up @@ -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);
})
);

Expand Down
6 changes: 4 additions & 2 deletions src/client/pac/PacAuthManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> = new EventEmitter<void>();
public readonly onDidChangeEnvironment: Event<void> = this._onDidChangeEnvironment.event;

public static getInstance(): PacAuthManager {
if (!PacAuthManager.instance) {
Expand All @@ -20,6 +21,7 @@ class PacAuthManager {

public setAuthInfo(authInfo: AuthInfo): void {
this.authInfo = authInfo;
this._onDidChangeEnvironment.fire();
}

public getAuthInfo(): AuthInfo | null {
Expand Down
2 changes: 1 addition & 1 deletion src/client/power-pages/actions-hub/ActionsHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class ActionsHub {
pacAuthManager.setAuthInfo(authInfo);
}

ActionsHubTreeDataProvider.initialize(context);
ActionsHubTreeDataProvider.initialize(context, pacTerminal);
} catch (error) {
oneDSLoggerWrapper.getLogger().traceError(Constants.EventNames.ACTIONS_HUB_INITIALIZATION_FAILED, error as string, error as Error, { methodName: ActionsHub.initialize.name }, {});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ActionsHubTreeItem> {
private readonly _disposables: vscode.Disposable[] = [];
private readonly _context: vscode.ExtensionContext;
private _onDidChangeTreeData: vscode.EventEmitter<ActionsHubTreeItem | undefined | void> = new vscode.EventEmitter<ActionsHubTreeItem | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<ActionsHubTreeItem | undefined | void> = 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;
Expand Down Expand Up @@ -61,7 +71,29 @@ export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider<Actio
}
}

public refresh(): void {
this._onDidChangeTreeData.fire();
}

public dispose(): void {
this._disposables.forEach(d => 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);
}
this.refresh();
amitjoshi438 marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
oneDSLoggerWrapper.getLogger().traceError(Constants.EventNames.ACTIONS_HUB_REFRESH_FAILED, error as string, error as Error, { methodName: this.refresh.name }, {});
}
})
];
}
}
21 changes: 21 additions & 0 deletions src/client/power-pages/actions-hub/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ 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 interface AuthInfo {
userType: string;
cloud: string;
tenantId: string;
tenantCountry: string;
user: string;
entraIdObjectId: string;
puid: string;
userCountryRegion: string;
tokenExpires: string;
authority: string;
environmentGeo: string;
environmentId: string;
environmentType: string;
organizationId: string;
organizationUniqueName: string;
organizationFriendlyName: string;
}
amitjoshi438 marked this conversation as resolved.
Show resolved Hide resolved

export const ENVIRONMENT_EXPIRED = vscode.l10n.t("Active Environment is expired or deleted. Please select a new environment.")
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ 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";

describe("ActionsHubTreeDataProvider", () => {
let context: vscode.ExtensionContext;
let traceInfoStub: sinon.SinonStub;
let traceErrorStub: sinon.SinonStub;
let authInfoStub: sinon.SinonStub;
let pacTerminal: PacTerminal;

beforeEach(() => {
context = {
Expand All @@ -34,6 +36,7 @@ describe("ActionsHubTreeDataProvider", () => {
featureUsage: sinon.stub()
});
authInfoStub = sinon.stub(pacAuthManager, "getAuthInfo");
pacTerminal = {} as PacTerminal;
});

afterEach(() => {
Expand All @@ -42,15 +45,15 @@ 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;
});
});

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);
});
});
Expand All @@ -75,7 +78,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;
Expand All @@ -87,7 +90,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;
Expand All @@ -101,7 +104,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;
Expand All @@ -112,7 +115,7 @@ 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;
Expand All @@ -123,7 +126,7 @@ describe("ActionsHubTreeDataProvider", () => {
it("should dispose all disposables", () => {
const disposable1 = { dispose: sinon.spy() };
const disposable2 = { dispose: sinon.spy() };
const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context);
const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
actionsHubTreeDataProvider["_disposables"].push(disposable1 as vscode.Disposable, disposable2 as vscode.Disposable);

actionsHubTreeDataProvider.dispose();
Expand Down