UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

374 lines 59.8 kB
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