Skip to content

Commit

Permalink
Making auth provider a common module flow (#928)
Browse files Browse the repository at this point in the history
  • Loading branch information
tyaginidhi authored May 3, 2024
1 parent 9468bb6 commit 87570bc
Show file tree
Hide file tree
Showing 23 changed files with 307 additions and 224 deletions.
2 changes: 2 additions & 0 deletions l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"You are editing a live, public site ": "You are editing a live, public site ",
"Preview site": "Preview site",
"Open in Power Pages studio": "Open in Power Pages studio",
"Preview site URL is not available": "Preview site URL is not available",
"Opening preview site...": "Opening preview site...",
"Power Pages studio URL is not available": "Power Pages studio URL is not available",
"Microsoft wants your feeback": "Microsoft wants your feeback",
"Check the URL and verify the parameters are correct": "Check the URL and verify the parameters are correct",
"Unable to complete the request": "Unable to complete the request",
Expand Down
6 changes: 6 additions & 0 deletions loc/translations-export/vscode-powerplatform.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,19 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
<source xml:lang="en">Power Pages Copilot is now connected to the environment: {0} : {1}</source>
<note>{0} represents the environment name</note>
</trans-unit>
<trans-unit id="++CODE++cce89cd3a0f118dafd3173f6444abdcd2b9782153af1b6bfa8f63ef3d4b68268">
<source xml:lang="en">Power Pages studio URL is not available</source>
</trans-unit>
<trans-unit id="++CODE++777d5f3afd46e18d42c0baad6d9a0c7a6867cdf31ae4d342ad5e695486f05ae7">
<source xml:lang="en">Preparing pac CLI (v{0})...</source>
<note>{0} represents the version number</note>
</trans-unit>
<trans-unit id="++CODE++f6412ced721a26ac3e3cb55d3ff713d8403c3dfc2efa0cff07b48478bdd57555">
<source xml:lang="en">Preview site</source>
</trans-unit>
<trans-unit id="++CODE++f190e8061b8cbadd991ff217210525eff0000a8c6ddc2d44baec8388b7bd1a3e">
<source xml:lang="en">Preview site URL is not available</source>
</trans-unit>
<trans-unit id="++CODE++7a6098eb5ff2c2401890216bb502ce6583ff7bddc99e62f8751551eab45ae1b4">
<source xml:lang="en">Profile Kind: {0}</source>
<note>The {0} represents the profile type (Admin vs Dataverse)</note>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,29 @@
*/

import * as vscode from "vscode";
import WebExtensionContext from "../WebExtensionContext";
import { telemetryEventNames } from "../telemetry/constants";
import {
INTELLIGENCE_SCOPE_DEFAULT,
PROVIDER_ID,
SCOPE_OPTION_CONTACTS_READ,
SCOPE_OPTION_DEFAULT,
SCOPE_OPTION_OFFLINE_ACCESS,
SCOPE_OPTION_USERS_READ_BASIC_ALL,
} from "./constants";
import { ERRORS, showErrorDialog } from "./errorHandler";
import { ITelemetry } from "../../../client/telemetry/ITelemetry";
import { sendTelemetryEvent } from "../../../common/copilot/telemetry/copilotTelemetry";
import { CopilotLoginFailureEvent, CopilotLoginSuccessEvent } from "../../../common/copilot/telemetry/telemetryConstants";
import { getUserAgent } from "../../../common/Utils";
} from "../web/client/common/constants";
import { showErrorDialog } from "../web/client/common/errorHandler";
import { ITelemetry } from "../client/telemetry/ITelemetry";
import { sendTelemetryEvent } from "./copilot/telemetry/copilotTelemetry";
import { CopilotLoginFailureEvent, CopilotLoginSuccessEvent } from "./copilot/telemetry/telemetryConstants";
import { getUserAgent } from "./Utils";
import {
VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_COMPLETED,
VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED,
VSCODE_EXTENSION_NPS_AUTHENTICATION_COMPLETED,
VSCODE_EXTENSION_NPS_AUTHENTICATION_FAILED,
VSCODE_EXTENSION_NPS_AUTHENTICATION_STARTED,
VSCODE_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_FAILED,
VSCODE_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_COMPLETED
} from "./TelemetryConstants";
import { ERRORS } from "./ErrorConstants";


