Pass RenderSurvey API parameter data & Added telemetry events/Localization (#457)
Passing proper parameter Added telemetry Added localization
This commit is contained in:
Родитель
eef7f10b43
Коммит
c91f580806
|
@ -337,6 +337,11 @@ The {2} represents Solution's Version number</note>
|
|||
<source xml:lang="en">Try again</source>
|
||||
</trans-unit>
|
||||
</body></file>
|
||||
<file original="./web/client/webViews/NPSWebView" source-language="en" datatype="plaintext"><body>
|
||||
<trans-unit id="microsoft-powerapps-portals.webExtension.npsSurvey.desc">
|
||||
<source xml:lang="en">Microsoft wants your feeback</source>
|
||||
</trans-unit>
|
||||
</body></file>
|
||||
<file original="./web/client/test/integration/errorHandler.test" source-language="en" datatype="plaintext"><body>
|
||||
<trans-unit id="microsoft-powerapps-portals.webExtension.init.app-not-found">
|
||||
<source xml:lang="en">Unable to find that app</source>
|
||||
|
|
|
@ -3,24 +3,15 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*/
|
||||
|
||||
// "use strict";
|
||||
// const path = require('path');
|
||||
// const cancel = path.resolve('../src/web/client/assets/cancel.svg');
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
function renderSurvey(parentElementId,FirstName, LastName, locale, environmentId, geo, UserId, TenantId, prompted)
|
||||
function renderSurvey(TenantId,UserId, EnvironmentId,Geo,ProductVersion,Culture,DeviceType,UrlReferrer)
|
||||
{
|
||||
// eslint-disable-next-line no-undef
|
||||
const se = new window['SurveyEmbed']("v4j5cvGGr0GRqy180BHbRytFqxSnvs1AqKx-mFT6qLBUOE5POUVGTVRDUDI1SEVaOFVaV1RGM0k4VyQlQCN0PWcu",
|
||||
"https://customervoice.microsoft.com/","https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit","true");
|
||||
const context = {
|
||||
"First Name": FirstName,
|
||||
"Last Name": LastName,
|
||||
"locale": locale,
|
||||
"environmentId": environmentId,
|
||||
"geo": geo,
|
||||
"UserId": UserId,
|
||||
"TenantId": TenantId,
|
||||
"prompted": prompted,
|
||||
TenantId,UserId, EnvironmentId,Geo,ProductVersion,Culture,DeviceType,UrlReferrer
|
||||
};
|
||||
se.renderPopup(context);
|
||||
}
|
||||
|
@ -61,14 +52,19 @@ function applyCustomStyles() {
|
|||
// eslint-disable-next-line no-undef
|
||||
const crossButtonDiv = document.getElementById('MfpEmbed_CrossButton');
|
||||
if (crossButtonDiv) {
|
||||
// TODO: need to render cancel button
|
||||
// crossButtonDiv.setAttribute('src',cancel.toString());
|
||||
crossButtonDiv.style.width = '14px';
|
||||
crossButtonDiv.style.height = '14px';
|
||||
crossButtonDiv.style.boxSizing = 'unset';
|
||||
crossButtonDiv.style.cursor = 'pointer';
|
||||
crossButtonDiv.remove();
|
||||
const cancelSvgDiv = document.createElement("div");
|
||||
cancelSvgDiv.innerHTML = `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.1016 1.60156L8.20312 7.5L14.1016 13.3984L13.3984 14.1016L7.5 8.20312L1.60156 14.1016L0.898438 13.3984L6.79688 7.5L0.898438 1.60156L1.60156 0.898438L7.5 6.79688L13.3984 0.898438L14.1016 1.60156Z" fill="#323130"/>
|
||||
</svg>`;
|
||||
cancelSvgDiv.style.width = '14px';
|
||||
cancelSvgDiv.style.height = '14px';
|
||||
cancelSvgDiv.style.boxSizing = 'unset';
|
||||
cancelSvgDiv.style.cursor = 'pointer';
|
||||
iconDiv.appendChild(cancelSvgDiv)
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const iFrame = document.getElementById('MfpEmbed_Popup_Iframe');
|
||||
if (iFrame) {
|
||||
|
@ -108,8 +104,16 @@ function applyCustomStyles() {
|
|||
}
|
||||
|
||||
function loadSurvey(){
|
||||
// TODO: Replace by actual value
|
||||
renderSurvey("surveyDiv", "Bert", "Hair", "en-US", "123", "IND", "bert.hair@contoso.com", "12345", "Product Overview");
|
||||
const el = document.querySelector("#npsContext");
|
||||
const tenantId = el.dataset.tid;
|
||||
const userId = el.dataset.uid;
|
||||
const envId = el.dataset.envId;
|
||||
const geo = el.dataset.geo;
|
||||
const culture = el.dataset.culture;
|
||||
const productVersion = el.dataset.productVersion;
|
||||
const urlReferrer = el.dataset.urlReferrer;
|
||||
const deviceType = el.dataset.deviceType;
|
||||
renderSurvey(tenantId,userId,envId,geo,productVersion,culture,deviceType,urlReferrer);
|
||||
resizeSurvey();
|
||||
applyCustomStyles();
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ class WebExtensionContext implements IWebExtensionContext {
|
|||
private _currentSchemaVersion: string;
|
||||
private _telemetry: WebExtensionTelemetry;
|
||||
private _npsEligibility: boolean;
|
||||
private _userId: string;
|
||||
|
||||
public get schemaDataSourcePropertiesMap() { return this._schemaDataSourcePropertiesMap; }
|
||||
public get schemaEntitiesMap() { return this._schemaEntitiesMap; }
|
||||
|
@ -82,6 +83,7 @@ class WebExtensionContext implements IWebExtensionContext {
|
|||
public get currentSchemaVersion() { return this._currentSchemaVersion; }
|
||||
public get telemetry() { return this._telemetry; }
|
||||
public get npsEligibility() { return this._npsEligibility; }
|
||||
public get userId() { return this._userId; }
|
||||
|
||||
constructor() {
|
||||
this._schemaDataSourcePropertiesMap = new Map<string, string>();
|
||||
|
@ -102,6 +104,7 @@ class WebExtensionContext implements IWebExtensionContext {
|
|||
this._currentSchemaVersion = "";
|
||||
this._telemetry = new WebExtensionTelemetry();
|
||||
this._npsEligibility = false;
|
||||
this._userId = "";
|
||||
}
|
||||
|
||||
public setWebExtensionContext(entityName: string, entityId: string, queryParamsMap: Map<string, string>) {
|
||||
|
@ -269,6 +272,9 @@ class WebExtensionContext implements IWebExtensionContext {
|
|||
public setNPSEligibility(eligibility: boolean) {
|
||||
this._npsEligibility = eligibility;
|
||||
}
|
||||
public setUserId(uid: string) {
|
||||
this._userId = uid;
|
||||
}
|
||||
}
|
||||
|
||||
export default new WebExtensionContext();
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.1016 1.60156L8.20312 7.5L14.1016 13.3984L13.3984 14.1016L7.5 8.20312L1.60156 14.1016L0.898438 13.3984L6.79688 7.5L0.898438 1.60156L1.60156 0.898438L7.5 6.79688L13.3984 0.898438L14.1016 1.60156Z" fill="#323130"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 328 B |
|
@ -38,15 +38,19 @@ export enum queryParameters {
|
|||
REFERRER = 'referrer',
|
||||
SITE_VISIBILITY = 'sitevisibility',
|
||||
WEBSITE_NAME = 'websitename',
|
||||
ORG_URL = 'orgurl'
|
||||
ORG_URL = 'orgurl',
|
||||
REGION = 'region',
|
||||
ENV_ID = 'envId',
|
||||
GEO = 'geo'
|
||||
}
|
||||
|
||||
export enum httpMethod {
|
||||
PATCH = 'PATCH',
|
||||
GET = 'GET'
|
||||
GET = 'GET',
|
||||
POST = 'POST'
|
||||
}
|
||||
|
||||
export enum CESSurvey {
|
||||
export enum SurveyConstants {
|
||||
TEAM_NAME = 'PowerPages',
|
||||
SURVEY_NAME = 'PowerPages-NPS',
|
||||
EVENT_NAME = 'vsCodeWeb',
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import { npsAuthentication } from "../common/authenticationProvider";
|
||||
import {CESSurvey} from '../common/constants';
|
||||
import {SurveyConstants, httpMethod, queryParameters} from '../common/constants';
|
||||
import fetch,{RequestInit} from 'node-fetch'
|
||||
import WebExtensionContext from '../WebExtensionContext';
|
||||
import { telemetryEventNames } from '../telemetry/constants';
|
||||
|
||||
export class NPSService{
|
||||
public static getCesHeader(accessToken: string) {
|
||||
|
@ -19,21 +20,29 @@ export class NPSService{
|
|||
}
|
||||
|
||||
public static async getEligibility() {
|
||||
const baseApiUrl = "https://ces-int.microsoftcloud.com/api/v1"; // TODO: change int to prod based on the query params from Studio
|
||||
const accessToken: string = await npsAuthentication(CESSurvey.AUTHORIZATION_ENDPOINT);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const parsedToken = jwt_decode(accessToken) as any;
|
||||
const apiEndpoint = `${baseApiUrl}/${CESSurvey.TEAM_NAME}/Eligibilities/${CESSurvey.SURVEY_NAME}?userId=${parsedToken?.oid}&eventName=${CESSurvey.EVENT_NAME}&tenantId=${parsedToken.tid}`;
|
||||
const requestInitPost: RequestInit = {
|
||||
method: 'POST',
|
||||
body:'{}',
|
||||
headers:NPSService.getCesHeader(accessToken)
|
||||
};
|
||||
const response = await fetch(apiEndpoint, requestInitPost);
|
||||
const result = await response?.json();
|
||||
// TODO: telemetry
|
||||
if( result?.eligibility){
|
||||
WebExtensionContext.setNPSEligibility(true);
|
||||
const region = WebExtensionContext.urlParametersMap?.get(queryParameters.REGION)
|
||||
const baseApiUrl = region === 'test' ? "https://ces-int.microsoftcloud.com/api/v1": "https://ces.microsoftcloud.com/api/v1";
|
||||
|
||||
try{
|
||||
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}/${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 fetch(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);
|
||||
WebExtensionContext.setNPSEligibility(true);
|
||||
}
|
||||
}catch(error){
|
||||
WebExtensionContext.telemetry.sendErrorTelemetry(telemetryEventNames.NPS_API_FAILED, (error as Error)?.message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,5 +30,8 @@ export enum telemetryEventNames {
|
|||
WEB_EXTENSION_ENTITY_CONTENT_SAME = 'WebExtensionEntityContentSame',
|
||||
NPS_AUTHENTICATION_STARTED = 'WebExtensionNPSAuthenticationStarted',
|
||||
NPS_AUTHENTICATION_COMPLETED = 'WebExtensionNPSAuthenticationCompleted',
|
||||
NPS_AUTHENTICATION_FAILED = 'WebExtensionNPSAuthenticationFailed'
|
||||
NPS_AUTHENTICATION_FAILED = 'WebExtensionNPSAuthenticationFailed',
|
||||
NPS_USER_ELIGIBLE = "WebExtensionUserIsEligible",
|
||||
NPS_API_FAILED = "WebExtensionNPSApiFailed",
|
||||
RENDER_NPS = 'WebExtensionNPSRenderSurveyForm'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*/
|
||||
|
||||
export function getDeviceType(): string {
|
||||
if (isMobileDevice()) {
|
||||
return 'Mobile';
|
||||
} else if (isTabletDevice()) {
|
||||
return 'Tablet';
|
||||
} else {
|
||||
return 'Desktop';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get a flag indicating whether or not the user's device is a mobile (but NOT tablet) device
|
||||
*
|
||||
* Regex taken from is-mobile package
|
||||
*/
|
||||
export function isMobileDevice(): boolean {
|
||||
const mobileRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i;
|
||||
|
||||
return mobileRE.test(navigator.userAgent);
|
||||
}
|
||||
/**
|
||||
* Get a flag indicating whether or not the user's device is a tablet device
|
||||
*
|
||||
* Regexes taken from is-mobile package
|
||||
*/
|
||||
export function isTabletDevice(): boolean {
|
||||
const tabletRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|android|ipad|playbook|silk/i;
|
||||
|
||||
return tabletRE.test(navigator.userAgent);
|
||||
}
|
||||
|
|
@ -4,6 +4,12 @@
|
|||
*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import WebExtensionContext from '../WebExtensionContext';
|
||||
import { queryParameters } from "../common/constants";
|
||||
import { getDeviceType } from '../utilities/deviceType';
|
||||
import { telemetryEventNames } from '../telemetry/constants';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
|
||||
|
||||
export class NPSWebView {
|
||||
private readonly _webviewPanel: vscode.WebviewPanel;
|
||||
|
@ -14,21 +20,29 @@ export class NPSWebView {
|
|||
}
|
||||
|
||||
private _getHtml() {
|
||||
const nonce = getNonce();
|
||||
const nonce = getNonce();
|
||||
const mainJs = this.extensionResourceUrl('media','main.js');
|
||||
const tid = WebExtensionContext.urlParametersMap?.get(queryParameters.TENANT_ID);
|
||||
const uid = WebExtensionContext.userId;
|
||||
const envId = WebExtensionContext.urlParametersMap?.get(queryParameters.ENV_ID);
|
||||
const geo = WebExtensionContext.urlParametersMap?.get(queryParameters.GEO);
|
||||
const culture = vscode.env.language;
|
||||
const productVersion = process?.env?.BUILD_NAME;
|
||||
const urlReferrer = window?.location?.href;
|
||||
const deviceType = getDeviceType();
|
||||
WebExtensionContext.telemetry.sendInfoTelemetry(telemetryEventNames.RENDER_NPS);
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test CES Survey</title>
|
||||
<script data-main="scripts/app" src="scripts/require.js"></script>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src https://customervoice.microsoft.com/ ; img-src https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit/cross.svg ; style-src https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit/Embed.css 'nonce-${nonce}';script-src https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit/Embed.js 'nonce-${nonce}';">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src https://customervoice.microsoft.com/ ; img-src * 'self' data: https:; style-src https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit/Embed.css 'nonce-${nonce}';script-src https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit/Embed.js 'nonce-${nonce}';">
|
||||
</head>
|
||||
<body>
|
||||
<div id="surveyDiv"></div>
|
||||
<script src="https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit/Embed.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="https://mfpembedcdnmsit.azureedge.net/mfpembedcontmsit/Embed.css" />
|
||||
<script nonce="${nonce}" type="module" src="${mainJs}"></script>
|
||||
<script id="npsContext" data-urlReferrer = "${urlReferrer}" data-img="./src/web/client/assets/cancel.svg" data-tid="${tid}" data-uid="${uid}" data-envId="${envId}" data-geo="${geo}" data-deviceType ="${deviceType}" data-culture ="${culture}" data-productVersion ="${productVersion}" nonce="${nonce}" type="module" src="${mainJs}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
@ -40,7 +54,7 @@ export class NPSWebView {
|
|||
public static createOrShow(extensionUri: vscode.Uri): NPSWebView {
|
||||
const webview = vscode.window.createWebviewPanel(
|
||||
'testCESSurvey',
|
||||
"Test CES Survey",
|
||||
localize("microsoft-powerapps-portals.webExtension.npsSurvey.desc", "Microsoft wants your feeback"),
|
||||
{viewColumn:vscode.ViewColumn.One,
|
||||
preserveFocus:false
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"sourceMap": true,
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"lib": [ "es2019", "WebWorker", "DOM.Iterable" ],
|
||||
"lib": [ "es2019", "WebWorker", "DOM.Iterable","DOM" ],
|
||||
"noImplicitAny": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"strict": true,
|
||||
|
|
Загрузка…
Ссылка в новой задаче