@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
283 lines • 56.4 kB
JavaScript
import { Location } from '@angular/common';
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { saveAs } from 'file-saver';
import { BehaviorSubject, from } from 'rxjs';
import { expand, reduce, shareReplay, takeWhile } from 'rxjs/operators';
import { TenantService, TenantStatus, UserService } from '@c8y/client';
import { AlertService, AppStateService, BuiltInActionType, GainsightService, gettext, ModalService, PasswordService, Permissions, Status, TenantUiService } from '@c8y/ngx-components';
import { CreationTimeFilteringFormRendererComponent } from './creation-time.filtering-form-renderer.component';
import { StatusFilteringFormRendererComponent } from './status.filtering-form-renderer.component';
import { PRODUCT_EXPERIENCE_TENANT_MANAGEMENT } from './../tenants.model';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/ngx-components";
import * as i2 from "@ngx-translate/core";
import * as i3 from "@c8y/client";
import * as i4 from "@angular/common";
import * as i5 from "@angular/router";
const { EVENTS, COMPONENTS, ACTIONS } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT;
export class TenantListComponent {
constructor(appState, alertService, modalService, translateService, tenantService, tenantUiService, location, passwordService, userService, permissionsService, gainsightService) {
this.appState = appState;
this.alertService = alertService;
this.modalService = modalService;
this.translateService = translateService;
this.tenantService = tenantService;
this.tenantUiService = tenantUiService;
this.location = location;
this.passwordService = passwordService;
this.userService = userService;
this.permissionsService = permissionsService;
this.gainsightService = gainsightService;
this.tenants$ = new BehaviorSubject(undefined);
this.isPermittedToCreateTenanant = this.permissionsService.hasAnyRole([
Permissions.ROLE_TENANT_MANAGEMENT_ADMIN,
Permissions.ROLE_TENANT_MANAGEMENT_CREATE
]);
this.TOP_TENANT_NAME = 'management';
this.title = null;
this.loadMoreItemsLabel = gettext('Load more tenants');
this.loadingItemsLabel = gettext('Loading tenants…');
this.displayOptions = {
bordered: false,
striped: true,
filter: true,
gridHeader: true,
hover: true
};
this.columns = this.getColumns();
this.pagination = this.getPagination();
this.showSearch = true;
this.actionControls = this.getActionControls();
this.noResultsMessage = gettext('No tenants to display.');
this.noDataMessage = gettext('There are no tenants defined.');
this.noResultsSubtitle = gettext('Refine your search terms or check your spelling.');
this.noDataSubtitle = gettext('Create the first tenant.');
this.TenantStatus = TenantStatus;
}
async ngOnInit() {
this.currentTenant = this.appState.currentTenant.value;
this.isManagementTenant = await this.tenantUiService.isManagementTenant();
this.loadTenants();
}
loadTenants() {
this.tenants$.next(undefined);
from(this.tenantService.list({ pageSize: 2000, withTotalPages: true, withApps: false }))
.pipe(expand(resultList => resultList.paging.nextPage !== null && resultList.paging.next()), takeWhile(resultList => resultList.paging.nextPage !== null, true), reduce((tenants, resultList) => [
...tenants,
...resultList.data
], []), shareReplay(1))
.subscribe(tenants => this.tenants$.next(tenants));
}
getColumns() {
return [
{
name: 'company',
header: gettext('Tenant'),
path: 'company',
filterable: true,
sortable: true,
sortOrder: 'asc'
},
{
name: 'id',
header: gettext('ID'),
path: 'id',
filterable: true,
sortable: true
},
{
name: 'domain',
header: gettext('Domain'),
path: 'domain',
filterable: true,
sortable: true
},
{
name: 'parent',
header: gettext('Parent tenant'),
path: 'parent',
filterable: true,
sortable: true
},
{
name: 'contactName',
header: gettext('Contact name'),
path: 'contactName',
filterable: true,
sortable: true
},
{
name: 'creationTime',
header: gettext('Created'),
path: 'creationTime',
filterable: true,
filteringFormRendererComponent: CreationTimeFilteringFormRendererComponent,
sortable: true
},
{
name: 'externalReference',
header: gettext('External reference'),
path: 'customProperties.externalReference',
filterable: true,
sortable: true
},
{
name: 'status',
header: gettext('Status'),
path: 'status',
filterable: true,
filteringFormRendererComponent: StatusFilteringFormRendererComponent,
sortable: true,
resizable: false
}
];
}
getPagination() {
return {
pageSize: 10,
currentPage: 1
};
}
getActionControls() {
return [
{
type: BuiltInActionType.Edit,
text: gettext('Edit`tenant`'),
callback: tenant => this.goToDetails(tenant)
},
{
type: 'activateTenantAction',
icon: 'power-off',
text: gettext('Activate`tenant`'),
callback: (tenant) => this.activateTenant(tenant),
showIf: (tenant) => this.isSuspended(tenant)
},
{
type: 'suspendTenantAction',
icon: 'power-off',
text: gettext('Suspend`tenant`'),
callback: (tenant) => this.suspendTenant(tenant),
showIf: (tenant) => this.isActive(tenant)
},
{
type: BuiltInActionType.Delete,
text: gettext('Delete`tenant`'),
callback: tenant => this.delete(tenant),
showIf: () => this.isManagementTenant
}
];
}
createTenant(options = { sendGainsightEvent: true }) {
if (options.sendGainsightEvent) {
this.gainsightService.triggerEvent(EVENTS.TENANT_MANAGEMENT, {
component: COMPONENTS.TENANT_LIST,
action: ACTIONS.TENANT_CREATION_INITIALIZED
});
}
this.location.go('/tenants/new');
}
goToDetails(tenant) {
this.location.go(`/tenants/${tenant.id}`);
}
async activateTenant(tenant) {
try {
const { data: savedTenant } = await this.tenantService.update({
id: tenant.id,
status: TenantStatus.ACTIVE
});
Object.assign(tenant, savedTenant);
this.alertService.success(gettext('Tenant activated.'));
}
catch (ex) {
if (ex) {
this.alertService.addServerFailure(ex);
}
}
}
async suspendTenant(tenant) {
const title = gettext('Suspend tenant');
const confirmationText = gettext('You are about to suspend tenant "{{ company }}" (ID "{{ id }}").');
const proceed = gettext('Do you want to proceed?');
const body = [
this.translateService.instant(confirmationText, {
company: tenant.company,
id: tenant.id
}),
this.translateService.instant(proceed)
].join(' ');
const labels = {
ok: gettext('Suspend`tenant`')
};
try {
await this.modalService.confirm(title, body, Status.DANGER, labels);
const confirmed = await this.passwordService.confirmPassword().toPromise();
if (confirmed === true) {
const { data: savedTenant } = await this.tenantService.update({
id: tenant.id,
status: TenantStatus.SUSPENDED
});
Object.assign(tenant, savedTenant);
this.alertService.success(gettext('Tenant suspended.'));
}
}
catch (ex) {
if (ex) {
this.alertService.addServerFailure(ex);
}
}
}
async delete(tenant) {
const title = gettext('Delete tenant');
const confirmationText = gettext('You are about to delete tenant "{{ company }}" (ID "{{ id }}").');
const hint = gettext('This operation is irreversible.');
const proceed = gettext('Do you want to proceed?');
const body = [
this.translateService.instant(confirmationText, {
company: tenant.company,
id: tenant.id
}),
this.translateService.instant(hint),
this.translateService.instant(proceed)
].join(' ');
const labels = {
ok: gettext('Delete`tenant`')
};
try {
await this.modalService.confirm(title, body, Status.DANGER, labels);
const confirmed = await this.passwordService.confirmPassword().toPromise();
if (confirmed === true) {
await this.tenantService.delete(tenant);
const tenantsWithoutRemovedOne = this.tenants$.value.filter(t => t !== tenant);
this.tenants$.next(tenantsWithoutRemovedOne);
this.alertService.success(gettext('Tenant is being deleted in the background. This might take a while…'));
}
}
catch (ex) {
if (ex) {
this.alertService.addServerFailure(ex);
}
}
}
isActive(tenant) {
return tenant.status === TenantStatus.ACTIVE;
}
isSuspended(tenant) {
return tenant.status === TenantStatus.SUSPENDED;
}
async downloadNewsletterEmails() {
const { res, data } = await this.userService.getNewsletterEmails();
const contentType = res.headers.get('content-type');
const contentDisposition = res.headers.get('content-disposition');
const filename = /filename="(.*)"/.exec(contentDisposition)[1];
const blob = new Blob([data], { type: contentType });
saveAs(blob, filename);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantListComponent, deps: [{ token: i1.AppStateService }, { token: i1.AlertService }, { token: i1.ModalService }, { token: i2.TranslateService }, { token: i3.TenantService }, { token: i1.TenantUiService }, { token: i4.Location }, { token: i1.PasswordService }, { token: i3.UserService }, { token: i1.Permissions }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TenantListComponent, selector: "c8y-tenant-list", ngImport: i0, template: "<c8y-title>\n {{ 'Subtenants' | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-layers\"\n label=\"{{ 'Tenants' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"!!(appState.state$ | async).newsletter\"\n [placement]=\"'right'\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{\n 'Downloads the list of emails of users subscribed for newsletter on the current tenant and its subtenants.'\n | translate\n }}\"\n type=\"button\"\n (click)=\"downloadNewsletterEmails()\"\n >\n <i c8yIcon=\"download\"></i>\n {{ 'Email addresses' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Create tenant' | translate }}\"\n type=\"button\"\n (click)=\"createTenant({ sendGainsightEvent: false })\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Create tenant' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#managing-subtenants\"></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"title\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [displayOptions]=\"displayOptions\"\n [columns]=\"columns\"\n [rows]=\"tenants$ | async\"\n [pagination]=\"pagination\"\n [showSearch]=\"showSearch\"\n [actionControls]=\"actionControls\"\n (onReload)=\"loadTenants()\"\n >\n <ng-container *ngIf=\"!(tenants$ | async); else empty\">\n <c8y-loading></c8y-loading>\n </ng-container>\n <ng-template #empty>\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-layers'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"\n stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\n \"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <ng-container *ngIf=\"stats?.size === 0\">\n <div>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Create tenant' | translate }}\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n {{ 'Create tenant' | translate }}\n </button>\n </div>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants\">user documentation</a>\n .\n </small>\n </p>\n </ng-container>\n </c8y-ui-empty-state>\n </ng-template>\n\n <c8y-column name=\"company\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n <a [routerLink]=\"['/tenants', context.item.id]\">{{ context.value }}</a>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"parent\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value || currentTenant.name }}\">\n {{ context.value || currentTenant.name }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"creationTime\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | c8yDate }}\">\n {{ context.value | c8yDate }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"status\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ 'Active`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.ACTIVE\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n </span>\n <span\n title=\"{{ 'Suspended`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.SUSPENDED\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n", dependencies: [{ kind: "component", type: i1.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1.EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: i1.CellRendererDefDirective, selector: "[c8yCellRendererDef]" }, { kind: "directive", type: i1.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i1.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "hideReload"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i1.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i1.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: i5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DatePipe, name: "c8yDate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-tenant-list', template: "<c8y-title>\n {{ 'Subtenants' | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-layers\"\n label=\"{{ 'Tenants' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"!!(appState.state$ | async).newsletter\"\n [placement]=\"'right'\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{\n 'Downloads the list of emails of users subscribed for newsletter on the current tenant and its subtenants.'\n | translate\n }}\"\n type=\"button\"\n (click)=\"downloadNewsletterEmails()\"\n >\n <i c8yIcon=\"download\"></i>\n {{ 'Email addresses' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Create tenant' | translate }}\"\n type=\"button\"\n (click)=\"createTenant({ sendGainsightEvent: false })\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Create tenant' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#managing-subtenants\"></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"title\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [displayOptions]=\"displayOptions\"\n [columns]=\"columns\"\n [rows]=\"tenants$ | async\"\n [pagination]=\"pagination\"\n [showSearch]=\"showSearch\"\n [actionControls]=\"actionControls\"\n (onReload)=\"loadTenants()\"\n >\n <ng-container *ngIf=\"!(tenants$ | async); else empty\">\n <c8y-loading></c8y-loading>\n </ng-container>\n <ng-template #empty>\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-layers'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"\n stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\n \"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <ng-container *ngIf=\"stats?.size === 0\">\n <div>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Create tenant' | translate }}\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n {{ 'Create tenant' | translate }}\n </button>\n </div>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants\">user documentation</a>\n .\n </small>\n </p>\n </ng-container>\n </c8y-ui-empty-state>\n </ng-template>\n\n <c8y-column name=\"company\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n <a [routerLink]=\"['/tenants', context.item.id]\">{{ context.value }}</a>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"parent\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value || currentTenant.name }}\">\n {{ context.value || currentTenant.name }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"creationTime\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | c8yDate }}\">\n {{ context.value | c8yDate }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"status\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ 'Active`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.ACTIVE\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n </span>\n <span\n title=\"{{ 'Suspended`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.SUSPENDED\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.AppStateService }, { type: i1.AlertService }, { type: i1.ModalService }, { type: i2.TranslateService }, { type: i3.TenantService }, { type: i1.TenantUiService }, { type: i4.Location }, { type: i1.PasswordService }, { type: i3.UserService }, { type: i1.Permissions }, { type: i1.GainsightService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LWxpc3QuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdGVuYW50cy90ZW5hbnQtbGlzdC90ZW5hbnQtbGlzdC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi90ZW5hbnRzL3RlbmFudC1saXN0L3RlbmFudC1saXN0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzQyxPQUFPLEVBQUUsU0FBUyxFQUFVLE1BQU0sZUFBZSxDQUFDO0FBQ2xELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDcEMsT0FBTyxFQUFFLGVBQWUsRUFBRSxJQUFJLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDN0MsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXhFLE9BQU8sRUFJTCxhQUFhLEVBQ2IsWUFBWSxFQUNaLFdBQVcsRUFDWixNQUFNLGFBQWEsQ0FBQztBQUNyQixPQUFPLEVBRUwsWUFBWSxFQUNaLGVBQWUsRUFDZixpQkFBaUIsRUFHakIsZ0JBQWdCLEVBQ2hCLE9BQU8sRUFDUCxZQUFZLEVBRVosZUFBZSxFQUNmLFdBQVcsRUFFWCxNQUFNLEVBQ04sZUFBZSxFQUNoQixNQUFNLHFCQUFxQixDQUFDO0FBRTdCLE9BQU8sRUFBRSwwQ0FBMEMsRUFBRSxNQUFNLG1EQUFtRCxDQUFDO0FBQy9HLE9BQU8sRUFBRSxvQ0FBb0MsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBQ2xHLE9BQU8sRUFBRSxvQ0FBb0MsRUFBRSxNQUFNLG9CQUFvQixDQUFDOzs7Ozs7O0FBRTFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxH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Q0FBQztJQUN6QixDQUFDOytHQWpTVSxtQkFBbUI7bUdBQW5CLG1CQUFtQix1REMzQ2hDLDYvSUFtSkE7OzRGRHhHYSxtQkFBbUI7a0JBSi9CLFNBQVM7K0JBQ0UsaUJBQWlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgTG9jYXRpb24gfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQ29tcG9uZW50LCBPbkluaXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFRyYW5zbGF0ZVNlcnZpY2UgfSBmcm9tICdAbmd4LXRyYW5zbGF0ZS9jb3JlJztcbmltcG9ydCB7IHNhdmVBcyB9IGZyb20gJ2ZpbGUtc2F2ZXInO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBmcm9tIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBleHBhbmQsIHJlZHVjZSwgc2hhcmVSZXBsYXksIHRha2VXaGlsZSB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHtcbiAgSUN1cnJlbnRUZW5hbnQsXG4gIElSZXN1bHRMaXN0LFxuICBJVGVuYW50LFxuICBUZW5hbnRTZXJ2aWNlLFxuICBUZW5hbnRTdGF0dXMsXG4gIFVzZXJTZXJ2aWNlXG59IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7XG4gIEFjdGlvbkNvbnRyb2wsXG4gIEFsZXJ0U2VydmljZSxcbiAgQXBwU3RhdGVTZXJ2aWNlLFxuICBCdWlsdEluQWN0aW9uVHlwZSxcbiAgQ29sdW1uLFxuICBEaXNwbGF5T3B0aW9ucyxcbiAgR2FpbnNpZ2h0U2VydmljZSxcbiAgZ2V0dGV4dCxcbiAgTW9kYWxTZXJ2aWNlLFxuICBQYWdpbmF0aW9uLFxuICBQYXNzd29yZFNlcnZpY2UsXG4gIFBlcm1pc3Npb25zLFxuICBTb3J0T3JkZXIsXG4gIFN0YXR1cyxcbiAgVGVuYW50VWlTZXJ2aWNlXG59IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMnO1xuXG5pbXBvcnQgeyBDcmVhdGlvblRpbWVGaWx0ZXJpbmdGb3JtUmVuZGVyZXJDb21wb25lbnQgfSBmcm9tICcuL2NyZWF0aW9uLXRpbWUuZmlsdGVyaW5nLWZvcm0tcmVuZGVyZXIuY29tcG9uZW50JztcbmltcG9ydCB7IFN0YXR1c0ZpbHRlcmluZ0Zvcm1SZW5kZXJlckNvbXBvbmVudCB9IGZyb20gJy4vc3RhdHVzLmZpbHRlcmluZy1mb3JtLXJlbmRlcmVyLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBQUk9EVUNUX0VYUEVSSUVOQ0VfVEVOQU5UX01BTkFHRU1FTlQgfSBmcm9tICcuLy4uL3RlbmFudHMubW9kZWwnO1xuXG5jb25zdCB7IEVWRU5UUywgQ09NUE9ORU5UUywgQUNUSU9OUyB9ID0gUFJPRFVDVF9FWFBFUklFTkNFX1RFTkFOVF9NQU5BR0VNRU5UO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdjOHktdGVuYW50LWxpc3QnLFxuICB0ZW1wbGF0ZVVybDogJy4vdGVuYW50LWxpc3QuY29tcG9uZW50Lmh0bWwnXG59KVxuZXhwb3J0IGNsYXNzIFRlbmFudExpc3RDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQge1xuICB0ZW5hbnRzJDogQmVoYXZpb3JTdWJqZWN0PElUZW5hbnRbXT4gPSBuZXcgQmVoYXZpb3JTdWJqZWN0KHVuZGVmaW5lZCk7XG4gIGlzUGVybWl0dGVkVG9DcmVhdGVUZW5hbmFudCA9IHRoaXMucGVybWlzc2lvbnNTZXJ2aWNlLmhhc0FueVJvbGUoW1xuICAgIFBlcm1pc3Npb25zLlJPTEVfVEVOQU5UX01BTkFHRU1FTlRfQURNSU4sXG4gICAgUGVybWlzc2lvbnMuUk9MRV9URU5BTlRfTUFOQUdFTUVOVF9DUkVBVEVcbiAgXSk7XG4gIGN1cnJlbnRUZW5hbnQ6IElDdXJyZW50VGVuYW50O1xuICBpc01hbmFnZW1lbnRUZW5hbnQ6IGJvb2xlYW47XG4gIFRPUF9URU5BTlRfTkFNRSA9ICdtYW5hZ2VtZW50JztcblxuICB0aXRsZTogc3RyaW5nID0gbnVsbDtcbiAgbG9hZE1vcmVJdGVtc0xhYmVsOiBzdHJpbmcgPSBnZXR0ZXh0KCdMb2FkIG1vcmUgdGVuYW50cycpO1xuICBsb2FkaW5nSXRlbXNMYWJlbDogc3RyaW5nID0gZ2V0dGV4dCgnTG9hZGluZyB0ZW5hbnRz4oCmJyk7XG5cbiAgZGlzcGxheU9wdGlvbnM6IERpc3BsYXlPcHRpb25zID0ge1xuICAgIGJvcmRlcmVkOiBmYWxzZSxcbiAgICBzdHJpcGVkOiB0cnVlLFxuICAgIGZpbHRlcjogdHJ1ZSxcbiAgICBncmlkSGVhZGVyOiB0cnVlLFxuICAgIGhvdmVyOiB0cnVlXG4gIH07XG5cbiAgY29sdW1uczogQ29sdW1uW10gPSB0aGlzLmdldENvbHVtbnMoKTtcbiAgcGFnaW5hdGlvbjogUGFnaW5hdGlvbiA9IHRoaXMuZ2V0UGFnaW5hdGlvbigpO1xuICBzaG93U2VhcmNoID0gdHJ1ZTtcbiAgYWN0aW9uQ29udHJvbHM6IEFjdGlvbkNvbnRyb2xbXSA9IHRoaXMuZ2V0QWN0aW9uQ29udHJvbHMoKTtcblxuICBub1Jlc3VsdHNNZXNzYWdlID0gZ2V0dGV4dCgnTm8gdGVuYW50cyB0byBkaXNwbGF5LicpO1xuICBub0RhdGFNZXNzYWdlID0gZ2V0dGV4dCgnVGhlcmUgYXJlIG5vIHRlbmFudHMgZGVmaW5lZC4nKTtcbiAgbm9SZXN1bHRzU3VidGl0bGUgPSBnZXR0ZXh0KCdSZWZpbmUgeW91ciBzZWFyY2ggdGVybXMgb3IgY2hlY2sgeW91ciBzcGVsbGluZy4nKTtcbiAgbm9EYXRhU3VidGl0bGUgPSBnZXR0ZXh0KCdDcmVhdGUgdGhlIGZpcnN0IHRlbmFudC4nKTtcblxuICBUZW5hbnRTdGF0dXMgPSBUZW5hbnRTdGF0dXM7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHVibGljIGFwcFN0YXRlOiBBcHBTdGF0ZVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBhbGVydFNlcnZpY2U6IEFsZXJ0U2VydmljZSxcbiAgICBwcml2YXRlIG1vZGFsU2VydmljZTogTW9kYWxTZXJ2aWNlLFxuICAgIHByaXZhdGUgdHJhbnNsYXRlU2VydmljZTogVHJhbnNsYXRlU2VydmljZSxcbiAgICBwcml2YXRlIHRlbmFudFNlcnZpY2U6IFRlbmFudFNlcnZpY2UsXG4gICAgcHJpdmF0ZSB0ZW5hbnRVaVNlcnZpY2U6IFRlbmFudFVpU2VydmljZSxcbiAgICBwcml2YXRlIGxvY2F0aW9uOiBMb2NhdGlvbixcbiAgICBwcml2YXRlIHBhc3N3b3JkU2VydmljZTogUGFzc3dvcmRTZXJ2aWNlLFxuICAgIHByaXZhdGUgdXNlclNlcnZpY2U6IFVzZXJTZXJ2aWNlLFxuICAgIHByaXZhdGUgcGVybWlzc2lvbnNTZXJ2aWNlOiBQZXJtaXNzaW9ucyxcbiAgICBwcml2YXRlIGdhaW5zaWdodFNlcnZpY2U6IEdhaW5zaWdodFNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIG5nT25Jbml0KCkge1xuICAgIHRoaXMuY3VycmVudFRlbmFudCA9IHRoaXMuYXBwU3RhdGUuY3VycmVudFRlbmFudC52YWx1ZTtcbiAgICB0aGlzLmlzTWFuYWdlbWVudFRlbmFudCA9IGF3YWl0IHRoaXMudGVuYW50VWlTZXJ2aWNlLmlzTWFuYWdlbWVudFRlbmFudCgpO1xuICAgIHRoaXMubG9hZFRlbmFudHMoKTtcbiAgfVxuXG4gIGxvYWRUZW5hbnRzKCkge1xuICAgIHRoaXMudGVuYW50cyQubmV4dCh1bmRlZmluZWQpO1xuICAgIGZyb20odGhpcy50ZW5hbnRTZXJ2aWNlLmxpc3QoeyBwYWdlU2l6ZTogMjAwMCwgd2l0aFRvdGFsUGFnZXM6IHRydWUsIHdpdGhBcHBzOiBmYWxzZSB9KSlcbiAgICAgIC5waXBlKFxuICAgICAgICBleHBhbmQocmVzdWx0TGlzdCA9PiByZXN1bHRMaXN0LnBhZ2luZy5uZXh0UGFnZSAhPT0gbnVsbCAmJiByZXN1bHRMaXN0LnBhZ2luZy5uZXh0KCkpLFxuICAgICAgICB0YWtlV2hpbGUocmVzdWx0TGlzdCA9PiByZXN1bHRMaXN0LnBhZ2luZy5uZXh0UGFnZSAhPT0gbnVsbCwgdHJ1ZSksXG4gICAgICAgIHJlZHVjZShcbiAgICAgICAgICAodGVuYW50czogSVRlbmFudFtdLCByZXN1bHRMaXN0OiBJUmVzdWx0TGlzdDxJVGVuYW50PikgPT4gW1xuICAgICAgICAgICAgLi4udGVuYW50cyxcbiAgICAgICAgICAgIC4uLnJlc3VsdExpc3QuZGF0YVxuICAgICAgICAgIF0sXG4gICAgICAgICAgW11cbiAgICAgICAgKSxcbiAgICAgICAgc2hhcmVSZXBsYXkoMSlcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUodGVuYW50cyA9PiB0aGlzLnRlbmFudHMkLm5leHQodGVuYW50cykpO1xuICB9XG5cbiAgZ2V0Q29sdW1ucygpOiBDb2x1bW5bXSB7XG4gICAgcmV0dXJuIFtcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ2NvbXBhbnknLFxuICAgICAgICBoZWFkZXI6IGdldHRleHQoJ1RlbmFudCcpLFxuICAgICAgICBwYXRoOiAnY29tcGFueScsXG4gICAgICAgIGZpbHRlcmFibGU6IHRydWUsXG4gICAgICAgIHNvcnRhYmxlOiB0cnVlLFxuICAgICAgICBzb3J0T3JkZXI6ICdhc2MnIGFzIFNvcnRPcmRlclxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ2lkJyxcbiAgICAgICAgaGVhZGVyOiBnZXR0ZXh0KCdJRCcpLFxuICAgICAgICBwYXRoOiAnaWQnLFxuICAgICAgICBmaWx0ZXJhYmxlOiB0cnVlLFxuICAgICAgICBzb3J0YWJsZTogdHJ1ZVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ2RvbWFpbicsXG4gICAgICAgIGhlYWRlcjogZ2V0dGV4dCgnRG9tYWluJyksXG4gICAgICAgIHBhdGg6ICdkb21haW4nLFxuICAgICAgICBmaWx0ZXJhYmxlOiB0cnVlLFxuICAgICAgICBzb3J0YWJsZTogdHJ1ZVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ3BhcmVudCcsXG4gICAgICAgIGhlYWRlcjogZ2V0dGV4dCgnUGFyZW50IHRlbmFudCcpLFxuICAgICAgICBwYXRoOiAncGFyZW50JyxcbiAgICAgICAgZmlsdGVyYWJsZTogdHJ1ZSxcbiAgICAgICAgc29ydGFibGU6IHRydWVcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdjb250YWN0TmFtZScsXG4gICAgICAgIGhlYWRlcjogZ2V0dGV4dCgnQ29udGFjdCBuYW1lJyksXG4gICAgICAgIHBhdGg6ICdjb250YWN0TmFtZScsXG4gICAgICAgIGZpbHRlcmFibGU6IHRydWUsXG4gICAgICAgIHNvcnRhYmxlOiB0cnVlXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiAnY3JlYXRpb25UaW1lJyxcbiAgICAgICAgaGVhZGVyOiBnZXR0ZXh0KCdDcmVhdGVkJyksXG4gICAgICAgIHBhdGg6ICdjcmVhdGlvblRpbWUnLFxuICAgICAgICBmaWx0ZXJhYmxlOiB0cnVlLFxuICAgICAgICBmaWx0ZXJpbmdGb3JtUmVuZGVyZXJDb21wb25lbnQ6IENyZWF0aW9uVGltZUZpbHRlcmluZ0Zvcm1SZW5kZXJlckNvbXBvbmVudCxcbiAgICAgICAgc29ydGFibGU6IHRydWVcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdleHRlcm5hbFJlZmVyZW5jZScsXG4gICAgICAgIGhlYWRlcjogZ2V0dGV4dCgnRXh0ZXJuYWwgcmVmZXJlbmNlJyksXG4gICAgICAgIHBhdGg6ICdjdXN0b21Qcm9wZXJ0aWVzLmV4dGVybmFsUmVmZXJlbmNlJyxcbiAgICAgICAgZmlsdGVyYWJsZTogdHJ1ZSxcbiAgICAgICAgc29ydGFibGU6IHRydWVcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdzdGF0dXMnLFxuICAgICAgICBoZWFkZXI6IGdldHRleHQoJ1N0YXR1cycpLFxuICAgICAgICBwYXRoOiAnc3RhdHVzJyxcbiAgICAgICAgZmlsdGVyYWJsZTogdHJ1ZSxcbiAgICAgICAgZmlsdGVyaW5nRm9ybVJlbmRlcmVyQ29tcG9uZW50OiBTdGF0dXNGaWx0ZXJpbmdGb3JtUmVuZGVyZXJDb21wb25lbnQsXG4gICAgICAgIHNvcnRhYmxlOiB0cnVlLFxuICAgICAgICByZXNpemFibGU6IGZhbHNlXG4gICAgICB9XG4gICAgXTtcbiAgfVxuXG4gIGdldFBhZ2luYXRpb24oKTogUGFnaW5hdGlvbiB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHBhZ2VTaXplOiAxMCxcbiAgICAgIGN1cnJlbnRQYWdlOiAxXG4gICAgfTtcbiAgfVxuXG4gIGdldEFjdGlvbkNvbnRyb2xzKCk6IEFjdGlvbkNvbnRyb2xbXSB7XG4gICAgcmV0dXJuIFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogQnVpbHRJbkFjdGlvblR5cGUuRWRpdCxcbiAgICAgICAgdGV4dDogZ2V0dGV4dCgnRWRpdGB0ZW5hbnRgJyksXG4gICAgICAgIGNhbGxiYWNrOiB0ZW5hbnQgPT4gdGhpcy5nb1RvRGV0YWlscyh0ZW5hbnQpXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICB0eXBlOiAnYWN0aXZhdGVUZW5hbnRBY3Rpb24nLFxuICAgICAgICBpY29uOiAncG93ZXItb2ZmJyxcbiAgICAgICAgdGV4dDogZ2V0dGV4dCgnQWN0aXZhdGVgdGVuYW50YCcpLFxuICAgICAgICBjYWxsYmFjazogKHRlbmFudDogSVRlbmFudCkgPT4gdGhpcy5hY3RpdmF0ZVRlbmFudCh0ZW5hbnQpLFxuICAgICAgICBzaG93SWY6ICh0ZW5hbnQ6IElUZW5hbnQpID0+IHRoaXMuaXNTdXNwZW5kZWQodGVuYW50KVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgdHlwZTogJ3N1c3BlbmRUZW5hbnRBY3Rpb24nLFxuICAgICAgICBpY29uOiAncG93ZXItb2ZmJyxcbiAgICAgICAgdGV4dDogZ2V0dGV4dCgnU3VzcGVuZGB0ZW5hbnRgJyksXG4gICAgICAgIGNhbGxiYWNrOiAodGVuYW50OiBJVGVuYW50KSA9PiB0aGlzLnN1c3BlbmRUZW5hbnQodGVuYW50KSxcbiAgICAgICAgc2hvd0lmOiAodGVuYW50OiBJVGVuYW50KSA9PiB0aGlzLmlzQWN0aXZlKHRlbmFudClcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIHR5cGU6IEJ1aWx0SW5BY3Rpb25UeXBlLkRlbGV0ZSxcbiAgICAgICAgdGV4dDogZ2V0dGV4dCgnRGVsZXRlYHRlbmFudGAnKSxcbiAgICAgICAgY2FsbGJhY2s6IHRlbmFudCA9PiB0aGlzLmRlbGV0ZSh0ZW5hbnQpLFxuICAgICAgICBzaG93SWY6ICgpID0+IHRoaXMuaXNNYW5hZ2VtZW50VGVuYW50XG4gICAgICB9XG4gICAgXTtcbiAgfVxuXG4gIGNyZWF0ZVRlbmFudChvcHRpb25zOiB7IHNlbmRHYWluc2lnaHRFdmVudDogYm9vbGVhbiB9ID0geyBzZW5kR2FpbnNpZ2h0RXZlbnQ6IHRydWUgfSkge1xuICAgIGlmIChvcHRpb25zLnNlbmRHYWluc2lnaHRFdmVudCkge1xuICAgICAgdGhpcy5nYWluc2lnaHRTZXJ2aWNlLnRyaWdnZXJFdmVudChFVkVOVFMuVEVOQU5UX01BTkFHRU1FTlQsIHtcbiAgICAgICAgY29tcG9uZW50OiBDT01QT05FTlRTLlRFTkFOVF9MSVNULFxuICAgICAgICBhY3Rpb246IEFDVElPTlMuVEVOQU5UX0NSRUFUSU9OX0lOSVRJQUxJWkVEXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICB0aGlzLmxvY2F0aW9uLmdvKCcvdGVuYW50cy9uZXcnKTtcbiAgfVxuXG4gIGdvVG9EZXRhaWxzKHRlbmFudDogSVRlbmFudCkge1xuICAgIHRoaXMubG9jYXRpb24uZ28oYC90ZW5hbnRzLyR7dGVuYW50LmlkfWApO1xuICB9XG5cbiAgYXN5bmMgYWN0aXZhdGVUZW5hbnQodGVuYW50KSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgZGF0YTogc2F2ZWRUZW5hbnQgfSA9IGF3YWl0IHRoaXMudGVuYW50U2VydmljZS51cGRhdGUoe1xuICAgICAgICBpZDogdGVuYW50LmlkLFxuICAgICAgICBzdGF0dXM6IFRlbmFudFN0YXR1cy5BQ1RJVkVcbiAgICAgIH0pO1xuICAgICAgT2JqZWN0LmFzc2lnbih0ZW5hbnQsIHNhdmVkVGVuYW50KTtcbiAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLnN1Y2Nlc3MoZ2V0dGV4dCgnVGVuYW50IGFjdGl2YXRlZC4nKSk7XG4gICAgfSBjYXRjaCAoZXgpIHtcbiAgICAgIGlmIChleCkge1xuICAgICAgICB0aGlzLmFsZXJ0U2VydmljZS5hZGRTZXJ2ZXJGYWlsdXJlKGV4KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBhc3luYyBzdXNwZW5kVGVuYW50KHRlbmFudCkge1xuICAgIGNvbnN0IHRpdGxlID0gZ2V0dGV4dCgnU3VzcGVuZCB0ZW5hbnQnKTtcbiAgICBjb25zdCBjb25maXJtYXRpb25UZXh0ID0gZ2V0dGV4dChcbiAgICAgICdZb3UgYXJlIGFib3V0IHRvIHN1c3BlbmQgdGVuYW50IFwie3sgY29tcGFueSB9fVwiIChJRCBcInt7IGlkIH19XCIpLidcbiAgICApO1xuICAgIGNvbnN0IHByb2NlZWQgPSBnZXR0ZXh0KCdEbyB5b3Ugd2FudCB0byBwcm9jZWVkPycpO1xuICAgIGNvbnN0IGJvZHkgPSBbXG4gICAgICB0aGlzLnRyYW5zbGF0ZVNlcnZpY2UuaW5zdGFudChjb25maXJtYXRpb25UZXh0LCB7XG4gICAgICAgIGNvbXBhbnk6IHRlbmFudC5jb21wYW55LFxuICAgICAgICBpZDogdGVuYW50LmlkXG4gICAgICB9KSxcbiAgICAgIHRoaXMudHJhbnNsYXRlU2VydmljZS5pbnN0YW50KHByb2NlZWQpXG4gICAgXS5qb2luKCcgJyk7XG4gICAgY29uc3QgbGFiZWxzID0ge1xuICAgICAgb2s6IGdldHRleHQoJ1N1c3BlbmRgdGVuYW50YCcpXG4gICAgfTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5tb2RhbFNlcnZpY2UuY29uZmlybSh0aXRsZSwgYm9keSwgU3RhdHVzLkRBTkdFUiwgbGFiZWxzKTtcbiAgICAgIGNvbnN0IGNvbmZpcm1lZCA9IGF3YWl0IHRoaXMucGFzc3dvcmRTZXJ2aWNlLmNvbmZpcm1QYXNzd29yZCgpLnRvUHJvbWlzZSgpO1xuICAgICAgaWYgKGNvbmZpcm1lZCA9PT0gdHJ1ZSkge1xuICAgICAgICBjb25zdCB7IGRhdGE6IHNhdmVkVGVuYW50IH0gPSBhd2FpdCB0aGlzLnRlbmFudFNlcnZpY2UudXBkYXRlKHtcbiAgICAgICAgICBpZDogdGVuYW50LmlkLFxuICAgICAgICAgIHN0YXR1czogVGVuYW50U3RhdHVzLlNVU1BFTkRFRFxuICAgICAgICB9KTtcbiAgICAgICAgT2JqZWN0LmFzc2lnbih0ZW5hbnQsIHNhdmVkVGVuYW50KTtcbiAgICAgICAgdGhpcy5hbGVydFNlcnZpY2Uuc3VjY2VzcyhnZXR0ZXh0KCdUZW5hbnQgc3VzcGVuZGVkLicpKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChleCkge1xuICAgICAgaWYgKGV4KSB7XG4gICAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLmFkZFNlcnZlckZhaWx1cmUoZXgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGRlbGV0ZSh0ZW5hbnQpIHtcbiAgICBjb25zdCB0aXRsZSA9IGdldHRleHQoJ0RlbGV0ZSB0ZW5hbnQnKTtcbiAgICBjb25zdCBjb25maXJtYXRpb25UZXh0ID0gZ2V0dGV4dChcbiAgICAgICdZb3UgYXJlIGFib3V0IHRvIGRlbGV0ZSB0ZW5hbnQgXCJ7eyBjb21wYW55IH19XCIgKElEIFwie3sgaWQgfX1cIikuJ1xuICAgICk7XG4gICAgY29uc3QgaGludCA9IGdldHRleHQoJ1RoaXMgb3BlcmF0aW9uIGlzIGlycmV2ZXJzaWJsZS4nKTtcbiAgICBjb25zdCBwcm9jZWVkID0gZ2V0dGV4dCgnRG8geW91IHdhbnQgdG8gcHJvY2VlZD8nKTtcbiAgICBjb25zdCBib2R5ID0gW1xuICAgICAgdGhpcy50cmFuc2xhdGVTZXJ2aWNlLmluc3RhbnQoY29uZmlybWF0aW9uVGV4dCwge1xuICAgICAgICBjb21wYW55OiB0ZW5hbnQuY29tcGFueSxcbiAgICAgICAgaWQ6IHRlbmFudC5pZFxuICAgICAgfSksXG4gICAgICB0aGlzLnRyYW5zbGF0ZVNlcnZpY2UuaW5zdGFudChoaW50KSxcbiAgICAgIHRoaXMudHJhbnNsYXRlU2VydmljZS5pbnN0YW50KHByb2NlZWQpXG4gICAgXS5qb2luKCcgJyk7XG4gICAgY29uc3QgbGFiZWxzID0ge1xuICAgICAgb2s6IGdldHRleHQoJ0RlbGV0ZWB0ZW5hbnRgJylcbiAgICB9O1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLm1vZGFsU2VydmljZS5jb25maXJtKHRpdGxlLCBib2R5LCBTdGF0dXMuREFOR0VSLCBsYWJlbHMpO1xuICAgICAgY29uc3QgY29uZmlybWVkID0gYXdhaXQgdGhpcy5wYXNzd29yZFNlcnZpY2UuY29uZmlybVBhc3N3b3JkKCkudG9Qcm9taXNlKCk7XG4gICAgICBpZiAoY29uZmlybWVkID09PSB0cnVlKSB7XG4gICAgICAgIGF3YWl0IHRoaXMudGVuYW50U2VydmljZS5kZWxldGUodGVuYW50KTtcbiAgICAgICAgY29uc3QgdGVuYW50c1dpdGhvdXRSZW1vdmVkT25lID0gdGhpcy50ZW5hbnRzJC52YWx1ZS5maWx0ZXIodCA9PiB0ICE9PSB0ZW5hbnQpO1xuICAgICAgICB0aGlzLnRlbmFudHMkLm5leHQodGVuYW50c1dpdGhvdXRSZW1vdmVkT25lKTtcbiAgICAgICAgdGhpcy5hbGVydFNlcnZpY2Uuc3VjY2VzcyhcbiAgICAgICAgICBnZXR0ZXh0KCdUZW5hbnQgaXMgYmVpbmcgZGVsZXRlZCBpbiB0aGUgYmFja2dyb3VuZC4gVGhpcyBtaWdodCB0YWtlIGEgd2hpbGXigKYnKVxuICAgICAgICApO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGV4KSB7XG4gICAgICBpZiAoZXgpIHtcbiAgICAgICAgdGhpcy5hbGVydFNlcnZpY2UuYWRkU2VydmVyRmFpbHVyZShleCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaXNBY3RpdmUodGVuYW50OiBJVGVuYW50KSB7XG4gICAgcmV0dXJuIHRlbmFudC5zdGF0dXMgPT09IFRlbmFudFN0YXR1cy5BQ1RJVkU7XG4gIH1cblxuICBpc1N1c3BlbmRlZCh0ZW5hbnQ6IElUZW5hbnQpIHtcbiAgICByZXR1cm4gdGVuYW50LnN0YXR1cyA9PT0gVGVuYW50U3RhdHVzLlNVU1BFTkRFRDtcbiAgfVxuXG4gIGFzeW5jIGRvd25sb2FkTmV3c2xldHRlckVtYWlscygpIHtcbiAgICBjb25zdCB7IHJlcywgZGF0YSB9ID0gYXdhaXQgdGhpcy51c2VyU2VydmljZS5nZXROZXdzbGV0dGVyRW1haWxzKCk7XG4gICAgY29uc3QgY29udGVudFR5cGUgPSByZXMuaGVhZGVycy5nZXQoJ2NvbnRlbnQtdHlwZScpO1xuICAgIGNvbnN0IGNvbnRlbnREaXNwb3NpdGlvbiA9IHJlcy5oZWFkZXJzLmdldCgnY29udGVudC1kaXNwb3NpdGlvbicpO1xuICAgIGNvbnN0IGZpbGVuY