export function getCommonHeadersForDataverse(
Expand Down Expand Up @@ -85,6 +93,7 @@ export async function intelligenceAPIAuthentication(telemetry: ITelemetry, sessi
}

export async function dataverseAuthentication(
telemetry: ITelemetry,
dataverseOrgURL: string,
firstTimeAuth = false
): Promise<{ accessToken: string, userId: string }> {
Expand Down Expand Up @@ -119,37 +128,38 @@ export async function dataverseAuthentication(
}

if (firstTimeAuth) {
WebExtensionContext.telemetry.sendInfoTelemetry(
telemetryEventNames.WEB_EXTENSION_DATAVERSE_AUTHENTICATION_COMPLETED,
sendTelemetryEvent(telemetry,
{
eventName: VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_COMPLETED,
userId: userId
}
);
}
} catch (error) {
const authError = (error as Error)?.message;
showErrorDialog(
vscode.l10n.t(
"Authorization Failed. Please run again to authorize it"
),
vscode.l10n.t("There was a permissions problem with the server")
);
WebExtensionContext.telemetry.sendErrorTelemetry(
telemetryEventNames.WEB_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED,
dataverseAuthentication.name,
authError
sendTelemetryEvent(
telemetry, {
eventName: VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED,
error: error as Error
}
);
}

return { accessToken, userId };
}

export async function npsAuthentication(
telemetry: ITelemetry,
cesSurveyAuthorizationEndpoint: string
): Promise<string> {
let accessToken = "";
WebExtensionContext.telemetry.sendInfoTelemetry(
telemetryEventNames.NPS_AUTHENTICATION_STARTED
sendTelemetryEvent(telemetry,
{ eventName: VSCODE_EXTENSION_NPS_AUTHENTICATION_STARTED }
);
try {
const session = await vscode.authentication.getSession(
Expand All @@ -161,28 +171,30 @@ export async function npsAuthentication(
if (!accessToken) {
throw new Error(ERRORS.NO_ACCESS_TOKEN);
}
WebExtensionContext.telemetry.sendInfoTelemetry(
telemetryEventNames.NPS_AUTHENTICATION_COMPLETED
sendTelemetryEvent(telemetry,
{ eventName: VSCODE_EXTENSION_NPS_AUTHENTICATION_COMPLETED }
);
} catch (error) {
const authError = (error as Error)?.message;
showErrorDialog(
vscode.l10n.t(
"Authorization Failed. Please run again to authorize it"
),
vscode.l10n.t("There was a permissions problem with the server")
);
WebExtensionContext.telemetry.sendErrorTelemetry(
telemetryEventNames.NPS_AUTHENTICATION_FAILED,
npsAuthentication.name,
authError
sendTelemetryEvent(
telemetry,
{
eventName: VSCODE_EXTENSION_NPS_AUTHENTICATION_FAILED,
error: error as Error
}
);
}

return accessToken;
}

export async function graphClientAuthentication(
telemetry: ITelemetry,
firstTimeAuth = false
): Promise<string> {
let accessToken = "";
Expand Down Expand Up @@ -213,29 +225,24 @@ export async function graphClientAuthentication(
}

if (firstTimeAuth) {
WebExtensionContext.telemetry.sendInfoTelemetry(
telemetryEventNames.WEB_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_COMPLETED,
{
userId:
session?.account.id.split("/").pop() ??
session?.account.id ??
"",
}
);
sendTelemetryEvent(telemetry, {
eventName: VSCODE_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_COMPLETED,
userId:
session?.account.id.split("/").pop() ??
session?.account.id ??
"",
});
}
} catch (error) {
const authError = (error as Error)?.message;
showErrorDialog(
vscode.l10n.t(
"Authorization Failed. Please run again to authorize it"
),
vscode.l10n.t("There was a permissions problem with the server")
);
WebExtensionContext.telemetry.sendErrorTelemetry(
telemetryEventNames.WEB_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_FAILED,
graphClientAuthentication.name,
authError
);
sendTelemetryEvent(telemetry,
{ eventName: VSCODE_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_FAILED, error: error as Error }
)
}

return accessToken;
Expand Down
55 changes: 55 additions & 0 deletions src/common/ErrorConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/


export const ERRORS = {
SUBURI_EMPTY: "SubURI value for entity file is empty",
NO_ACCESS_TOKEN: "No access token was created",
PORTAL_FOLDER_NAME_EMPTY: "portalFolderName value for entity file is empty",
ATTRIBUTES_EMPTY: "Entity file attribute or extension field empty",
WORKSPACE_INITIAL_LOAD: "There was a problem opening the workspace",
WORKSPACE_INITIAL_LOAD_DESC: "Try refreshing the browser",
UNKNOWN_APP: "Unable to find that app",
AUTHORIZATION_FAILED:
"Authorization Failed. Please run again to authorize it",
FILE_NOT_FOUND: "The file was not found",
RETRY_LIMIT_EXCEEDED: "Unable to complete that operation",
RETRY_LIMIT_EXCEEDED_DESC: "You've exceeded the retry limit ",
PRECONDITION_CHECK_FAILED: "The precondition check did not work",
PRECONDITION_CHECK_FAILED_DESC: "Try again",
SERVER_ERROR_RETRY_LATER: "There was a problem with the server",
SERVER_ERROR_RETRY_LATER_DESC: "Please try again in a minute or two",
SERVER_ERROR_PERMISSION_DENIED:
"There was a permissions problem with the server",
SERVER_ERROR_PERMISSION_DENIED_DESC: "Please try again in a minute or two",
EMPTY_RESPONSE: "There was no response",
EMPTY_RESPONSE_DESC: "Try again",
THRESHOLD_LIMIT_EXCEEDED: "Threshold for dataverse api",
THRESHOLD_LIMIT_EXCEEDED_DESC:
"You’ve exceeded the threshold rate limit for the Dataverse API",
BAD_REQUEST: "Unable to complete the request",
BAD_REQUEST_DESC:
"One or more attribute names have been changed or removed. Contact your admin.",
BACKEND_ERROR: "There’s a problem on the back end",
SERVICE_UNAVAILABLE: "There’s a problem connecting to Dataverse",
SERVICE_ERROR: "There’s a problem connecting to Dataverse",
INVALID_ARGUMENT: "One or more commands are invalid or malformed",
BACKEND_ERROR_DESC: "Try again",
SERVICE_UNAVAILABLE_DESC: "Try again",
SERVICE_ERROR_DESC: "Try again",
INVALID_ARGUMENT_DESC: "Check the parameters and try again",
MANDATORY_PARAMETERS_NULL: "The workspace is not available ",
MANDATORY_PARAMETERS_NULL_DESC:
"Check the URL and verify the parameters are correct",
FILE_NAME_NOT_SET: "That file is not available",
FILE_NAME_NOT_SET_DESC:
"The metadata may have changed in the Dataverse side. Contact your admin. {message_attribute}",
FILE_NAME_EMPTY: "File name is empty",
FILE_ID_EMPTY: "File ID is empty",
LANGUAGE_CODE_ID_VALUE_NULL: "Language code ID is empty",
LANGUAGE_CODE_EMPTY: "Language code is empty",
BULKHEAD_LIMITS_EXCEEDED: "Bulkhead queue limits exceeded",
NPS_FAILED_AUTH: "Failed to authenticate with NPS"
};
15 changes: 15 additions & 0 deletions src/common/TelemetryConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

// Telemetry Event Names
export const VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_STARTED = "VSCodeExtensionDataVerseAuthenticationStarted";
export const VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED = "VSCodeExtensionDataVerseAuthenticationFailed";
export const VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_MISSING = "VSCodeExtensionDataVerseAuthenticationMissing";
export const VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_COMPLETED = "VSCodeExtensionDataVerseAuthenticationCompleted";
export const VSCODE_EXTENSION_NPS_AUTHENTICATION_STARTED = "VSCodeExtensionNPSAuthenticationStarted";
export const VSCODE_EXTENSION_NPS_AUTHENTICATION_COMPLETED = "VSCodeExtensionNPSAuthenticationCompleted";
export const VSCODE_EXTENSION_NPS_AUTHENTICATION_FAILED = "VSCodeExtensionNPSAuthenticationFailed";
export const VSCODE_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_FAILED = "VSCodeExtensionGraphClientAuthenticationFailed";
export const VSCODE_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_COMPLETED = "VSCodeExtensionGraphClientAuthenticationCompleted";
4 changes: 2 additions & 2 deletions src/common/copilot/PowerPagesCopilot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import * as vscode from "vscode";
import { sendApiRequest } from "./IntelligenceApiService";
import { dataverseAuthentication, intelligenceAPIAuthentication } from "../../web/client/common/authenticationProvider";
import { dataverseAuthentication, intelligenceAPIAuthentication } from "../AuthenticationProvider";
import { v4 as uuidv4 } from 'uuid'
import { PacWrapper } from "../../client/pac/PacWrapper";
import { ITelemetry } from "../../client/telemetry/ITelemetry";
Expand Down Expand Up @@ -361,7 +361,7 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider {
if (activeFileParams.dataverseEntity == ADX_ENTITYFORM || activeFileParams.dataverseEntity == ADX_ENTITYLIST) {
metadataInfo = await getEntityName(telemetry, sessionID, activeFileParams.dataverseEntity);

const dataverseToken = (await dataverseAuthentication(activeOrgUrl, true)).accessToken;
const dataverseToken = (await dataverseAuthentication(telemetry, activeOrgUrl, true)).accessToken;

if (activeFileParams.dataverseEntity == ADX_ENTITYFORM) {
const formColumns = await getFormXml(metadataInfo.entityName, metadataInfo.formName, activeOrgUrl, dataverseToken, telemetry, sessionID);
Expand Down
4 changes: 3 additions & 1 deletion src/common/copilot/telemetry/ITelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface IProDevCopilotTelemetryData {
eventName: string,
durationInMills?: number,
exception?: Error,
copilotSessionId: string,
copilotSessionId?: string,
orgId?: string,
FeedbackId?: string
error?: Error,
Expand All @@ -21,4 +21,6 @@ export interface IProDevCopilotTelemetryData {
tokenSize?: string
isSuggestedPrompt?: string;
subScenario?: string;
userId?: string;
errorMsg?: string;
}
1 change: 1 addition & 0 deletions src/common/copilot/telemetry/telemetryConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export const CopilotNotAvailable = 'CopilotNotAvailable';
export const CopilotNotAvailableECSConfig = 'CopilotNotAvailableECSConfig';
export const CopilotExplainCode = 'CopilotExplainCode';
export const CopilotExplainCodeSize = 'CopilotExplainCodeSize';
export const CopilotNpsAuthenticationCompleted = "CopilotNpsAuthenticationCompleted";
17 changes: 12 additions & 5 deletions src/common/copilot/user-feedback/CESSurvey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@
*/

import * as vscode from "vscode";
import { npsAuthentication } from "../../../web/client/common/authenticationProvider";
import { npsAuthentication } from "../../AuthenticationProvider";
import { SurveyConstants } from "../../../web/client/common/constants";
import fetch from "node-fetch";
import { getNonce } from "../../Utils";
import { ITelemetry } from "../../../client/telemetry/ITelemetry";
import { CopilotUserFeedbackFailureEvent, CopilotUserFeedbackSuccessEvent } from "../telemetry/telemetryConstants";
import { CopilotNpsAuthenticationCompleted, CopilotUserFeedbackFailureEvent, CopilotUserFeedbackSuccessEvent } from "../telemetry/telemetryConstants";
import { sendTelemetryEvent } from "../telemetry/copilotTelemetry";
import { IFeedbackData } from "../model";
import { EUROPE_GEO, UK_GEO } from "../constants";
import { ERRORS } from "../../ErrorConstants";

let feedbackPanel: vscode.WebviewPanel | undefined;


export async function CESUserFeedback(context: vscode.ExtensionContext, sessionId: string, userID: string, thumbType: string, telemetry: ITelemetry, geoName: string, messageScenario: string, tenantId?: string) {
export async function CESUserFeedback(context: vscode.ExtensionContext, sessionId: string, userID: string, thumbType: string, telemetry: ITelemetry, geoName: string, messageScenario: string, tenantId?: string) {

if (feedbackPanel) {
feedbackPanel.dispose();
Expand All @@ -35,7 +36,13 @@ export async function CESUserFeedback(context: vscode.ExtensionContext, sessionI

const feedbackData = initializeFeedbackData(sessionId, vscode.env.uiKind === vscode.UIKind.Web, geoName, messageScenario, tenantId);

const apiToken: string = await npsAuthentication(SurveyConstants.AUTHORIZATION_ENDPOINT);
const apiToken: string = await npsAuthentication(telemetry, SurveyConstants.AUTHORIZATION_ENDPOINT);

if (apiToken) {
sendTelemetryEvent(telemetry, { eventName: CopilotNpsAuthenticationCompleted, feedbackType: thumbType, copilotSessionId: sessionId });
} else {
sendTelemetryEvent(telemetry, { eventName: CopilotUserFeedbackFailureEvent, feedbackType: thumbType, copilotSessionId: sessionId, error: new Error(ERRORS.NPS_FAILED_AUTH) });
}

const endpointUrl = useEUEndpoint(geoName) ? `https://europe.ces.microsoftcloud.com/api/v1/portalsdesigner/Surveys/powerpageschatgpt/Feedbacks?userId=${userID}` :
`https://world.ces.microsoftcloud.com/api/v1/portalsdesigner/Surveys/powerpageschatgpt/Feedbacks?userId=${userID}`;
Expand Down Expand Up @@ -79,7 +86,7 @@ function getWebviewURIs(context: vscode.ExtensionContext, feedbackPanel: vscode.
return { feedbackCssUri, feedbackJsUri };
}

function initializeFeedbackData(sessionId: string, isWebExtension: boolean, geoName: string, messageScenario: string, tenantId?: string): IFeedbackData {
function initializeFeedbackData(sessionId: string, isWebExtension: boolean, geoName: string, messageScenario: string, tenantId?: string): IFeedbackData {
const feedbackData: IFeedbackData = {
TenantId: tenantId ? tenantId : '',
Geo: geoName,
Expand Down
12 changes: 11 additions & 1 deletion src/web/client/WebExtensionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as vscode from "vscode";
import {
dataverseAuthentication,
getCommonHeadersForDataverse,
} from "./common/authenticationProvider";
} from "../../common/AuthenticationProvider";
import * as Constants from "./common/constants";
import {
getDataSourcePropertiesMap,
Expand Down Expand Up @@ -371,6 +371,7 @@ class WebExtensionContext implements IWebExtensionContext {
Constants.queryParameters.ORG_URL
) as string;
const { accessToken, userId } = await dataverseAuthentication(
this._telemetry.getTelemetryReporter(),
dataverseOrgUrl,
firstTimeAuth
);
Expand All @@ -396,6 +397,15 @@ class WebExtensionContext implements IWebExtensionContext {

this._dataverseAccessToken = accessToken;
this._userId = userId;

if (firstTimeAuth) {
this._telemetry.sendInfoTelemetry(
telemetryEventNames.WEB_EXTENSION_DATAVERSE_AUTHENTICATION_COMPLETED,
{
userId: userId
}
);
}
}

public async updateFileDetailsInContext(
Expand Down
Loading

0 comments on commit 87570bc

Please sign in to comment.