@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
394 lines • 55.6 kB
JavaScript
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { camelCase, flatMap } from 'lodash-es';
import { BehaviorSubject, Subject, combineLatest, fromEvent } from 'rxjs';
import { delay, filter, map, take } from 'rxjs/operators';
import { CookieBannerService } from '../bootstrap/cookie-banner/cookie-banner.service';
import { OptionsService } from '../common/options.service';
import { AppStateService } from '../common/ui-state.service';
import { UserPreferencesService } from '../common/user-preferences/user-preferences.service';
import * as i0 from "@angular/core";
import * as i1 from "../common/ui-state.service";
import * as i2 from "../common/options.service";
import * as i3 from "../bootstrap/cookie-banner/cookie-banner.service";
import * as i4 from "../common/user-preferences/user-preferences.service";
import * as i5 from "@ngx-translate/core";
/**
* A service to manage the Gainsight integration. It allows to load the
* tag and
*/
export class GainsightService {
constructor(appState, options, cookieBannerService, userPreferencesService, translateService) {
this.appState = appState;
this.options = options;
this.cookieBannerService = cookieBannerService;
this.userPreferencesService = userPreferencesService;
this.translateService = translateService;
/**
* A subject that emits the tag function as soon as a new tag is set.
*/
this.tagFunction$ = new BehaviorSubject(null);
this.trackingLoaded$ = new Subject();
/**
* Gainsight is activated only when the cookie banner is present. If functional cookies are enabled, both personally identifiable information (PII) and required data are sent.
* Otherwise, only the required data is transmitted during the identity step execution.
*/
this.USER_PREFERENCES_GAINSIGHT_KEY = 'gainsightEnabled';
/**
* The name of the key remained unchanged, but applies to all engagements.
*/
this.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY = 'gainsightBotEnabled';
this.HIDE_GAINSIGHT_BOT_STYLE_ID = 'hide-gs-bot';
this.GAINSIGHT_URL = 'web-sdk.aptrinsic.com/api/aptrinsic.js?a=';
this.GAINSIGHT_GLOBAL_SCOPE = 'aptrinsic';
this.SCRIPT_EXECUTION_WAIT_TIME = 500;
this.OPTIONS_KEY_CATEGORY = 'gainsight';
this.OPTIONS_KEY_NAME = 'api.key';
this.isScriptLoaded = false;
}
/**
* Checks if the specified Gainsight preference is disabled in user preferences.
* @param preferenceName - Name of the Gainsight preference.
* @returns A promise that resolves to `true` if the preference is disabled, otherwise `false`.
*/
async isGainsightPreferenceDisabledInUserPreferences(preferenceName) {
const userGainsightPref = await this.userPreferencesService.get(preferenceName).toPromise();
return userGainsightPref === false;
}
/**
* Sets the state of the functional cookie.
* @param value - A boolean value to indicate whether the functional cookie should be enabled (`true`) or disabled (`false`).
*/
setFunctionalCookie(value) {
const cookies = this.cookieBannerService.getUserCookiePreferences();
if (cookies) {
Object.keys(cookies).forEach(cookieName => {
if (cookieName === 'functional') {
cookies[cookieName] = value;
return;
}
});
localStorage.setItem('acceptCookieNotice', JSON.stringify(cookies));
}
}
async getGainsightKey() {
this.gainsightKey =
this.options.gainsightKey ||
(await this.options.getSystemOption(this.OPTIONS_KEY_CATEGORY, this.OPTIONS_KEY_NAME));
return this.gainsightKey;
}
/**
* Returns the tag global function which can be used to identify user
* or add special events.
*/
get tagFunction() {
return window[this.GAINSIGHT_GLOBAL_SCOPE];
}
/**
* Load the script tag and calls the identify function to start the tracking.
* @param currentTenant The current tenant.
* @param sendPiiData Flag for sending personally identifiable information (PII) during identification in Gainsight.
*/
async loadTag(currentTenant, sendPiiData) {
const scriptTag = document.createElement('script');
const key = await this.getGainsightKey();
if (key && !this.isScriptLoaded) {
this.loadScriptTag(scriptTag, key);
const currentUserStream = this.appState.currentUser;
const scriptLoadStream = fromEvent(scriptTag, 'load');
const versionStream = this.appState.state$.pipe(filter(({ versions }) => versions.backend), map(({ versions }) => versions), take(1));
const sourceStreams = sendPiiData
? [currentUserStream, scriptLoadStream, versionStream]
: [currentUserStream, scriptLoadStream];
combineLatest(sourceStreams)
.pipe(delay(this.SCRIPT_EXECUTION_WAIT_TIME), filter(([user, scriptEvent]) => !!(scriptEvent && user)))
.subscribe(args => {
const [user, , versions] = args;
this.setGlobalContext();
const instanceId = this.getInstanceIdFromUrl();
if (sendPiiData) {
const versionUI = versions.ui.ngx;
const versionBE = versions.backend;
const extendedIdentifyData = {
user,
currentTenant,
instanceId,
versionUI,
versionBE
};
this.identify(sendPiiData, extendedIdentifyData);
}
else {
const requiredIdentifyData = { user, currentTenant, instanceId };
this.identify(sendPiiData, requiredIdentifyData);
}
this.isScriptLoaded = true;
this.tagFunction$.next(this.tagFunction);
this.trackingLoaded$.next(true);
});
}
}
/**
* Identifies the user/account at Gainsight.
* @param sendPiiData Flag for sending personally identifiable information.
* @param identifyData Object containing identification data.
*/
identify(sendPiiData, identifyData) {
const windowRef = window;
const { id: userId, email, roles } = identifyData.user;
const { name: tenantID, customProperties, domainName } = identifyData.currentTenant;
const { instanceId, versionUI, versionBE } = identifyData;
/**
* Passing ID is a minimum required data to make an identify call to Gainsight.
* isUserCreatedAfterAnonymizationWasActivated parameter is passed to later distinguish between users created before and after data anonymization done by Gainsight.
* tenantID Used to distinguish between tenants when same email is used for different tenants.
*
* Due to GS limitations (GS does not allow clearing user attr/preferences via the GS tag!),
* we always need to initialize fields related to PII to prevent leaking this data to GS when the user has disabled functional cookies.
*/
const requiredIdentify = {
/**
* Email was not mandatory form field until 10.14
*/
id: email ? email : `${userId}_${tenantID}_${instanceId}`,
isUserCreatedAfterAnonymizationWasActivated: true,
tenantID: tenantID,
email: '--',
userName: '--',
firstName: '--',
lastName: '--',
domainName,
versionUI,
versionBE,
userLanguage: this.translateService.currentLang,
browserLanguage: this.translateService.getBrowserLang(),
instanceId,
externalReference: customProperties?.externalReference,
userRoles: this.transformUserRolesToStr(roles?.references),
customBranding: this.isCustomBranding(),
fullTracking: sendPiiData
};
if (sendPiiData) {
const { userName, firstName, lastName } = identifyData.user;
const extendedIdentify = {
...requiredIdentify,
email,
userName,
firstName,
lastName
};
windowRef[this.GAINSIGHT_GLOBAL_SCOPE]('identify', extendedIdentify, {
id: `${tenantID}_${instanceId}`,
instanceId
});
return;
}
windowRef[this.GAINSIGHT_GLOBAL_SCOPE]('identify', requiredIdentify);
}
/**
* Triggers an event to be recorded by Gainsight PX.
* This method calls the Gainsight PX's tracking mechanism to log a specific event
* along with its associated properties.
* @param eventName - Name of the event to be triggered.
* @param props - Optional properties associated with the event.
*/
triggerEvent(eventName, props) {
if (this.tagFunction && eventName) {
eventName = this.prepareEventName(eventName);
this.tagFunction('track', eventName, props);
}
}
translateToEnglish(textToTranslate) {
const { currentLang } = this.translateService;
if (currentLang === 'en') {
return textToTranslate;
}
if (currentLang && currentLang !== this.cachedLanguage) {
this.cachedRevertedTranslations = undefined;
}
if (!this.cachedRevertedTranslations) {
this.cachedLanguage = currentLang;
this.cachedRevertedTranslations = this.getRevertedTranslations(currentLang);
}
return this.getEnTranslation(textToTranslate, this.cachedRevertedTranslations);
}
/**
* Determines whether personally identifiable information (PII) should be sent while loading a tag.
* The decision to activate Gainsight and send PII relies on whether the cookiePreferences option is defined in the application settings,
* if the functional cookie is enabled, and if the user grants permission.
*/
async shouldSendPiiData() {
return (this.cookieBannerService.isConfigCookiePreferencesDefined() &&
this.cookieBannerService.isFunctionalCookieEnabled() &&
!(await this.isGainsightPreferenceDisabledInUserPreferences(this.USER_PREFERENCES_GAINSIGHT_KEY)));
}
/**
* Updates a specific user attribute in the Gainsight global scope.
* This method interfaces with the Gainsight global object to set a user's specific attribute with a provided value.
* @param name - Name of the user attribute to be updated.
* @param value - Value to set for the specified user attribute.
*/
updateUserAttribute(name, value) {
window[this.GAINSIGHT_GLOBAL_SCOPE]?.('set', 'user', { [name]: value });
}
/**
* Determines if the current user has the capability to modify Gainsight PX settings.
*
* This method checks multiple conditions:
* 1. Whether tracking has been disabled globally via application options.
* 2. Whether Gainsight is disabled at the tenant level through custom properties.
* 3. Whether a Gainsight key is available, either currently loaded or fetched asynchronously.
* 4. Whether cookie preferences are defined and available for the user.
*
* @returns Promise that resolves to a boolean. True indicates the user can edit product experience settings, and false otherwise.
*/
async canEditProductExperienceSettings() {
const currentTenant = this.appState.currentTenant.value;
const { customProperties } = currentTenant;
if (this.isTrackingDisabled() ||
this.isGainsightDisabledAtTenantCustomProperties(customProperties)) {
return false;
}
const gainsightKey = !!this.gainsightKey || !!(await this.getGainsightKey());
return (gainsightKey &&
this.cookieBannerService.isConfigCookiePreferencesDefined() &&
!!this.cookieBannerService.getUserCookiePreferences());
}
async hashGroupName(groupName) {
if (!groupName) {
return;
}
// Convert the group name to an ArrayBuffer
const encoder = new TextEncoder();
const data = encoder.encode(groupName);
// Use the SHA-256 algorithm to hash the data
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// Convert the hash to a hexadecimal string
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashedName = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
return hashedName;
}
/**
* Sets the global context for Gainsight with the current application name.
* The global context can be utilized by Gainsight for various purposes, such as segmenting users.
*/
setGlobalContext() {
const currentAppState = this.appState.state$.value;
const currentAppName = currentAppState.app.name;
window[this.GAINSIGHT_GLOBAL_SCOPE]?.('set', 'globalContext', { projectName: currentAppName });
}
transformUserRolesToStr(userRoles) {
if (!userRoles) {
return '';
}
return flatMap(userRoles, (userRole) => userRole.role.name).join();
}
/**
* Checks if Gainsight is disabled based on tenant custom properties.
*
* @param customProperties - The custom properties of the tenant.
* @returns {boolean} - True if Gainsight is disabled, false otherwise.
*/
isGainsightDisabledAtTenantCustomProperties(customProperties) {
const gainsightEnabled = customProperties && customProperties.gainsightEnabled;
return gainsightEnabled === false;
}
/**
* Determines if custom branding is enabled based on the presence of a brand logo.
*
* @returns {boolean} - True if custom branding is applied, false otherwise.
*/
isCustomBranding() {
const brandingCssVars = this.options.get('brandingCssVars') || {};
return !!brandingCssVars['brand-logo-img'];
}
/**
* Determines if tracking is disabled based on the application options.
* @returns `true` if tracking is disabled, otherwise `false`.
*/
isTrackingDisabled() {
return this.options.disableTracking === true;
}
prepareEventName(baseEventName) {
return baseEventName
.split(':')
.map(eventNamePart => camelCase(removeTranslationContext(eventNamePart)))
.join(':');
function removeTranslationContext(eventNamePart) {
return eventNamePart.replace(/`[\w\W]*`/g, '');
}
}
loadScriptTag(scriptTag, key) {
try {
const windowRef = window;
const firstTag = document.getElementsByTagName('script')[0];
const protocol = location.protocol;
const gainsightGlobalScope = this.GAINSIGHT_GLOBAL_SCOPE;
scriptTag.src = `${protocol}//${this.GAINSIGHT_URL}${key}`;
(windowRef[this.GAINSIGHT_GLOBAL_SCOPE] =
windowRef[this.GAINSIGHT_GLOBAL_SCOPE] ||
// tslint:disable-next-line:only-arrow-functions
function (...args) {
(windowRef[gainsightGlobalScope].q = windowRef[gainsightGlobalScope].q || []).push(args);
}),
(windowRef[gainsightGlobalScope].p = key);
scriptTag.async = true;
firstTag.parentNode.insertBefore(scriptTag, firstTag);
}
catch (ex) {
console.warn('Failed to load Gainsight PX', ex);
}
}
getInstanceIdFromUrl() {
const hostName = location.hostname;
return hostName.substring(hostName.indexOf('.') + 1);
}
/**
* Reverses the translation object.
*
* **Example**
* { Add widget: "Widget hinzufügen" }
*
* will be changed to:
*
* { Widget hinzufügen: "Add widget" }
*
* @param currentLang Language whose translated values are to be placed in the object key.
* @returns Returns an inverted object where the keys have been swapped with the values.
*/
getRevertedTranslations(currentLang) {
const translations = this.translateService.store.translations[currentLang];
if (!translations) {
return {};
}
const swappedKeysWithValues = {};
Object.keys(translations).forEach(key => {
swappedKeysWithValues[translations[key]] = key;
});
return swappedKeysWithValues;
}
/**Translates string back into English.
* If the current application language is set to English, the string passed as an argument is returned.
* @param textToTranslate string to translate.
* @returns Returns the string translated into English.
*/
getEnTranslation(textToTranslate, translations) {
let enTranslation = translations[textToTranslate];
if (!enTranslation) {
return textToTranslate;
}
/** remove translation context */
const regex = /\`(.*?)\`/;
enTranslation = enTranslation.replace(regex, '');
return enTranslation;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GainsightService, deps: [{ token: i1.AppStateService }, { token: i2.OptionsService }, { token: i3.CookieBannerService }, { token: i4.UserPreferencesService }, { token: i5.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GainsightService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GainsightService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1.AppStateService }, { type: i2.OptionsService }, { type: i3.CookieBannerService }, { type: i4.UserPreferencesService }, { type: i5.TranslateService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2FpbnNpZ2h0LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9jb3JlL3Byb2R1Y3QtZXhwZXJpZW5jZS9nYWluc2lnaHQuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTNDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQy9DLE9BQU8sRUFBRSxlQUFlLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDMUUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzFELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGtEQUFrRCxDQUFDO0FBQ3ZGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDN0QsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0scURBQXFELENBQUM7Ozs7Ozs7QUF1QjdGOzs7R0FHRztBQUlILE1BQU0sT0FBTyxnQkFBZ0I7SUEyQjNCLFlBQ1UsUUFBeUIsRUFDekIsT0FBdUIsRUFDdkIsbUJBQXdDLEVBQ3hDLHNCQUE4QyxFQUM5QyxnQkFBa0M7UUFKbEMsYUFBUSxHQUFSLFFBQVEsQ0FBaUI7UUFDekIsWUFBTyxHQUFQLE9BQU8sQ0FBZ0I7UUFDdkIsd0JBQW1CLEdBQW5CLG1CQUFtQixDQUFxQjtRQUN4QywyQkFBc0IsR0FBdEIsc0JBQXNCLENBQXdCO1FBQzlDLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7UUEvQjVDOztXQUVHO1FBQ0gsaUJBQVksR0FBRyxJ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Q0FBQztZQUNuQixPQUFPLGVBQWUsQ0FBQztRQUN6QixDQUFDO1FBQ0QsaUNBQWlDO1FBQ2pDLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQztRQUMxQixhQUFhLEdBQUcsYUFBYSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakQsT0FBTyxhQUFhLENBQUM7SUFDdkIsQ0FBQzsrR0E5YVUsZ0JBQWdCO21IQUFoQixnQkFBZ0IsY0FGZixNQUFNOzs0RkFFUCxnQkFBZ0I7a0JBSDVCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSUN1cnJlbnRUZW5hbnQsIElDdXN0b21Qcm9wZXJ0aWVzLCBJSWRlbnRpZmllZCwgSVVzZXIgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQgeyBUcmFuc2xhdGVTZXJ2aWNlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBjYW1lbENhc2UsIGZsYXRNYXAgfSBmcm9tICdsb2Rhc2gtZXMnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBTdWJqZWN0LCBjb21iaW5lTGF0ZXN0LCBmcm9tRXZlbnQgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGRlbGF5LCBmaWx0ZXIsIG1hcCwgdGFrZSB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IENvb2tpZUJhbm5lclNlcnZpY2UgfSBmcm9tICcuLi9ib290c3RyYXAvY29va2llLWJhbm5lci9jb29raWUtYmFubmVyLnNlcnZpY2UnO1xuaW1wb3J0IHsgT3B0aW9uc1NlcnZpY2UgfSBmcm9tICcuLi9jb21tb24vb3B0aW9ucy5zZXJ2aWNlJztcbmltcG9ydCB7IEFwcFN0YXRlU2VydmljZSB9IGZyb20gJy4uL2NvbW1vbi91aS1zdGF0ZS5zZXJ2aWNlJztcbmltcG9ydCB7IFVzZXJQcmVmZXJlbmNlc1NlcnZpY2UgfSBmcm9tICcuLi9jb21tb24vdXNlci1wcmVmZXJlbmNlcy91c2VyLXByZWZlcmVuY2VzLnNlcnZpY2UnO1xuaW1wb3J0IHsgUHhFdmVudERhdGEgfSBmcm9tICcuL3Byb2R1Y3QtZXhwZXJpZW5jZS5tb2RlbCc7XG5cbmludGVyZmFjZSBVc2VyUm9sZSB7XG4gIGlkOiBzdHJpbmc7XG4gIG5hbWU6IHN0cmluZztcbn1cblxuLyoqXG4gKiBAcHJvcGVydHkgdXNlciBUaGUgdXNlciB3aGljaCBpcyBnaXZlbiB0byBHYWluc2lnaHQuXG4gKiBAcHJvcGVydHkgdGVuYW50IFRoZSB0ZW5hbnQgd2hpY2ggaXMgZ2l2ZW4gdG8gR2FpbnNpZ2h0LlxuICogQHByb3BlcnR5IGluc3RhbmNlSWQgRXh0cmFjdGVkIGRvbWFpbiBwYXJ0IG9mIGFuIFVSTC5cbiAqIEBwcm9wZXJ0eSB2ZXJzaW9uVUkgVGhlIFVJIHZlcnNpb24gdXNlZC5cbiAqIEBwcm9wZXJ0eSB2ZXJzaW9uQkUgVGhlIEJFIHZlcnNpb24gdXNlZC5cbiAqL1xuaW50ZXJmYWNlIElkZW50aWZ5RGF0YSB7XG4gIHVzZXI6IElVc2VyO1xuICBjdXJyZW50VGVuYW50OiBJQ3VycmVudFRlbmFudDtcbiAgaW5zdGFuY2VJZDogc3RyaW5nO1xuICB2ZXJzaW9uVUk/OiBzdHJpbmc7XG4gIHZlcnNpb25CRT86IHN0cmluZztcbn1cblxuLyoqXG4gKiBBIHNlcnZpY2UgdG8gbWFuYWdlIHRoZSBHYWluc2lnaHQgaW50ZWdyYXRpb24uIEl0IGFsbG93cyB0byBsb2FkIHRoZVxuICogdGFnIGFuZFxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBHYWluc2lnaHRTZXJ2aWNlIHtcbiAgLyoqXG4gICAqIEEgc3ViamVjdCB0aGF0IGVtaXRzIHRoZSB0YWcgZnVuY3Rpb24gYXMgc29vbiBhcyBhIG5ldyB0YWcgaXMgc2V0LlxuICAgKi9cbiAgdGFnRnVuY3Rpb24kID0gbmV3IEJlaGF2aW9yU3ViamVjdChudWxsKTtcbiAgdHJhY2tpbmdMb2FkZWQkOiBTdWJqZWN0PGJvb2xlYW4+ID0gbmV3IFN1YmplY3QoKTtcblxuICAvKipcbiAgICogR2FpbnNpZ2h0IGlzIGFjdGl2YXRlZCBvbmx5IHdoZW4gdGhlIGNvb2tpZSBiYW5uZXIgaXMgcHJlc2VudC4gSWYgZnVuY3Rpb25hbCBjb29raWVzIGFyZSBlbmFibGVkLCBib3RoIHBlcnNvbmFsbHkgaWRlbnRpZmlhYmxlIGluZm9ybWF0aW9uIChQSUkpIGFuZCByZXF1aXJlZCBkYXRhIGFyZSBzZW50LlxuICAgKiBPdGhlcndpc2UsIG9ubHkgdGhlIHJlcXVpcmVkIGRhdGEgaXMgdHJhbnNtaXR0ZWQgZHVyaW5nIHRoZSBpZGVudGl0eSBzdGVwIGV4ZWN1dGlvbi5cbiAgICovXG4gIHJlYWRvbmx5IFVTRVJfUFJFRkVSRU5DRVNfR0FJTlNJR0hUX0tFWSA9ICdnYWluc2lnaHRFbmFibGVkJztcbiAgLyoqXG4gICAqIFRoZSBuYW1lIG9mIHRoZSBrZXkgcmVtYWluZWQgdW5jaGFuZ2VkLCBidXQgYXBwbGllcyB0byBhbGwgZW5nYWdlbWVudHMuXG4gICAqL1xuICByZWFkb25seSBVU0VSX1BSRUZFUkVOQ0VTX0dBSU5TSUdIVF9FTkdBR0VNRU5UU19LRVkgPSAnZ2FpbnNpZ2h0Qm90RW5hYmxlZCc7XG4gIHJlYWRvbmx5IEhJREVfR0FJTlNJR0hUX0JPVF9TVFlMRV9JRCA9ICdoaWRlLWdzLWJvdCc7XG4gIHByaXZhdGUgcmVhZG9ubHkgR0FJTlNJR0hUX1VSTCA9ICd3ZWItc2RrLmFwdHJpbnNpYy5jb20vYXBpL2FwdHJpbnNpYy5qcz9hPSc7XG4gIHByaXZhdGUgcmVhZG9ubHkgR0FJTlNJR0hUX0dMT0JBTF9TQ09QRSA9ICdhcHRyaW5zaWMnO1xuICBwcml2YXRlIHJlYWRvbmx5IFNDUklQVF9FWEVDVVRJT05fV0FJVF9USU1FID0gNTAwO1xuICBwcml2YXRlIHJlYWRvbmx5IE9QVElPTlNfS0VZX0NBVEVHT1JZID0gJ2dhaW5zaWdodCc7XG4gIHByaXZhdGUgcmVhZG9ubHkgT1BUSU9OU19LRVlfTkFNRSA9ICdhcGkua2V5JztcbiAgcHJpdmF0ZSBpc1NjcmlwdExvYWRlZCA9IGZhbHNlO1xuICBwcml2YXRlIGdhaW5zaWdodEtleTogc3RyaW5nO1xuICBwcml2YXRlIGNhY2hlZFJldmVydGVkVHJhbnNsYXRpb25zOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9O1xuICBwcml2YXRlIGNhY2hlZExhbmd1YWdlOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBhcHBTdGF0ZTogQXBwU3RhdGVTZXJ2aWNlLFxuICAgIHByaXZhdGUgb3B0aW9uczogT3B0aW9uc1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBjb29raWVCYW5uZXJTZXJ2aWNlOiBDb29raWVCYW5uZXJTZXJ2aWNlLFxuICAgIHByaXZhdGUgdXNlclByZWZlcmVuY2VzU2VydmljZTogVXNlclByZWZlcmVuY2VzU2VydmljZSxcbiAgICBwcml2YXRlIHRyYW5zbGF0ZVNlcnZpY2U6IFRyYW5zbGF0ZVNlcnZpY2VcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIHNwZWNpZmllZCBHYWluc2lnaHQgcHJlZmVyZW5jZSBpcyBkaXNhYmxlZCBpbiB1c2VyIHByZWZlcmVuY2VzLlxuICAgKiBAcGFyYW0gcHJlZmVyZW5jZU5hbWUgLSBOYW1lIG9mIHRoZSBHYWluc2lnaHQgcHJlZmVyZW5jZS5cbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYHRydWVgIGlmIHRoZSBwcmVmZXJlbmNlIGlzIGRpc2FibGVkLCBvdGhlcndpc2UgYGZhbHNlYC5cbiAgICovXG4gIGFzeW5jIGlzR2FpbnNpZ2h0UHJlZmVyZW5jZURpc2FibGVkSW5Vc2VyUHJlZmVyZW5jZXMocHJlZmVyZW5jZU5hbWU6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IHVzZXJHYWluc2lnaHRQcmVmID0gYXdhaXQgdGhpcy51c2VyUHJlZmVyZW5jZXNTZXJ2aWNlLmdldChwcmVmZXJlbmNlTmFtZSkudG9Qcm9taXNlKCk7XG5cbiAgICByZXR1cm4gdXNlckdhaW5zaWdodFByZWYgPT09IGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIHN0YXRlIG9mIHRoZSBmdW5jdGlvbmFsIGNvb2tpZS5cbiAgICogQHBhcmFtIHZhbHVlIC0gQSBib29sZWFuIHZhbHVlIHRvIGluZGljYXRlIHdoZXRoZXIgdGhlIGZ1bmN0aW9uYWwgY29va2llIHNob3VsZCBiZSBlbmFibGVkIChgdHJ1ZWApIG9yIGRpc2FibGVkIChgZmFsc2VgKS5cbiAgICovXG4gIHNldEZ1bmN0aW9uYWxDb29raWUodmFsdWU6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBjb29raWVzID0gdGhpcy5jb29raWVCYW5uZXJTZXJ2aWNlLmdldFVzZXJDb29raWVQcmVmZXJlbmNlcygpO1xuICAgIGlmIChjb29raWVzKSB7XG4gICAgICBPYmplY3Qua2V5cyhjb29raWVzKS5mb3JFYWNoKGNvb2tpZU5hbWUgPT4ge1xuICAgICAgICBpZiAoY29va2llTmFtZSA9PT0gJ2Z1bmN0aW9uYWwnKSB7XG4gICAgICAgICAgY29va2llc1tjb29raWVOYW1lXSA9IHZhbHVlO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnYWNjZXB0Q29va2llTm90aWNlJywgSlNPTi5zdHJpbmdpZnkoY29va2llcykpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGdldEdhaW5zaWdodEtleSgpIHtcbiAgICB0aGlzLmdhaW5zaWdodEtleSA9XG4gICAgICB0aGlzLm9wdGlvbnMuZ2FpbnNpZ2h0S2V5IHx8XG4gICAgICAoYXdhaXQgdGhpcy5vcHRpb25zLmdldFN5c3RlbU9wdGlvbih0aGlzLk9QVElPTlNfS0VZX0NBVEVHT1JZLCB0aGlzLk9QVElPTlNfS0VZX05BTUUpKTtcbiAgICByZXR1cm4gdGhpcy5nYWluc2lnaHRLZXk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGFnIGdsb2JhbCBmdW5jdGlvbiB3aGljaCBjYW4gYmUgdXNlZCB0byBpZGVudGlmeSB1c2VyXG4gICAqIG9yIGFkZCBzcGVjaWFsIGV2ZW50cy5cbiAgICovXG4gIGdldCB0YWdGdW5jdGlvbigpIHtcbiAgICByZXR1cm4gKHdpbmRvdyBhcyBhbnkpW3RoaXMuR0FJTlNJR0hUX0dMT0JBTF9TQ09QRV07XG4gIH1cblxuICAvKipcbiAgICogTG9hZCB0aGUgc2NyaXB0IHRhZyBhbmQgY2FsbHMgdGhlIGlkZW50aWZ5IGZ1bmN0aW9uIHRvIHN0YXJ0IHRoZSB0cmFja2luZy5cbiAgICogQHBhcmFtIGN1cnJlbnRUZW5hbnQgVGhlIGN1cnJlbnQgdGVuYW50LlxuICAgKiBAcGFyYW0gc2VuZFBpaURhdGEgRmxhZyBmb3Igc2VuZGluZyBwZXJzb25hbGx5IGlkZW50aWZpYWJsZSBpbmZvcm1hdGlvbiAoUElJKSBkdXJpbmcgaWRlbnRpZmljYXRpb24gaW4gR2FpbnNpZ2h0LlxuICAgKi9cbiAgYXN5bmMgbG9hZFRhZyhjdXJyZW50VGVuYW50OiBJQ3VycmVudFRlbmFudCwgc2VuZFBpaURhdGE6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBzY3JpcHRUYWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTtcbiAgICBjb25zdCBrZXkgPSBhd2FpdCB0aGlzLmdldEdhaW5zaWdodEtleSgpO1xuXG4gICAgaWYgKGtleSAmJiAhdGhpcy5pc1NjcmlwdExvYWRlZCkge1xuICAgICAgdGhpcy5sb2FkU2NyaXB0VGFnKHNjcmlwdFRhZywga2V5KTtcblxuICAgICAgY29uc3QgY3VycmVudFVzZXJTdHJlYW0gPSB0aGlzLmFwcFN0YXRlLmN1cnJlbnRVc2VyO1xuICAgICAgY29uc3Qgc2NyaXB0TG9hZFN0cmVhbSA9IGZyb21FdmVudChzY3JpcHRUYWcsICdsb2FkJyk7XG4gICAgICBjb25zdCB2ZXJzaW9uU3RyZWFtID0gdGhpcy5hcHBTdGF0ZS5zdGF0ZSQucGlwZShcbiAgICAgICAgZmlsdGVyKCh7IHZlcnNpb25zIH0pID0+IHZlcnNpb25zLmJhY2tlbmQpLFxuICAgICAgICBtYXAoKHsgdmVyc2lvbnMgfSkgPT4gdmVyc2lvbnMpLFxuICAgICAgICB0YWtlKDEpXG4gICAgICApO1xuXG4gICAgICBjb25zdCBzb3VyY2VTdHJlYW1zID0gc2VuZFBpaURhdGFcbiAgICAgICAgPyBbY3VycmVudFVzZXJTdHJlYW0sIHNjcmlwdExvYWRTdHJlYW0sIHZlcnNpb25TdHJlYW1dXG4gICAgICAgIDogW2N1cnJlbnRVc2VyU3RyZWFtLCBzY3JpcHRMb2FkU3RyZWFtXTtcblxuICAgICAgY29tYmluZUxhdGVzdChzb3VyY2VTdHJlYW1zKVxuICAgICAgICAucGlwZShcbiAgICAgICAgICBkZWxheSh0aGlzLlNDUklQVF9FWEVDVVRJT05fV0FJVF9USU1FKSxcbiAgICAgICAgICBmaWx0ZXIoKFt1c2VyLCBzY3JpcHRFdmVudF0pID0+ICEhKHNjcmlwdEV2ZW50ICYmIHVzZXIpKVxuICAgICAgICApXG4gICAgICAgIC5zdWJzY3JpYmUoYXJncyA9PiB7XG4gICAgICAgICAgY29uc3QgW3VzZXIsICwgdmVyc2lvbnNdID0gYXJncztcbiAgICAgICAgICB0aGlzLnNldEdsb2JhbENvbnRleHQoKTtcblxuICAgICAgICAgIGNvbnN0IGluc3RhbmNlSWQgPSB0aGlzLmdldEluc3RhbmNlSWRGcm9tVXJsKCk7XG4gICAgICAgICAgaWYgKHNlbmRQaWlEYXRhKSB7XG4gICAgICAgICAgICBjb25zdCB2ZXJzaW9uVUk6IHN0cmluZyA9IHZlcnNpb25zLnVpLm5neDtcbiAgICAgICAgICAgIGNvbnN0IHZlcnNpb25CRTogc3RyaW5nID0gdmVyc2lvbnMuYmFja2VuZDtcbiAgICAgICAgICAgIGNvbnN0IGV4dGVuZGVkSWRlbnRpZnlEYXRhOiBJZGVudGlmeURhdGEgPSB7XG4gICAgICAgICAgICAgIHVzZXIsXG4gICAgICAgICAgICAgIGN1cnJlbnRUZW5hbnQsXG4gICAgICAgICAgICAgIGluc3RhbmNlSWQsXG4gICAgICAgICAgICAgIHZlcnNpb25VSSxcbiAgICAgICAgICAgICAgdmVyc2lvbkJFXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgdGhpcy5pZGVudGlmeShzZW5kUGlpRGF0YSwgZXh0ZW5kZWRJZGVudGlmeURhdGEpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCByZXF1aXJlZElkZW50aWZ5RGF0YTogSWRlbnRpZnlEYXRhID0geyB1c2VyLCBjdXJyZW50VGVuYW50LCBpbnN0YW5jZUlkIH07XG4gICAgICAgICAgICB0aGlzLmlkZW50aWZ5KHNlbmRQaWlEYXRhLCByZXF1aXJlZElkZW50aWZ5RGF0YSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMuaXNTY3JpcHRMb2FkZWQgPSB0cnVlO1xuICAgICAgICAgIHRoaXMudGFnRnVuY3Rpb24kLm5leHQodGhpcy50YWdGdW5jdGlvbik7XG4gICAgICAgICAgdGhpcy50cmFja2luZ0xvYWRlZCQubmV4dCh0cnVlKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIElkZW50aWZpZXMgdGhlIHVzZXIvYWNjb3VudCBhdCBHYWluc2lnaHQuXG4gICAqIEBwYXJhbSBzZW5kUGlpRGF0YSBGbGFnIGZvciBzZW5kaW5nIHBlcnNvbmFsbHkgaWRlbnRpZmlhYmxlIGluZm9ybWF0aW9uLlxuICAgKiBAcGFyYW0gaWRlbnRpZnlEYXRhIE9iamVjdCBjb250YWluaW5nIGlkZW50aWZpY2F0aW9uIGRhdGEuXG4gICAqL1xuICBpZGVudGlmeShzZW5kUGlpRGF0YTogYm9vbGVhbiwgaWRlbnRpZnlEYXRhOiBJZGVudGlmeURhdGEpIHtcbiAgICBjb25zdCB3aW5kb3dSZWYgPSB3aW5kb3cgYXMgYW55O1xuICAgIGNvbnN0IHsgaWQ6IHVzZXJJZCwgZW1haWwsIHJvbGVzIH0gPSBpZGVudGlmeURhdGEudXNlcjtcbiAgICBjb25zdCB7IG5hbWU6IHRlbmFudElELCBjdXN0b21Qcm9wZXJ0aWVzLCBkb21haW5OYW1lIH0gPSBpZGVudGlmeURhdGEuY3VycmVudFRlbmFudDtcbiAgICBjb25zdCB7IGluc3RhbmNlSWQsIHZlcnNpb25VSSwgdmVyc2lvbkJFIH0gPSBpZGVudGlmeURhdGE7XG5cbiAgICAvKipcbiAgICAgKiBQYXNzaW5nIElEIGlzIGEgbWluaW11bSByZXF1aXJlZCBkYXRhIHRvIG1ha2UgYW4gaWRlbnRpZnkgY2FsbCB0byBHYWluc2lnaHQuXG4gICAgICogaXNVc2VyQ3JlYXRlZEFmdGVyQW5vbnltaXphdGlvbldhc0FjdGl2YXRlZCBwYXJhbWV0ZXIgaXMgcGFzc2VkIHRvIGxhdGVyIGRpc3Rpbmd1aXNoIGJldHdlZW4gdXNlcnMgY3JlYXRlZCBiZWZvcmUgYW5kIGFmdGVyIGRhdGEgYW5vbnltaXphdGlvbiBkb25lIGJ5IEdhaW5zaWdodC5cbiAgICAgKiB0ZW5hbnRJRCBVc2VkIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdGVuYW50cyB3aGVuIHNhbWUgZW1haWwgaXMgdXNlZCBmb3IgZGlmZmVyZW50IHRlbmFudHMuXG4gICAgICpcbiAgICAgKiBEdWUgdG8gR1MgbGltaXRhdGlvbnMgKEdTIGRvZXMgbm90IGFsbG93IGNsZWFyaW5nIHVzZXIgYXR0ci9wcmVmZXJlbmNlcyB2aWEgdGhlIEdTIHRhZyEpLFxuICAgICAqIHdlIGFsd2F5cyBuZWVkIHRvIGluaXRpYWxpemUgZmllbGRzIHJlbGF0ZWQgdG8gUElJIHRvIHByZXZlbnQgbGVha2luZyB0aGlzIGRhdGEgdG8gR1Mgd2hlbiB0aGUgdXNlciBoYXMgZGlzYWJsZWQgZnVuY3Rpb25hbCBjb29raWVzLlxuICAgICAqL1xuICAgIGNvbnN0IHJlcXVpcmVkSWRlbnRpZnkgPSB7XG4gICAgICAvKipcbiAgICAgICAqIEVtYWlsIHdhcyBub3QgbWFuZGF0b3J5IGZvcm0gZmllbGQgdW50aWwgMTAuMTRcbiAgICAgICAqL1xuICAgICAgaWQ6IGVtYWlsID8gZW1haWwgOiBgJHt1c2VySWR9XyR7dGVuYW50SUR9XyR7aW5zdGFuY2VJZH1gLFxuICAgICAgaXNVc2VyQ3JlYXRlZEFmdGVyQW5vbnltaXphdGlvbldhc0FjdGl2YXRlZDogdHJ1ZSxcbiAgICAgIHRlbmFudElEOiB0ZW5hbnRJRCxcbiAgICAgIGVtYWlsOiAnLS0nLFxuICAgICAgdXNlck5hbWU6ICctLScsXG4gICAgICBmaXJzdE5hbWU6ICctLScsXG4gICAgICBsYXN0TmFtZTogJy0tJyxcbiAgICAgIGRvbWFpbk5hbWUsXG4gICAgICB2ZXJzaW9uVUksXG4gICAgICB2ZXJzaW9uQkUsXG4gICAgICB1c2VyTGFuZ3VhZ2U6IHRoaXMudHJhbnNsYXRlU2VydmljZS5jdXJyZW50TGFuZyxcbiAgICAgIGJyb3dzZXJMYW5ndWFnZTogdGhpcy50cmFuc2xhdGVTZXJ2aWNlLmdldEJyb3dzZXJMYW5nKCksXG4gICAgICBpbnN0YW5jZUlkLFxuICAgICAgZXh0ZXJuYWxSZWZlcmVuY2U6IGN1c3RvbVByb3BlcnRpZXM/LmV4dGVybmFsUmVmZXJlbmNlLFxuICAgICAgdXNlclJvbGVzOiB0aGlzLnRyYW5zZm9ybVVzZXJSb2xlc1RvU3RyKHJvbGVzPy5yZWZlcmVuY2VzKSxcbiAgICAgIGN1c3RvbUJyYW5kaW5nOiB0aGlzLmlzQ3VzdG9tQnJhbmRpbmcoKSxcbiAgICAgIGZ1bGxUcmFja2luZzogc2VuZFBpaURhdGFcbiAgICB9O1xuXG4gICAgaWYgKHNlbmRQaWlEYXRhKSB7XG4gICAgICBjb25zdCB7IHVzZXJOYW1lLCBmaXJzdE5hbWUsIGxhc3ROYW1lIH0gPSBpZGVudGlmeURhdGEudXNlcjtcblxuICAgICAgY29uc3QgZXh0ZW5kZWRJZGVudGlmeSA9IHtcbiAgICAgICAgLi4ucmVxdWlyZWRJZGVudGlmeSxcbiAgICAgICAgZW1haWwsXG4gICAgICAgIHVzZXJOYW1lLFxuICAgICAgICBmaXJzdE5hbWUsXG4gICAgICAgIGxhc3ROYW1lXG4gICAgICB9O1xuXG4gICAgICB3aW5kb3dSZWZbdGhpcy5HQUlOU0lHSFRfR0xPQkFMX1NDT1BFXSgnaWRlbnRpZnknLCBleHRlbmRlZElkZW50aWZ5LCB7XG4gICAgICAgIGlkOiBgJHt0ZW5hbnRJRH1fJHtpbnN0YW5jZUlkfWAsXG4gICAgICAgIGluc3RhbmNlSWRcbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHdpbmRvd1JlZlt0aGlzLkdBSU5TSUdIVF9HTE9CQUxfU0NPUEVdKCdpZGVudGlmeScsIHJlcXVpcmVkSWRlbnRpZnkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXJzIGFuIGV2ZW50IHRvIGJlIHJlY29yZGVkIGJ5IEdhaW5zaWdodCBQWC5cbiAgICogVGhpcyBtZXRob2QgY2FsbHMgdGhlIEdhaW5zaWdodCBQWCdzIHRyYWNraW5nIG1lY2hhbmlzbSB0byBsb2cgYSBzcGVjaWZpYyBldmVudFxuICAgKiBhbG9uZyB3aXRoIGl0cyBhc3NvY2lhdGVkIHByb3BlcnRpZXMuXG4gICAqIEBwYXJhbSBldmVudE5hbWUgLSBOYW1lIG9mIHRoZSBldmVudCB0byBiZSB0cmlnZ2VyZWQuXG4gICAqIEBwYXJhbSBwcm9wcyAtIE9wdGlvbmFsIHByb3BlcnRpZXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBldmVudC5cbiAgICovXG4gIHRyaWdnZXJFdmVudChldmVudE5hbWU6IHN0cmluZywgcHJvcHM/OiBQeEV2ZW50RGF0YSkge1xuICAgIGlmICh0aGlzLnRhZ0Z1bmN0aW9uICYmIGV2ZW50TmFtZSkge1xuICAgICAgZXZlbnROYW1lID0gdGhpcy5wcmVwYXJlRXZlbnROYW1lKGV2ZW50TmFtZSk7XG4gICAgICB0aGlzLnRhZ0Z1bmN0aW9uKCd0cmFjaycsIGV2ZW50TmFtZSwgcHJvcHMpO1xuICAgIH1cbiAgfVxuXG4gIHRyYW5zbGF0ZVRvRW5nbGlzaCh0ZXh0VG9UcmFuc2xhdGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgeyBjdXJyZW50TGFuZyB9ID0gdGhpcy50cmFuc2xhdGVTZXJ2aWNlO1xuXG4gICAgaWYgKGN1cnJlbnRMYW5nID09PSAnZW4nKSB7XG4gICAgICByZXR1cm4gdGV4dFRvVHJhbnNsYXRlO1xuICAgIH1cblxuICAgIGlmIChjdXJyZW50TGFuZyAmJiBjdXJyZW50TGFuZyAhPT0gdGhpcy5jYWNoZWRMYW5ndWFnZSkge1xuICAgICAgdGhpcy5jYWNoZWRSZXZlcnRlZFRyYW5zbGF0aW9ucyA9IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuY2FjaGVkUmV2ZXJ0ZWRUcmFuc2xhdGlvbnMpIHtcbiAgICAgIHRoaXMuY2FjaGVkTGFuZ3VhZ2UgPSBjdXJyZW50TGFuZztcbiAgICAgIHRoaXMuY2FjaGVkUmV2ZXJ0ZWRUcmFuc2xhdGlvbnMgPSB0aGlzLmdldFJldmVydGVkVHJhbnNsYXRpb25zKGN1cnJlbnRMYW5nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5nZXRFblRyYW5zbGF0aW9uKHRleHRUb1RyYW5zbGF0ZSwgdGhpcy5jYWNoZWRSZXZlcnRlZFRyYW5zbGF0aW9ucyk7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyB3aGV0aGVyIHBlcnNvbmFsbHkgaWRlbnRpZmlhYmxlIGluZm9ybWF0aW9uIChQSUkpIHNob3VsZCBiZSBzZW50IHdoaWxlIGxvYWRpbmcgYSB0YWcuXG4gICAqIFRoZSBkZWNpc2lvbiB0byBhY3RpdmF0ZSBHYWluc2lnaHQgYW5kIHNlbmQgUElJIHJlbGllcyBvbiB3aGV0aGVyIHRoZSBjb29raWVQcmVmZXJlbmNlcyBvcHRpb24gaXMgZGVmaW5lZCBpbiB0aGUgYXBwbGljYXRpb24gc2V0dGluZ3MsXG4gICAqIGlmIHRoZSBmdW5jdGlvbmFsIGNvb2tpZSBpcyBlbmFibGVkLCBhbmQgaWYgdGhlIHVzZXIgZ3JhbnRzIHBlcm1pc3Npb24uXG4gICAqL1xuICBhc3luYyBzaG91bGRTZW5kUGlpRGF0YSgpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5jb29raWVCYW5uZXJTZXJ2aWNlLmlzQ29uZmlnQ29va2llUHJlZmVyZW5jZXNEZWZpbmVkKCkgJiZcbiAgICAgIHRoaXMuY29va2llQmFubmVyU2VydmljZS5pc0Z1bmN0aW9uYWxDb29raWVFbmFibGVkKCkgJiZcbiAgICAgICEoYXdhaXQgdGhpcy5pc0dhaW5zaWdodFByZWZlcmVuY2VEaXNhYmxlZEluVXNlclByZWZlcmVuY2VzKFxuICAgICAgICB0aGlzLlVTRVJfUFJFRkVSRU5DRVNfR0FJTlNJR0hUX0tFWVxuICAgICAgKSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgYSBzcGVjaWZpYyB1c2VyIGF0dHJpYnV0ZSBpbiB0aGUgR2FpbnNpZ2h0IGdsb2JhbCBzY29wZS5cbiAgICogVGhpcyBtZXRob2QgaW50ZXJmYWNlcyB3aXRoIHRoZSBHYWluc2lnaHQgZ2xvYmFsIG9iamVjdCB0byBzZXQgYSB1c2VyJ3Mgc3BlY2lmaWMgYXR0cmlidXRlIHdpdGggYSBwcm92aWRlZCB2YWx1ZS5cbiAgICogQHBhcmFtIG5hbWUgLSBOYW1lIG9mIHRoZSB1c2VyIGF0dHJpYnV0ZSB0byBiZSB1cGRhdGVkLlxuICAgKiBAcGFyYW0gdmFsdWUgLSBWYWx1ZSB0byBzZXQgZm9yIHRoZSBzcGVjaWZpZWQgdXNlciBhdHRyaWJ1dGUuXG4gICAqL1xuICB1cGRhdGVVc2VyQXR0cmlidXRlKG5hbWU6IHN0cmluZywgdmFsdWU6IHN0cmluZyB8IERhdGUgfCBudW1iZXIgfCBib29sZWFuKTogdm9pZCB7XG4gICAgd2luZG93W3RoaXMuR0FJTlNJR0hUX0dMT0JBTF9TQ09QRV0/Lignc2V0JywgJ3VzZXInLCB7IFtuYW1lXTogdmFsdWUgfSk7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyBpZiB0aGUgY3VycmVudCB1c2VyIGhhcyB0aGUgY2FwYWJpbGl0eSB0byBtb2RpZnkgR2FpbnNpZ2h0IFBYIHNldHRpbmdzLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBjaGVja3MgbXVsdGlwbGUgY29uZGl0aW9uczpcbiAgICogMS4gV2hldGhlciB0cmFja2luZyBoYXMgYmVlbiBkaXNhYmxlZCBnbG9iYWxseSB2aWEgYXBwbGljYXRpb24gb3B0aW9ucy5cbiAgICogMi4gV2hldGhlciBHYWluc2lnaHQgaXMgZGlzYWJsZWQgYXQgdGhlIHRlbmFudCBsZXZlbCB0aHJvdWdoIGN1c3RvbSBwcm9wZXJ0aWVzLlxuICAgKiAzLiBXaGV0aGVyIGEgR2FpbnNpZ2h0IGtleSBpcyBhdmFpbGFibGUsIGVpdGhlciBjdXJyZW50bHkgbG9hZGVkIG9yIGZldGNoZWQgYXN5bmNocm9ub3VzbHkuXG4gICAqIDQuIFdoZXRoZXIgY29va2llIHByZWZlcmVuY2VzIGFyZSBkZWZpbmVkIGFuZCBhdmFpbGFibGUgZm9yIHRoZSB1c2VyLlxuICAgKlxuICAgKiBAcmV0dXJucyBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSBib29sZWFuLiBUcnVlIGluZGljYXRlcyB0aGUgdXNlciBjYW4gZWRpdCBwcm9kdWN0IGV4cGVyaWVuY2Ugc2V0dGluZ3MsIGFuZCBmYWxzZSBvdGhlcndpc2UuXG4gICAqL1xuICBhc3luYyBjYW5FZGl0UHJvZHVjdEV4cGVyaWVuY2VTZXR0aW5ncygpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBjdXJyZW50VGVuYW50ID0gdGhpcy5hcHBTdGF0ZS5jdXJyZW50VGVuYW50LnZhbHVlO1xuICAgIGNvbnN0IHsgY3VzdG9tUHJvcGVydGllcyB9ID0gY3VycmVudFRlbmFudDtcbiAgICBpZiAoXG4gICAgICB0aGlzLmlzVHJhY2tpbmdEaXNhYmxlZCgpIHx8XG4gICAgICB0aGlzLmlzR2FpbnNpZ2h0RGlzYWJsZWRBdFRlbmFudEN1c3RvbVByb3BlcnRpZXMoY3VzdG9tUHJvcGVydGllcylcbiAgICApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29uc3QgZ2FpbnNpZ2h0S2V5ID0gISF0aGlzLmdhaW5zaWdodEtleSB8fCAhIShhd2FpdCB0aGlzLmdldEdhaW5zaWdodEtleSgpKTtcbiAgICByZXR1cm4gKFxuICAgICAgZ2FpbnNpZ2h0S2V5ICYmXG4gICAgICB0aGlzLmNvb2tpZUJhbm5lclNlcnZpY2UuaXNDb25maWdDb29raWVQcmVmZXJlbmNlc0RlZmluZWQoKSAmJlxuICAgICAgISF0aGlzLmNvb2tpZUJhbm5lclNlcnZpY2UuZ2V0VXNlckNvb2tpZVByZWZlcmVuY2VzKClcbiAgICApO1xuICB9XG5cbiAgYXN5bmMgaGFzaEdyb3VwTmFtZShncm91cE5hbWU6IHN0cmluZykge1xuICAgIGlmICghZ3JvdXBOYW1lKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIENvbnZlcnQgdGhlIGdyb3VwIG5hbWUgdG8gYW4gQXJyYXlCdWZmZXJcbiAgICBjb25zdCBlbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKCk7XG4gICAgY29uc3QgZGF0YSA9IGVuY29kZXIuZW5jb2RlKGdyb3VwTmFtZSk7XG5cbiAgICAvLyBVc2UgdGhlIFNIQS0yNTYgYWxnb3JpdGhtIHRvIGhhc2ggdGhlIGRhdGFcbiAgICBjb25zdCBoYXNoQnVmZmVyID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5kaWdlc3QoJ1NIQS0yNTYnLCBkYXRhKTtcblxuICAgIC8vIENvbnZlcnQgdGhlIGhhc2ggdG8gYSBoZXhhZGVjaW1hbCBzdHJpbmdcbiAgICBjb25zdCBoYXNoQXJyYXkgPSBBcnJheS5mcm9tKG5ldyBVaW50OEFycmF5KGhhc2hCdWZmZXIpKTtcbiAgICBjb25zdCBoYXNoZWROYW1lID0gaGFzaEFycmF5Lm1hcChieXRlID0+IGJ5dGUudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDIsICcwJykpLmpvaW4oJycpO1xuXG4gICAgcmV0dXJuIGhhc2hlZE5hbWU7XG4gIH1cblxuICAvKipcbiAgICogU2V0cyB0aGUgZ2xvYmFsIGNvbnRleHQgZm9yIEdhaW5zaWdodCB3aXRoIHRoZSBjdXJyZW50IGFwcGxpY2F0aW9uIG5hbWUuXG4gICAqIFRoZSBnbG9iYWwgY29udGV4dCBjYW4gYmUgdXRpbGl6ZWQgYnkgR2FpbnNpZ2h0IGZvciB2YXJpb3VzIHB1cnBvc2VzLCBzdWNoIGFzIHNlZ21lbnRpbmcgdXNlcnMuXG4gICAqL1xuICBzZXRHbG9iYWxDb250ZXh0KCk6IHZvaWQge1xuICAgIGNvbnN0IGN1cnJlbnRBcHBTdGF0ZSA9IHRoaXMuYXBwU3RhdGUuc3RhdGUkLnZhbHVlO1xuICAgIGNvbnN0IGN1cnJlbnRBcHBOYW1lID0gY3VycmVudEFwcFN0YXRlLmFwcC5uYW1lO1xuXG4gICAgd2luZG93W3RoaXMuR0FJTlNJR0hUX0