@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
374 lines • 59.8 kB
JavaScript
import { Injectable } from '@angular/core';
import { ApplicationAvailability, ApplicationType, FetchClient, InventoryBinaryService, InventoryService } from '@c8y/client';
import { ApplicationService } from '@c8y/client';
import { AppStateService, ThemeSwitcherService, ZipService } from '@c8y/ngx-components';
import { defer, BehaviorSubject } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { cloneDeep, uniq } from 'lodash-es';
import { BrandingVersionService } from './branding-version.service';
import { allBrandingCSSVars, numberBrandingVars } from './branding.type';
import { StaticAssetsService } from '@c8y/ngx-components/static-assets/data';
import { BrandingTrackingService } from './branding-tracking.service';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/client";
import * as i2 from "@c8y/ngx-components";
import * as i3 from "./branding-version.service";
import * as i4 from "@c8y/ngx-components/static-assets/data";
import * as i5 from "./branding-tracking.service";
/**
* Service to load and store the branding of the tenant.
*/
export class StoreBrandingService {
constructor(applicationService, inventory, binary, zip, fetch, brandingVersionService, appState, staticAssets, themeSwitcher, brandingTracking) {
this.applicationService = applicationService;
this.inventory = inventory;
this.binary = binary;
this.zip = zip;
this.fetch = fetch;
this.brandingVersionService = brandingVersionService;
this.appState = appState;
this.staticAssets = staticAssets;
this.themeSwitcher = themeSwitcher;
this.brandingTracking = brandingTracking;
this.fileNames = {
options: 'options.json',
manifest: 'cumulocity.json',
exportZipName: 'public-options.zip'
};
this.manifestValues = {
name: 'public-options',
contextPath: 'public-options',
key: 'public-options-key',
description: 'Application containing static assets used by e.g. branding.',
noAppSwitcher: true,
type: ApplicationType.HOSTED,
availability: ApplicationAvailability.SHARED
};
this.refreshTriggerBrandingVariants = new BehaviorSubject(null);
}
/**
* Sets the `latest` tag on the provided branding version. Making it the global active branding.
*/
async markAsActive(brandingVersion, app) {
const publicOptions = app || (await this.getPublicOptionsApp());
const tags = brandingVersion.tags || [];
const version = brandingVersion.version;
await this.applicationService.setPackageVersionTag(publicOptions, version, [...tags, 'latest']);
}
/**
* Opens a new tab with to preview the branding. The branding must have been saved beforehand.
* @param brandingName the name of the branding to be previewed
*/
openPreviewForBranding(brandingName) {
this.brandingTracking.openPreviewForBranding();
window.open(`/apps/${this.appState.currentApplication.value?.contextPath || 'cockpit'}/index.html?brandingPreview=true&dynamicOptionsUrl=/apps/public/public-options@${brandingName}/options.json`, '_blank', 'noopener,noreferrer');
}
/**
* Returns the brandings of the tenant.
* If no public options app is found, publicOptions will be undefined and variants an empty array.
* For old brandings (created without versioning) a default version is returned.
*/
async loadBrandingVariants(app) {
const publicOptions = app || (await this.getPublicOptionsApp());
if (!publicOptions) {
return { publicOptions: undefined, variants: [] };
}
const { data: versions } = await this.applicationService.listVersions(publicOptions);
let mappedVersions = versions
?.map(tmp => {
try {
const { name, iteration } = this.brandingVersionService.splitBrandingIntoNameAndIteration(tmp.version);
return {
revision: iteration,
name: name,
id: tmp.binaryId,
version: tmp.version,
tags: tmp.tags || [],
publicOptionsApp: publicOptions
};
}
catch (e) {
console.warn('Failed to parse version', tmp, e);
return undefined;
}
})
?.filter(Boolean) || [];
if (!versions && publicOptions.activeVersionId) {
mappedVersions = [
{
name: 'default',
id: publicOptions.activeVersionId,
version: 'default-1',
revision: 1,
tags: ['latest', 'default'],
publicOptionsApp: publicOptions
}
];
}
const variants = await this.getMetadataOfBrandingBinaries(mappedVersions);
return { publicOptions, variants };
}
/**
* As the branding is not immediately available after creation, this method will wait for the branding to be present.
* @param version The version of the branding to be retrieved.
*/
async waitForBrandingToBePresent(version) {
const numberOfAttempts = 120;
const sleepDurationInSeconds = 1;
for (let i = 0; i < numberOfAttempts; i++) {
try {
// do not sleep before the first attempt
if (i !== 0) {
await new Promise(resolve => setTimeout(resolve, sleepDurationInSeconds * 1_000));
}
await this.getBrandingOptionsForVersion(version);
console.info(`Branding "${version}" available now.`);
return;
}
catch (e) {
console.warn(`Branding "${version}" not yet present, retrying in ${sleepDurationInSeconds} seconds...`, e);
}
}
throw new Error(`Branding "${version}" not available after ${numberOfAttempts * sleepDurationInSeconds} seconds, giving up.`);
}
/**
* Will create a the initial branding based on the currently applied CSS variables.
*/
async getStartedUsingBranding() {
const allVariables = {};
const themeToRetrieveVars = ['light', 'dark'];
const unretrievableVars = new Array();
for (const theme of themeToRetrieveVars) {
this.themeSwitcher.temporaryChangeTheme(theme);
const styles = getComputedStyle(document.body);
const variablesToSkip = [
'navigator-platform-logo',
'brand-logo-img'
];
const values = allBrandingCSSVars
.filter(cssVar => !variablesToSkip.includes(cssVar))
.reduce((prev, key) => {
const directValue = styles.getPropertyValue(`--${key}`);
const c8yPrefixedValue = styles.getPropertyValue(`--c8y-${key}`);
const newKey = theme === 'light' ? key : `dark-${key}`;
if (!directValue && !c8yPrefixedValue) {
unretrievableVars.push(newKey);
return prev;
}
let value = directValue || c8yPrefixedValue;
if (numberBrandingVars.includes(key)) {
try {
value = Number.parseFloat(value.replace(/[A-Za-z]/g, '').trim());
}
catch (e) {
console.warn(`Failed to parse number for "${key}" value that failed to parse: "${value}"`, e);
return prev;
}
}
return Object.assign(prev, { [newKey]: value });
}, {});
Object.assign(allVariables, values);
}
this.themeSwitcher.resetTemporaryTheme();
try {
await this.createPublicOptionsAppFromInheritedOptions(allVariables);
}
catch (e) {
console.warn(e);
}
}
getZipForBinary(binaryId, fileName = this.fileNames.exportZipName) {
return defer(() => this.binary.download(binaryId)).pipe(switchMap(response => response.blob()), map(blob => new File([blob], fileName)));
}
/**
* Deletes the public options app and therefore all brandings.
* The public options app can be optionally provided to avoid another request for it.
*/
async deleteAllBrandings(publicOptions) {
publicOptions = publicOptions || (await this.getPublicOptionsApp());
await this.applicationService.delete(publicOptions);
}
/**
* Enhances the provided branding versions with metadata from the linked binaries.
* It will add the owner and lastUpdated fields to the versions.
* The provided array is altered.
*/
async getMetadataOfBrandingBinaries(versions) {
const binaryIds = uniq(versions.map(tmp => tmp.id));
if (!binaryIds.length) {
return versions;
}
const { data: metadata } = await this.inventory.list({
ids: binaryIds.join(','),
pageSize: 2000
});
return versions.map(version => {
const metadataForVersion = metadata.find(tmp => tmp.id === version.id) || {};
const { owner, lastUpdated } = metadataForVersion;
return Object.assign(version, { owner, lastUpdated });
});
}
/**
* Saves the provided branding as a new version for the public options app.
* The public options app can be optionally provided to avoid another request for it.
*/
async saveBranding(blob, version, tags = [], publicOptionsApp) {
const file = blob instanceof File ? blob : new File([blob], `public-options-${version}.zip`);
const publicOptions = publicOptionsApp || (await this.getPublicOptionsApp());
const { data: binary } = await this.applicationService
.binary(publicOptions)
.uploadApplicationVersion(file, version, tags);
if (!publicOptions.activeVersionId || tags.includes('latest')) {
await this.applicationService.update({
id: publicOptions.id,
activeVersionId: `${binary.binaryId}`
});
}
this.refreshTriggerBrandingVariants.next();
return { version, binary, tags };
}
/**
* Removes a branding version from the public options app.
* The public options app can be optionally provided to avoid another request for it.
*/
async deleteBrandingVersion(version, publicOptions) {
publicOptions = publicOptions || (await this.getPublicOptionsApp());
await this.applicationService.deleteVersionPackage(publicOptions, { version });
this.refreshTriggerBrandingVariants.next();
}
/**
* Returns the blob of a zip file containing the provided branding options (options.json) and the manifest (cumulocity.json).
*/
async getBrandingZip(content = {}) {
content.lastUpdated = new Date().toISOString();
const contentAsString = JSON.stringify(content);
const zip = await this.zip.createZip([
{ fileName: this.fileNames.options, blob: new Blob([contentAsString]) },
{ fileName: this.fileNames.manifest, blob: new Blob([JSON.stringify(this.manifestValues)]) }
]);
return zip;
}
/**
* Adds a new branding version to the public options app.
* The public options app can be optionally provided to avoid another request for it.
*/
async addBranding(version, content = {}, tags = [], publicOptionsApp) {
const zip = await this.getBrandingZip(content);
return await this.saveBranding(zip, version, tags, publicOptionsApp);
}
/**
* Returns the branding options for the provided version.
* If no branding was found (e.g. status 404), an error is thrown.
*/
async getBrandingOptionsForVersion(version) {
const url = `/apps/public/public-options${version ? `@${version}` : ''}/${this.fileNames.options}?nocache=${new Date().getTime()}`;
const response = await this.fetch.fetch(url);
if (response.status !== 200) {
throw Error(`Unexpected status code: ${response.status}`);
}
const content = await response.json();
return content;
}
/**
* Saves a new iteration of an already existing branding.
*/
async saveExistingBranding(branding, currentVersion, tagsOfCurrentVersion = [], newVersion) {
const publicOptions = await this.getPublicOptionsApp();
const versionToBeSet = newVersion
? this.brandingVersionService.createInitialBrandingVersion(newVersion)
: this.brandingVersionService.bumpBrandingIteration(currentVersion);
const finalTags = tagsOfCurrentVersion;
// only apply latest tag directly
// there seems to be some special handling for the latest tag in the backend, that allows to directly move it while uploading the new version.
// for all other tags this results in a 409 conflict. We therefore need to first delete the old version, before setting the other tags on the new version.
if (!newVersion) {
tagsOfCurrentVersion = tagsOfCurrentVersion.filter(tag => tag === 'latest');
}
await this.addBranding(versionToBeSet, branding, tagsOfCurrentVersion, publicOptions);
if (!newVersion) {
try {
await this.deleteBrandingVersion(currentVersion, publicOptions);
}
catch (e) {
if (e.res.status !== 404) {
throw e;
}
}
await this.applicationService.setPackageVersionTag(publicOptions, versionToBeSet, finalTags);
}
return versionToBeSet;
}
/**
* Combines current branding options with the provided branding variables and creates a new public options app.
* Any assets in the branding will be cloned.
*/
async createPublicOptionsAppFromInheritedOptions(brandingVars) {
let currentlyAppliedBranding;
let fallBackBranding = {};
try {
currentlyAppliedBranding = await this.getBrandingOptionsForVersion();
fallBackBranding = cloneDeep(currentlyAppliedBranding);
}
catch (e) {
console.warn('Failed to get currently applied branding, proceeding with empty branding.', e);
}
if (brandingVars) {
currentlyAppliedBranding = currentlyAppliedBranding || {};
const brandingCssVars = currentlyAppliedBranding.brandingCssVars || {};
Object.assign(brandingCssVars, brandingVars);
currentlyAppliedBranding.brandingCssVars = brandingCssVars;
}
if (currentlyAppliedBranding) {
try {
const { oldAssets, newAssets } = await this.staticAssets.cloneAssetsIntoTenant('branding');
currentlyAppliedBranding = this.replaceBrandingAssetsInBrandingOptions(currentlyAppliedBranding, oldAssets, newAssets);
}
catch (e) {
console.warn('Failed to relocate branding assets into current tenant.', e);
}
}
const publicOptionsApp = await this.createPublicOptionsApp();
const defaultBrandingName = `default`;
const result = await this.addBranding(this.brandingVersionService.createInitialBrandingVersion(defaultBrandingName), currentlyAppliedBranding || {}, ['latest', defaultBrandingName], publicOptionsApp);
const fallbackBrandingName = `fallback`;
await this.addBranding(this.brandingVersionService.createInitialBrandingVersion(fallbackBrandingName), fallBackBranding, [fallbackBrandingName], publicOptionsApp);
return result;
}
/**
* Replaces the assets in the branding options with the new assets.
* Goes through the provided `oldAssets` and replaces their occurrences in the branding with the corresponding `newAssets` entry sharing the same fileName.
* Returns the updated branding options.
*/
replaceBrandingAssetsInBrandingOptions(branding, oldAssets, newAssets) {
let brandingAsJSONString = JSON.stringify(branding);
for (const oldAsset of oldAssets) {
const newLocatedAsset = newAssets.find(tmp => tmp.fileName === oldAsset.fileName);
if (!newLocatedAsset) {
continue;
}
// if the path is the same, we don't need to replace it.
if (oldAsset.path === newLocatedAsset.path) {
continue;
}
// TODO: use proper replaceAll once it's available with es2021
brandingAsJSONString = brandingAsJSONString.split(oldAsset.path).join(newLocatedAsset.path);
}
return JSON.parse(brandingAsJSONString);
}
async getPublicOptionsApp() {
let { data: apps } = await this.applicationService.listByName(this.manifestValues.name);
apps = apps.filter(app => app.owner?.tenant?.id === this.appState.currentTenant.value?.name);
return apps[0];
}
async createPublicOptionsApp() {
const { data: app } = await this.applicationService.create(this.manifestValues);
return app;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StoreBrandingService, deps: [{ token: i1.ApplicationService }, { token: i1.InventoryService }, { token: i1.InventoryBinaryService }, { token: i2.ZipService }, { token: i1.FetchClient }, { token: i3.BrandingVersionService }, { token: i2.AppStateService }, { token: i4.StaticAssetsService }, { token: i2.ThemeSwitcherService }, { token: i5.BrandingTrackingService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StoreBrandingService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StoreBrandingService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1.ApplicationService }, { type: i1.InventoryService }, { type: i1.InventoryBinaryService }, { type: i2.ZipService }, { type: i1.FetchClient }, { type: i3.BrandingVersionService }, { type: i2.AppStateService }, { type: i4.StaticAssetsService }, { type: i2.ThemeSwitcherService }, { type: i5.BrandingTrackingService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmUtYnJhbmRpbmcuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2JyYW5kaW5nL3NoYXJlZC9kYXRhL3N0b3JlLWJyYW5kaW5nLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLGVBQWUsRUFDZixXQUFXLEVBSVgsc0JBQXNCLEVBQ3RCLGdCQUFnQixFQUNqQixNQUFNLGFBQWEsQ0FBQztBQUNyQixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDakQsT0FBTyxFQUFFLGVBQWUsRUFBRSxvQkFBb0IsRUFBRSxVQUFVLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RixPQUFPLEVBQUUsS0FBSyxFQUFFLGVBQWUsRUFBYyxNQUFNLE1BQU0sQ0FBQztBQUMxRCxPQUFPLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzVDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BFLE9BQU8sRUFBdUIsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5RixPQUFPLEVBQWUsbUJBQW1CLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUMxRixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQzs7Ozs7OztBQTJCdEU7O0dBRUc7QUFFSCxNQUFNLE9BQU8sb0JBQW9CO0lBZ0IvQixZQUNVLGtCQUFzQyxFQUN0QyxTQUEyQixFQUMzQixNQUE4QixFQUM5QixHQUFlLEVBQ2YsS0FBa0IsRUFDbEIsc0JBQThDLEVBQzlDLFFBQXlCLEVBQ3pCLFlBQWlDLEVBQ2pDLGFB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YmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBBcHBsaWNhdGlvbkF2YWlsYWJpbGl0eSxcbiAgQXBwbGljYXRpb25UeXBlLFxuICBGZXRjaENsaWVudCxcbiAgSUFwcGxpY2F0aW9uLFxuICBJQXBwbGljYXRpb25CaW5hcnksXG4gIElNYW5hZ2VkT2JqZWN0LFxuICBJbnZlbnRvcnlCaW5hcnlTZXJ2aWNlLFxuICBJbnZlbnRvcnlTZXJ2aWNlXG59IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7IEFwcGxpY2F0aW9uU2VydmljZSB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7IEFwcFN0YXRlU2VydmljZSwgVGhlbWVTd2l0Y2hlclNlcnZpY2UsIFppcFNlcnZpY2UgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzJztcbmltcG9ydCB7IGRlZmVyLCBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IG1hcCwgc3dpdGNoTWFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgY2xvbmVEZWVwLCB1bmlxIH0gZnJvbSAnbG9kYXNoLWVzJztcbmltcG9ydCB7IEJyYW5kaW5nVmVyc2lvblNlcnZpY2UgfSBmcm9tICcuL2JyYW5kaW5nLXZlcnNpb24uc2VydmljZSc7XG5pbXBvcnQgeyBCcmFuZGluZ09wdGlvbnNKc29uLCBhbGxCcmFuZGluZ0NTU1ZhcnMsIG51bWJlckJyYW5kaW5nVmFycyB9IGZyb20gJy4vYnJhbmRpbmcudHlwZSc7XG5pbXBvcnQgeyBTdGF0aWNBc3NldCwgU3RhdGljQXNzZXRzU2VydmljZSB9IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvc3RhdGljLWFzc2V0cy9kYXRhJztcbmltcG9ydCB7IEJyYW5kaW5nVHJhY2tpbmdTZXJ2aWNlIH0gZnJvbSAnLi9icmFuZGluZy10cmFja2luZy5zZXJ2aWNlJztcblxuZXhwb3J0IGludGVyZmFjZSBCcmFuZFZlcnNpb24ge1xuICBuYW1lOiBzdHJpbmc7XG4gIGlkOiBzdHJpbmc7XG4gIHJldmlzaW9uOiBudW1iZXI7XG4gIHZlcnNpb246IHN0cmluZztcbiAgdGFnczogc3RyaW5nW107XG4gIG93bmVyPzogc3RyaW5nO1xuICBsYXN0VXBkYXRlZD86IHN0cmluZztcbiAgcHVibGljT3B0aW9uc0FwcD86IElBcHBsaWNhdGlvbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBCcmFuZGluZ0ZpbGVEZXRhaWxzIHtcbiAgZmlsZU5hbWU6IHN0cmluZztcbiAgYmxvYjogQmxvYjtcbiAganNvbkNvbnRlbnQ/OiBhbnk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnJhbmRpbmdGaWxlRGV0YWlsc0xlZ2FjeSB7XG4gIHBhdGg6IHN0cmluZztcbiAgdmFsdWU6IHN0cmluZztcbiAgYmxvYj86IEJsb2I7XG4gIGZpbGVOYW1lOiBzdHJpbmc7XG4gIHVybFdyYXBwZWQ6IGJvb2xlYW47XG59XG5cbi8qKlxuICogU2VydmljZSB0byBsb2FkIGFuZCBzdG9yZSB0aGUgYnJhbmRpbmcgb2YgdGhlIHRlbmFudC5cbiAqL1xuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcbmV4cG9ydCBjbGFzcyBTdG9yZUJyYW5kaW5nU2VydmljZSB7XG4gIHJlYWRvbmx5IGZpbGVOYW1lcyA9IHtcbiAgICBvcHRpb25zOiAnb3B0aW9ucy5qc29uJyxcbiAgICBtYW5pZmVzdDogJ2N1bXVsb2NpdHkuanNvbicsXG4gICAgZXhwb3J0WmlwTmFtZTogJ3B1YmxpYy1vcHRpb25zLnppcCdcbiAgfSBhcyBjb25zdDtcbiAgcmVhZG9ubHkgbWFuaWZlc3RWYWx1ZXMgPSB7XG4gICAgbmFtZTogJ3B1YmxpYy1vcHRpb25zJyxcbiAgICBjb250ZXh0UGF0aDogJ3B1YmxpYy1vcHRpb25zJyxcbiAgICBrZXk6ICdwdWJsaWMtb3B0aW9ucy1rZXknLFxuICAgIGRlc2NyaXB0aW9uOiAnQXBwbGljYXRpb24gY29udGFpbmluZyBzdGF0aWMgYXNzZXRzIHVzZWQgYnkgZS5nLiBicmFuZGluZy4nLFxuICAgIG5vQXBwU3dpdGNoZXI6IHRydWUsXG4gICAgdHlwZTogQXBwbGljYXRpb25UeXBlLkhPU1RFRCxcbiAgICBhdmFpbGFiaWxpdHk6IEFwcGxpY2F0aW9uQXZhaWxhYmlsaXR5LlNIQVJFRFxuICB9IGFzIGNvbnN0IHNhdGlzZmllcyBJQXBwbGljYXRpb247XG4gIHJlZnJlc2hUcmlnZ2VyQnJhbmRpbmdWYXJpYW50cyA9IG5ldyBCZWhhdmlvclN1YmplY3Q8dm9pZD4obnVsbCk7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgYXBwbGljYXRpb25TZXJ2aWNlOiBBcHBsaWNhdGlvblNlcnZpY2UsXG4gICAgcHJpdmF0ZSBpbnZlbnRvcnk6IEludmVudG9yeVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBiaW5hcnk6IEludmVudG9yeUJpbmFyeVNlcnZpY2UsXG4gICAgcHJpdmF0ZSB6aXA6IFppcFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBmZXRjaDogRmV0Y2hDbGllbnQsXG4gICAgcHJpdmF0ZSBicmFuZGluZ1ZlcnNpb25TZXJ2aWNlOiBCcmFuZGluZ1ZlcnNpb25TZXJ2aWNlLFxuICAgIHByaXZhdGUgYXBwU3RhdGU6IEFwcFN0YXRlU2VydmljZSxcbiAgICBwcml2YXRlIHN0YXRpY0Fzc2V0czogU3RhdGljQXNzZXRzU2VydmljZSxcbiAgICBwcml2YXRlIHRoZW1lU3dpdGNoZXI6IFRoZW1lU3dpdGNoZXJTZXJ2aWNlLFxuICAgIHByaXZhdGUgYnJhbmRpbmdUcmFja2luZzogQnJhbmRpbmdUcmFja2luZ1NlcnZpY2VcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSBgbGF0ZXN0YCB0YWcgb24gdGhlIHByb3ZpZGVkIGJyYW5kaW5nIHZlcnNpb24uIE1ha2luZyBpdCB0aGUgZ2xvYmFsIGFjdGl2ZSBicmFuZGluZy5cbiAgICovXG4gIGFzeW5jIG1hcmtBc0FjdGl2ZShicmFuZGluZ1ZlcnNpb246IEJyYW5kVmVyc2lvbiwgYXBwPzogSUFwcGxpY2F0aW9uKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcHVibGljT3B0aW9ucyA9IGFwcCB8fCAoYXdhaXQgdGhpcy5nZXRQdWJsaWNPcHRpb25zQXBwKCkpO1xuICAgIGNvbnN0IHRhZ3MgPSBicmFuZGluZ1ZlcnNpb24udGFncyB8fCBbXTtcbiAgICBjb25zdCB2ZXJzaW9uID0gYnJhbmRpbmdWZXJzaW9uLnZlcnNpb247XG4gICAgYXdhaXQgdGhpcy5hcHBsaWNhdGlvblNlcnZpY2Uuc2V0UGFja2FnZVZlcnNpb25UYWcocHVibGljT3B0aW9ucywgdmVyc2lvbiwgWy4uLnRhZ3MsICdsYXRlc3QnXSk7XG4gIH1cblxuICAvKipcbiAgICogT3BlbnMgYSBuZXcgdGFiIHdpdGggdG8gcHJldmlldyB0aGUgYnJhbmRpbmcuIFRoZSBicmFuZGluZyBtdXN0IGhhdmUgYmVlbiBzYXZlZCBiZWZvcmVoYW5kLlxuICAgKiBAcGFyYW0gYnJhbmRpbmdOYW1lIHRoZSBuYW1lIG9mIHRoZSBicmFuZGluZyB0byBiZSBwcmV2aWV3ZWRcbiAgICovXG4gIG9wZW5QcmV2aWV3Rm9yQnJhbmRpbmcoYnJhbmRpbmdOYW1lOiBzdHJpbmcpIHtcbiAgICB0aGlzLmJyYW5kaW5nVHJhY2tpbmcub3BlblByZXZpZXdGb3JCcmFuZGluZygpO1xuICAgIHdpbmRvdy5vcGVuKFxuICAgICAgYC9hcHBzLyR7XG4gICAgICAgIHRoaXMuYXBwU3RhdGUuY3VycmVudEFwcGxpY2F0aW9uLnZhbHVlPy5jb250ZXh0UGF0aCB8fCAnY29ja3BpdCdcbiAgICAgIH0vaW5kZXguaHRtbD9icmFuZGluZ1ByZXZpZXc9dHJ1ZSZkeW5hbWljT3B0aW9uc1VybD0vYXBwcy9wdWJsaWMvcHVibGljLW9wdGlvbnNAJHticmFuZGluZ05hbWV9L29wdGlvbnMuanNvbmAsXG4gICAgICAnX2JsYW5rJyxcbiAgICAgICdub29wZW5lcixub3JlZmVycmVyJ1xuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgYnJhbmRpbmdzIG9mIHRoZSB0ZW5hbnQuXG4gICAqIElmIG5vIHB1YmxpYyBvcHRpb25zIGFwcCBpcyBmb3VuZCwgcHVibGljT3B0aW9ucyB3aWxsIGJlIHVuZGVmaW5lZCBhbmQgdmFyaWFudHMgYW4gZW1wdHkgYXJyYXkuXG4gICAqIEZvciBvbGQgYnJhbmRpbmdzIChjcmVhdGVkIHdpdGhvdXQgdmVyc2lvbmluZykgYSBkZWZhdWx0IHZlcnNpb24gaXMgcmV0dXJuZWQuXG4gICAqL1xuICBhc3luYyBsb2FkQnJhbmRpbmdWYXJpYW50cyhcbiAgICBhcHA/OiBJQXBwbGljYXRpb25cbiAgKTogUHJvbWlzZTx7IHB1YmxpY09wdGlvbnM6IElBcHBsaWNhdGlvbiB8IHVuZGVmaW5lZDsgdmFyaWFudHM6IEJyYW5kVmVyc2lvbltdIH0+IHtcbiAgICBjb25zdCBwdWJsaWNPcHRpb25zID0gYXBwIHx8IChhd2FpdCB0aGlzLmdldFB1YmxpY09wdGlvbnNBcHAoKSk7XG4gICAgaWYgKCFwdWJsaWNPcHRpb25zKSB7XG4gICAgICByZXR1cm4geyBwdWJsaWNPcHRpb25zOiB1bmRlZmluZWQsIHZhcmlhbnRzOiBbXSB9O1xuICAgIH1cbiAgICBjb25zdCB7IGRhdGE6IHZlcnNpb25zIH0gPSBhd2FpdCB0aGlzLmFwcGxpY2F0aW9uU2VydmljZS5saXN0VmVyc2lvbnMocHVibGljT3B0aW9ucyk7XG4gICAgbGV0IG1hcHBlZFZlcnNpb25zOiBCcmFuZFZlcnNpb25bXSA9XG4gICAgICB2ZXJzaW9uc1xuICAgICAgICA/Lm1hcCh0bXAgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB7IG5hbWUsIGl0ZXJhdGlvbiB9ID1cbiAgICAgICAgICAgICAgdGhpcy5icmFuZGluZ1ZlcnNpb25TZXJ2aWNlLnNwbGl0QnJhbmRpbmdJbnRvTmFtZUFuZEl0ZXJhdGlvbih0bXAudmVyc2lvbik7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICByZXZpc2lvbjogaXRlcmF0aW9uLFxuICAgICAgICAgICAgICBuYW1lOiBuYW1lLFxuICAgICAgICAgICAgICBpZDogdG1wLmJpbmFyeUlkLFxuICAgICAgICAgICAgICB2ZXJzaW9uOiB0bXAudmVyc2lvbixcbiAgICAgICAgICAgICAgdGFnczogdG1wLnRhZ3MgfHwgW10sXG4gICAgICAgICAgICAgIHB1YmxpY09wdGlvbnNBcHA6IHB1YmxpY09wdGlvbnNcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKCdGYWlsZWQgdG8gcGFyc2UgdmVyc2lvbicsIHRtcCwgZSk7XG4gICAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgPy5maWx0ZXIoQm9vbGVhbikgfHwgW107XG4gICAgaWYgKCF2ZXJzaW9ucyAmJiBwdWJsaWNPcHRpb25zLmFjdGl2ZVZlcnNpb25JZCkge1xuICAgICAgbWFwcGVkVmVyc2lvbnMgPSBbXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAnZGVmYXVsdCcsXG4gICAgICAgICAgaWQ6IHB1YmxpY09wdGlvbnMuYWN0aXZlVmVyc2lvbklkLFxuICAgICAgICAgIHZlcnNpb246ICdkZWZhdWx0LTEnLFxuICAgICAgICAgIHJldmlzaW9uOiAxLFxuICAgICAgICAgIHRhZ3M6IFsnbGF0ZXN0JywgJ2RlZmF1bHQnXSxcbiAgICAgICAgICBwdWJsaWNPcHRpb25zQXBwOiBwdWJsaWNPcHRpb25zXG4gICAgICAgIH1cbiAgICAgIF07XG4gICAgfVxuXG4gICAgY29uc3QgdmFyaWFudHMgPSBhd2FpdCB0aGlzLmdldE1ldGFkYXRhT2ZCcmFuZGluZ0JpbmFyaWVzKG1hcHBlZFZlcnNpb25zKTtcbiAgICByZXR1cm4geyBwdWJsaWNPcHRpb25zLCB2YXJpYW50cyB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEFzIHRoZSBicmFuZGluZyBpcyBub3QgaW1tZWRpYXRlbHkgYXZhaWxhYmxlIGFmdGVyIGNyZWF0aW9uLCB0aGlzIG1ldGhvZCB3aWxsIHdhaXQgZm9yIHRoZSBicmFuZGluZyB0byBiZSBwcmVzZW50LlxuICAgKiBAcGFyYW0gdmVyc2lvbiBUaGUgdmVyc2lvbiBvZiB0aGUgYnJhbmRpbmcgdG8gYmUgcmV0cmlldmVkLlxuICAgKi9cbiAgYXN5bmMgd2FpdEZvckJyYW5kaW5nVG9CZVByZXNlbnQodmVyc2lvbjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbnVtYmVyT2ZBdHRlbXB0cyA9IDEyMDtcbiAgICBjb25zdCBzbGVlcER1cmF0aW9uSW5TZWNvbmRzID0gMTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG51bWJlck9mQXR0ZW1wdHM7IGkrKykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gZG8gbm90IHNsZWVwIGJlZm9yZSB0aGUgZmlyc3QgYXR0ZW1wdFxuICAgICAgICBpZiAoaSAhPT0gMCkge1xuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCBzbGVlcER1cmF0aW9uSW5TZWNvbmRzICogMV8wMDApKTtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB0aGlzLmdldEJyYW5kaW5nT3B0aW9uc0ZvclZlcnNpb24odmVyc2lvbik7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQnJhbmRpbmcgXCIke3ZlcnNpb259XCIgYXZhaWxhYmxlIG5vdy5gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgYEJyYW5kaW5nIFwiJHt2ZXJzaW9ufVwiIG5vdCB5ZXQgcHJlc2VudCwgcmV0cnlpbmcgaW4gJHtzbGVlcER1cmF0aW9uSW5TZWNvbmRzfSBzZWNvbmRzLi4uYCxcbiAgICAgICAgICBlXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBCcmFuZGluZyBcIiR7dmVyc2lvbn1cIiBub3QgYXZhaWxhYmxlIGFmdGVyICR7bnVtYmVyT2ZBdHRlbXB0cyAqIHNsZWVwRHVyYXRpb25JblNlY29uZHN9IHNlY29uZHMsIGdpdmluZyB1cC5gXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaWxsIGNyZWF0ZSBhIHRoZSBpbml0aWFsIGJyYW5kaW5nIGJhc2VkIG9uIHRoZSBjdXJyZW50bHkgYXBwbGllZCBDU1MgdmFyaWFibGVzLlxuICAgKi9cbiAgYXN5bmMgZ2V0U3RhcnRlZFVzaW5nQnJhbmRpbmcoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgYWxsVmFyaWFibGVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgY29uc3QgdGhlbWVUb1JldHJpZXZlVmFycyA9IFsnbGlnaHQnLCAnZGFyayddIGFzIGNvbnN0O1xuICAgIGNvbnN0IHVucmV0cmlldmFibGVWYXJzID0gbmV3IEFycmF5PHN0cmluZz4oKTtcbiAgICBmb3IgKGNvbnN0IHRoZW1lIG9mIHRoZW1lVG9SZXRyaWV2ZVZhcnMpIHtcbiAgICAgIHRoaXMudGhlbWVTd2l0Y2hlci50ZW1wb3JhcnlDaGFuZ2VUaGVtZSh0aGVtZSk7XG4gICAgICBjb25zdCBzdHlsZXMgPSBnZXRDb21wdXRlZFN0eWxlKGRvY3VtZW50LmJvZHkpO1xuICAgICAgY29uc3QgdmFyaWFibGVzVG9Ta2lwID0gW1xuICAgICAgICAnbmF2aWdhdG9yLXBsYXRmb3JtLWxvZ28nLFxuICAgICAgICAnYnJhbmQtbG9nby1pbWcnXG4gICAgICBdIHNhdGlzZmllcyAodHlwZW9mIGFsbEJyYW5kaW5nQ1NTVmFycylbbnVtYmVyXVtdO1xuICAgICAgY29uc3QgdmFsdWVzID0gYWxsQnJhbmRpbmdDU1NWYXJzXG4gICAgICAgIC5maWx0ZXIoY3NzVmFyID0+ICF2YXJpYWJsZXNUb1NraXAuaW5jbHVkZXMoY3NzVmFyIGFzIGFueSkpXG4gICAgICAgIC5yZWR1Y2UoKHByZXYsIGtleSkgPT4ge1xuICAgICAgICAgIGNvbnN0IGRpcmVjdFZhbHVlID0gc3R5bGVzLmdldFByb3BlcnR5VmFsdWUoYC0tJHtrZXl9YCk7XG4gICAgICAgICAgY29uc3QgYzh5UHJlZml4ZWRWYWx1ZSA9IHN0eWxlcy5nZXRQcm9wZXJ0eVZhbHVlKGAtLWM4eS0ke2tleX1gKTtcbiAgICAgICAgICBjb25zdCBuZXdLZXkgPSB0aGVtZSA9PT0gJ2xpZ2h0JyA/IGtleSA6IGBkYXJrLSR7a2V5fWA7XG4gICAgICAgICAgaWYgKCFkaXJlY3RWYWx1ZSAmJiAhYzh5UHJlZml4ZWRWYWx1ZSkge1xuICAgICAgICAgICAgdW5yZXRyaWV2YWJsZVZhcnMucHVzaChuZXdLZXkpO1xuICAgICAgICAgICAgcmV0dXJuIHByZXY7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxldCB2YWx1ZTogc3RyaW5nIHwgbnVtYmVyID0gZGlyZWN0VmFsdWUgfHwgYzh5UHJlZml4ZWRWYWx1ZTtcbiAgICAgICAgICBpZiAobnVtYmVyQnJhbmRpbmdWYXJzLmluY2x1ZGVzKGtleSBhcyBhbnkpKSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICB2YWx1ZSA9IE51bWJlci5wYXJzZUZsb2F0KHZhbHVlLnJlcGxhY2UoL1tBLVphLXpdL2csICcnKS50cmltKCkpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgICAgICAgYEZhaWxlZCB0byBwYXJzZSBudW1iZXIgZm9yIFwiJHtrZXl9XCIgdmFsdWUgdGhhdCBmYWlsZWQgdG8gcGFyc2U6IFwiJHt2YWx1ZX1cImAsXG4gICAgICAgICAgICAgICAgZVxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICByZXR1cm4gcHJldjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIE9iamVjdC5hc3NpZ24ocHJldiwgeyBbbmV3S2V5XTogdmFsdWUgfSk7XG4gICAgICAgIH0sIHt9KTtcbiAgICAgIE9iamVjdC5hc3NpZ24oYWxsVmFyaWFibGVzLCB2YWx1ZXMpO1xuICAgIH1cbiAgICB0aGlzLnRoZW1lU3dpdGNoZXIucmVzZXRUZW1wb3JhcnlUaGVtZSgpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuY3JlYXRlUHVibGljT3B0aW9uc0FwcEZyb21Jbmhlcml0ZWRPcHRpb25zKGFsbFZhcmlhYmxlcyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS53YXJuKGUpO1xuICAgIH1cbiAgfVxuXG4gIGdldFppcEZvckJpbmFyeShcbiAgICBiaW5hcnlJZDogc3RyaW5nLFxuICAgIGZpbGVOYW1lOiBzdHJpbmcgPSB0aGlzLmZpbGVOYW1lcy5leHBvcnRaaXBOYW1lXG4gICk6IE9ic2VydmFibGU8RmlsZT4ge1xuICAgIHJldHVybiBkZWZlcigoKSA9PiB0aGlzLmJpbmFyeS5kb3dubG9hZChiaW5hcnlJZCkpLnBpcGUoXG4gICAgICBzd2l0Y2hNYXAocmVzcG9uc2UgPT4gcmVzcG9uc2UuYmxvYigpKSxcbiAgICAgIG1hcChibG9iID0+IG5ldyBGaWxlKFtibG9iXSwgZmlsZU5hbWUpKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlcyB0aGUgcHVibGljIG9wdGlvbnMgYXBwIGFuZCB0aGVyZWZvcmUgYWxsIGJyYW5kaW5ncy5cbiAgICogVGhlIHB1YmxpYyBvcHRpb25zIGFwcCBjYW4gYmUgb3B0aW9uYWxseSBwcm92aWRlZCB0byBhdm9pZCBhbm90aGVyIHJlcXVlc3QgZm9yIGl0LlxuICAgKi9cbiAgYXN5bmMgZGVsZXRlQWxsQnJhbmRpbmdzKHB1YmxpY09wdGlvbnM/OiBJQXBwbGljYXRpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBwdWJsaWNPcHRpb25zID0gcHVibGljT3B0aW9ucyB8fCAoYXdhaXQgdGhpcy5nZXRQdWJsaWNPcHRpb25zQXBwKCkpO1xuICAgIGF3YWl0IHRoaXMuYXBwbGljYXRpb25TZXJ2aWNlLmRlbGV0ZShwdWJsaWNPcHRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmhhbmNlcyB0aGUgcHJvdmlkZWQgYnJhbmRpbmcgdmVyc2lvbnMgd2l0aCBtZXRhZGF0YSBmcm9tIHRoZSBsaW5rZWQgYmluYXJpZXMuXG4gICAqIEl0IHdpbGwgYWRkIHRoZSBvd25lciBhbmQgbGFzdFVwZGF0ZWQgZmllbGRzIHRvIHRoZSB2ZXJzaW9ucy5cbiAgICogVGhlIHByb3ZpZGVkIGFycmF5IGlzIGFsdGVyZWQuXG4gICAqL1xuICBhc3luYyBnZXRNZXRhZGF0YU9mQnJhbmRpbmdCaW5hcmllcyh2ZXJzaW9uczogQnJhbmRWZXJzaW9uW10pOiBQcm9taXNlPEJyYW5kVmVyc2lvbltdPiB7XG4gICAgY29uc3QgYmluYXJ5SWRzID0gdW5pcSh2ZXJzaW9ucy5tYXAodG1wID0+IHRtcC5pZCkpO1xuICAgIGlmICghYmluYXJ5SWRzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHZlcnNpb25zO1xuICAgIH1cbiAgICBjb25zdCB7IGRhdGE6IG1ldGFkYXRhIH0gPSBhd2FpdCB0aGlzLmludmVudG9yeS5saXN0KHtcbiAgICAgIGlkczogYmluYXJ5SWRzLmpvaW4oJywnKSxcbiAgICAgIHBhZ2VTaXplOiAyMDAwXG4gICAgfSk7XG4gICAgcmV0dXJuIHZlcnNpb25zLm1hcCh2ZXJzaW9uID0+IHtcbiAgICAgIGNvbnN0IG1ldGFkYXRhRm9yVmVyc2lvbiA9XG4gICAgICAgIG1ldGFkYXRhLmZpbmQodG1wID0+IHRtcC5pZCA9PT0gdmVyc2lvbi5pZCkgfHwgKHt9IGFzIElNYW5hZ2VkT2JqZWN0KTtcbiAgICAgIGNvbnN0IHsgb3duZXIsIGxhc3RVcGRhdGVkIH0gPSBtZXRhZGF0YUZvclZlcnNpb247XG4gICAgICByZXR1cm4gT2JqZWN0LmFzc2lnbih2ZXJzaW9uLCB7IG93bmVyLCBsYXN0VXBkYXRlZCB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYXZlcyB0aGUgcHJvdmlkZWQgYnJhbmRpbmcgYXMgYSBuZXcgdmVyc2lvbiBmb3IgdGhlIHB1YmxpYyBvcHRpb25zIGFwcC5cbiAgICogVGhlIHB1YmxpYyBvcHRpb25zIGFwcCBjYW4gYmUgb3B0aW9uYWxseSBwcm92aWRlZCB0byBhdm9pZCBhbm90aGVyIHJlcXVlc3QgZm9yIGl0LlxuICAgKi9cbiAgYXN5bmMgc2F2ZUJyYW5kaW5nKFxuICAgIGJsb2I6IEJsb2IgfCBGaWxlLFxuICAgIHZlcnNpb246IHN0cmluZyxcbiAgICB0YWdzOiBzdHJpbmdbXSA9IFtdLFxuICAgIHB1YmxpY09wdGlvbnNBcHA/OiBJQXBwbGljYXRpb25cbiAgKTogUHJvbWlzZTx7XG4gICAgdmVyc2lvbjogc3RyaW5nO1xuICAgIGJpbmFyeTogSUFwcGxpY2F0aW9uQmluYXJ5O1xuICAgIHRhZ3M6IHN0cmluZ1tdO1xuICB9PiB7XG4gICAgY29uc3QgZmlsZSA9IGJsb2IgaW5zdGFuY2VvZiBGaWxlID8gYmxvYiA6IG5ldyBGaWxlKFtibG9iXSwgYHB1YmxpYy1vcHRpb25zLSR7dmVyc2lvbn0uemlwYCk7XG4gICAgY29uc3QgcHVibGljT3B0aW9ucyA9IHB1YmxpY09wdGlvbnNBcHAgfHwgKGF3YWl0IHRoaXMuZ2V0UHVibGljT3B0aW9uc0FwcCgpKTtcblxuICAgIGNvbnN0IHsgZGF0YTogYmluYXJ5IH0gPSBhd2FpdCB0aGlzLmFwcGxpY2F0aW9uU2VydmljZVxuICAgICAgLmJpbmFyeShwdWJsaWNPcHRpb25zKVxuICAgICAgLnVwbG9hZEFwcGxpY2F0aW9uVmVyc2lvbihmaWxlLCB2ZXJzaW9uLCB0YWdzKTtcbiAgICBpZiAoIXB1YmxpY09wdGlvbnMuYWN0aXZlVmVyc2lvbklkIHx8IHRhZ3MuaW5jbHVkZXMoJ2xhdGVzdCcpKSB7XG4gICAgICBhd2FpdCB0aGlzLmFwcGxpY2F0aW9uU2VydmljZS51cGRhdGUoe1xuICAgICAgICBpZDogcHVibGljT3B0aW9ucy5pZCxcbiAgICAgICAgYWN0aXZlVmVyc2lvbklkOiBgJHtiaW5hcnkuYmluYXJ5SWR9YFxuICAgICAgfSk7XG4gICAgfVxuICAgIHRoaXMucmVmcmVzaFRyaWdnZXJCcmFuZGluZ1ZhcmlhbnRzLm5leHQoKTtcbiAgICByZXR1cm4geyB2