From 94ba2c336b3f536f51edc6f6d590b2edc71c5af5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 06:50:47 +0000 Subject: [PATCH 1/3] Initial plan From 3ba275da153811ddba9d64c04f1720f3c684898a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 07:07:06 +0000 Subject: [PATCH 2/3] Fix auth profile not found by syncing PAC CLI auth before showing input box When the user clicks Login in Actions Hub but PacContext hasn't been populated yet, the handler now checks PAC CLI directly for an active auth profile before falling back to showing the "enter environment URL" input box. This prevents the confusing "auth profile not found" prompt when the user has already authenticated via PAC CLI. Agent-Logs-Url: https://github.com/microsoft/powerplatform-vscode/sessions/c26b8917-62e6-4e1c-a2d0-39b52ddf5cb5 Co-authored-by: amitjoshi438 <54068463+amitjoshi438@users.noreply.github.com> --- .../power-pages/actions-hub/Constants.ts | 1 + .../handlers/CreateNewAuthProfileHandler.ts | 39 ++++++ .../CreateNewAuthProfileHandler.test.ts | 132 +++++++++++++++++- 3 files changed, 171 insertions(+), 1 deletion(-) diff --git a/src/client/power-pages/actions-hub/Constants.ts b/src/client/power-pages/actions-hub/Constants.ts index 8196f5d18..a0cb88878 100644 --- a/src/client/power-pages/actions-hub/Constants.ts +++ b/src/client/power-pages/actions-hub/Constants.ts @@ -535,6 +535,7 @@ export const Constants = { ACTIONS_HUB_PREVIEW_SITE_FAILED: "ActionsHubPreviewSiteFailed", ACTIONS_HUB_CREATE_AUTH_PROFILE_CALLED: "ActionsHubCreateAuthProfileCalled", ACTIONS_HUB_CREATE_AUTH_PROFILE_FAILED: "ActionsHubCreateAuthProfileFailed", + ACTIONS_HUB_AUTH_SYNCED_FROM_PAC_CLI: "ActionsHubAuthSyncedFromPacCli", ACTIONS_HUB_FETCH_WEBSITES_CALLED: "ActionsHubFetchWebsitesCalled", ACTIONS_HUB_FETCH_WEBSITES_FAILED: "ActionsHubFetchWebsitesFailed", ACTIONS_HUB_REVEAL_IN_OS_CALLED: "ActionsHubRevealInOSCalled", diff --git a/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts b/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts index 8383f7018..3d0a298e0 100644 --- a/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts +++ b/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts @@ -8,6 +8,7 @@ import { authenticateUserInVSCode } from "../../../../common/services/Authentica import { createAuthProfileExp } from "../../../../common/utilities/PacAuthUtil"; import PacContext from "../../../pac/PacContext"; import { PacWrapper } from "../../../pac/PacWrapper"; +import { extractAuthInfo, extractOrgInfo } from "../../commonUtility"; import { Constants } from "../Constants"; import { traceError, traceInfo } from "../TelemetryHelper"; @@ -22,6 +23,12 @@ export const createNewAuthProfile = async (pacWrapper: PacWrapper): Promise => { + try { + const pacActiveAuth = await pacWrapper.activeAuth(); + if (pacActiveAuth && pacActiveAuth.Status === SUCCESS) { + const authInfo = extractAuthInfo(pacActiveAuth.Results); + if (authInfo.OrganizationFriendlyName) { + let orgInfo = null; + try { + const pacActiveOrg = await pacWrapper.activeOrg(); + if (pacActiveOrg && pacActiveOrg.Status === SUCCESS) { + orgInfo = extractOrgInfo(pacActiveOrg.Results); + } + } catch { + // Org info fetch failed, continue with auth info only + } + PacContext.setContext(authInfo, orgInfo); + traceInfo(Constants.EventNames.ACTIONS_HUB_AUTH_SYNCED_FROM_PAC_CLI, { methodName: syncAuthFromPacCli.name }); + return true; + } + } + } catch { + // PAC CLI auth not available, continue with auth profile creation flow + } + return false; +}; diff --git a/src/client/test/integration/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.test.ts b/src/client/test/integration/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.test.ts index 5286abbdc..277d9a70e 100644 --- a/src/client/test/integration/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.test.ts +++ b/src/client/test/integration/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.test.ts @@ -6,12 +6,14 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; import * as vscode from 'vscode'; -import { createNewAuthProfile } from '../../../../../power-pages/actions-hub/handlers/CreateNewAuthProfileHandler'; +import { createNewAuthProfile, syncAuthFromPacCli } from '../../../../../power-pages/actions-hub/handlers/CreateNewAuthProfileHandler'; import { PacWrapper } from '../../../../../pac/PacWrapper'; import * as authProvider from '../../../../../../common/services/AuthenticationProvider'; import * as PacAuthUtil from '../../../../../../common/utilities/PacAuthUtil'; +import * as CommonUtility from '../../../../../power-pages/commonUtility'; import PacContext from '../../../../../pac/PacContext'; import * as TelemetryHelper from '../../../../../power-pages/actions-hub/TelemetryHelper'; +import { AuthInfo, CloudInstance, EnvironmentType, OrgInfo, PacAuthWhoOutput, PacOrgWhoOutput } from '../../../../../pac/PacTypes'; describe('CreateNewAuthProfileHandler', () => { let sandbox: sinon.SinonSandbox; @@ -99,5 +101,133 @@ describe('CreateNewAuthProfileHandler', () => { expect(traceErrorStub.calledOnce).to.be.true; expect(traceErrorStub.firstCall.args[0]).to.equal('ActionsHubCreateAuthProfileFailed'); }); + + it('should sync auth from PAC CLI and authenticate in VS Code when PAC has active auth but PacContext is not populated', async () => { + const mockAuthResults = [ + { Key: 'OrganizationFriendlyName', Value: 'Test Organization' }, + { Key: 'EntraIdObjectId', Value: 'test-object-id' } + ]; + mockPacWrapper.activeAuth.resolves({ Status: 'Success', Results: mockAuthResults } as unknown as PacAuthWhoOutput); + mockPacWrapper.activeOrg.resolves({ + Status: 'Success', + Results: { OrgId: 'org-id', UniqueName: 'testorg', FriendlyName: 'Test Organization', OrgUrl: 'https://test.crm.dynamics.com', UserEmail: 'test@test.com', UserId: 'user-id', EnvironmentId: 'env-id' } + } as unknown as PacOrgWhoOutput); + sandbox.stub(CommonUtility, 'extractAuthInfo').returns({ + OrganizationFriendlyName: 'Test Organization', + EntraIdObjectId: 'test-object-id' + } as unknown as AuthInfo); + sandbox.stub(CommonUtility, 'extractOrgInfo').returns({ + OrgId: 'org-id', + OrgUrl: 'https://test.crm.dynamics.com' + } as OrgInfo); + const setContextStub = sandbox.stub(PacContext, 'setContext'); + + await createNewAuthProfile(mockPacWrapper); + + expect(mockPacWrapper.activeAuth.calledOnce).to.be.true; + expect(setContextStub.calledOnce).to.be.true; + expect(mockAuthenticationInVsCode.calledOnce).to.be.true; + expect(mockCreateAuthProfileExp.called).to.be.false; + }); + + it('should fall through to createAuthProfileExp when PAC CLI has no active auth', async () => { + mockPacWrapper.activeAuth.resolves({ Status: 'Failed', Results: [] } as unknown as PacAuthWhoOutput); + mockCreateAuthProfileExp.resolves({ Status: 'Failed', Results: null }); + + await createNewAuthProfile(mockPacWrapper); + + expect(mockPacWrapper.activeAuth.calledOnce).to.be.true; + expect(mockCreateAuthProfileExp.calledOnce).to.be.true; + }); + + it('should fall through to createAuthProfileExp when PAC CLI activeAuth throws', async () => { + mockPacWrapper.activeAuth.rejects(new Error('PAC CLI error')); + mockCreateAuthProfileExp.resolves({ Status: 'Failed', Results: null }); + + await createNewAuthProfile(mockPacWrapper); + + expect(mockPacWrapper.activeAuth.calledOnce).to.be.true; + expect(mockCreateAuthProfileExp.calledOnce).to.be.true; + }); + }); + + describe('syncAuthFromPacCli', () => { + let mockPacWrapper: sinon.SinonStubbedInstance; + + beforeEach(() => { + mockPacWrapper = sandbox.createStubInstance(PacWrapper); + }); + + it('should return true and set context when PAC CLI has valid auth with OrganizationFriendlyName', async () => { + const mockAuthResults = [ + { Key: 'OrganizationFriendlyName', Value: 'Test Organization' } + ]; + mockPacWrapper.activeAuth.resolves({ Status: 'Success', Results: mockAuthResults } as unknown as PacAuthWhoOutput); + mockPacWrapper.activeOrg.resolves({ + Status: 'Success', + Results: { OrgId: 'org-id', UniqueName: 'testorg', FriendlyName: 'Test Org', OrgUrl: 'https://test.crm.dynamics.com', UserEmail: 'test@test.com', UserId: 'user-id', EnvironmentId: 'env-id' } + } as unknown as PacOrgWhoOutput); + sandbox.stub(CommonUtility, 'extractAuthInfo').returns({ + OrganizationFriendlyName: 'Test Organization' + } as unknown as AuthInfo); + sandbox.stub(CommonUtility, 'extractOrgInfo').returns({ + OrgId: 'org-id', OrgUrl: 'https://test.crm.dynamics.com' + } as OrgInfo); + const setContextStub = sandbox.stub(PacContext, 'setContext'); + + const result = await syncAuthFromPacCli(mockPacWrapper); + + expect(result).to.be.true; + expect(setContextStub.calledOnce).to.be.true; + }); + + it('should return false when PAC CLI has no active auth', async () => { + mockPacWrapper.activeAuth.resolves({ Status: 'Failed', Results: [] } as unknown as PacAuthWhoOutput); + + const result = await syncAuthFromPacCli(mockPacWrapper); + + expect(result).to.be.false; + }); + + it('should return false when PAC CLI auth has empty OrganizationFriendlyName', async () => { + const mockAuthResults = [ + { Key: 'OrganizationFriendlyName', Value: '' } + ]; + mockPacWrapper.activeAuth.resolves({ Status: 'Success', Results: mockAuthResults } as unknown as PacAuthWhoOutput); + sandbox.stub(CommonUtility, 'extractAuthInfo').returns({ + OrganizationFriendlyName: '' + } as unknown as AuthInfo); + + const result = await syncAuthFromPacCli(mockPacWrapper); + + expect(result).to.be.false; + }); + + it('should return false when PAC CLI activeAuth throws', async () => { + mockPacWrapper.activeAuth.rejects(new Error('PAC CLI error')); + + const result = await syncAuthFromPacCli(mockPacWrapper); + + expect(result).to.be.false; + }); + + it('should still succeed when activeOrg fails', async () => { + const mockAuthResults = [ + { Key: 'OrganizationFriendlyName', Value: 'Test Organization' } + ]; + mockPacWrapper.activeAuth.resolves({ Status: 'Success', Results: mockAuthResults } as unknown as PacAuthWhoOutput); + mockPacWrapper.activeOrg.rejects(new Error('Org fetch failed')); + sandbox.stub(CommonUtility, 'extractAuthInfo').returns({ + OrganizationFriendlyName: 'Test Organization' + } as unknown as AuthInfo); + const setContextStub = sandbox.stub(PacContext, 'setContext'); + + const result = await syncAuthFromPacCli(mockPacWrapper); + + expect(result).to.be.true; + expect(setContextStub.calledOnce).to.be.true; + // Should be called with authInfo and null orgInfo + expect(setContextStub.firstCall.args[1]).to.be.null; + }); }); }); From 1f715602282db760a1cd068297102d6cc56fafee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 07:09:35 +0000 Subject: [PATCH 3/3] Add telemetry logging to catch blocks in syncAuthFromPacCli Agent-Logs-Url: https://github.com/microsoft/powerplatform-vscode/sessions/c26b8917-62e6-4e1c-a2d0-39b52ddf5cb5 Co-authored-by: amitjoshi438 <54068463+amitjoshi438@users.noreply.github.com> --- .../handlers/CreateNewAuthProfileHandler.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts b/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts index 3d0a298e0..15cf7ef41 100644 --- a/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts +++ b/src/client/power-pages/actions-hub/handlers/CreateNewAuthProfileHandler.ts @@ -84,16 +84,26 @@ export const syncAuthFromPacCli = async (pacWrapper: PacWrapper): Promise