@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
242 lines • 42.7 kB
JavaScript
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { ApplicationType } from '@c8y/client';
import { AlertService, GainsightService, PluginsService, gettext, HumanizeAppNamePipe } from '@c8y/ngx-components';
import { EcosystemService, PRODUCT_EXPERIENCE_ECOSYSTEM } from '@c8y/ngx-components/ecosystem/shared';
import { TranslateService } from '@ngx-translate/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
import { AppsToUpdateRemotesSelectComponent } from './apps-to-update-remotes-select.component';
import { pick } from 'lodash-es';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/ngx-components/ecosystem/shared";
import * as i2 from "ngx-bootstrap/modal";
import * as i3 from "@c8y/ngx-components";
import * as i4 from "@ngx-translate/core";
import * as i5 from "@angular/common";
import * as i6 from "./plugin-list-item.component";
export class PluginListComponent {
constructor(ecosystemService, bsModalService, pluginsService, alertService, translateService, gainsightService, humanizeAppNamePipe) {
this.ecosystemService = ecosystemService;
this.bsModalService = bsModalService;
this.pluginsService = pluginsService;
this.alertService = alertService;
this.translateService = translateService;
this.gainsightService = gainsightService;
this.humanizeAppNamePipe = humanizeAppNamePipe;
this.CURRENT_LOCATION = location.href;
this.emptyListText = '';
this.hideSource = false;
/**
* Shows the install button for each plugin separately. Currently used in package-details view.
*/
this.installable = false;
this.selectedItems = new EventEmitter();
this.remotePlugins$ = new BehaviorSubject({});
this.selectedPlugins = {};
this.updatingPluginId = { install: '', uninstall: '' };
this.appsDisabled = new Set();
}
updateSelectedItems(selected, plugin) {
this.selectedPlugins[plugin.id] = selected ? plugin : undefined;
const onlyInstalledPlugins = Object.values(this.selectedPlugins).filter(Boolean);
this.selectedItems.emit(onlyInstalledPlugins);
}
async installPlugin(plugin) {
await this.updateAppRemotes(plugin, 'install');
}
async uninstallPlugin(plugin) {
await this.updateAppRemotes(plugin, 'uninstall');
}
async updateAppRemotes(plugin, updateType) {
this.updatingPluginId[updateType] = plugin?.id;
let initialState;
try {
const apps = await this.getAppsForUpdate(plugin, updateType);
initialState = {
apps,
updateType,
pluginName: plugin.name,
appsDisabled: this.appsDisabled
};
}
catch (e) {
this.alertService.addServerFailure(e);
this.updatingPluginId[updateType] = '';
return;
}
let selectedApps;
try {
selectedApps = await this.selectApps(initialState);
if (!selectedApps) {
this.updatingPluginId[updateType] = '';
return;
}
}
catch {
// unreached
}
if (updateType === 'install') {
const isArchived = await this.ecosystemService.verifyArchived([plugin]);
if (!isArchived) {
this.updatingPluginId[updateType] = '';
return;
}
const licensesVerifiedByUser = await this.ecosystemService.verifyLicenses([plugin]);
if (!licensesVerifiedByUser) {
this.updatingPluginId[updateType] = '';
return;
}
}
for (const app of selectedApps) {
try {
if (updateType === 'install') {
const versionIsCompatible = await this.ecosystemService.verifyPluginVersionsCompatibility([plugin], app);
if (!versionIsCompatible) {
continue;
}
}
await this.handleRemotesUpdate(app, plugin, updateType);
const humanizedAppName = await firstValueFrom(this.humanizeAppNamePipe.transform(app));
const successText = updateType === 'install'
? this.translateService.instant(gettext('Plugin installed to application "{{ appName }}".'), {
appName: humanizedAppName
})
: this.translateService.instant(gettext('Plugin uninstalled from application "{{ appName }}".'), { appName: humanizedAppName });
this.alertService.success(successText);
this.onUpdateEventHandleGS(plugin, app, updateType);
}
catch (error) {
this.onUpdateEventHandleGS(plugin, app, updateType, error);
}
}
this.updatingPluginId[updateType] = '';
}
onUpdateEventHandleGS(plugin, app, updateType, error) {
const pluginCustomEventInfo = pick(plugin, [
'name',
'contextPath',
'module',
'version',
'type',
'id'
]);
const gsEventResult = updateType === 'install'
? PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.RESULTS.PLUGIN_INSTALLED
: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.RESULTS.PLUGIN_REMOVED;
const eventData = {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.PLUGIN_LIST,
result: error || gsEventResult,
url: this.CURRENT_LOCATION,
...pluginCustomEventInfo,
targetApplicationName: app.name,
targetApplicationContextPath: app.contextPath,
...(error && { error })
};
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.PACKAGE_PLUGINS, eventData);
}
async getAppsForUpdate(plugin, updateType) {
let apps = (await this.ecosystemService.getWebApplications()).filter(app => this.ecosystemService.isOwner(app) && app.type !== ApplicationType.EXTERNAL);
if (updateType === 'install') {
this.appsDisabled.clear();
for (const app of apps) {
if (this.isPluginInstalledInApp(plugin, app)) {
this.appsDisabled.add(app.id);
}
}
}
if (updateType === 'uninstall') {
const installedApps = [];
for (const app of apps) {
if (this.isPluginInstalledInApp(plugin, app)) {
installedApps.push(app);
}
}
apps = installedApps;
}
return apps;
}
isPluginInstalledInApp(plugin, app) {
const appRemotes = this.pluginsService.getMFRemotes(app) || {};
for (const [remoteName, modules] of Object.entries(appRemotes)) {
const pluginFromThisPackageIsInstalled = this.getPluginContextPathWithoutVersion(remoteName) === plugin.contextPath;
const specificPluginModuleIsInstalled = modules.some(module => module === plugin.module);
if (pluginFromThisPackageIsInstalled && specificPluginModuleIsInstalled) {
return true;
}
}
return false;
}
getPluginContextPathWithoutVersion(remote) {
return remote.split('@')[0];
}
async handleRemotesUpdate(application, plugin, updateType) {
try {
// When remotes object is not set in the configuration object of an application.
// Fallback to setInitialRemotes is triggered.
const { remotes, excludedRemotes } = await (updateType === 'install'
? this.pluginsService.addRemotes(application, plugin)
: this.pluginsService.removeRemotes(application, this.getAllPluginsToRemove(plugin)));
if (!application.config) {
application.config = {};
}
application.config.remotes = remotes;
application.config.excludedRemotes = excludedRemotes;
const actualRemotes = this.pluginsService.getMFRemotes(application);
return this.emitRemotes(actualRemotes);
}
catch (er) {
if (er) {
this.alertService.addServerFailure(er);
}
throw er;
}
}
getAllPluginsToRemove(plugin) {
return this.package.applicationVersions.map(av => ({
id: `${plugin.contextPath}@${av.version}/${plugin.module}`,
idLatest: `${plugin.contextPath}/${plugin.module}`,
module: plugin.module,
path: plugin.path
}));
}
emitRemotes(remotes) {
this.remotePlugins$.next(remotes);
return { ...this.remotePlugins$.value };
}
async selectApps(initialState) {
try {
return await this.bsModalService.show(AppsToUpdateRemotesSelectComponent, {
class: 'modal-sm',
ariaDescribedby: 'modal-body',
ariaLabelledBy: 'modal-title',
initialState,
ignoreBackdropClick: true,
keyboard: false
}).content.result;
}
catch (er) {
return;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PluginListComponent, deps: [{ token: i1.EcosystemService }, { token: i2.BsModalService }, { token: i3.PluginsService }, { token: i3.AlertService }, { token: i4.TranslateService }, { token: i3.GainsightService }, { token: i3.HumanizeAppNamePipe }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: PluginListComponent, selector: "c8y-plugin-list", inputs: { plugins$: "plugins$", emptyListText: "emptyListText", selectable: "selectable", hideSource: "hideSource", installable: "installable", package: "package" }, outputs: { selectedItems: "selectedItems" }, ngImport: i0, template: "<c8y-list-group class=\"bg-inherit\">\n <ng-container *ngIf=\"(plugins$ | async)?.length !== 0; else emptyList\">\n <ng-container *ngFor=\"let plugin of plugins$ | async\">\n <c8y-li [ngClass]=\"{ disabled: plugin.installed }\" class=\"bg-inherit\">\n <c8y-plugin-list-item\n (isItemSelected)=\"updateSelectedItems($event, plugin)\"\n [plugin]=\"plugin\"\n [selectable]=\"selectable\"\n [hideSource]=\"hideSource\"\n class=\"d-flex\"\n ></c8y-plugin-list-item>\n <div class=\"p-l-40 m-t-4\">\n <button\n *ngIf=\"installable\"\n (click)=\"uninstallPlugin(plugin)\"\n [ngClass]=\"{ 'btn-pending': plugin.id === updatingPluginId.uninstall }\"\n [disabled]=\"updatingPluginId.uninstall && plugin.id !== updatingPluginId.uninstall\"\n class=\"btn btn-danger btn-sm m-l-4\"\n title=\"{{ 'Uninstall plugin' | translate }}\"\n data-cy=\"plugin-list--uninstall-plugin-button\"\n translate\n >\n Uninstall plugin\n </button>\n <button\n *ngIf=\"installable\"\n (click)=\"installPlugin(plugin)\"\n [ngClass]=\"{ 'btn-pending': plugin.id === updatingPluginId.install }\"\n [disabled]=\"updatingPluginId.install && plugin.id !== updatingPluginId.install\"\n class=\"btn btn-default btn-sm m-l-8\"\n title=\"{{ 'Install plugin' | translate }}\"\n data-cy=\"plugin-list--install-plugin-button\"\n translate\n >\n Install plugin\n </button>\n </div>\n </c8y-li>\n </ng-container>\n </ng-container>\n</c8y-list-group>\n<ng-template #emptyList>\n <div class=\"c8y-empty-state text-left\" *ngIf=\"emptyListText\">\n <h1 c8yIcon=\"plugin\"></h1>\n <p>\n {{ emptyListText | translate }}\n </p>\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i3.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i6.PluginListItemComponent, selector: "c8y-plugin-list-item", inputs: ["plugin", "selectable", "hideSource"], outputs: ["isItemSelected"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PluginListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-plugin-list', template: "<c8y-list-group class=\"bg-inherit\">\n <ng-container *ngIf=\"(plugins$ | async)?.length !== 0; else emptyList\">\n <ng-container *ngFor=\"let plugin of plugins$ | async\">\n <c8y-li [ngClass]=\"{ disabled: plugin.installed }\" class=\"bg-inherit\">\n <c8y-plugin-list-item\n (isItemSelected)=\"updateSelectedItems($event, plugin)\"\n [plugin]=\"plugin\"\n [selectable]=\"selectable\"\n [hideSource]=\"hideSource\"\n class=\"d-flex\"\n ></c8y-plugin-list-item>\n <div class=\"p-l-40 m-t-4\">\n <button\n *ngIf=\"installable\"\n (click)=\"uninstallPlugin(plugin)\"\n [ngClass]=\"{ 'btn-pending': plugin.id === updatingPluginId.uninstall }\"\n [disabled]=\"updatingPluginId.uninstall && plugin.id !== updatingPluginId.uninstall\"\n class=\"btn btn-danger btn-sm m-l-4\"\n title=\"{{ 'Uninstall plugin' | translate }}\"\n data-cy=\"plugin-list--uninstall-plugin-button\"\n translate\n >\n Uninstall plugin\n </button>\n <button\n *ngIf=\"installable\"\n (click)=\"installPlugin(plugin)\"\n [ngClass]=\"{ 'btn-pending': plugin.id === updatingPluginId.install }\"\n [disabled]=\"updatingPluginId.install && plugin.id !== updatingPluginId.install\"\n class=\"btn btn-default btn-sm m-l-8\"\n title=\"{{ 'Install plugin' | translate }}\"\n data-cy=\"plugin-list--install-plugin-button\"\n translate\n >\n Install plugin\n </button>\n </div>\n </c8y-li>\n </ng-container>\n </ng-container>\n</c8y-list-group>\n<ng-template #emptyList>\n <div class=\"c8y-empty-state text-left\" *ngIf=\"emptyListText\">\n <h1 c8yIcon=\"plugin\"></h1>\n <p>\n {{ emptyListText | translate }}\n </p>\n </div>\n</ng-template>\n" }]
}], ctorParameters: () => [{ type: i1.EcosystemService }, { type: i2.BsModalService }, { type: i3.PluginsService }, { type: i3.AlertService }, { type: i4.TranslateService }, { type: i3.GainsightService }, { type: i3.HumanizeAppNamePipe }], propDecorators: { plugins$: [{
type: Input
}], emptyListText: [{
type: Input
}], selectable: [{
type: Input
}], hideSource: [{
type: Input
}], installable: [{
type: Input
}], package: [{
type: Input
}], selectedItems: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLWxpc3QuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vZWNvc3lzdGVtL2FwcGxpY2F0aW9uLXBsdWdpbnMvcGx1Z2luLWxpc3QuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vZWNvc3lzdGVtL2FwcGxpY2F0aW9uLXBsdWdpbnMvcGx1Z2luLWxpc3QuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN2RSxPQUFPLEVBQTRCLGVBQWUsRUFBZ0IsTUFBTSxhQUFhLENBQUM7QUFDdEYsT0FBTyxFQUNMLFlBQVksRUFFWixnQkFBZ0IsRUFDaEIsY0FBYyxFQUNkLE9BQU8sRUFDUCxtQkFBbUIsRUFDcEIsTUFBTSxxQkFBcUIsQ0FBQztBQUM3QixPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLDRCQUE0QixFQUM3QixNQUFNLHNDQUFzQyxDQUFDO0FBQzlDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsZUFBZSxFQUFFLGNBQWMsRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtDQUFrQyxFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFFL0YsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQzs7Ozs7Ozs7QUFNakMsTUFBTSxPQUFPLG1CQUFtQjtJQWtCOUIsWUFDVSxnQkFBa0MsRUFDbEMsY0FBOEIsRUFDOUIsY0FBOEIsRUFDOUIsWUFBMEIsRUFDMUIsZ0JBQWtDLEVBQ2xDLGdCQUFrQyxFQUNsQyxtQkFBd0M7UUFOeEMscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUNsQyxtQkFB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QUFJLE1BQU0sQ0FBQyxNQUFNLEVBQUU7WUFDbEQsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO1lBQ3JCLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtTQUNsQixDQUFDLENBQUMsQ0FBQztJQUNOLENBQUM7SUFFTyxXQUFXLENBQUMsT0FBaUM7UUFDbkQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbEMsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUMxQyxDQUFDO0lBRU8sS0FBSyxDQUFDLFVBQVUsQ0FDdEIsWUFHQztRQUVELElBQUksQ0FBQztZQUNILE9BQU8sTUFDTCxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxrQ0FBa0MsRUFBRTtnQkFDM0QsS0FBSyxFQUFFLFVBQVU7Z0JBQ2pCLGVBQWUsRUFBRSxZQUFZO2dCQUM3QixjQUFjLEVBQUUsYUFBYTtnQkFDN0IsWUFBWTtnQkFDWixtQkFBbUIsRUFBRSxJQUFJO2dCQUN6QixRQUFRLEVBQUUsS0FBSzthQUNoQixDQUFDLENBQUMsT0FDSixDQUFDLE1BQU0sQ0FBQztRQUNYLENBQUM7UUFBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO1lBQ1osT0FBTztRQUNULENBQUM7SUFDSCxDQUFDOytHQXZRVSxtQkFBbUI7bUdBQW5CLG1CQUFtQiwwUUN6QmhDLHc2REFpREE7OzRGRHhCYSxtQkFBbUI7a0JBSi9CLFNBQVM7K0JBQ0UsaUJBQWlCOzBRQU1sQixRQUFRO3NCQUFoQixLQUFLO2dCQUNHLGFBQWE7c0JBQXJCLEtBQUs7Z0JBQ0csVUFBVTtzQkFBbEIsS0FBSztnQkFDRyxVQUFVO3NCQUFsQixLQUFLO2dCQUlHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUNJLGFBQWE7c0JBQXRCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQXBwbGljYXRpb25SZW1vdGVQbHVnaW5zLCBBcHBsaWNhdGlvblR5cGUsIElBcHBsaWNhdGlvbiB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7XG4gIEFsZXJ0U2VydmljZSxcbiAgQXBwbGljYXRpb25QbHVnaW4sXG4gIEdhaW5zaWdodFNlcnZpY2UsXG4gIFBsdWdpbnNTZXJ2aWNlLFxuICBnZXR0ZXh0LFxuICBIdW1hbml6ZUFwcE5hbWVQaXBlXG59IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMnO1xuaW1wb3J0IHtcbiAgRWNvc3lzdGVtU2VydmljZSxcbiAgUFJPRFVDVF9FWFBFUklFTkNFX0VDT1NZU1RFTVxufSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2Vjb3N5c3RlbS9zaGFyZWQnO1xuaW1wb3J0IHsgVHJhbnNsYXRlU2VydmljZSB9IGZyb20gJ0BuZ3gtdHJhbnNsYXRlL2NvcmUnO1xuaW1wb3J0IHsgQnNNb2RhbFNlcnZpY2UgfSBmcm9tICduZ3gtYm9vdHN0cmFwL21vZGFsJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgZmlyc3RWYWx1ZUZyb20sIE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEFwcHNUb1VwZGF0ZVJlbW90ZXNTZWxlY3RDb21wb25lbnQgfSBmcm9tICcuL2FwcHMtdG8tdXBkYXRlLXJlbW90ZXMtc2VsZWN0LmNvbXBvbmVudCc7XG5pbXBvcnQgeyBVcGRhdGVUeXBlIH0gZnJvbSAnLi9hcHBzLXRvLXVwZGF0ZS1yZW1vdGVzLXNlbGVjdC5tb2RlbCc7XG5pbXBvcnQgeyBwaWNrIH0gZnJvbSAnbG9kYXNoLWVzJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LXBsdWdpbi1saXN0JyxcbiAgdGVtcGxhdGVVcmw6ICcuL3BsdWdpbi1saXN0LmNvbXBvbmVudC5odG1sJ1xufSlcbmV4cG9ydCBjbGFzcyBQbHVnaW5MaXN0Q29tcG9uZW50IHtcbiAgQ1VSUkVOVF9MT0NBVElPTiA9IGxvY2F0aW9uLmhyZWY7XG5cbiAgQElucHV0KCkgcGx1Z2lucyQ6IE9ic2VydmFibGU8QXBwbGljYXRpb25QbHVnaW5bXT47XG4gIEBJbnB1dCgpIGVtcHR5TGlzdFRleHQgPSAnJztcbiAgQElucHV0KCkgc2VsZWN0YWJsZTogYm9vbGVhbjtcbiAgQElucHV0KCkgaGlkZVNvdXJjZSA9IGZhbHNlO1xuICAvKipcbiAgICogU2hvd3MgdGhlIGluc3RhbGwgYnV0dG9uIGZvciBlYWNoIHBsdWdpbiBzZXBhcmF0ZWx5LiBDdXJyZW50bHkgdXNlZCBpbiBwYWNrYWdlLWRldGFpbHMgdmlldy5cbiAgICovXG4gIEBJbnB1dCgpIGluc3RhbGxhYmxlID0gZmFsc2U7XG4gIEBJbnB1dCgpIHBhY2thZ2U6IElBcHBsaWNhdGlvbjtcbiAgQE91dHB1dCgpIHNlbGVjdGVkSXRlbXM6IEV2ZW50RW1pdHRlcjxBcHBsaWNhdGlvblBsdWdpbltdPiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgcmVtb3RlUGx1Z2lucyQ6IEJlaGF2aW9yU3ViamVjdDxBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnM+ID0gbmV3IEJlaGF2aW9yU3ViamVjdCh7fSk7XG4gIHNlbGVjdGVkUGx1Z2luczogeyBba2V5OiBzdHJpbmddOiBBcHBsaWNhdGlvblBsdWdpbiB9ID0ge307XG4gIHVwZGF0aW5nUGx1Z2luSWQ6IFJlY29yZDxVcGRhdGVUeXBlLCBzdHJpbmc+ID0geyBpbnN0YWxsOiAnJywgdW5pbnN0YWxsOiAnJyB9O1xuICBhcHBzRGlzYWJsZWQ6IFNldDxJQXBwbGljYXRpb25bJ2lkJ10+ID0gbmV3IFNldDxJQXBwbGljYXRpb25bJ2lkJ10+KCk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBlY29zeXN0ZW1TZXJ2aWNlOiBFY29zeXN0ZW1TZXJ2aWNlLFxuICAgIHByaXZhdGUgYnNNb2RhbFNlcnZpY2U6IEJzTW9kYWxTZXJ2aWNlLFxuICAgIHByaXZhdGUgcGx1Z2luc1NlcnZpY2U6IFBsdWdpbnNTZXJ2aWNlLFxuICAgIHByaXZhdGUgYWxlcnRTZXJ2aWNlOiBBbGVydFNlcnZpY2UsXG4gICAgcHJpdmF0ZSB0cmFuc2xhdGVTZXJ2aWNlOiBUcmFuc2xhdGVTZXJ2aWNlLFxuICAgIHByaXZhdGUgZ2FpbnNpZ2h0U2VydmljZTogR2FpbnNpZ2h0U2VydmljZSxcbiAgICBwcml2YXRlIGh1bWFuaXplQXBwTmFtZVBpcGU6IEh1bWFuaXplQXBwTmFtZVBpcGVcbiAgKSB7fVxuXG4gIHVwZGF0ZVNlbGVjdGVkSXRlbXMoc2VsZWN0ZWQ6IGJvb2xlYW4sIHBsdWdpbjogQXBwbGljYXRpb25QbHVnaW4pIHtcbiAgICB0aGlzLnNlbGVjdGVkUGx1Z2luc1twbHVnaW4uaWRdID0gc2VsZWN0ZWQgPyBwbHVnaW4gOiB1bmRlZmluZWQ7XG4gICAgY29uc3Qgb25seUluc3RhbGxlZFBsdWdpbnMgPSBPYmplY3QudmFsdWVzKHRoaXMuc2VsZWN0ZWRQbHVnaW5zKS5maWx0ZXIoQm9vbGVhbik7XG4gICAgdGhpcy5zZWxlY3RlZEl0ZW1zLmVtaXQob25seUluc3RhbGxlZFBsdWdpbnMpO1xuICB9XG5cbiAgYXN5bmMgaW5zdGFsbFBsdWdpbihwbHVnaW46IEFwcGxpY2F0aW9uUGx1Z2luKSB7XG4gICAgYXdhaXQgdGhpcy51cGRhdGVBcHBSZW1vdGVzKHBsdWdpbiwgJ2luc3RhbGwnKTtcbiAgfVxuXG4gIGFzeW5jIHVuaW5zdGFsbFBsdWdpbihwbHVnaW46IEFwcGxpY2F0aW9uUGx1Z2luKSB7XG4gICAgYXdhaXQgdGhpcy51cGRhdGVBcHBSZW1vdGVzKHBsdWdpbiwgJ3VuaW5zdGFsbCcpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB1cGRhdGVBcHBSZW1vdGVzKHBsdWdpbjogQXBwbGljYXRpb25QbHVnaW4sIHVwZGF0ZVR5cGU6IFVwZGF0ZVR5cGUpIHtcbiAgICB0aGlzLnVwZGF0aW5nUGx1Z2luSWRbdXBkYXRlVHlwZV0gPSBwbHVnaW4/LmlkO1xuICAgIGxldCBpbml0aWFsU3RhdGU6IFBpY2s8XG4gICAgICBBcHBzVG9VcGRhdGVSZW1vdGVzU2VsZWN0Q29tcG9uZW50LFxuICAgICAgJ2FwcHMnIHwgJ3VwZGF0ZVR5cGUnIHwgJ3BsdWdpbk5hbWUnIHwgJ2FwcHNEaXNhYmxlZCdcbiAgICA+O1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBhcHBzID0gYXdhaXQgdGhpcy5nZXRBcHBzRm9yVXBkYXRlKHBsdWdpbiwgdXBkYXRlVHlwZSk7XG4gICAgICBpbml0aWFsU3RhdGUgPSB7XG4gICAgICAgIGFwcHMsXG4gICAgICAgIHVwZGF0ZVR5cGUsXG4gICAgICAgIHBsdWdpbk5hbWU6IHBsdWdpbi5uYW1lLFxuICAgICAgICBhcHBzRGlzYWJsZWQ6IHRoaXMuYXBwc0Rpc2FibGVkXG4gICAgICB9O1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLmFkZFNlcnZlckZhaWx1cmUoZSk7XG4gICAgICB0aGlzLnVwZGF0aW5nUGx1Z2luSWRbdXBkYXRlVHlwZV0gPSAnJztcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBsZXQgc2VsZWN0ZWRBcHBzOiBJQXBwbGljYXRpb25bXTtcbiAgICB0cnkge1xuICAgICAgc2VsZWN0ZWRBcHBzID0gYXdhaXQgdGhpcy5zZWxlY3RBcHBzKGluaXRpYWxTdGF0ZSk7XG4gICAgICBpZiAoIXNlbGVjdGVkQXBwcykge1xuICAgICAgICB0aGlzLnVwZGF0aW5nUGx1Z2luSWRbdXBkYXRlVHlwZV0gPSAnJztcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gdW5yZWFjaGVkXG4gICAgfVxuXG4gICAgaWYgKHVwZGF0ZVR5cGUgPT09ICdpbnN0YWxsJykge1xuICAgICAgY29uc3QgaXNBcmNoaXZlZCA9IGF3YWl0IHRoaXMuZWNvc3lzdGVtU2VydmljZS52ZXJpZnlBcmNoaXZlZChbcGx1Z2luXSk7XG4gICAgICBpZiAoIWlzQXJjaGl2ZWQpIHtcbiAgICAgICAgdGhpcy51cGRhdGluZ1BsdWdpbklkW3VwZGF0ZVR5cGVdID0gJyc7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbGljZW5zZXNWZXJpZmllZEJ5VXNlciA9IGF3YWl0IHRoaXMuZWNvc3lzdGVtU2VydmljZS52ZXJpZnlMaWNlbnNlcyhbcGx1Z2luXSk7XG4gICAgICBpZiAoIWxpY2Vuc2VzVmVyaWZpZWRCeVVzZXIpIHtcbiAgICAgICAgdGhpcy51cGRhdGluZ1BsdWdpbklkW3VwZGF0ZVR5cGVdID0gJyc7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGFwcCBvZiBzZWxlY3RlZEFwcHMpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGlmICh1cGRhdGVUeXBlID09PSAnaW5zdGFsbCcpIHtcbiAgICAgICAgICBjb25zdCB2ZXJzaW9uSXNDb21wYXRpYmxlID0gYXdhaXQgdGhpcy5lY29zeXN0ZW1TZXJ2aWNlLnZlcmlmeVBsdWdpblZlcnNpb25zQ29tcGF0aWJpbGl0eShcbiAgICAgICAgICAgIFtwbHVnaW5dLFxuICAgICAgICAgICAgYXBwXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIGlmICghdmVyc2lvbklzQ29tcGF0aWJsZSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgdGhpcy5oYW5kbGVSZW1vdGVzVXBkYXRlKGFwcCwgcGx1Z2luLCB1cGRhdGVUeXBlKTtcbiAgICAgICAgY29uc3QgaHVtYW5pemVkQXBwTmFtZSA9IGF3YWl0IGZpcnN0VmFsdWVGcm9tKHRoaXMuaHVtYW5pemVBcHBOYW1lUGlwZS50cmFuc2Zvcm0oYXBwKSk7XG4gICAgICAgIGNvbnN0IHN1Y2Nlc3NUZXh0ID1cbiAgICAgICAgICB1cGRhdGVUeXBlID09PSAnaW5zdGFsbCdcbiAgICAgICAgICAgID8gdGhpcy50cmFuc2xhdGVTZXJ2aWNlLmluc3RhbnQoXG4gICAgICAgICAgICAgICAgZ2V0dGV4dCgnUGx1Z2luIGluc3RhbGxlZCB0byBhcHBsaWNhdGlvbiBcInt7IGFwcE5hbWUgfX1cIi4nKSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBhcHBOYW1lOiBodW1hbml6ZWRBcHBOYW1lXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICA6IHRoaXMudHJhbnNsYXRlU2VydmljZS5pbnN0YW50KFxuICAgICAgICAgICAgICAgIGdldHRleHQoJ1BsdWdpbiB1bmluc3RhbGxlZCBmcm9tIGFwcGxpY2F0aW9uIFwie3sgYXBwTmFtZSB9fVwiLicpLFxuICAgICAgICAgICAgICAgIHsgYXBwTmFtZTogaHVtYW5pemVkQXBwTmFtZSB9XG4gICAgICAgICAgICAgICk7XG4gICAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLnN1Y2Nlc3Moc3VjY2Vzc1RleHQpO1xuICAgICAgICB0aGlzLm9uVXBkYXRlRXZlbnRIYW5kbGVHUyhwbHVnaW4sIGFwcCwgdXBkYXRlVHlwZSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICB0aGlzLm9uVXBkYXRlRXZlbnRIYW5kbGVHUyhwbHVnaW4sIGFwcCwgdXBkYXRlVHlwZSwgZXJyb3IpO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnVwZGF0aW5nUGx1Z2luSWRbdXBkYXRlVHlwZV0gPSAnJztcbiAgfVxuXG4gIHByaXZhdGUgb25VcGRhdGVFdmVudEhhbmRsZUdTKFxuICAgIHBsdWdpbjogQXBwbGljYXRpb25QbHVnaW4sXG4gICAgYXBwOiBJQXBwbGljYXRpb24sXG4gICAgdXBkYXRlVHlwZTogVXBkYXRlVHlwZSxcbiAgICBlcnJvcj86IHVua25vd25cbiAgKSB7XG4gICAgY29uc3QgcGx1Z2luQ3VzdG9tRXZlbnRJbmZvID0gcGljayhwbHVnaW4sIFtcbiAgICAgICduYW1lJyxcbiAgICAgICdjb250ZXh0UGF0aCcsXG4gICAgICAnbW9kdWxlJyxcbiAgICAgICd2ZXJzaW9uJyxcbiAgICAgICd0eXBlJyxcbiAgICAgICdpZCdcbiAgICBdIHNhdGlzZmllcyAoa2V5b2YgQXBwbGljYXRpb25QbHVnaW4pW10pO1xuXG4gICAgY29uc3QgZ3NFdmVudFJlc3VsdCA9XG4gICAgICB1cGRhdGVUeXBlID09PSAnaW5zdGFsbCdcbiAgICAgICAgPyBQUk9EVUNUX0VYUEVSSUVOQ0VfRUNPU1lTVEVNLkFQUExJQ0FUSU9OUy5SRVNVTFRTLlBMVUdJTl9JTlNUQUxMRURcbiAgICAgICAgOiBQUk9EVUNUX0VYUEVSSUVOQ0VfRUNPU1lTVEVNLkFQUExJQ0FUSU9OUy5SRVNVTFRTLlBMVUdJTl9SRU1PVkVEO1xuXG4gICAgY29uc3QgZXZlbnREYXRhID0ge1xuICAgICAgY29tcG9uZW50OiBQUk9EVUNUX0VYUEVSSUVOQ0VfRUNPU1lTVEVNLkFQUExJQ0FUSU9OUy5DT01QT05FTlRTLlBMVUdJTl9MSVNULFxuICAgICAgcmVzdWx0OiBlcnJvciB8fCBnc0V2ZW50UmVzdWx0LFxuICAgICAgdXJsOiB0aGlzLkNVUlJFTlRfTE9DQVRJT04sXG4gICAgICAuLi5wbHVnaW5DdXN0b21FdmVudEluZm8sXG4gICAgICB0YXJnZXRBcHBsaWNhdGlvbk5hbWU6IGFwcC5uYW1lLFxuICAgICAgdGFyZ2V0QXBwbGljYXRpb25Db250ZXh0UGF0aDogYXBwLmNvbnRleHRQYXRoLFxuICAgICAgLi4uKGVycm9yICYmIHsgZXJyb3IgfSlcbiAgICB9O1xuXG4gICAgdGhpcy5nYWluc2lnaHRTZXJ2aWNlLnRyaWdnZXJFdmVudChcbiAgICAgIFBST0RVQ1RfRVhQRVJJRU5DRV9FQ09TWVNURU0uQVBQTElDQVRJT05TLkVWRU5UUy5QQUNLQUdFX1BMVUdJTlMsXG4gICAgICBldmVudERhdGFcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXRBcHBzRm9yVXBkYXRlKHBsdWdpbjogQXBwbGljYXRpb25QbHVnaW4sIHVwZGF0ZVR5cGU6IFVwZGF0ZVR5cGUpIHtcbiAgICBsZXQgYXBwcyA9IChhd2FpdCB0aGlzLmVjb3N5c3RlbVNlcnZpY2UuZ2V0V2ViQXBwbGljYXRpb25zKCkpLmZpbHRlcihcbiAgICAgIGFwcCA9PiB0aGlzLmVjb3N5c3RlbVNlcnZpY2UuaXNPd25lcihhcHApICYmIGFwcC50eXBlICE9PSBBcHBsaWNhdGlvblR5cGUuRVhURVJOQUxcbiAgICApO1xuXG4gICAgaWYgKHVwZGF0ZVR5cGUgPT09ICdpbnN0YWxsJykge1xuICAgICAgdGhpcy5hcHBzRGlzYWJsZWQuY2xlYXIoKTtcbiAgICAgIGZvciAoY29uc3QgYXBwIG9mIGFwcHMpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNQbHVnaW5JbnN0YWxsZWRJbkFwcChwbHVnaW4sIGFwcCkpIHtcbiAgICAgICAgICB0aGlzLmFwcHNEaXNhYmxlZC5hZGQoYXBwLmlkKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh1cGRhdGVUeXBlID09PSAndW5pbnN0YWxsJykge1xuICAgICAgY29uc3QgaW5zdGFsbGVkQXBwczogSUFwcGxpY2F0aW9uW10gPSBbXTtcbiAgICAgIGZvciAoY29uc3QgYXBwIG9mIGFwcHMpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNQbHVnaW5JbnN0YWxsZWRJbkFwcChwbHVnaW4sIGFwcCkpIHtcbiAgICAgICAgICBpbnN0YWxsZWRBcHBzLnB1c2goYXBwKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgYXBwcyA9IGluc3RhbGxlZEFwcHM7XG4gICAgfVxuICAgIHJldHVybiBhcHBzO1xuICB9XG5cbiAgcHJpdmF0ZSBpc1BsdWdpbkluc3RhbGxlZEluQXBwKHBsdWdpbjogQXBwbGljYXRpb25QbHVnaW4sIGFwcDogSUFwcGxpY2F0aW9uKTogYm9vbGVhbiB7XG4gICAgY29uc3QgYXBwUmVtb3RlcyA9IHRoaXMucGx1Z2luc1NlcnZpY2UuZ2V0TUZSZW1vdGVzKGFwcCkgfHwge307XG5cbiAgICBmb3IgKGNvbnN0IFtyZW1vdGVOYW1lLCBtb2R1bGVzXSBvZiBPYmplY3QuZW50cmllcyhhcHBSZW1vdGVzKSkge1xuICAgICAgY29uc3QgcGx1Z2luRnJvbVRoaXNQYWNrYWdlSXNJbnN0YWxsZWQgPVxuICAgICAgICB0aGlzLmdldFBsdWdpbkNvbnRleHRQYXRoV2l0aG91dFZlcnNpb24ocmVtb3RlTmFtZSkgPT09IHBsdWdpbi5jb250ZXh0UGF0aDtcbiAgICAgIGNvbnN0IHNwZWNpZmljUGx1Z2luTW9kdWxlSXNJbnN0YWxsZWQgPSBtb2R1bGVzLnNvbWUobW9kdWxlID0+IG1vZHVsZSA9PT0gcGx1Z2luLm1vZHVsZSk7XG4gICAgICBpZiAocGx1Z2luRnJvbVRoaXNQYWNrYWdlSXNJbnN0YWxsZWQgJiYgc3BlY2lmaWNQbHVnaW5Nb2R1bGVJc0luc3RhbGxlZCkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRQbHVnaW5Db250ZXh0UGF0aFdpdGhvdXRWZXJzaW9uKHJlbW90ZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHJlbW90ZS5zcGxpdCgnQCcpWzBdO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBoYW5kbGVSZW1vdGVzVXBkYXRlKFxuICAgIGFwcGxpY2F0aW9uOiBJQXBwbGljYXRpb24sXG4gICAgcGx1Z2luOiBBcHBsaWNhdGlvblBsdWdpbixcbiAgICB1cGRhdGVUeXBlOiBVcGRhdGVUeXBlXG4gICkge1xuICAgIHRyeSB7XG4gICAgICAvLyBXaGVuIHJlbW90ZXMgb2JqZWN0IGlzIG5vdCBzZXQgaW4gdGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IG9mIGFuIGFwcGxpY2F0aW9uLlxuICAgICAgLy8gRmFsbGJhY2sgdG8gc2V0SW5pdGlhbFJlbW90ZXMgaXMgdHJpZ2dlcmVkLlxuICAgICAgY29uc3QgeyByZW1vdGVzLCBleGNsdWRlZFJlbW90ZXMgfSA9IGF3YWl0ICh1cGRhdGVUeXBlID09PSAnaW5zdGFsbCdcbiAgICAgICAgPyB0aGlzLnBsdWdpbnNTZXJ2aWNlLmFkZFJlbW90ZXMoYXBwbGljYXRpb24sIHBsdWdpbilcbiAgICAgICAgOiB0aGlzLnBsdWdpbnNTZXJ2aWNlLnJlbW92ZVJlbW90ZXMoYXBwbGljYXRpb24sIHRoaXMuZ2V0QWxsUGx1Z2luc1RvUmVtb3ZlKHBsdWdpbikpKTtcbiAgICAgIGlmICghYXBwbGljYXRpb24uY29uZmlnKSB7XG4gICAgICAgIGFwcGxpY2F0aW9uLmNvbmZpZyA9IHt9O1xuICAgICAgfVxuICAgICAgYXBwbGljYXRpb24uY29uZmlnLnJlbW90ZXMgPSByZW1vdGVzO1xuICAgICAgYXBwbGljYXRpb24uY29uZmlnLmV4Y2x1ZGVkUmVtb3RlcyA9IGV4Y2x1ZGVkUmVtb3RlcztcbiAgICAgIGNvbnN0IGFjdHVhbFJlbW90ZXMgPSB0aGlzLnBsdWdpbnNTZXJ2aWNlLmdldE1GUmVtb3RlcyhhcHBsaWNhdGlvbik7XG4gICAgICByZXR1cm4gdGhpcy5lbWl0UmVtb3RlcyhhY3R1YWxSZW1vdGVzKTtcbiAgICB9IGNhdGNoIChlcikge1xuICAgICAgaWYgKGVyKSB7XG4gICAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLmFkZFNlcnZlckZhaWx1cmUoZXIpO1xuICAgICAgfVxuICAgICAgdGhyb3cgZXI7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBnZXRBbGxQbHVnaW5zVG9SZW1vdmUocGx1Z2luOiBBcHBsaWNhdGlvblBsdWdpbik6IEFwcGxpY2F0aW9uUGx1Z2luW10ge1xuICAgIHJldHVybiB0aGlzLnBhY2thZ2UuYXBwbGljYXRpb25WZXJzaW9ucy5tYXAoYXYgPT4gKHtcbiAgICAgIGlkOiBgJHtwbHVnaW4uY29udGV4dFBhdGh9QCR7YXYudmVyc2lvbn0vJHtwbHVnaW4ubW9kdWxlfWAsXG4gICAgICBpZExhdGVzdDogYCR7cGx1Z2luLmNvbnRleHRQYXRofS8ke3BsdWdpbi5tb2R1bGV9YCxcbiAgICAgIG1vZHVsZTogcGx1Z2luLm1vZHVsZSxcbiAgICAgIHBhdGg6IHBsdWdpbi5wYXRoXG4gICAgfSkpO1xuICB9XG5cbiAgcHJpdmF0ZSBlbWl0UmVtb3RlcyhyZW1vdGVzOiBBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnMpOiBBcHBsaWNhdGlvblJlbW90ZVBsdWdpbnMge1xuICAgIHRoaXMucmVtb3RlUGx1Z2lucyQubmV4dChyZW1vdGVzKTtcbiAgICByZXR1cm4geyAuLi50aGlzLnJlbW90ZVBsdWdpbnMkLnZhbHVlIH07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHNlbGVjdEFwcHMoXG4gICAgaW5pdGlhbFN0YXRlOiBQaWNrPFxuICAgICAgQXBwc1RvVXBkYXRlUmVtb3Rlc1NlbGVjdENvbXBvbmVudCxcbiAgICAgICdhcHBzJyB8ICd1cGRhdGVUeXBlJyB8ICdwbHVnaW5OYW1lJyB8ICdhcHBzRGlzYWJsZWQnXG4gICAgPlxuICApOiBQcm9taXNlPElBcHBsaWNhdGlvbltdPiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCAoXG4gICAgICAgIHRoaXMuYnNNb2RhbFNlcnZpY2Uuc2hvdyhBcHBzVG9VcGRhdGVSZW1vdGVzU2VsZWN0Q29tcG9uZW50LCB7XG4gICAgICAgICAgY2xhc3M6ICdtb2RhbC1zbScsXG4gICAgICAgICAgYXJpYURlc2NyaWJlZGJ5OiAnbW9kYWwtYm9keScsXG4gICAgICAgICAgYXJpYUxhYmVsbGVkQnk6ICdtb2RhbC10aXRsZScsXG4gICAgICAgICAgaW5pdGlhbFN0YXRlLFxuICAgICAgICAgIGlnbm9yZUJhY2tkcm9wQ2xpY2s6IHRydWUsXG4gICAgICAgICAga2V5Ym9hcmQ6IGZhbHNlXG4gICAgICAgIH0pLmNvbnRlbnQgYXMgQXBwc1RvVXBkYXRlUmVtb3Rlc1NlbGVjdENvbXBvbmVudFxuICAgICAgKS5yZXN1bHQ7XG4gICAgfSBjYXRjaCAoZXIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gIH1cbn1cbiIsIjxjOHktbGlzdC1ncm91cCBjbGFzcz1cImJnLWluaGVyaXRcIj5cbiAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIihwbHVnaW5zJCB8IGFzeW5jKT8ubGVuZ3RoICE9PSAwOyBlbHNlIGVtcHR5TGlzdFwiPlxuICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IHBsdWdpbiBvZiBwbHVnaW5zJCB8IGFzeW5jXCI+XG4gICAgICA8Yzh5LWxpIFtuZ0NsYXNzXT1cInsgZGlzYWJsZWQ6IHBsdWdpbi5pbnN0YWxsZWQgfVwiIGNsYXNzPVwiYmctaW5oZXJpdFwiPlxuICAgICAgICA8Yzh5LXBsdWdpbi1saXN0LWl0ZW1cbiAgICAgICAgICAoaXNJdGVtU2VsZWN0ZWQpPVwidXBkYXRlU2VsZWN0ZWRJdGVtcygkZXZlbnQsIHBsdWdpbilcIlxuICAgICAgICAgIFtwbHVnaW5dPVwicGx1Z2luXCJcbiAgICAgICAgICBbc2VsZWN0YWJsZV09XCJzZWxlY3RhYmxlXCJcbiAgICAgICAgICBbaGlkZVNvdXJjZV09XCJoaWRlU291cmNlXCJcbiAgICAgICAgICBjbGFzcz1cImQtZmxleFwiXG4gICAgICAgID48L2M4eS1wbHVnaW4tbGlzdC1pdGVtPlxuICAgICAgICA8ZGl2IGNsYXNzPVwicC1sLTQwIG0tdC00XCI+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgKm5nSWY9XCJpbnN0YWxsYWJsZVwiXG4gICAgICAgICAgICAoY2xpY2spPVwidW5pbnN0YWxsUGx1Z2luKHBsdWdpbilcIlxuICAgICAgICAgICAgW25nQ2xhc3NdPVwieyAnYnRuLXBlbmRpbmcnOiBwbHVnaW4uaWQgPT09IHVwZGF0aW5nUGx1Z2luSWQudW5pbnN0YWxsIH1cIlxuICAgICAgICAgICAgW2Rpc2FibGVkXT1cInVwZGF0aW5nUGx1Z2luSWQudW5pbnN0YWxsICYmIHBsdWdpbi5pZCAhPT0gdXBkYXRpbmdQbHVnaW5JZC51bmluc3RhbGxcIlxuICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLWRhbmdlciBidG4tc20gbS1sLTRcIlxuICAgICAgICAgICAgdGl0bGU9XCJ7eyAnVW5pbnN0YWxsIHBsdWdpbicgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgZGF0YS1jeT1cInBsdWdpbi1saXN0LS11bmluc3RhbGwtcGx1Z2luLWJ1dHRvblwiXG4gICAgICAgICAgICB0cmFuc2xhdGVcbiAgICAgICAgICA+XG4gICAgICAgICAgICBVbmluc3RhbGwgcGx1Z2luXG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgKm5nSWY9XCJpbnN0YWxsYWJsZVwiXG4gICAgICAgICAgICAoY2xpY2spPVwiaW5zdGFsbFBsdWdpbihwbHVnaW4pXCJcbiAgICAgICAgICAgIFtuZ0NsYXNzXT1cInsgJ2J0bi1wZW5kaW5nJzogcGx1Z2luLmlkID09PSB1cGRhdGluZ1BsdWdpbklkLmluc3RhbGwgfVwiXG4gICAgICAgICAgICBbZGlzYWJsZWRdPVwidXBkYXRpbmdQbHVnaW5JZC5pbnN0YWxsICYmIHBsdWdpbi5pZCAhPT0gdXBkYXRpbmdQbHVnaW5JZC5pbnN0YWxsXCJcbiAgICAgICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1kZWZhdWx0IGJ0bi1zbSBtLWwtOFwiXG4gICAgICAgICAgICB0aXRsZT1cInt7ICdJbnN0YWxsIHBsdWdpbicgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgZGF0YS1jeT1cInBsdWdpbi1saXN0LS1pbnN0YWxsLXBsdWdpbi1idXR0b25cIlxuICAgICAgICAgICAgdHJhbnNsYXRlXG4gICAgICAgICAgPlxuICAgICAgICAgICAgSW5zdGFsbCBwbHVnaW5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2M4eS1saT5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgPC9uZy1jb250YWluZXI+XG48L2M4eS1saXN0LWdyb3VwPlxuPG5nLXRlbXBsYXRlICNlbXB0eUxpc3Q+XG4gIDxkaXYgY2xhc3M9XCJjOHktZW1wdHktc3RhdGUgdGV4dC1sZWZ0XCIgKm5nSWY9XCJlbXB0eUxpc3RUZXh0XCI+XG4gICAgPGgxIGM4eUljb249XCJwbHVnaW5cIj48L2gxPlxuICAgIDxwPlxuICAgICAge3sgZW1wdHlMaXN0VGV4dCB8IHRyYW5zbGF0ZSB9fVxuICAgIDwvcD5cbiAgPC9kaXY+XG48L25nLXRlbXBsYXRlPlxuIl19