/// <reference path="./surveyResponseConfig.ts" />
/// <reference path="../../telemetry/eventData.ts" />
/// <reference path="surveyConfig.ts" />
/// <reference path="component.ts" />
/// <reference path="../../packages.ts" />
/// <reference path="../../api/modalities/callbackModality.ts" />
/// <reference path="../../telemetry/awa/eventWriter.ts" />
/// <reference path="../../telemetry/telemetry.ts" />
/// <reference path="../../utils/modality.ts" />
/// <reference path="../../utils/object.ts" />
/// <reference path="../constants.ts" />
/// <reference path="config.ts" />
/// <reference path="surveyOfferBuilder.ts" />
/// <reference path="surveyResponseBuilder.ts" />
/// <reference path="surveyRequestConfig.ts" />
/// <reference path="surveyModel.ts" />
namespace internal.ui.survey {
// AWA Logging Cosntants
const AWA_PAGE_NAME = "WebSDK";
const AWA_BEHAVIOR_SUBMIT_SURVEY = 142; // awa.behavior.SurveyComplete
const AWA_BEHAVIOR_SURVEY_INITIATE = 141; // awa.behavior.SurveyInitiate
const AWA_ACTION_TYPE_AUTOMATIC = "A";
const SURVEY_COMPONENT = "Survey";
const SURVEY_PLATFORM_PROD = "survey.support.services.microsoft.com";
/**
* Class that defines operations that can be performed on a survey
* @class Survey
* @constructor
*/
export class Survey {
private surveyConfig: SurveyConfig;
private responseId: string;
/**
* Create a survey instance
* @class Survey
* @constructor
* @param {ui.survey.SurveyConfig} config A {{#crossLink "ui.survey.SurveyConfig"}}{{/crossLink}} class to configure this instance and all subsequent survey calls
* @example
* var surveyConfig = {
* "surveyId": "integrationsurvey",
* "locale": "en-us"
* };
*
* var survey = window.MsSupportSdk.ui.survey.createSurvey(surveyConfig);
*/
constructor(config: SurveyConfig) {
this.surveyConfig = utils.cloneObject({}, config);
this.responseId = utils.createFormattedGuid();
}
/**
* Returns the response ID for this survey isntance.
* @method getResponseId
* @return {string} The response ID set for this survey instance
* @example
* var surveyConfig = {
* "surveyId": "integrationsurvey",
* "locale": "en-us"
* };
*
* var survey = window.MsSupportSdk.ui.survey.createSurvey(surveyConfig);
* var responseId = survey.getResponseId();
*/
getResponseId(): string {
return this.responseId;
}
/**
* Log a survey offer
* @method logOfferResponse
* @param {string} offerState The {{#crossLink "ui.survey.OfferState"}}{{/crossLink}} offer state
* @example
* var surveyConfig = {
* "surveyId": "integrationsurvey",
* "locale": "en-us"
* };
*
* var survey = window.MsSupportSdk.ui.survey.createSurvey(surveyConfig);
* survey.logOfferResponse(window.MsSupportSdk.ui.survey.OfferState.DISPLAYED);
*/
logOfferResponse(offerState: string) {
let surveyOfferBuilder = new SurveyOfferBuilder();
let awaEvent = surveyOfferBuilder.createSurveyOffer(this.responseId, this.surveyConfig, offerState);
telemetry.awa.writeAwaEvent(awaEvent);
let content: any = {
responseId: this.responseId,
partnerId: internal.SdkConfig.current.partnerId,
surveyId: this.surveyConfig.surveyId,
locale: this.surveyConfig.locale,
flightId: this.surveyConfig.flightId,
offerState: offerState,
partnerContextJson: this.surveyConfig.partnerContext
};
let overrideValues: any = {
appId: awaEvent.appId,
behavior: AWA_BEHAVIOR_SURVEY_INITIATE,
Uri: window.location.href,
referrerUri: document.referrer,
pageName: AWA_PAGE_NAME,
actionType: AWA_ACTION_TYPE_AUTOMATIC, // automatic. Alternative could be C or CL (click / left click)
pageHeight: window.innerHeight,
content: content // Automatically strigified by awa
};
// JSLL4 is required for AWA logging, otherwise this event will be dropped.
let awaInstance = internal.telemetry.getAwaInstance();
if (awaInstance && awaInstance.ct && awaInstance.ct.captureContentPageAction instanceof Function) {
awaInstance.ct.captureContentPageAction(overrideValues);
}
}
/**
* Render the Survey UI
* @method render
* @param {ui.survey.Config} config A {{#crossLink "ui.survey.Config"}}{{/crossLink}} object
* @return {Promise} A promise that resolves to a {{#crossLink "ui.survey.SDK"}}{{/crossLink}} object when the UI component has successfully loaded
* @example
*
* var surveyConfig = {
* "surveyId": "integrationsurvey",
* "locale": "en-us"
* };
*
* var survey = window.MsSupportSdk.ui.survey.createSurvey(surveyConfig);
*
* // Please handle survey offers in addition to this
* var surveyRenderConfig = {
* "preview": false,
* "viewMode": "wizard",
* "environment": "https://survey.support.services.microsoft.com/viewsurvey.html",
* "surveyPlatformEnvironment": "production",
* "uiInfo": {
* "type": 1,
* "containerSelector": "#iframe_container"
* }
* };
*
* survey.render(surveyRenderConfig);
*/
render(config: Config): JQueryPromise<any> {
// Note we bypass renderComponent due to the requirement to pass multiple config objects
return telemetry.captureIncomingRequestAsync(
(qos: telemetry.QosEventData): JQueryPromise<any> => {
return new Component(this.surveyConfig, config, this.responseId).render();
},
internal.ui.RENDER_OPERATION_NAME,
SURVEY_COMPONENT,
config.uiInfo);
}
/**
* Log a survey response
* @method logSurveyResponse
* @param {ui.survey.surveyResponseConfig} config A {{#crossLink "ui.survey.SurveyResponseConfig"}}{{/crossLink}} object
* @example
*
* var surveyConfig = {
* "surveyId": "integrationsurvey",
* "locale": "en-us"
* };
*
* var survey = window.MsSupportSdk.ui.survey.createSurvey(surveyConfig);
*
* // Log a survey response. It is suggested you first get the survey before logging a response
* var config = {
* "mode": "mode", // Mode of the submitted survey (from the survey platform)
* "quality": "good" // Quality of the submitted survey. poor | good. Set to poor to ignore the data during reports.
* "type": "site" // Type of the survey. site | email
* "locale": "en-us", // Locale of the submitted survey
* "isPartialResponse": true, // Set to true if the survey is partially completed. Otherwise set to false
* "displayTime": 2017-07-14T22:29:37.714Z, // Time the survey was displayed to the user (NOT the current time)
* "partnerContext": "{c1:true}", // Partner context data (any JSON KVP)
* "surveyResponse": [{ // A collection of {{#crossLink "ui.survey.SurveyQuestionConfig"}}
* "questionId": 1, // ID of the submitted question (from the survey platform)
* "questionDisplayIndex": 0, // 0 based index of the question display order
* "questionCategory": "CSAT", // Category of the question (from the survey platform)
* "percannedResponse": 0, // either a precanned response (preset ID for an option from the survey platform)
* "verbatim": "Your site rules", // or a verbatim response (freeform text) should be provided
* ]}
* };
*
* survey.logSurveyResponse(config);
*/
logSurveyResponse(responseConfig: SurveyResponseConfig) {
let surveyResponseBuilder = new SurveyResponseBuilder();
let awaEvent = surveyResponseBuilder.createSurveyResponse(this.responseId, responseConfig, this.surveyConfig);
telemetry.awa.writeAwaEvent(awaEvent);
let content = {
responseId: this.responseId,
partnerId: internal.SdkConfig.current.partnerId,
surveyId: this.surveyConfig.surveyId,
flightId: responseConfig.flightId,
mode: responseConfig.mode,
quality: responseConfig.quality,
type: responseConfig.type,
locale: this.surveyConfig.locale,
isPartialResponse: responseConfig.isPartialResponse,
displayTime: responseConfig.displayTime,
surveyResponseJson: responseConfig.surveyResponse,
partnerContextJson: this.surveyConfig.partnerContext
};
let overrideValues: any = {
appId: awaEvent.appId,
behavior: AWA_BEHAVIOR_SUBMIT_SURVEY,
Uri: window.location.href,
referrerUri: document.referrer,
pageName: AWA_PAGE_NAME,
actionType: AWA_ACTION_TYPE_AUTOMATIC, // automatic. Alternative could be C or CL (click / left click)
pageHeight: window.innerHeight,
content: content // Automatically strigified by awa
};
// JSLL4 is required for AWA logging, otherwise this event will be dropped.
let awaInstance = internal.telemetry.getAwaInstance();
if (awaInstance && awaInstance.ct && awaInstance.ct.captureContentPageAction instanceof Function) {
awaInstance.ct.captureContentPageAction(overrideValues);
}
}
/**
* Get survey data
* @method getSurveyData
* @param [requestConfig=""] {ui.survey.SurveyRequestConfig} requestConfig A {{#crossLink "ui.survey.SurveyRequestConfig"}}{{/crossLink}} object
* @return {Promise} A promise that resolves to a {{#crossLink "ui.survey.SurveyModel"}}{{/crossLink}} object when response has been received
* @example
*
* var surveyConfig = {
* "surveyId": "integrationsurvey",
* "locale": "en-us"
* };
*
* var survey = window.MsSupportSdk.ui.survey.createSurvey(surveyConfig);
*
* var requestConfig = {
* "preview": true // Optional Set to true to return preview survey data
* "surveyPlatformHostName": "survey.com" // Optional hostname for the survey platform
* };
*
* // config is optional
* survey.getSurveyData(requestConfig).then(
* function(data) {
* console.log("Survey data loaded. See survey platform documentation for details");
* },
* function(err) {
* console.log(err);
* });
*/
getSurveyData(requestConfig?: SurveyRequestConfig): JQueryPromise<SurveyModel> {
// Setup the deferral
let deferred = jQuery.Deferred();
// Fail early if parameters do not match.
if (!(this.surveyConfig && this.surveyConfig.locale && this.surveyConfig.surveyId)) {
deferred.reject("Invalid Parameters. Missing Locale or Survey ID.");
return deferred;
}
// Build the survey platform URL
let surveyHostName = (requestConfig && requestConfig.surveyPlatformHostName) || SURVEY_PLATFORM_PROD;
let surveyPlatformUrl = `https://${surveyHostName}/surveyplatform/api/v1/locale/${this.surveyConfig.locale}/partner/${internal.SdkConfig.current.partnerId}/survey/${this.surveyConfig.surveyId}`;
if (this.surveyConfig.flightId) {
surveyPlatformUrl += `/flight/${this.surveyConfig.flightId}`;
}
if (requestConfig && requestConfig.preview) {
surveyPlatformUrl += `/preview/${requestConfig.preview}`;
}
// Get the survey data
let requestOptions: internal.HttpRequestOptions = {
url: surveyPlatformUrl,
operationName: "WebSdkGetSurveyData",
dependencyName: "SurveyPlatform",
dependencyOperationName: "getSurvey"
};
utils.httpRequest.get(requestOptions).then((result) => {
let surveyData: SurveyModel = result;
if (this.validateSurveyData(surveyData)) {
deferred.resolve((surveyData));
}
else {
deferred.reject("Survey response failed basic validation.");
}
}).fail((xhr) => {
deferred.reject(xhr.responseText);
});
return deferred;
}
private validateSurveyData(surveyData: SurveyModel) {
return surveyData &&
surveyData.description &&
surveyData.flights &&
surveyData.flights[0] &&
surveyData.flights[0].flightId &&
surveyData.flights[0].mode &&
surveyData.flights[0].questions &&
surveyData.flights[0].questions[0] &&
surveyData.flights[0].title;
}
}
}