From 87570bcdadaa0c7eb9b350826d570c3302b56dea Mon Sep 17 00:00:00 2001 From: tyaginidhi Date: Fri, 3 May 2024 15:45:18 +0530 Subject: [PATCH] Making auth provider a common module flow (#928) --- l10n/bundle.l10n.json | 2 + .../vscode-powerplatform.xlf | 6 + .../AuthenticationProvider.ts} | 85 +++++----- src/common/ErrorConstants.ts | 55 ++++++ src/common/TelemetryConstants.ts | 15 ++ src/common/copilot/PowerPagesCopilot.ts | 4 +- src/common/copilot/telemetry/ITelemetry.ts | 4 +- .../copilot/telemetry/telemetryConstants.ts | 1 + src/common/copilot/user-feedback/CESSurvey.ts | 17 +- src/web/client/WebExtensionContext.ts | 12 +- src/web/client/common/errorHandler.ts | 49 ------ src/web/client/dal/concurrencyHandler.ts | 4 +- src/web/client/dal/fileSystemProvider.ts | 2 +- src/web/client/dal/remoteFetchProvider.ts | 5 +- src/web/client/dal/remoteSaveProvider.ts | 4 +- src/web/client/services/NPSService.ts | 157 +++++++++--------- src/web/client/services/etagHandlerService.ts | 2 +- src/web/client/services/graphClientService.ts | 17 +- src/web/client/telemetry/constants.ts | 14 +- .../AuthenticationProvider.test.ts | 54 +++--- .../integration/WebExtensionContext.test.ts | 10 +- .../integration/remoteFetchProvider.test.ts | 8 +- src/web/client/webViews/NPSWebView.ts | 4 +- 23 files changed, 307 insertions(+), 224 deletions(-) rename src/{web/client/common/authenticationProvider.ts => common/AuthenticationProvider.ts} (75%) create mode 100644 src/common/ErrorConstants.ts create mode 100644 src/common/TelemetryConstants.ts diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index c08f1ed2..6aba81ae 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -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", diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf index 79905571..8ff6b43c 100644 --- a/loc/translations-export/vscode-powerplatform.xlf +++ b/loc/translations-export/vscode-powerplatform.xlf @@ -263,6 +263,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Power Pages Copilot is now connected to the environment: {0} : {1} {0} represents the environment name + + Power Pages studio URL is not available + Preparing pac CLI (v{0})... {0} represents the version number @@ -270,6 +273,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Preview site + + Preview site URL is not available + Profile Kind: {0} The {0} represents the profile type (Admin vs Dataverse) diff --git a/src/web/client/common/authenticationProvider.ts b/src/common/AuthenticationProvider.ts similarity index 75% rename from src/web/client/common/authenticationProvider.ts rename to src/common/AuthenticationProvider.ts index 24837e98..8a1ff646 100644 --- a/src/web/client/common/authenticationProvider.ts +++ b/src/common/AuthenticationProvider.ts @@ -4,8 +4,6 @@ */ import * as vscode from "vscode"; -import WebExtensionContext from "../WebExtensionContext"; -import { telemetryEventNames } from "../telemetry/constants"; import { INTELLIGENCE_SCOPE_DEFAULT, PROVIDER_ID, @@ -13,12 +11,22 @@ import { 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( @@ -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 }> { @@ -119,25 +128,25 @@ 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 + } ); } @@ -145,11 +154,12 @@ export async function dataverseAuthentication( } export async function npsAuthentication( + telemetry: ITelemetry, cesSurveyAuthorizationEndpoint: string ): Promise { let accessToken = ""; - WebExtensionContext.telemetry.sendInfoTelemetry( - telemetryEventNames.NPS_AUTHENTICATION_STARTED + sendTelemetryEvent(telemetry, + { eventName: VSCODE_EXTENSION_NPS_AUTHENTICATION_STARTED } ); try { const session = await vscode.authentication.getSession( @@ -161,21 +171,22 @@ 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 + } ); } @@ -183,6 +194,7 @@ export async function npsAuthentication( } export async function graphClientAuthentication( + telemetry: ITelemetry, firstTimeAuth = false ): Promise { let accessToken = ""; @@ -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; diff --git a/src/common/ErrorConstants.ts b/src/common/ErrorConstants.ts new file mode 100644 index 00000000..92bace75 --- /dev/null +++ b/src/common/ErrorConstants.ts @@ -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" +}; diff --git a/src/common/TelemetryConstants.ts b/src/common/TelemetryConstants.ts new file mode 100644 index 00000000..6cc6353f --- /dev/null +++ b/src/common/TelemetryConstants.ts @@ -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"; diff --git a/src/common/copilot/PowerPagesCopilot.ts b/src/common/copilot/PowerPagesCopilot.ts index 9f56fa33..e5049451 100644 --- a/src/common/copilot/PowerPagesCopilot.ts +++ b/src/common/copilot/PowerPagesCopilot.ts @@ -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"; @@ -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); diff --git a/src/common/copilot/telemetry/ITelemetry.ts b/src/common/copilot/telemetry/ITelemetry.ts index 8702df1e..ee5a327e 100644 --- a/src/common/copilot/telemetry/ITelemetry.ts +++ b/src/common/copilot/telemetry/ITelemetry.ts @@ -7,7 +7,7 @@ export interface IProDevCopilotTelemetryData { eventName: string, durationInMills?: number, exception?: Error, - copilotSessionId: string, + copilotSessionId?: string, orgId?: string, FeedbackId?: string error?: Error, @@ -21,4 +21,6 @@ export interface IProDevCopilotTelemetryData { tokenSize?: string isSuggestedPrompt?: string; subScenario?: string; + userId?: string; + errorMsg?: string; } diff --git a/src/common/copilot/telemetry/telemetryConstants.ts b/src/common/copilot/telemetry/telemetryConstants.ts index c9723806..9baddb0d 100644 --- a/src/common/copilot/telemetry/telemetryConstants.ts +++ b/src/common/copilot/telemetry/telemetryConstants.ts @@ -36,3 +36,4 @@ export const CopilotNotAvailable = 'CopilotNotAvailable'; export const CopilotNotAvailableECSConfig = 'CopilotNotAvailableECSConfig'; export const CopilotExplainCode = 'CopilotExplainCode'; export const CopilotExplainCodeSize = 'CopilotExplainCodeSize'; +export const CopilotNpsAuthenticationCompleted = "CopilotNpsAuthenticationCompleted"; diff --git a/src/common/copilot/user-feedback/CESSurvey.ts b/src/common/copilot/user-feedback/CESSurvey.ts index 2b2a4dd9..025315ed 100644 --- a/src/common/copilot/user-feedback/CESSurvey.ts +++ b/src/common/copilot/user-feedback/CESSurvey.ts @@ -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(); @@ -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}`; @@ -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, diff --git a/src/web/client/WebExtensionContext.ts b/src/web/client/WebExtensionContext.ts index 13778f90..b3166d48 100644 --- a/src/web/client/WebExtensionContext.ts +++ b/src/web/client/WebExtensionContext.ts @@ -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, @@ -371,6 +371,7 @@ class WebExtensionContext implements IWebExtensionContext { Constants.queryParameters.ORG_URL ) as string; const { accessToken, userId } = await dataverseAuthentication( + this._telemetry.getTelemetryReporter(), dataverseOrgUrl, firstTimeAuth ); @@ -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( diff --git a/src/web/client/common/errorHandler.ts b/src/web/client/common/errorHandler.ts index 5e2658a9..b209d2d1 100644 --- a/src/web/client/common/errorHandler.ts +++ b/src/web/client/common/errorHandler.ts @@ -10,55 +10,6 @@ import { telemetryEventNames } from "../telemetry/constants"; import { PORTALS_FOLDER_NAME_DEFAULT, queryParameters } from "./constants"; import { isMultifileEnabled } from "../utilities/commonUtil"; -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", -}; - export function showErrorDialog(errorString: string, detailMessage?: string) { const options = { detail: detailMessage, modal: true }; vscode.window.showErrorMessage(errorString, options); diff --git a/src/web/client/dal/concurrencyHandler.ts b/src/web/client/dal/concurrencyHandler.ts index 3874b299..4bb218fc 100644 --- a/src/web/client/dal/concurrencyHandler.ts +++ b/src/web/client/dal/concurrencyHandler.ts @@ -6,9 +6,9 @@ import { BulkheadRejectedError, bulkhead } from 'cockatiel'; import fetch, { RequestInfo, RequestInit } from "node-fetch"; import { MAX_CONCURRENT_REQUEST_COUNT, MAX_CONCURRENT_REQUEST_QUEUE_COUNT } from '../common/constants'; -import { ERRORS } from '../common/errorHandler'; import WebExtensionContext from "../WebExtensionContext"; import { telemetryEventNames } from '../telemetry/constants'; +import { ERRORS } from '../../../common/ErrorConstants'; export class ConcurrencyHandler { private _bulkhead = bulkhead(MAX_CONCURRENT_REQUEST_COUNT, MAX_CONCURRENT_REQUEST_QUEUE_COUNT); @@ -32,4 +32,4 @@ export class ConcurrencyHandler { } } } -} \ No newline at end of file +} diff --git a/src/web/client/dal/fileSystemProvider.ts b/src/web/client/dal/fileSystemProvider.ts index fa96e42d..dacc62f6 100644 --- a/src/web/client/dal/fileSystemProvider.ts +++ b/src/web/client/dal/fileSystemProvider.ts @@ -13,7 +13,6 @@ import { import WebExtensionContext from "../WebExtensionContext"; import { fetchDataFromDataverseAndUpdateVFS } from "./remoteFetchProvider"; import { saveData } from "./remoteSaveProvider"; -import { ERRORS } from "../common/errorHandler"; import { telemetryEventNames } from "../telemetry/constants"; import { getFolderSubUris } from "../utilities/folderHelperUtility"; import { EtagHandlerService } from "../services/etagHandlerService"; @@ -32,6 +31,7 @@ import { } from "../utilities/fileAndEntityUtil"; import { getImageFileContent, getRangeForMultilineMatch, isImageFileSupportedForEdit, isVersionControlEnabled, updateFileContentInFileDataMap } from "../utilities/commonUtil"; import { IFileInfo, ISearchQueryMatch, ISearchQueryResults } from "../common/interfaces"; +import { ERRORS } from "../../../common/ErrorConstants"; export class File implements vscode.FileStat { type: vscode.FileType; diff --git a/src/web/client/dal/remoteFetchProvider.ts b/src/web/client/dal/remoteFetchProvider.ts index 4cdbc957..99168707 100644 --- a/src/web/client/dal/remoteFetchProvider.ts +++ b/src/web/client/dal/remoteFetchProvider.ts @@ -15,9 +15,9 @@ import { setFileContent, } from "../utilities/commonUtil"; import { getCustomRequestURL, getMappingEntityContent, getMetadataInfo, getMappingEntityId, getMimeType, getRequestURL } from "../utilities/urlBuilderUtil"; -import { getCommonHeadersForDataverse } from "../common/authenticationProvider"; +import { getCommonHeadersForDataverse } from "../../../common/AuthenticationProvider"; import * as Constants from "../common/constants"; -import { ERRORS, showErrorDialog } from "../common/errorHandler"; +import { showErrorDialog } from "../common/errorHandler"; import { PortalsFS } from "./fileSystemProvider"; import { encodeAsBase64, @@ -32,6 +32,7 @@ import { EntityMetadataKeyCore, SchemaEntityMetadata, folderExportType, schemaEn import { getEntityNameForExpandedEntityContent, getRequestUrlForEntities } from "../utilities/folderHelperUtility"; import { IAttributePath, IFileInfo } from "../common/interfaces"; import { portal_schema_V2 } from "../schema/portalSchema"; +import { ERRORS } from "../../../common/ErrorConstants"; export async function fetchDataFromDataverseAndUpdateVFS( portalFs: PortalsFS, diff --git a/src/web/client/dal/remoteSaveProvider.ts b/src/web/client/dal/remoteSaveProvider.ts index ae108f1b..dc7db03a 100644 --- a/src/web/client/dal/remoteSaveProvider.ts +++ b/src/web/client/dal/remoteSaveProvider.ts @@ -5,7 +5,7 @@ import { RequestInit } from "node-fetch"; import * as vscode from "vscode"; -import { getCommonHeadersForDataverse } from "../common/authenticationProvider"; +import { getCommonHeadersForDataverse } from "../../../common/AuthenticationProvider"; import { BAD_REQUEST, MIMETYPE, queryParameters } from "../common/constants"; import { showErrorDialog } from "../common/errorHandler"; import { FileData } from "../context/fileData"; @@ -231,4 +231,4 @@ async function saveDataToDataverse( throw error; } } -} \ No newline at end of file +} diff --git a/src/web/client/services/NPSService.ts b/src/web/client/services/NPSService.ts index b4a2a928..5ee290c4 100644 --- a/src/web/client/services/NPSService.ts +++ b/src/web/client/services/NPSService.ts @@ -4,7 +4,7 @@ */ import jwt_decode from 'jwt-decode'; -import { npsAuthentication } from "../common/authenticationProvider"; +import { npsAuthentication } from "../../../common/AuthenticationProvider"; import { SurveyConstants, httpMethod, queryParameters } from '../common/constants'; import { RequestInit } from 'node-fetch' import WebExtensionContext from '../WebExtensionContext'; @@ -12,85 +12,90 @@ import { telemetryEventNames } from '../telemetry/constants'; import { getCurrentDataBoundary } from '../utilities/dataBoundary'; export class NPSService { - public static getCesHeader(accessToken: string) { - return { - authorization: "Bearer " + accessToken, - Accept: 'application/json', - 'Content-Type': 'application/json', - }; - } + public static getCesHeader(accessToken: string) { + return { + authorization: "Bearer " + accessToken, + Accept: 'application/json', + 'Content-Type': 'application/json', + }; + } - public static getNpsSurveyEndpoint(): string { - const region = WebExtensionContext.urlParametersMap?.get(queryParameters.REGION)?.toLowerCase(); - const dataBoundary = getCurrentDataBoundary(); - let npsSurveyEndpoint = ''; - switch (region) { - case 'tie': - case 'test': - case 'preprod': - switch (dataBoundary) { - case 'eu': - npsSurveyEndpoint = 'https://europe.tip1.ces.microsoftcloud.com'; - break; - default: - npsSurveyEndpoint = 'https://world.tip1.ces.microsoftcloud.com'; - } - break; - case 'prod': - case 'preview': - switch (dataBoundary) { - case 'eu': - npsSurveyEndpoint = 'https://europe.ces.microsoftcloud.com'; - break; - default: - npsSurveyEndpoint = 'https://world.ces.microsoftcloud.com'; + public static getNpsSurveyEndpoint(): string { + const region = WebExtensionContext.urlParametersMap?.get(queryParameters.REGION)?.toLowerCase(); + const dataBoundary = getCurrentDataBoundary(); + let npsSurveyEndpoint = ''; + switch (region) { + case 'tie': + case 'test': + case 'preprod': + switch (dataBoundary) { + case 'eu': + npsSurveyEndpoint = 'https://europe.tip1.ces.microsoftcloud.com'; + break; + default: + npsSurveyEndpoint = 'https://world.tip1.ces.microsoftcloud.com'; + } + break; + case 'prod': + case 'preview': + switch (dataBoundary) { + case 'eu': + npsSurveyEndpoint = 'https://europe.ces.microsoftcloud.com'; + break; + default: + npsSurveyEndpoint = 'https://world.ces.microsoftcloud.com'; + } + break; + case 'gov': + case 'high': + case 'dod': + case 'mooncake': + npsSurveyEndpoint = 'https://world.ces.microsoftcloud.com'; + break; + case 'ex': + case 'rx': + default: + break; } - break; - case 'gov': - case 'high': - case 'dod': - case 'mooncake': - npsSurveyEndpoint = 'https://world.ces.microsoftcloud.com'; - break; - case 'ex': - case 'rx': - default: - break; + + return npsSurveyEndpoint; } - return npsSurveyEndpoint; - } + public static async setEligibility() { + try { - public static async setEligibility() { - try { + const baseApiUrl = this.getNpsSurveyEndpoint(); + const accessToken: string = await npsAuthentication(WebExtensionContext.telemetry.getTelemetryReporter(), SurveyConstants.AUTHORIZATION_ENDPOINT); - const baseApiUrl = this.getNpsSurveyEndpoint(); - const accessToken: string = await npsAuthentication(SurveyConstants.AUTHORIZATION_ENDPOINT); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const parsedToken = jwt_decode(accessToken) as any; - WebExtensionContext.setUserId(parsedToken?.oid) - const apiEndpoint = `${baseApiUrl}/api/v1/${SurveyConstants.TEAM_NAME}/Eligibilities/${SurveyConstants.SURVEY_NAME}?userId=${parsedToken?.oid}&eventName=${SurveyConstants.EVENT_NAME}&tenantId=${parsedToken.tid}`; - const requestInitPost: RequestInit = { - method: httpMethod.POST, - body: '{}', - headers: NPSService.getCesHeader(accessToken) - }; - const requestSentAtTime = new Date().getTime(); - const response = await WebExtensionContext.concurrencyHandler.handleRequest(apiEndpoint, requestInitPost); - const result = await response?.json(); - if (result?.Eligibility) { - WebExtensionContext.telemetry.sendAPISuccessTelemetry( - telemetryEventNames.NPS_USER_ELIGIBLE, - "NPS Api", - httpMethod.POST, - new Date().getTime() - requestSentAtTime, - this.setEligibility.name - ); - WebExtensionContext.setNPSEligibility(true); - WebExtensionContext.setFormsProEligibilityId(result?.FormsProEligibilityId); - } - } catch (error) { - WebExtensionContext.telemetry.sendErrorTelemetry(telemetryEventNames.NPS_API_FAILED, this.setEligibility.name,(error as Error)?.message, error as Error); + if (accessToken) { + WebExtensionContext.telemetry.sendInfoTelemetry(telemetryEventNames.WEB_EXTENSION_NPS_AUTHENTICATION_COMPLETED); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const parsedToken = jwt_decode(accessToken) as any; + WebExtensionContext.setUserId(parsedToken?.oid) + const apiEndpoint = `${baseApiUrl}/api/v1/${SurveyConstants.TEAM_NAME}/Eligibilities/${SurveyConstants.SURVEY_NAME}?userId=${parsedToken?.oid}&eventName=${SurveyConstants.EVENT_NAME}&tenantId=${parsedToken.tid}`; + const requestInitPost: RequestInit = { + method: httpMethod.POST, + body: '{}', + headers: NPSService.getCesHeader(accessToken) + }; + const requestSentAtTime = new Date().getTime(); + const response = await WebExtensionContext.concurrencyHandler.handleRequest(apiEndpoint, requestInitPost); + const result = await response?.json(); + if (result?.Eligibility) { + WebExtensionContext.telemetry.sendAPISuccessTelemetry( + telemetryEventNames.WEB_EXTENSION_NPS_USER_ELIGIBLE, + "NPS Api", + httpMethod.POST, + new Date().getTime() - requestSentAtTime, + this.setEligibility.name + ); + WebExtensionContext.setNPSEligibility(true); + WebExtensionContext.setFormsProEligibilityId(result?.FormsProEligibilityId); + } + } catch (error) { + WebExtensionContext.telemetry.sendErrorTelemetry(telemetryEventNames.WEB_EXTENSION_NPS_API_FAILED, this.setEligibility.name, (error as Error)?.message, error as Error); + } } - } -} \ No newline at end of file +} diff --git a/src/web/client/services/etagHandlerService.ts b/src/web/client/services/etagHandlerService.ts index 5b3d298d..75178787 100644 --- a/src/web/client/services/etagHandlerService.ts +++ b/src/web/client/services/etagHandlerService.ts @@ -5,7 +5,7 @@ import * as vscode from "vscode"; import { RequestInit } from "node-fetch"; -import { getCommonHeadersForDataverse } from "../common/authenticationProvider"; +import { getCommonHeadersForDataverse } from "../../../common/AuthenticationProvider"; import { httpMethod, ODATA_ETAG, queryParameters } from "../common/constants"; import { IAttributePath } from "../common/interfaces"; import { PortalsFS } from "../dal/fileSystemProvider"; diff --git a/src/web/client/services/graphClientService.ts b/src/web/client/services/graphClientService.ts index ce6c4945..bb97a6f5 100644 --- a/src/web/client/services/graphClientService.ts +++ b/src/web/client/services/graphClientService.ts @@ -5,7 +5,7 @@ import path from "path"; import WebExtensionContext from "../WebExtensionContext"; -import { getCommonHeaders, graphClientAuthentication } from "../common/authenticationProvider"; +import { getCommonHeaders, graphClientAuthentication } from "../../../common/AuthenticationProvider"; import * as Constants from "../common/constants"; import { telemetryEventNames } from "../telemetry/constants"; @@ -21,7 +21,20 @@ export class GraphClientService { } public async graphClientAuthentication(firstTimeAuth = false) { - const accessToken = await graphClientAuthentication(firstTimeAuth); + const accessToken = await graphClientAuthentication(WebExtensionContext.telemetry.getTelemetryReporter(), firstTimeAuth); + if (!accessToken) { + WebExtensionContext.telemetry.sendErrorTelemetry( + telemetryEventNames.WEB_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_FAILED, + graphClientAuthentication.name + ); + } + + if (firstTimeAuth && accessToken) { + WebExtensionContext.telemetry.sendInfoTelemetry( + telemetryEventNames.WEB_EXTENSION_GRAPH_CLIENT_AUTHENTICATION_COMPLETED + ); + } + this._graphToken = accessToken; } diff --git a/src/web/client/telemetry/constants.ts b/src/web/client/telemetry/constants.ts index 730612f6..76399dd5 100644 --- a/src/web/client/telemetry/constants.ts +++ b/src/web/client/telemetry/constants.ts @@ -35,13 +35,13 @@ export enum telemetryEventNames { WEB_EXTENSION_ENTITY_CONTENT_CHANGED = "WebExtensionEntityConentChanged", WEB_EXTENSION_ENTITY_CONTENT_SAME = "WebExtensionEntityContentSame", WEB_EXTENSION_ENTITY_CONTENT_UNEXPECTED_RESPONSE = "WebExtensionEntityContentUnexpectedResponse", - NPS_AUTHENTICATION_STARTED = "WebExtensionNPSAuthenticationStarted", - NPS_AUTHENTICATION_COMPLETED = "WebExtensionNPSAuthenticationCompleted", - NPS_AUTHENTICATION_FAILED = "WebExtensionNPSAuthenticationFailed", - NPS_USER_ELIGIBLE = "WebExtensionUserIsEligible", - NPS_API_FAILED = "WebExtensionNPSApiFailed", - RENDER_NPS = "WebExtensionNPSRenderSurveyForm", - RENDER_NPS_FAILED = "WebExtensionNPSRenderSurveyFormFailed", + WEB_EXTENSION_NPS_AUTHENTICATION_STARTED = "WebExtensionNPSAuthenticationStarted", + WEB_EXTENSION_NPS_AUTHENTICATION_COMPLETED = "WebExtensionNPSAuthenticationCompleted", + WEB_EXTENSION_NPS_AUTHENTICATION_FAILED = "WebExtensionNPSAuthenticationFailed", + WEB_EXTENSION_NPS_USER_ELIGIBLE = "WebExtensionUserIsEligible", + WEB_EXTENSION_NPS_API_FAILED = "WebExtensionNPSApiFailed", + WEB_EXTENSION_RENDER_NPS = "WebExtensionNPSRenderSurveyForm", + WEB_EXTENSION_RENDER_NPS_FAILED = "WebExtensionNPSRenderSurveyFormFailed", WEB_EXTENSION_CREATE_ENTITY_FOLDER = "WebExtensionCreateEntityFolder", WEB_EXTENSION_FILE_HAS_DIRTY_CHANGES = "WebExtensionFileHasDirtyChanges", WEB_EXTENSION_DIFF_VIEW_TRIGGERED = "WebExtensionDiffViewTriggered", diff --git a/src/web/client/test/integration/AuthenticationProvider.test.ts b/src/web/client/test/integration/AuthenticationProvider.test.ts index 04ec0c01..76fec74d 100644 --- a/src/web/client/test/integration/AuthenticationProvider.test.ts +++ b/src/web/client/test/integration/AuthenticationProvider.test.ts @@ -8,12 +8,14 @@ import { expect } from "chai"; import { dataverseAuthentication, getCommonHeaders, -} from "../../common/authenticationProvider"; +} from "../../../../common/AuthenticationProvider"; import vscode from "vscode"; -import WebExtensionContext from "../../WebExtensionContext"; -import { telemetryEventNames } from "../../telemetry/constants"; import * as errorHandler from "../../common/errorHandler"; import { oneDSLoggerWrapper } from "../../../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper"; +import * as copilotTelemetry from "../../../../common/copilot/telemetry/copilotTelemetry"; +import { WebExtensionTelemetry } from "../../telemetry/webExtensionTelemetry"; +import { vscodeExtAppInsightsResourceProvider } from "../../../../common/telemetry-generated/telemetryConfiguration"; +import { VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED } from "../../../../common/TelemetryConstants"; // eslint-disable-next-line @typescript-eslint/no-explicit-any let traceError: any @@ -32,6 +34,16 @@ describe("Authentication Provider", () => { // Restore the default sandbox here sinon.restore(); }); + + const webExtensionTelemetry = new WebExtensionTelemetry(); + const appInsightsResource = + vscodeExtAppInsightsResourceProvider.GetAppInsightsResourceForDataBoundary( + undefined + ); + webExtensionTelemetry.setTelemetryReporter("", "", appInsightsResource); + + const telemetry = webExtensionTelemetry.getTelemetryReporter(); + it("getHeader", () => { const accessToken = "f068ee9f-a010-47b9-b1e1-7e6353730e7d"; const result = getCommonHeaders(accessToken); @@ -54,7 +66,7 @@ describe("Authentication Provider", () => { scopes: [], }); - const result = await dataverseAuthentication(dataverseOrgURL); + const result = await dataverseAuthentication(telemetry, dataverseOrgURL); sinon.assert.calledOnce(_mockgetSession); expect(result.accessToken).eq("f068ee9f-a010-47b9-b1e1-7e6353730e7d"); expect(result.userId).empty; @@ -76,26 +88,21 @@ describe("Authentication Provider", () => { const showErrorDialog = sinon.spy(errorHandler, "showErrorDialog"); - const sendErrorTelemetry = sinon.spy( - WebExtensionContext.telemetry, - "sendErrorTelemetry" + const sendTelemetryEvent = sinon.spy( + copilotTelemetry, + "sendTelemetryEvent" ); //Act - await dataverseAuthentication(dataverseOrgURL); + await dataverseAuthentication(telemetry, dataverseOrgURL); sinon.assert.calledWith( showErrorDialog, "Authorization Failed. Please run again to authorize it" ); - sinon.assert.calledWith( - sendErrorTelemetry, - telemetryEventNames.WEB_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED - ); - + sinon.assert.calledOnce(sendTelemetryEvent); sinon.assert.calledOnce(showErrorDialog); - sinon.assert.calledOnce(sendErrorTelemetry); sinon.assert.calledOnce(_mockgetSession); }); @@ -107,21 +114,22 @@ describe("Authentication Provider", () => { .stub(await vscode.authentication, "getSession") .throws({ message: errorMessage }); - const sendError = sinon.spy( - WebExtensionContext.telemetry, - "sendErrorTelemetry" + const sendTelemetryEvent = sinon.spy( + copilotTelemetry, + "sendTelemetryEvent" ); // Act - const result = await dataverseAuthentication(dataverseOrgURL); + const result = await dataverseAuthentication(telemetry, dataverseOrgURL); //Assert - sinon.assert.calledOnce(sendError); + sinon.assert.calledOnce(sendTelemetryEvent); sinon.assert.calledWith( - sendError, - telemetryEventNames.WEB_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED, - dataverseAuthentication.name, - errorMessage + sendTelemetryEvent, + telemetry, { + eventName: VSCODE_EXTENSION_DATAVERSE_AUTHENTICATION_FAILED, + error: { message: errorMessage } as Error + } ); sinon.assert.calledOnce(_mockgetSession); expect(result.accessToken).empty; diff --git a/src/web/client/test/integration/WebExtensionContext.test.ts b/src/web/client/test/integration/WebExtensionContext.test.ts index 9274379d..05b25fcb 100644 --- a/src/web/client/test/integration/WebExtensionContext.test.ts +++ b/src/web/client/test/integration/WebExtensionContext.test.ts @@ -11,11 +11,11 @@ import WebExtensionContext from "../../WebExtensionContext"; import { schemaKey, schemaEntityKey } from "../../schema/constants"; import * as portalSchemaReader from "../../schema/portalSchemaReader"; import * as Constants from "../../common/constants"; -import * as authenticationProvider from "../../common/authenticationProvider"; +import * as authenticationProvider from "../../../../common/AuthenticationProvider"; import { telemetryEventNames } from "../../telemetry/constants"; import * as schemaHelperUtil from "../../utilities/schemaHelperUtil"; import * as urlBuilderUtil from "../../utilities/urlBuilderUtil"; -import { getCommonHeadersForDataverse } from "../../common/authenticationProvider"; +import { getCommonHeadersForDataverse } from "../../../../common/AuthenticationProvider"; import { IAttributePath } from "../../common/interfaces"; describe("WebExtensionContext", () => { @@ -463,7 +463,7 @@ describe("WebExtensionContext", () => { ); expect(WebExtensionContext.dataverseAccessToken).eq(accessToken); - assert.calledOnceWithExactly(dataverseAuthentication, ORG_URL, true); + assert.calledOnceWithExactly(dataverseAuthentication, WebExtensionContext.telemetry.getTelemetryReporter(), ORG_URL, true); assert.callCount(sendAPISuccessTelemetry, 3); assert.calledOnceWithExactly( getLcidCodeMap, @@ -641,7 +641,7 @@ describe("WebExtensionContext", () => { ); expect(WebExtensionContext.dataverseAccessToken).eq(accessToken); - assert.calledOnceWithExactly(dataverseAuthentication, ORG_URL, true); + assert.calledOnceWithExactly(dataverseAuthentication, WebExtensionContext.telemetry.getTelemetryReporter(), ORG_URL, true); assert.notCalled(getLcidCodeMap); assert.notCalled(getWebsiteIdToLcidMap); assert.notCalled(getPortalLanguageIdToLcidMap); @@ -757,7 +757,7 @@ describe("WebExtensionContext", () => { ); expect(WebExtensionContext.dataverseAccessToken).eq(accessToken); - assert.calledOnceWithExactly(dataverseAuthentication, ORG_URL, true); + assert.calledOnceWithExactly(dataverseAuthentication, WebExtensionContext.telemetry.getTelemetryReporter(), ORG_URL, true); //#region Fetch const header = getCommonHeadersForDataverse(accessToken); assert.calledThrice(_mockFetch); diff --git a/src/web/client/test/integration/remoteFetchProvider.test.ts b/src/web/client/test/integration/remoteFetchProvider.test.ts index ac66efd1..79d84bc6 100644 --- a/src/web/client/test/integration/remoteFetchProvider.test.ts +++ b/src/web/client/test/integration/remoteFetchProvider.test.ts @@ -15,7 +15,7 @@ import { schemaEntityKey, schemaKey } from "../../schema/constants"; import * as urlBuilderUtil from "../../utilities/urlBuilderUtil"; import * as commonUtil from "../../utilities/commonUtil"; import { expect } from "chai"; -import * as authenticationProvider from "../../common/authenticationProvider"; +import * as authenticationProvider from "../../../../common/AuthenticationProvider"; import { telemetryEventNames } from "../../telemetry/constants"; describe("remoteFetchProvider", () => { @@ -209,7 +209,7 @@ describe("remoteFetchProvider", () => { assert.callCount(writeFile, 3); assert.calledOnce(updateSingleFileUrisInContext); - assert.callCount(sendInfoTelemetry, 4); + assert.callCount(sendInfoTelemetry, 5); assert.callCount(sendAPISuccessTelemetry, 4); }); @@ -399,7 +399,7 @@ describe("remoteFetchProvider", () => { expect(updateFileDetailsInContextCalls[1].args[7], "false"); assert.callCount(writeFile, 3); - assert.callCount(sendInfoTelemetry, 6); + assert.callCount(sendInfoTelemetry, 7); assert.calledOnce(executeCommand); assert.callCount(sendAPISuccessTelemetry, 4); }); @@ -1145,7 +1145,7 @@ describe("remoteFetchProvider", () => { assert.callCount(writeFile, 1); assert.calledOnce(updateSingleFileUrisInContext); - assert.callCount(sendInfoTelemetry, 4); + assert.callCount(sendInfoTelemetry, 5); assert.callCount(sendAPISuccessTelemetry, 5); }); }); diff --git a/src/web/client/webViews/NPSWebView.ts b/src/web/client/webViews/NPSWebView.ts index 35fcdb40..455af31c 100644 --- a/src/web/client/webViews/NPSWebView.ts +++ b/src/web/client/webViews/NPSWebView.ts @@ -46,7 +46,7 @@ export class NPSWebView { const formsProEligibilityId = WebExtensionContext.formsProEligibilityId; WebExtensionContext.telemetry.sendInfoTelemetry( - telemetryEventNames.RENDER_NPS + telemetryEventNames.WEB_EXTENSION_RENDER_NPS ); return ` @@ -64,7 +64,7 @@ export class NPSWebView { `; } catch (error) { WebExtensionContext.telemetry.sendErrorTelemetry( - telemetryEventNames.RENDER_NPS_FAILED, + telemetryEventNames.WEB_EXTENSION_RENDER_NPS_FAILED, this._getHtml.name, (error as Error)?.message );