UNPKG

@nova-ui/dashboards

Version:

Nova Dashboards is a framework designed to provide feature developers with a common solution for presenting data coming from various sources within a single view, as well as a set of predefined widget visualizations that are 100% configuration-driven and

153 lines 33.1 kB
// © 2022 SolarWinds Worldwide, LLC. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, Injector, Input, Output, } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { Subject } from "rxjs"; import { take } from "rxjs/operators"; import { EventBus, LoggerService } from "@nova-ui/bits"; import { ProviderRegistryService } from "../../../../../services/provider-registry.service"; import { PIZZAGNA_EVENT_BUS, } from "../../../../../types"; import { ConfiguratorHeadingService } from "../../../../services/configurator-heading.service"; import { DATA_SOURCE_CHANGE, DATA_SOURCE_CREATED, DATA_SOURCE_OUTPUT, } from "../../../../types"; import { DataSourceErrorComponent } from "../data-source-error/data-source-error.component"; import * as i0 from "@angular/core"; import * as i1 from "../../../../services/configurator-heading.service"; import * as i2 from "@angular/forms"; import * as i3 from "../../../../../services/provider-registry.service"; import * as i4 from "@nova-ui/bits"; import * as i5 from "@angular/common"; import * as i6 from "@angular/cdk/portal"; import * as i7 from "../../../../../pizzagna/directives/component-portal/component-portal.directive"; import * as i8 from "../../../widget-editor-accordion/widget-editor-accordion.component"; import * as i9 from "../../../widget-editor-accordion/widget-editor-accordion-header/widget-editor-accordion-header.component"; import * as i10 from "../../../../pipe/widget-editor-accordion-form-state.pipe"; import * as i11 from "../../../../pipe/widget-editor-accordion-header-icon.pipe"; /** * This is a basic implementation of a data source configuration component. In the real world scenario, this component will most likely be replaced by a * custom one, but still can be used as a template for developing a custom specific solution. */ export class DataSourceConfigurationComponent { static { this.lateLoadKey = "DataSourceConfigurationComponent"; } constructor(changeDetector, configuratorHeading, formBuilder, providerRegistryService, eventBus, injector, logger) { this.changeDetector = changeDetector; this.configuratorHeading = configuratorHeading; this.formBuilder = formBuilder; this.providerRegistryService = providerRegistryService; this.eventBus = eventBus; this.injector = injector; this.logger = logger; /** * This component shows a dropdown with options for selecting a data source, this input represents these options. */ this.dataSourceProviders = []; this.errorComponent = DataSourceErrorComponent.lateLoadKey; this.formReady = new EventEmitter(); this.hasDataSourceError = false; // used by the Broadcaster this.dsOutput = new Subject(); this.dataFieldIds = new Subject(); } ngOnInit() { this.form = this.formBuilder.group({ providerId: [this.providerId || "", [Validators.required]], properties: this.formBuilder.group(this.properties || {}), }); this.form.setValidators([ () => (this.hasDataSourceError ? { dataSourceError: true } : null), ]); this.formReady.emit(this.form); } ngOnChanges(changes) { if (changes.providerId && !changes.providerId.isFirstChange()) { const previousValue = changes.providerId.previousValue; if (previousValue !== this.providerId) { this.form.get("providerId")?.setValue(this.providerId); this.invokeDataSource(this.providerId); } } } onDataSourceChange(providerId) { this.eventBus.next(DATA_SOURCE_CHANGE, {}); this.invokeDataSource(providerId); } /** * The data source is invoked here to notify the rest of the form about changes in the data source output. * DATA_SOURCE_OUTPUT event is emitted through the event bus carrying the data source result as the payload. * This might not be necessary in every situation. If the important information is already stored in the * data source properties, this step can be omitted. * * @param providerId */ invokeDataSource(providerId) { if (!providerId) { return; } const provider = this.providerRegistryService.getProvider(providerId); if (provider) { this.dataSource = this.providerRegistryService.getProviderInstance(provider, this.injector); this.eventBus.next(DATA_SOURCE_CREATED, { payload: this.dataSource, }); this.dataSource.outputsSubject .pipe(take(1)) .subscribe((result) => { this.eventBus.next(DATA_SOURCE_OUTPUT, { payload: result }); this.dsOutput.next(result); const dataFieldIdsResult = result?.result || result; if (dataFieldIdsResult) { this.dataFieldIds.next(Object.keys(dataFieldIdsResult)); } }); // This setTimeout is because the output of the data source might come faster than the data-source-error-component is initiated setTimeout(() => { this.dataSource.applyFilters(); }); } else { this.logger.warn("No provider found for id:", providerId); } } onErrorState(isError) { this.hasDataSourceError = isError; this.form.markAsTouched({ onlySelf: true }); this.form.updateValueAndValidity({ emitEvent: false }); this.changeDetector.detectChanges(); } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DataSourceConfigurationComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.ConfiguratorHeadingService }, { token: i2.FormBuilder }, { token: i3.ProviderRegistryService }, { token: PIZZAGNA_EVENT_BUS }, { token: i0.Injector }, { token: i4.LoggerService }], target: i0.ɵɵFactoryTarget.Component }); } /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: DataSourceConfigurationComponent, selector: "nui-data-source-configuration", inputs: { dataSourceProviders: "dataSourceProviders", errorComponent: "errorComponent", properties: "properties", providerId: "providerId" }, outputs: { formReady: "formReady" }, usesOnChanges: true, ngImport: i0, template: "<nui-widget-editor-accordion\n [formGroup]=\"form\"\n [state]=\"form | nuiWidgetEditorAccordionFormState | async\"\n>\n <nui-widget-editor-accordion-header\n [headerIcon]=\"form | nuiFormHeaderIconPipe : 'database' | async\"\n [subtitle]=\"form.get('providerId')?.value\"\n headerTitle=\"Data Source\"\n />\n <div class=\"datasource-configuration__accordion-content\">\n <nui-form-field\n caption=\"Select Data Source\"\n i18n-caption\n [control]=\"form.get('providerId')\"\n >\n <nui-select-v2\n placeholder=\"Data Sources\"\n i18n-placeholder\n [title]=\"form.get('providerId')?.value\"\n formControlName=\"providerId\"\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n (valueSelected)=\"onDataSourceChange($event)\"\n class=\"datasource-configuration__accordion-content__datasource-input\"\n >\n <nui-select-v2-option\n *ngFor=\"let dataSourceProvider of dataSourceProviders\"\n [title]=\"dataSourceProvider\"\n [value]=\"dataSourceProvider\"\n >\n {{ dataSourceProvider }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n <ng-container\n nuiComponentPortal\n [componentId]=\"errorComponent\"\n [componentType]=\"errorComponent\"\n [outputs]=\"['errorState']\"\n [properties]=\"{ dataSource: dataSource }\"\n (output)=\"onErrorState($event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n >\n </ng-template>\n </ng-container>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".datasource-configuration__accordion-content{padding:15px 15px 15px 46px}\n"], dependencies: [{ kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i6.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: i7.ComponentPortalDirective, selector: "[nuiComponentPortal]", inputs: ["componentId", "componentType", "properties", "providers", "outputs"], outputs: ["output"], exportAs: ["nuiComponentPortal"] }, { kind: "component", type: i4.FormFieldComponent, selector: "nui-form-field", inputs: ["control", "caption", "info", "infoTemplate", "hint", "hintTemplate", "customBoxWidth", "showOptionalText"] }, { kind: "component", type: i4.SelectV2Component, selector: "nui-select-v2", inputs: ["displayValueTemplate"] }, { kind: "component", type: i4.SelectV2OptionComponent, selector: "nui-select-v2-option", inputs: ["value", "index", "displayValueContext", "outfiltered"] }, { kind: "component", type: i8.WidgetEditorAccordionComponent, selector: "nui-widget-editor-accordion", inputs: ["showOpenStateIcon", "state"], outputs: ["openToggle"] }, { kind: "component", type: i9.WidgetEditorAccordionHeaderComponent, selector: "nui-widget-editor-accordion-header", inputs: ["headerIcon", "iconColor", "subtitle", "headerTitle"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i10.WidgetEditorAccordionFormStatePipe, name: "nuiWidgetEditorAccordionFormState" }, { kind: "pipe", type: i11.FormHeaderIconPipePipe, name: "nuiFormHeaderIconPipe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DataSourceConfigurationComponent, decorators: [{ type: Component, args: [{ selector: "nui-data-source-configuration", changeDetection: ChangeDetectionStrategy.OnPush, template: "<nui-widget-editor-accordion\n [formGroup]=\"form\"\n [state]=\"form | nuiWidgetEditorAccordionFormState | async\"\n>\n <nui-widget-editor-accordion-header\n [headerIcon]=\"form | nuiFormHeaderIconPipe : 'database' | async\"\n [subtitle]=\"form.get('providerId')?.value\"\n headerTitle=\"Data Source\"\n />\n <div class=\"datasource-configuration__accordion-content\">\n <nui-form-field\n caption=\"Select Data Source\"\n i18n-caption\n [control]=\"form.get('providerId')\"\n >\n <nui-select-v2\n placeholder=\"Data Sources\"\n i18n-placeholder\n [title]=\"form.get('providerId')?.value\"\n formControlName=\"providerId\"\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n (valueSelected)=\"onDataSourceChange($event)\"\n class=\"datasource-configuration__accordion-content__datasource-input\"\n >\n <nui-select-v2-option\n *ngFor=\"let dataSourceProvider of dataSourceProviders\"\n [title]=\"dataSourceProvider\"\n [value]=\"dataSourceProvider\"\n >\n {{ dataSourceProvider }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n <ng-container\n nuiComponentPortal\n [componentId]=\"errorComponent\"\n [componentType]=\"errorComponent\"\n [outputs]=\"['errorState']\"\n [properties]=\"{ dataSource: dataSource }\"\n (output)=\"onErrorState($event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n >\n </ng-template>\n </ng-container>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".datasource-configuration__accordion-content{padding:15px 15px 15px 46px}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.ConfiguratorHeadingService }, { type: i2.FormBuilder }, { type: i3.ProviderRegistryService }, { type: i4.EventBus, decorators: [{ type: Inject, args: [PIZZAGNA_EVENT_BUS] }] }, { type: i0.Injector }, { type: i4.LoggerService }], propDecorators: { dataSourceProviders: [{ type: Input }], errorComponent: [{ type: Input }], properties: [{ type: Input }], providerId: [{ type: Input }], formReady: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1zb3VyY2UtY29uZmlndXJhdGlvbi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldHMvY29uZmlndXJhdG9yLWl0ZW1zL2RhdGEtc291cmNlLWNvbmZpZ3VyYXRpb24vZGF0YS1zb3VyY2UtY29uZmlndXJhdGlvbi5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldHMvY29uZmlndXJhdG9yLWl0ZW1zL2RhdGEtc291cmNlLWNvbmZpZ3VyYXRpb24vZGF0YS1zb3VyY2UtY29uZmlndXJhdGlvbi5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx5REFBeUQ7QUFDekQsRUFBRTtBQUNGLCtFQUErRTtBQUMvRSw0RUFBNEU7QUFDNUUsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSw4RUFBOEU7QUFDOUUsNERBQTREO0FBQzVELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsdURBQXVEO0FBQ3ZELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsNEVBQTRFO0FBQzVFLCtFQUErRTtBQUMvRSwwRUFBMEU7QUFDMUUsaUZBQWlGO0FBQ2pGLDZFQUE2RTtBQUM3RSxpQkFBaUI7QUFFakIsT0FBTyxFQUNILHVCQUF1QixFQUN2QixpQkFBaUIsRUFDakIsU0FBUyxFQUNULFlBQVksRUFDWixNQUFNLEVBQ04sUUFBUSxFQUNSLEtBQUssRUFHTCxNQUFNLEdBRVQsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFdBQVcsRUFBYSxVQUFVLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxnQkFBZ0Is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QUU7Z0JBQ1osSUFBSSxDQUFDLFVBQVUsQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUNuQyxDQUFDLENBQUMsQ0FBQztTQUNOO2FBQU07WUFDSCxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQywyQkFBMkIsRUFBRSxVQUFVLENBQUMsQ0FBQztTQUM3RDtJQUNMLENBQUM7SUFFTSxZQUFZLENBQUMsT0FBZ0I7UUFDaEMsSUFBSSxDQUFDLGtCQUFrQixHQUFHLE9BQU8sQ0FBQztRQUNsQyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzVDLElBQUksQ0FBQyxJQUFJLENBQUMsc0JBQXNCLENBQUMsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUN2RCxJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsRUFBRSxDQUFDO0lBQ3hDLENBQUM7a0lBMUdRLGdDQUFnQywrSkE2QjdCLGtCQUFrQjtzSEE3QnJCLGdDQUFnQyw2UUNqRTdDLDAvREFrREE7OzRGRGVhLGdDQUFnQztrQkFONUMsU0FBUzsrQkFDSSwrQkFBK0IsbUJBR3hCLHVCQUF1QixDQUFDLE1BQU07OzBCQStCMUMsTUFBTTsyQkFBQyxrQkFBa0I7NEZBckJyQixtQkFBbUI7c0JBQTNCLEtBQUs7Z0JBQ0csY0FBYztzQkFBdEIsS0FBSztnQkFFRyxVQUFVO3NCQUFsQixLQUFLO2dCQUNHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBRUksU0FBUztzQkFBbEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQge1xuICAgIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICAgIENoYW5nZURldGVjdG9yUmVmLFxuICAgIENvbXBvbmVudCxcbiAgICBFdmVudEVtaXR0ZXIsXG4gICAgSW5qZWN0LFxuICAgIEluamVjdG9yLFxuICAgIElucHV0LFxuICAgIE9uQ2hhbmdlcyxcbiAgICBPbkluaXQsXG4gICAgT3V0cHV0LFxuICAgIFNpbXBsZUNoYW5nZXMsXG59IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQgeyBGb3JtQnVpbGRlciwgRm9ybUdyb3VwLCBWYWxpZGF0b3JzIH0gZnJvbSBcIkBhbmd1bGFyL2Zvcm1zXCI7XG5pbXBvcnQgeyBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IHRha2UgfSBmcm9tIFwicnhqcy9vcGVyYXRvcnNcIjtcblxuaW1wb3J0IHsgRXZlbnRCdXMsIElEYXRhU291cmNlLCBJRXZlbnQsIExvZ2dlclNlcnZpY2UgfSBmcm9tIFwiQG5vdmEtdWkvYml0c1wiO1xuXG5pbXBvcnQgeyBJRGF0YVNvdXJjZU91dHB1dCB9IGZyb20gXCIuLi8uLi8uLi8uLi8uLi9jb21wb25lbnRzL3Byb3ZpZGVycy90eXBlc1wiO1xuaW1wb3J0IHsgUHJvdmlkZXJSZWdpc3RyeVNlcnZpY2UgfSBmcm9tIFwiLi4vLi4vLi4vLi4vLi4vc2VydmljZXMvcHJvdmlkZXItcmVnaXN0cnkuc2VydmljZVwiO1xuaW1wb3J0IHtcbiAgICBJSGFzQ2hhbmdlRGV0ZWN0b3IsXG4gICAgSUhhc0Zvcm0sXG4gICAgSVByb3BlcnRpZXMsXG4gICAgUElaWkFHTkFfRVZFTlRfQlVTLFxufSBmcm9tIFwiLi4vLi4vLi4vLi4vLi4vdHlwZXNcIjtcbmltcG9ydCB7IENvbmZpZ3VyYXRvckhlYWRpbmdTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uLy4uL3NlcnZpY2VzL2NvbmZpZ3VyYXRvci1oZWFkaW5nLnNlcnZpY2VcIjtcbmltcG9ydCB7XG4gICAgREFUQV9TT1VSQ0VfQ0hBTkdFLFxuICAgIERBVEFfU09VUkNFX0NSRUFURUQsXG4gICAgREFUQV9TT1VSQ0VfT1VUUFVULFxufSBmcm9tIFwiLi4vLi4vLi4vLi4vdHlwZXNcIjtcbmltcG9ydCB7IERhdGFTb3VyY2VFcnJvckNvbXBvbmVudCB9IGZyb20gXCIuLi9kYXRhLXNvdXJjZS1lcnJvci9kYXRhLXNvdXJjZS1lcnJvci5jb21wb25lbnRcIjtcblxuLyoqXG4gKiBUaGlzIGlzIGEgYmFzaWMgaW1wbGVtZW50YXRpb24gb2YgYSBkYXRhIHNvdXJjZSBjb25maWd1cmF0aW9uIGNvbXBvbmVudC4gSW4gdGhlIHJlYWwgd29ybGQgc2NlbmFyaW8sIHRoaXMgY29tcG9uZW50IHdpbGwgbW9zdCBsaWtlbHkgYmUgcmVwbGFjZWQgYnkgYVxuICogY3VzdG9tIG9uZSwgYnV0IHN0aWxsIGNhbiBiZSB1c2VkIGFzIGEgdGVtcGxhdGUgZm9yIGRldmVsb3BpbmcgYSBjdXN0b20gc3BlY2lmaWMgc29sdXRpb24uXG4gKi9cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiBcIm51aS1kYXRhLXNvdXJjZS1jb25maWd1cmF0aW9uXCIsXG4gICAgdGVtcGxhdGVVcmw6IFwiLi9kYXRhLXNvdXJjZS1jb25maWd1cmF0aW9uLmNvbXBvbmVudC5odG1sXCIsXG4gICAgc3R5bGVVcmxzOiBbXCIuL2RhdGEtc291cmNlLWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmxlc3NcIl0sXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIERhdGFTb3VyY2VDb25maWd1cmF0aW9uQ29tcG9uZW50XG4gICAgaW1wbGVtZW50cyBJSGFzQ2hhbmdlRGV0ZWN0b3IsIElIYXNGb3JtLCBPbkluaXQsIE9uQ2hhbmdlc1xue1xuICAgIHB1YmxpYyBzdGF0aWMgbGF0ZUxvYWRLZXkgPSBcIkRhdGFTb3VyY2VDb25maWd1cmF0aW9uQ29tcG9uZW50XCI7XG5cbiAgICAvKipcbiAgICAgKiBUaGlzIGNvbXBvbmVudCBzaG93cyBhIGRyb3Bkb3duIHdpdGggb3B0aW9ucyBmb3Igc2VsZWN0aW5nIGEgZGF0YSBzb3VyY2UsIHRoaXMgaW5wdXQgcmVwcmVzZW50cyB0aGVzZSBvcHRpb25zLlxuICAgICAqL1xuICAgIEBJbnB1dCgpIGRhdGFTb3VyY2VQcm92aWRlcnM6IHN0cmluZ1tdID0gW107XG4gICAgQElucHV0KCkgZXJyb3JDb21wb25lbnQ6IHN0cmluZyA9IERhdGFTb3VyY2VFcnJvckNvbXBvbmVudC5sYXRlTG9hZEtleTtcblxuICAgIEBJbnB1dCgpIHByb3BlcnRpZXM6IElQcm9wZXJ0aWVzO1xuICAgIEBJbnB1dCgpIHByb3ZpZGVySWQ6IHN0cmluZztcblxuICAgIEBPdXRwdXQoKSBmb3JtUmVhZHkgPSBuZXcgRXZlbnRFbWl0dGVyPEZvcm1Hcm91cD4oKTtcblxuICAgIHB1YmxpYyBmb3JtOiBGb3JtR3JvdXA7XG4gICAgcHVibGljIGhhc0RhdGFTb3VyY2VFcnJvcjogYm9vbGVhbiA9IGZhbHNlO1xuICAgIHB1YmxpYyBkYXRhU291cmNlOiBJRGF0YVNvdXJjZTtcblxuICAgIC8vIHVzZWQgYnkgdGhlIEJyb2FkY2FzdGVyXG4gICAgcHVibGljIGRzT3V0cHV0ID0gbmV3IFN1YmplY3Q8YW55PigpO1xuICAgIHB1YmxpYyBkYXRhRmllbGRJZHMgPSBuZXcgU3ViamVjdDxhbnk+KCk7XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHVibGljIGNoYW5nZURldGVjdG9yOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICAgICAgcHVibGljIGNvbmZpZ3VyYXRvckhlYWRpbmc6IENvbmZpZ3VyYXRvckhlYWRpbmdTZXJ2aWNlLFxuICAgICAgICBwcml2YXRlIGZvcm1CdWlsZGVyOiBGb3JtQnVpbGRlcixcbiAgICAgICAgcHJpdmF0ZSBwcm92aWRlclJlZ2lzdHJ5U2VydmljZTogUHJvdmlkZXJSZWdpc3RyeVNlcnZpY2UsXG4gICAgICAgIEBJbmplY3QoUElaWkFHTkFfRVZFTlRfQlVTKSBwcml2YXRlIGV2ZW50QnVzOiBFdmVudEJ1czxJRXZlbnQ+LFxuICAgICAgICBwcml2YXRlIGluamVjdG9yOiBJbmplY3RvcixcbiAgICAgICAgcHJpdmF0ZSBsb2dnZXI6IExvZ2dlclNlcnZpY2VcbiAgICApIHt9XG5cbiAgICBwdWJsaWMgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZm9ybSA9IHRoaXMuZm9ybUJ1aWxkZXIuZ3JvdXAoe1xuICAgICAgICAgICAgcHJvdmlkZXJJZDogW3RoaXMucHJvdmlkZXJJZCB8fCBcIlwiLCBbVmFsaWRhdG9ycy5yZXF1aXJlZF1dLFxuICAgICAgICAgICAgcHJvcGVydGllczogdGhpcy5mb3JtQnVpbGRlci5ncm91cCh0aGlzLnByb3BlcnRpZXMgfHwge30pLFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5mb3JtLnNldFZhbGlkYXRvcnMoW1xuICAgICAgICAgICAgKCkgPT4gKHRoaXMuaGFzRGF0YVNvdXJjZUVycm9yID8geyBkYXRhU291cmNlRXJyb3I6IHRydWUgfSA6IG51bGwpLFxuICAgICAgICBdKTtcblxuICAgICAgICB0aGlzLmZvcm1SZWFkeS5lbWl0KHRoaXMuZm9ybSk7XG4gICAgfVxuXG4gICAgcHVibGljIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICAgICAgaWYgKGNoYW5nZXMucHJvdmlkZXJJZCAmJiAhY2hhbmdlcy5wcm92aWRlcklkLmlzRmlyc3RDaGFuZ2UoKSkge1xuICAgICAgICAgICAgY29uc3QgcHJldmlvdXNWYWx1ZTogc3RyaW5nID0gY2hhbmdlcy5wcm92aWRlcklkLnByZXZpb3VzVmFsdWU7XG4gICAgICAgICAgICBpZiAocHJldmlvdXNWYWx1ZSAhPT0gdGhpcy5wcm92aWRlcklkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5mb3JtLmdldChcInByb3ZpZGVySWRcIik/LnNldFZhbHVlKHRoaXMucHJvdmlkZXJJZCk7XG4gICAgICAgICAgICAgICAgdGhpcy5pbnZva2VEYXRhU291cmNlKHRoaXMucHJvdmlkZXJJZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgb25EYXRhU291cmNlQ2hhbmdlKHByb3ZpZGVySWQ6IHN0cmluZyk6IHZvaWQge1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm5leHQoREFUQV9TT1VSQ0VfQ0hBTkdFLCB7fSk7XG4gICAgICAgIHRoaXMuaW52b2tlRGF0YVNvdXJjZShwcm92aWRlcklkKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUaGUgZGF0YSBzb3VyY2UgaXMgaW52b2tlZCBoZXJlIHRvIG5vdGlmeSB0aGUgcmVzdCBvZiB0aGUgZm9ybSBhYm91dCAgY2hhbmdlcyBpbiB0aGUgZGF0YSBzb3VyY2Ugb3V0cHV0LlxuICAgICAqIERBVEFfU09VUkNFX09VVFBVVCBldmVudCBpcyBlbWl0dGVkIHRocm91Z2ggdGhlIGV2ZW50IGJ1cyBjYXJyeWluZyB0aGUgZGF0YSBzb3VyY2UgcmVzdWx0IGFzIHRoZSBwYXlsb2FkLlxuICAgICAqIFRoaXMgbWlnaHQgbm90IGJlIG5lY2Vzc2FyeSBpbiBldmVyeSBzaXR1YXRpb24uIElmIHRoZSBpbXBvcnRhbnQgaW5mb3JtYXRpb24gaXMgYWxyZWFkeSBzdG9yZWQgaW4gdGhlXG4gICAgICogZGF0YSBzb3VyY2UgcHJvcGVydGllcywgdGhpcyBzdGVwIGNhbiBiZSBvbWl0dGVkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHByb3ZpZGVySWRcbiAgICAgKi9cbiAgICBwdWJsaWMgaW52b2tlRGF0YVNvdXJjZShwcm92aWRlcklkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICAgICAgaWYgKCFwcm92aWRlcklkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcHJvdmlkZXIgPSB0aGlzLnByb3ZpZGVyUmVnaXN0cnlTZXJ2aWNlLmdldFByb3ZpZGVyKHByb3ZpZGVySWQpO1xuICAgICAgICBpZiAocHJvdmlkZXIpIHtcbiAgICAgICAgICAgIHRoaXMuZGF0YVNvdXJjZSA9IHRoaXMucHJvdmlkZXJSZWdpc3RyeVNlcnZpY2UuZ2V0UHJvdmlkZXJJbnN0YW5jZShcbiAgICAgICAgICAgICAgICBwcm92aWRlcixcbiAgICAgICAgICAgICAgICB0aGlzLmluamVjdG9yXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5uZXh0KERBVEFfU09VUkNFX0NSRUFURUQsIHtcbiAgICAgICAgICAgICAgICBwYXlsb2FkOiB0aGlzLmRhdGFTb3VyY2UsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuZGF0YVNvdXJjZS5vdXRwdXRzU3ViamVjdFxuICAgICAgICAgICAgICAgIC5waXBlKHRha2UoMSkpXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZSgocmVzdWx0OiBhbnkgfCBJRGF0YVNvdXJjZU91dHB1dDxhbnk+KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMubmV4dChEQVRBX1NPVVJDRV9PVVRQVVQsIHsgcGF5bG9hZDogcmVzdWx0IH0pO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmRzT3V0cHV0Lm5leHQocmVzdWx0KTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZGF0YUZpZWxkSWRzUmVzdWx0ID0gcmVzdWx0Py5yZXN1bHQgfHwgcmVzdWx0O1xuICAgICAgICAgICAgICAgICAgICBpZiAoZGF0YUZpZWxkSWRzUmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRhdGFGaWVsZElkcy5uZXh0KE9iamVjdC5rZXlzKGRhdGFGaWVsZElkc1Jlc3VsdCkpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBUaGlzIHNldFRpbWVvdXQgaXMgYmVjYXVzZSB0aGUgb3V0cHV0IG9mIHRoZSBkYXRhIHNvdXJjZSBtaWdodCBjb21lIGZhc3RlciB0aGFuIHRoZSBkYXRhLXNvdXJjZS1lcnJvci1jb21wb25lbnQgaXMgaW5pdGlhdGVkXG4gICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmRhdGFTb3VyY2UuYXBwbHlGaWx0ZXJzKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLndhcm4oXCJObyBwcm92aWRlciBmb3VuZCBmb3IgaWQ6XCIsIHByb3ZpZGVySWQpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIG9uRXJyb3JTdGF0ZShpc0Vycm9yOiBib29sZWFuKTogdm9pZCB7XG4gICAgICAgIHRoaXMuaGFzRGF0YVNvdXJjZUVycm9yID0gaXNFcnJvcjtcbiAgICAgICAgdGhpcy5mb3JtLm1hcmtBc1RvdWNoZWQoeyBvbmx5U2VsZjogdHJ1ZSB9KTtcbiAgICAgICAgdGhpcy5mb3JtLnVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xuICAgICAgICB0aGlzLmNoYW5nZURldGVjdG9yLmRldGVjdENoYW5nZXMoKTtcbiAgICB9XG59XG4iLCI8bnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uXG4gICAgW2Zvcm1Hcm91cF09XCJmb3JtXCJcbiAgICBbc3RhdGVdPVwiZm9ybSB8IG51aVdpZGdldEVkaXRvckFjY29yZGlvbkZvcm1TdGF0ZSB8IGFzeW5jXCJcbj5cbiAgICA8bnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uLWhlYWRlclxuICAgICAgICBbaGVhZGVySWNvbl09XCJmb3JtIHwgbnVpRm9ybUhlYWRlckljb25QaXBlIDogJ2RhdGFiYXNlJyB8IGFzeW5jXCJcbiAgICAgICAgW3N1YnRpdGxlXT1cImZvcm0uZ2V0KCdwcm92aWRlcklkJyk/LnZhbHVlXCJcbiAgICAgICAgaGVhZGVyVGl0bGU9XCJEYXRhIFNvdXJjZVwiXG4gICAgLz5cbiAgICA8ZGl2IGNsYXNzPVwiZGF0YXNvdXJjZS1jb25maWd1cmF0aW9uX19hY2NvcmRpb24tY29udGVudFwiPlxuICAgICAgICA8bnVpLWZvcm0tZmllbGRcbiAgICAgICAgICAgIGNhcHRpb249XCJTZWxlY3QgRGF0YSBTb3VyY2VcIlxuICAgICAgICAgICAgaTE4bi1jYXB0aW9uXG4gICAgICAgICAgICBbY29udHJvbF09XCJmb3JtLmdldCgncHJvdmlkZXJJZCcpXCJcbiAgICAgICAgPlxuICAgICAgICAgICAgPG51aS1zZWxlY3QtdjJcbiAgICAgICAgICAgICAgICBwbGFjZWhvbGRlcj1cIkRhdGEgU291cmNlc1wiXG4gICAgICAgICAgICAgICAgaTE4bi1wbGFjZWhvbGRlclxuICAgICAgICAgICAgICAgIFt0aXRsZV09XCJmb3JtLmdldCgncHJvdmlkZXJJZCcpPy52YWx1ZVwiXG4gICAgICAgICAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwicHJvdmlkZXJJZFwiXG4gICAgICAgICAgICAgICAgW3BvcHVwVmlld3BvcnRNYXJnaW5dPVwiY29uZmlndXJhdG9ySGVhZGluZy5oZWlnaHQkIHwgYXN5bmNcIlxuICAgICAgICAgICAgICAgICh2YWx1ZVNlbGVjdGVkKT1cIm9uRGF0YVNvdXJjZUNoYW5nZSgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICBjbGFzcz1cImRhdGFzb3VyY2UtY29uZmlndXJhdGlvbl9fYWNjb3JkaW9uLWNvbnRlbnRfX2RhdGFzb3VyY2UtaW5wdXRcIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxudWktc2VsZWN0LXYyLW9wdGlvblxuICAgICAgICAgICAgICAgICAgICAqbmdGb3I9XCJsZXQgZGF0YVNvdXJjZVByb3ZpZGVyIG9mIGRhdGFTb3VyY2VQcm92aWRlcnNcIlxuICAgICAgICAgICAgICAgICAgICBbdGl0bGVdPVwiZGF0YVNvdXJjZVByb3ZpZGVyXCJcbiAgICAgICAgICAgICAgICAgICAgW3ZhbHVlXT1cImRhdGFTb3VyY2VQcm92aWRlclwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICB7eyBkYXRhU291cmNlUHJvdmlkZXIgfX1cbiAgICAgICAgICAgICAgICA8L251aS1zZWxlY3QtdjItb3B0aW9uPlxuICAgICAgICAgICAgPC9udWktc2VsZWN0LXYyPlxuICAgICAgICA8L251aS1mb3JtLWZpZWxkPlxuICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICBudWlDb21wb25lbnRQb3J0YWxcbiAgICAgICAgICAgIFtjb21wb25lbnRJZF09XCJlcnJvckNvbXBvbmVudFwiXG4gICAgICAgICAgICBbY29tcG9uZW50VHlwZV09XCJlcnJvckNvbXBvbmVudFwiXG4gICAgICAgICAgICBbb3V0cHV0c109XCJbJ2Vycm9yU3RhdGUnXVwiXG4gICAgICAgICAgICBbcHJvcGVydGllc109XCJ7IGRhdGFTb3VyY2U6IGRhdGFTb3VyY2UgfVwiXG4gICAgICAgICAgICAob3V0cHV0KT1cIm9uRXJyb3JTdGF0ZSgkZXZlbnQucGF5bG9hZClcIlxuICAgICAgICAgICAgI2NvbXBvbmVudFBvcnRhbD1cIm51aUNvbXBvbmVudFBvcnRhbFwiXG4gICAgICAgID5cbiAgICAgICAgICAgIDxuZy10ZW1wbGF0ZVxuICAgICAgICAgICAgICAgIFtjZGtQb3J0YWxPdXRsZXRdPVwiY29tcG9uZW50UG9ydGFsLnBvcnRhbFwiXG4gICAgICAgICAgICAgICAgKGF0dGFjaGVkKT1cImNvbXBvbmVudFBvcnRhbC5hdHRhY2hlZCgkZXZlbnQpXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvZGl2PlxuPC9udWktd2lkZ2V0LWVkaXRvci1hY2NvcmRpb24+XG4iXX0=