tobias-istvan closed pull request #67: [AMBARI-25028] [Log Search UI] Populate `Component Name` in validator URL: https://github.com/apache/ambari-logsearch/pull/67
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html index 52f97be377..b24d15aabb 100644 --- a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html +++ b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html @@ -15,16 +15,18 @@ limitations under the License. --> -<div [ngClass]="{'dropup': isDropup, 'has-selection': hasSelection}"> - <button [ngClass]="['btn', 'dropdown-toggle', buttonClass]" data-toggle="dropdown"> +<div [ngClass]="{ dropup: isDropup, 'has-selection': hasSelection }"> + <button [ngClass]="['btn', 'dropdown-toggle', buttonClass]" [class.disabled]="disabled" data-toggle="dropdown"> <span class="filter-label"> <span *ngIf="iconClass || label" [class.plain]="!isMultipleChoice && !hideCaret && showSelectedValue"> <span *ngIf="iconClass" [ngClass]="iconClass"></span> - <span *ngIf="label && (!hasSelection || isMultipleChoice || showCommonLabelWithSelection)" - [class.label-before-selection]="isSelectionDisplayable"> - {{label}} + <span + *ngIf="label && (!hasSelection || isMultipleChoice || showCommonLabelWithSelection)" + [class.label-before-selection]="isSelectionDisplayable" + > + {{ label }} </span> - <span *ngIf="showTotalSelection && totalSelection" class="total-selection badge">{{totalSelection}}</span> + <span *ngIf="showTotalSelection && totalSelection" class="total-selection badge">{{ totalSelection }}</span> </span> <span *ngIf="isSelectionDisplayable"> <span class="selected-item-label" *ngFor="let item of selectedItems">{{ item.label | translate }}</span> @@ -32,8 +34,15 @@ <span *ngIf="!hideCaret" class="caret"></span> </span> </button> - <ul data-component="dropdown-list" (selectedItemChange)="updateSelection($event)" - [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': isRightAlign}" [closeOnSelection]="closeOnSelection" - [items]="options" [actionArguments]="listItemArguments" [isMultipleChoice]="isMultipleChoice" - [useClearToDefaultSelection]="useClearToDefaultSelection" [useLocalFilter]="useDropDownLocalFilter"></ul> + <ul + data-component="dropdown-list" + (selectedItemChange)="updateSelection($event)" + [ngClass]="{ 'dropdown-menu': true, 'dropdown-menu-right': isRightAlign }" + [closeOnSelection]="closeOnSelection" + [items]="options" + [actionArguments]="listItemArguments" + [isMultipleChoice]="isMultipleChoice" + [useClearToDefaultSelection]="useClearToDefaultSelection" + [useLocalFilter]="useDropDownLocalFilter" + ></ul> </div> diff --git a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts index 77ea8e12b0..fb8a9819bc 100644 --- a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts +++ b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts @@ -16,7 +16,7 @@ * limitations under the License. */ -import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core'; import { ListItem } from '@app/classes/list-item'; import { UtilsService } from '@app/services/utils.service'; @@ -25,8 +25,7 @@ import { UtilsService } from '@app/services/utils.service'; templateUrl: './dropdown-button.component.html', styleUrls: ['./dropdown-button.component.less'] }) -export class DropdownButtonComponent { - +export class DropdownButtonComponent implements OnChanges { @Input() label?: string; @@ -76,6 +75,9 @@ export class DropdownButtonComponent { @Input() closeOnSelection = true; + @Input() + disabled = false; + protected selectedItems: ListItem[] = []; get selection(): ListItem[] { @@ -83,15 +85,19 @@ export class DropdownButtonComponent { } set selection(items: ListItem[]) { - this.selectedItems = <ListItem[]>(Array.isArray(items) ? items : (items || [])); + this.selectedItems = <ListItem[]>(Array.isArray(items) ? items : items || []); if (this.selectedItems.length > 1 && !this.isMultipleChoice) { this.selectedItems = this.selectedItems.slice(0, 1); } if (this.isMultipleChoice && this.options) { - this.options.forEach((option: ListItem): void => { - const selectionItem = this.selectedItems.find((item: ListItem): boolean => this.utils.isEqual(item.value, option.value)); - option.isChecked = !!selectionItem; - }); + this.options.forEach( + (option: ListItem): void => { + const selectionItem = this.selectedItems.find( + (item: ListItem): boolean => this.utils.isEqual(item.value, option.value) + ); + option.isChecked = !!selectionItem; + } + ); } } @@ -113,9 +119,22 @@ export class DropdownButtonComponent { return this.showSelectedValue && !this.isMultipleChoice && this.hasSelection; } - constructor( - protected utils: UtilsService - ) {} + get value(): any { + const values = this.selectedItems && this.selectedItems.length && this.selectedItems.map(item => item.value); + return this.isMultipleChoice ? values : values[0]; + } + + constructor(protected utils: UtilsService) {} + + ngOnChanges(changes: SimpleChanges) { + if (changes.options) { + this.hanldeOptionsChange(); + } + } + + hanldeOptionsChange() { + this.filterAndSetSelection(); + } clearSelection(silent: boolean = false) { let hasChange = false; @@ -128,14 +147,16 @@ export class DropdownButtonComponent { } } - updateSelection(updates: ListItem | ListItem[], callOnChange: boolean = true): boolean { + updateSelection(updates: ListItem | ListItem[]): boolean { let hasChange = false; if (updates && (!Array.isArray(updates) || updates.length)) { const items: ListItem[] = Array.isArray(updates) ? updates : [updates]; if (this.isMultipleChoice) { items.forEach((item: ListItem) => { if (this.options && this.options.length) { - const itemToUpdate: ListItem = this.options.find((option: ListItem) => this.utils.isEqual(option.value, item.value)); + const itemToUpdate: ListItem = this.options.find((option: ListItem) => + this.utils.isEqual(option.value, item.value) + ); if (itemToUpdate) { hasChange = hasChange || itemToUpdate.isChecked !== item.isChecked; itemToUpdate.isChecked = item.isChecked; @@ -151,15 +172,18 @@ export class DropdownButtonComponent { }); } } else { - this.options.forEach((item: ListItem) => item.isChecked = false); + this.options.forEach((item: ListItem) => (item.isChecked = false)); } - const checkedItems = this.options.filter((option: ListItem): boolean => option.isChecked); - this.selection = checkedItems; + this.filterAndSetSelection(); if (hasChange) { - const selectedValues = checkedItems.map((option: ListItem): any => option.value); + const selectedValues = this.selection.map((option: ListItem): any => option.value); this.selectItem.emit(this.isMultipleChoice ? selectedValues : selectedValues.shift()); } return hasChange; } + protected filterAndSetSelection() { + const checkedItems = this.options.filter((option: ListItem): boolean => option.isChecked); + this.selection = checkedItems; + } } diff --git a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts index 4157333da7..a8aff3f811 100644 --- a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts +++ b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts @@ -15,37 +15,41 @@ * limitations under the License. */ -import {NO_ERRORS_SCHEMA} from '@angular/core'; -import {async, ComponentFixture, TestBed} from '@angular/core/testing'; -import {MockHttpRequestModules, TranslationModules} from '@app/test-config.spec'; -import {StoreModule} from '@ngrx/store'; -import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service'; -import {AppStateService, appState} from '@app/services/storage/app-state.service'; -import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service'; -import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service'; -import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service'; -import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service'; -import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service'; +import { NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockHttpRequestModules, TranslationModules } from '@app/test-config.spec'; +import { StoreModule } from '@ngrx/store'; +import { AppSettingsService, appSettings } from '@app/services/storage/app-settings.service'; +import { AppStateService, appState } from '@app/services/storage/app-state.service'; +import { AuditLogsService, auditLogs } from '@app/services/storage/audit-logs.service'; +import { AuditLogsFieldsService, auditLogsFields } from '@app/services/storage/audit-logs-fields.service'; +import { AuditLogsGraphDataService, auditLogsGraphData } from '@app/services/storage/audit-logs-graph-data.service'; +import { ServiceLogsService, serviceLogs } from '@app/services/storage/service-logs.service'; +import { ServiceLogsFieldsService, serviceLogsFields } from '@app/services/storage/service-logs-fields.service'; import { - ServiceLogsHistogramDataService, serviceLogsHistogramData + ServiceLogsHistogramDataService, + serviceLogsHistogramData } from '@app/services/storage/service-logs-histogram-data.service'; -import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service'; -import {TabsService, tabs} from '@app/services/storage/tabs.service'; -import {ClustersService, clusters} from '@app/services/storage/clusters.service'; -import {ComponentsService, components} from '@app/services/storage/components.service'; -import {HostsService, hosts} from '@app/services/storage/hosts.service'; -import {UtilsService} from '@app/services/utils.service'; -import {LogsContainerService} from '@app/services/logs-container.service'; -import {AuthService} from '@app/services/auth.service'; +import { + ServiceLogsTruncatedService, + serviceLogsTruncated +} from '@app/services/storage/service-logs-truncated.service'; +import { TabsService, tabs } from '@app/services/storage/tabs.service'; +import { ClustersService, clusters } from '@app/services/storage/clusters.service'; +import { ComponentsService, components } from '@app/services/storage/components.service'; +import { HostsService, hosts } from '@app/services/storage/hosts.service'; +import { UtilsService } from '@app/services/utils.service'; +import { LogsContainerService } from '@app/services/logs-container.service'; +import { AuthService } from '@app/services/auth.service'; -import {FilterDropdownComponent} from './filter-dropdown.component'; -import {ClusterSelectionService} from '@app/services/storage/cluster-selection.service'; -import {LogsStateService} from '@app/services/storage/logs-state.service'; -import {RoutingUtilsService} from '@app/services/routing-utils.service'; -import {LogsFilteringUtilsService} from '@app/services/logs-filtering-utils.service'; -import {RouterTestingModule} from '@angular/router/testing'; -import {NotificationService} from '@modules/shared/services/notification.service'; -import {NotificationsService} from 'angular2-notifications/src/notifications.service'; +import { FilterDropdownComponent } from './filter-dropdown.component'; +import { ClusterSelectionService } from '@app/services/storage/cluster-selection.service'; +import { LogsStateService } from '@app/services/storage/logs-state.service'; +import { RoutingUtilsService } from '@app/services/routing-utils.service'; +import { LogsFilteringUtilsService } from '@app/services/logs-filtering-utils.service'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NotificationService } from '@modules/shared/services/notification.service'; +import { NotificationsService } from 'angular2-notifications/src/notifications.service'; import * as auth from '@app/store/reducers/auth.reducers'; import { EffectsModule } from '@ngrx/effects'; @@ -77,8 +81,7 @@ describe('FilterDropdownComponent', () => { const httpClient = { get: () => { return { - subscribe: () => { - } + subscribe: () => {} }; } }; @@ -136,9 +139,8 @@ describe('FilterDropdownComponent', () => { NotificationsService, NotificationService ], - schemas: [NO_ERRORS_SCHEMA] - }) - .compileComponents(); + schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -150,5 +152,4 @@ describe('FilterDropdownComponent', () => { it('should create component', () => { expect(component).toBeTruthy(); }); - }); diff --git a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts index b0c766e933..2bcf4a206d 100644 --- a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts +++ b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts @@ -15,10 +15,10 @@ * limitations under the License. */ -import {Component, forwardRef} from '@angular/core'; -import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; -import {DropdownButtonComponent} from '@modules/shared/components/dropdown-button/dropdown-button.component'; -import {ListItem} from '@app/classes/list-item'; +import { Component, forwardRef, Input } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { DropdownButtonComponent } from '@modules/shared/components/dropdown-button/dropdown-button.component'; +import { ListItem } from '@app/classes/list-item'; @Component({ selector: 'filter-dropdown', @@ -33,7 +33,6 @@ import {ListItem} from '@app/classes/list-item'; ] }) export class FilterDropdownComponent extends DropdownButtonComponent implements ControlValueAccessor { - private onChange; private _onChange(value) { @@ -42,6 +41,9 @@ export class FilterDropdownComponent extends DropdownButtonComponent implements } } + @Input() + options: ListItem[] = []; + updateSelection(updates: ListItem | ListItem[], callOnChange: boolean = true): boolean { const hasChange = super.updateSelection(updates); if (hasChange && callOnChange) { @@ -50,15 +52,18 @@ export class FilterDropdownComponent extends DropdownButtonComponent implements return hasChange; } + hanldeOptionsChange() { + super.hanldeOptionsChange(); + this._onChange(this.selection); + } + writeValue(items: ListItem[]) { - this.selection = items ? (Array.isArray(items) ? items : [items] ) : []; + this.selection = items ? (Array.isArray(items) ? items : [items]) : []; } registerOnChange(callback: any): void { this.onChange = callback; } - registerOnTouched() { - } - + registerOnTouched() {} } diff --git a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts index 0ec4f56690..a8e2b1f50a 100644 --- a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts +++ b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts @@ -16,39 +16,45 @@ * limitations under the License. */ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import {ShipperConfigurationComponent} from './shipper-configuration.component'; -import {StoreModule} from '@ngrx/store'; -import {auditLogs, AuditLogsService} from '@app/services/storage/audit-logs.service'; -import {serviceLogsTruncated, ServiceLogsTruncatedService} from '@app/services/storage/service-logs-truncated.service'; -import {components, ComponentsService} from '@app/services/storage/components.service'; -import {UtilsService} from '@app/services/utils.service'; -import {tabs, TabsService} from '@app/services/storage/tabs.service'; -import {serviceLogs, ServiceLogsService} from '@app/services/storage/service-logs.service'; -import {hosts, HostsService} from '@app/services/storage/hosts.service'; -import {MockHttpRequestModules, TranslationModules} from '@app/test-config.spec'; -import {ComponentGeneratorService} from '@app/services/component-generator.service'; -import {auditLogsGraphData, AuditLogsGraphDataService} from '@app/services/storage/audit-logs-graph-data.service'; -import {serviceLogsHistogramData, ServiceLogsHistogramDataService} from '@app/services/storage/service-logs-histogram-data.service'; -import {clusters, ClustersService} from '@app/services/storage/clusters.service'; -import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service'; -import {appSettings, AppSettingsService} from '@app/services/storage/app-settings.service'; -import {appState, AppStateService} from '@app/services/storage/app-state.service'; -import {ClusterSelectionService} from '@app/services/storage/cluster-selection.service'; -import {serviceLogsFields, ServiceLogsFieldsService} from '@app/services/storage/service-logs-fields.service'; -import {LogsContainerService} from '@app/services/logs-container.service'; -import {ShipperRoutingModule} from '@modules/shipper/shipper-routing.module'; -import {ShipperClusterServiceListComponent} from '@modules/shipper/components/shipper-cluster-service-list/shipper-cluster-service-list.component'; -import {ShipperServiceConfigurationFormComponent} from '@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {TypeaheadModule} from 'ngx-bootstrap'; -import {DisableControlDirective} from '@modules/shared/directives/disable-control.directive'; -import {ModalComponent} from '@modules/shared/components/modal/modal.component'; -import {RouterTestingModule} from '@angular/router/testing'; -import {ShipperClusterServiceListService} from '@modules/shipper/services/shipper-cluster-service-list.service'; -import {ShipperConfigurationService} from '@modules/shipper/services/shipper-configuration.service'; -import {NotificationService} from '@modules/shared/services/notification.service'; -import {NotificationsService} from 'angular2-notifications/src/notifications.service'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ShipperConfigurationComponent } from './shipper-configuration.component'; +import { StoreModule } from '@ngrx/store'; +import { auditLogs, AuditLogsService } from '@app/services/storage/audit-logs.service'; +import { + serviceLogsTruncated, + ServiceLogsTruncatedService +} from '@app/services/storage/service-logs-truncated.service'; +import { components, ComponentsService } from '@app/services/storage/components.service'; +import { UtilsService } from '@app/services/utils.service'; +import { tabs, TabsService } from '@app/services/storage/tabs.service'; +import { serviceLogs, ServiceLogsService } from '@app/services/storage/service-logs.service'; +import { hosts, HostsService } from '@app/services/storage/hosts.service'; +import { MockHttpRequestModules, TranslationModules } from '@app/test-config.spec'; +import { ComponentGeneratorService } from '@app/services/component-generator.service'; +import { auditLogsGraphData, AuditLogsGraphDataService } from '@app/services/storage/audit-logs-graph-data.service'; +import { + serviceLogsHistogramData, + ServiceLogsHistogramDataService +} from '@app/services/storage/service-logs-histogram-data.service'; +import { clusters, ClustersService } from '@app/services/storage/clusters.service'; +import { AuditLogsFieldsService, auditLogsFields } from '@app/services/storage/audit-logs-fields.service'; +import { appSettings, AppSettingsService } from '@app/services/storage/app-settings.service'; +import { appState, AppStateService } from '@app/services/storage/app-state.service'; +import { ClusterSelectionService } from '@app/services/storage/cluster-selection.service'; +import { serviceLogsFields, ServiceLogsFieldsService } from '@app/services/storage/service-logs-fields.service'; +import { LogsContainerService } from '@app/services/logs-container.service'; +import { ShipperRoutingModule } from '@modules/shipper/shipper-routing.module'; +import { ShipperClusterServiceListComponent } from '@modules/shipper/components/shipper-cluster-service-list/shipper-cluster-service-list.component'; +import { ShipperServiceConfigurationFormComponent } from '@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TypeaheadModule } from 'ngx-bootstrap'; +import { DisableControlDirective } from '@modules/shared/directives/disable-control.directive'; +import { ModalComponent } from '@modules/shared/components/modal/modal.component'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ShipperClusterServiceListService } from '@modules/shipper/services/shipper-cluster-service-list.service'; +import { ShipperConfigurationService } from '@modules/shipper/services/shipper-configuration.service'; +import { NotificationService } from '@modules/shared/services/notification.service'; +import { NotificationsService } from 'angular2-notifications/src/notifications.service'; describe('ShipperConfigurationComponent', () => { let component: ShipperConfigurationComponent; @@ -110,9 +116,9 @@ describe('ShipperConfigurationComponent', () => { ShipperServiceConfigurationFormComponent, DisableControlDirective, ModalComponent - ] - }) - .compileComponents(); + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts index d8fccbdd61..82ae869ee4 100644 --- a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts +++ b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts @@ -15,25 +15,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core'; -import {ActivatedRoute, Router} from '@angular/router'; -import {Response} from '@angular/http'; -import {Observable} from 'rxjs/Observable'; +import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/skipWhile'; -import {NotificationService, NotificationType} from '@modules/shared/services/notification.service'; -import {CanComponentDeactivate} from '@modules/shared/services/can-deactivate-guard.service'; - -import {ShipperCluster} from '../../models/shipper-cluster.type'; -import {ShipperClusterService} from '../../models/shipper-cluster-service.type'; -import {ShipperConfigurationService} from '../../services/shipper-configuration.service'; -import {ShipperClusterServiceListService} from '../../services/shipper-cluster-service-list.service'; -import { - ShipperServiceConfigurationFormComponent -} from '@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component'; -import {TranslateService} from '@ngx-translate/core'; -import {ClusterSelectionService} from '@app/services/storage/cluster-selection.service'; -import {Subscription} from 'rxjs/Subscription'; +import { NotificationService, NotificationType } from '@modules/shared/services/notification.service'; +import { CanComponentDeactivate } from '@modules/shared/services/can-deactivate-guard.service'; + +import { ShipperCluster } from '../../models/shipper-cluster.type'; +import { ShipperClusterService } from '../../models/shipper-cluster-service.type'; +import { ShipperConfigurationService } from '../../services/shipper-configuration.service'; +import { ShipperClusterServiceListService } from '../../services/shipper-cluster-service-list.service'; +import { ShipperServiceConfigurationFormComponent } from '@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component'; +import { TranslateService } from '@ngx-translate/core'; +import { ClusterSelectionService } from '@app/services/storage/cluster-selection.service'; +import { Subscription } from 'rxjs/Subscription'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { FormGroup } from '@angular/forms'; @@ -43,7 +41,6 @@ import { FormGroup } from '@angular/forms'; styleUrls: ['./shipper-configuration.component.less'] }) export class ShipperConfigurationComponent implements CanComponentDeactivate, OnInit, OnDestroy { - static clusterSelectionStoreKey = 'shipper'; @Input() @@ -57,21 +54,25 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On private clusterName$: Observable<ShipperClusterService> = this.activatedRoute.params.map(params => params.cluster); private serviceName$: Observable<ShipperClusterService> = this.activatedRoute.params.map(params => params.service); - private serviceNamesList$: Observable<ShipperClusterService[]> = this.clusterName$.switchMap((cluster: ShipperCluster) => { - return cluster ? this.shipperClusterServiceListService.getServicesForCluster(cluster) : Observable.of(undefined); - }); - - private configuration$: Observable<{[key: string]: any}> = Observable.combineLatest( - this.clusterName$, - this.serviceName$ - ).switchMap(([clusterName, serviceName]: [ShipperCluster, ShipperClusterService]) => { - return clusterName && serviceName ? - this.shipperConfigurationService.loadConfiguration(clusterName, serviceName) : Observable.of(undefined); - }); + private serviceNamesList$: Observable<ShipperClusterService[]> = this.clusterName$.switchMap( + (cluster: ShipperCluster) => { + return cluster ? this.shipperClusterServiceListService.getServicesForCluster(cluster) : Observable.of(undefined); + } + ); + + private configuration$: Observable<{ + [key: string]: any; + }> = Observable.combineLatest(this.clusterName$, this.serviceName$).switchMap( + ([clusterName, serviceName]: [ShipperCluster, ShipperClusterService]) => { + return clusterName && serviceName + ? this.shipperConfigurationService.loadConfiguration(clusterName, serviceName) + : Observable.of(undefined); + } + ); private subscriptions: Subscription[] = []; - validationResponse: {[key: string]: any}; + validationResponse: { [key: string]: any }; constructor( private router: Router, @@ -81,7 +82,7 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On private notificationService: NotificationService, private translate: TranslateService, private clusterSelectionStoreService: ClusterSelectionService - ) { } + ) {} ngOnInit() { this.subscriptions.push( @@ -98,7 +99,8 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On } private getPathMapForClusterFirstService(cluster: ShipperCluster): Observable<string[]> { - return this.shipperClusterServiceListService.getServicesForCluster(cluster) + return this.shipperClusterServiceListService + .getServicesForCluster(cluster) .switchMap((serviceNamesList: ShipperClusterService[]) => { return Observable.of(this.getRouterLink([cluster, serviceNamesList[0]])); }); @@ -112,26 +114,42 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On if (clusterName) { this.clusterName$.first().subscribe((currentClusterName: ShipperCluster) => { if (currentClusterName !== clusterName) { - this.getPathMapForClusterFirstService(clusterName).first().subscribe((path: string[]) => this.router.navigate(path)); + this.getPathMapForClusterFirstService(clusterName) + .first() + .subscribe((path: string[]) => this.router.navigate(path)); } }); } - } + }; private getRouterLink(path: string | string[]): string[] { return [...this.routerPath, ...(Array.isArray(path) ? path : [path])]; } - getResponseHandler(cmd: string, type: string, form?: FormGroup) { - const msgVariables = form.getRawValue(); + getResponseHandler(cmd: string, type: string, msgVariables: any = {}, form?: FormGroup) { return (response: Response) => { const result = response.json(); // @ToDo change the backend response status to some error code if the configuration is not valid and don't use the .message prop - const resultType = response ? (response.ok && !result.errorMessage ? NotificationType.SUCCESS : NotificationType.ERROR) : type; - const translateParams = {errorMessage: (result && result.message) || '', ...msgVariables, ...result}; + const resultType = response + ? response.ok && !result.errorMessage + ? NotificationType.SUCCESS + : NotificationType.ERROR + : type; + const translateParams = { + errorMessage: (result && result.message) || '', + ...msgVariables, + ...result + }; const title = this.translate.instant(`shipperConfiguration.action.${cmd}.title`, translateParams); - const message = this.translate.instant(`shipperConfiguration.action.${cmd}.${resultType}.message`, translateParams); - this.notificationService.addNotification({type: resultType, title, message}); + const message = this.translate.instant( + `shipperConfiguration.action.${cmd}.${resultType}.message`, + translateParams + ); + this.notificationService.addNotification({ + type: resultType, + title, + message + }); if (resultType !== NotificationType.ERROR) { form.markAsPristine(); } @@ -149,23 +167,24 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On service: rawValue.serviceName, configuration: JSON.parse(rawValue.configuration) }).subscribe( - this.getResponseHandler(cmd, NotificationType.SUCCESS, configurationForm), - this.getResponseHandler(cmd, NotificationType.ERROR, configurationForm) + this.getResponseHandler(cmd, NotificationType.SUCCESS, rawValue, configurationForm), + this.getResponseHandler(cmd, NotificationType.ERROR, rawValue, configurationForm) ); }); } - private setValidationResult = (result: {[key: string]: any}) => { + private setValidationResult = (result: { [key: string]: any }) => { this.validationResponse = result; - } + }; onValidationFormSubmit(validationForm: FormGroup): void { this.validationResponse = null; const rawValue = validationForm.getRawValue(); + rawValue.componentName = rawValue.componentName[0].value; const request$: Observable<Response> = this.shipperConfigurationService.testConfiguration(rawValue); request$.subscribe( - this.getResponseHandler('validate', NotificationType.SUCCESS, validationForm), - this.getResponseHandler('validate', NotificationType.ERROR, validationForm) + this.getResponseHandler('validate', NotificationType.SUCCESS, rawValue, validationForm), + this.getResponseHandler('validate', NotificationType.ERROR, rawValue, validationForm) ); request$ .filter((response: Response): boolean => response.ok) @@ -178,5 +197,4 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On canDeactivate() { return this.configurationFormRef.canDeactivate(); } - } diff --git a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html index 7e9113700b..742eb1dd91 100644 --- a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html +++ b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html @@ -14,52 +14,78 @@ See the License for the specific language governing permissions and limitations under the License. --> -<ng-template #typeAheadTpl let-item="item"> - {{item | translate}} -</ng-template> +<ng-template #typeAheadTpl let-item="item"> {{ item | translate }} </ng-template> <div class="container-fluid"> <div class="row"> <div class="shipper-form-configuration col-md-6"> <form [formGroup]="configurationForm" (ngSubmit)="onConfigurationSubmit($event)"> <fieldset [disabled]="disabled"> - <h2>{{(serviceName ? 'shipperConfiguration.form.titleEdit' : 'shipperConfiguration.form.titleAdd') | translate}}</h2> - <div [ngClass]="{'has-error': serviceNameField.invalid, 'form-group': true}"> + <h2> + {{ + (serviceName ? 'shipperConfiguration.form.titleEdit' : 'shipperConfiguration.form.titleAdd') | translate + }} + </h2> + <div + [ngClass]="{ + 'has-error': serviceNameField.invalid && configurationForm.touched, + 'form-group': true + }" + > <label> - {{'shipperConfiguration.form.serviceLabel' | translate}} - <span *ngIf="serviceNameField.errors && serviceNameField.errors.serviceNameExists" - class="help-block validation-block pull-right"> - {{'shipperConfiguration.form.errors.serviceName.exists' | translate}} + {{ 'shipperConfiguration.form.serviceLabel' | translate }} + <span + *ngIf="serviceNameField.errors && serviceNameField.errors.serviceNameExists" + class="help-block validation-block pull-right" + > + {{ 'shipperConfiguration.form.errors.serviceName.exists' | translate }} </span> - <span *ngIf="serviceNameField.errors && serviceNameField.errors.required" - class="help-block validation-block pull-right"> - {{'common.form.errors.required' | translate}} + <span + *ngIf="serviceNameField.errors && serviceNameField.errors.required" + class="help-block validation-block pull-right" + > + {{ 'common.form.errors.required' | translate }} </span> </label> - <input *ngIf="!serviceName" formControlName="serviceName" class="form-control"> + <input *ngIf="!serviceName" formControlName="serviceName" class="form-control" /> <ng-container *ngIf="serviceName"> - <div class="shipper-configuration-service-name">{{serviceName}}</div> - <input type="hidden" name="serviceName" formControlName="serviceName"> + <div class="shipper-configuration-service-name">{{ serviceName }}</div> + <input type="hidden" name="serviceName" formControlName="serviceName" /> </ng-container> </div> - <input type="hidden" name="clusterName" formControlName="clusterName"> - <div [ngClass]="{'has-error': configurationField.invalid, 'form-group': true}"> + <input type="hidden" name="clusterName" formControlName="clusterName" /> + <div + [ngClass]="{ + 'has-error': configurationField.invalid, + 'form-group': true + }" + > <label> - {{'shipperConfiguration.form.configurationJSONLabel' | translate}} - <span *ngIf="configurationField.errors && configurationField.errors.invalidJSON" - class="help-block validation-block pull-right"> - {{'shipperConfiguration.form.errors.configuration.invalidJSON' | translate}} + {{ 'shipperConfiguration.form.configurationJSONLabel' | translate }} + <span + *ngIf="configurationField.errors && configurationField.errors.invalidJSON" + class="help-block validation-block pull-right" + > + {{ 'shipperConfiguration.form.errors.configuration.invalidJSON' | translate }} </span> - <span *ngIf="configurationField.errors && configurationField.errors.required" - class="help-block validation-block pull-right"> - {{'common.form.errors.required' | translate}} + <span + *ngIf="configurationField.errors && configurationField.errors.required" + class="help-block validation-block pull-right" + > + {{ 'common.form.errors.required' | translate }} </span> </label> - <textarea class="form-control configuration" name="configuration" - formControlName="configuration"></textarea> + <textarea + class="form-control configuration" + name="configuration" + formControlName="configuration" + ></textarea> </div> - <button class="btn btn-primary pull-right" type="submit" - [disabled]="(!configurationForm.valid || configurationForm.pristine)"> - {{'shipperConfiguration.form.saveBtn.label' | translate}} + <button + class="btn btn-primary pull-right" + type="submit" + [disabled]="!configurationForm.valid || configurationForm.pristine" + > + {{ 'shipperConfiguration.form.saveBtn.label' | translate }} </button> </fieldset> </form> @@ -68,47 +94,67 @@ <h2>{{(serviceName ? 'shipperConfiguration.form.titleEdit' : 'shipperConfigurati <div class="container-fluid"> <div class="row"> <form [formGroup]="validatorForm" (ngSubmit)="onValidationSubmit($event)"> - <h2>{{'shipperConfiguration.validator.title' | translate}}</h2> - <input type="hidden" name="clusterName" formControlName="clusterName"> + <h2>{{ 'shipperConfiguration.validator.title' | translate }}</h2> + <input type="hidden" name="clusterName" formControlName="clusterName" /> <div class="form-group" [class.has-warning]="componentNameField.invalid"> <label> - {{'shipperConfiguration.validator.componentNameLabel' | translate}} - <span [class.hide]="!componentNameField.errors || !componentNameField.errors.required" - class="help-block validation-block pull-right"> - {{'common.form.errors.required' | translate}} + <span + [class.hide]=" + !componentNameField.errors || !componentNameField.errors.required || configurationForm.invalid + " + class="help-block validation-block pull-right" + > + {{ 'common.form.errors.required' | translate }} </span> - <span [class.hide]="!componentNameField.value || !componentNameField.errors || !componentNameField.errors.serviceNameDoesNotExistInConfiguration" - class="help-block validation-block pull-right"> - {{'shipperConfiguration.form.errors.componentNameField.serviceNameDoesNotExistInConfiguration' | translate}} + <span + [class.hide]=" + !componentNameField.value || + !componentNameField.errors || + !componentNameField.errors.serviceNameDoesNotExistInConfiguration + " + class="help-block validation-block pull-right" + > + {{ + 'shipperConfiguration.form.errors.componentNameField.serviceNameDoesNotExistInConfiguration' + | translate + }} </span> </label> - <input class="form-control component-name" name="componentName" formControlName="componentName" - [typeahead]="configurationComponents$ | async" - [typeaheadItemTemplate]="typeAheadTpl" - [typeaheadMinLength]="0" - [disableControl]="configurationForm.invalid" - autocomplete="off"> + <filter-dropdown + [options]="configurationComponentsList$ | async" + formControlName="componentName" + [label]="'shipperConfiguration.validator.componentNameLabel' | translate" + [showCommonLabelWithSelection]="true" + [disabled]="configurationForm.invalid || !(configurationComponentsList$ | async).length" + ></filter-dropdown> </div> <div class="form-group" [class.has-warning]="sampleDataField.invalid"> <label> - {{'shipperConfiguration.validator.sampleDataLabel' | translate}} - <span [class.hide]="!sampleDataField.errors || !sampleDataField.errors.required" - class="help-block validation-block pull-right"> - {{'common.form.errors.required' | translate}} + {{ 'shipperConfiguration.validator.sampleDataLabel' | translate }} + <span + [class.hide]="!sampleDataField.errors || !sampleDataField.errors.required" + class="help-block validation-block pull-right" + > + {{ 'common.form.errors.required' | translate }} </span> </label> - <textarea class="form-control sample-data" name="sampleData" formControlName="sampleData" - [disableControl]="configurationForm.invalid"></textarea> + <textarea + class="form-control sample-data" + name="sampleData" + formControlName="sampleData" + [disableControl]="configurationForm.invalid" + ></textarea> </div> <div *ngIf="validationResponse" class="form-group"> - <label> - {{'shipperConfiguration.validator.result' | translate}} - </label> - <pre>{{validationResponse | json}}</pre> + <label> {{ 'shipperConfiguration.validator.result' | translate }} </label> + <pre>{{ validationResponse | json }}</pre> </div> - <button class="btn btn-default pull-right" type="submit" - [disabled]="(!validatorForm.valid)"> - <i class="fa fa-table" aria-hidden="true"></i> {{'shipperConfiguration.form.testBtn.label' | translate}} + <button + class="btn btn-default pull-right" + type="submit" + [disabled]="validatorForm.invalid || configurationForm.invalid" + > + <i class="fa fa-table" aria-hidden="true"></i> {{ 'shipperConfiguration.form.testBtn.label' | translate }} </button> </form> </div> @@ -116,8 +162,14 @@ <h2>{{'shipperConfiguration.validator.title' | translate}}</h2> </div> </div> </div> -<modal *ngIf="isLeavingDirtyForm" (submit)="leaveDirtyFormConfirmed()" (cancel)="leaveDirtyFormCancelled()" - (close)="leaveDirtyFormCancelled()" showCloseButton="false" isSmallModal="true" - title="{{'shipperConfiguration.form.leavingDirty.title' | translate}}" - bodyText="{{'shipperConfiguration.form.leavingDirty.message' | translate}}"> +<modal + *ngIf="isLeavingDirtyForm" + (submit)="leaveDirtyFormConfirmed()" + (cancel)="leaveDirtyFormCancelled()" + (close)="leaveDirtyFormCancelled()" + showCloseButton="false" + isSmallModal="true" + title="{{ 'shipperConfiguration.form.leavingDirty.title' | translate }}" + bodyText="{{ 'shipperConfiguration.form.leavingDirty.message' | translate }}" +> </modal> diff --git a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less index f6f371388d..0fa195c9d8 100644 --- a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less +++ b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less @@ -16,39 +16,51 @@ * limitations under the License. */ -@import "../../../shared/forms"; - -textarea { - @extend input.form-control - min-height: 5em; - resize: vertical; - width: 100%; - &.validation-result, - &.configuration { - min-height: 30em; +@import '../../../shared/forms'; +:host { + textarea { + &:extend(input.form-control); + min-height: 5em; + resize: vertical; + width: 100%; + &.validation-result, + &.configuration { + min-height: 30em; + } + &.sample-data { + min-height: 8em; + } } - &.sample-data { - min-height: 8em; + label { + width: 100%; } -} -label { - width: 100%; -} -.help-block.validation-block { - display: none; - font-size: .9em; - margin: 0; - opacity: 0; - transition: all 1s ease-in; -} -.has-error, .has-warning { .help-block.validation-block { - display: inline-block; - opacity: 1; + display: none; + font-size: 0.9em; + margin: 0; + opacity: 0; + transition: all 1s ease-in; + } + .has-error, + .has-warning { + .help-block.validation-block { + display: inline-block; + opacity: 1; + } + } + + .shipper-form-configuration { + background-color: @main-background-color; + padding-bottom: 4em; } -} -.shipper-form-configuration { - background-color: @main-background-color; - padding-bottom: 4em; + /deep/ filter-dropdown { + button, + button:focus { + padding: 0; + .label-before-selection { + font-weight: bold; + } + } + } } diff --git a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts index b41b9413e2..d3d319cc2d 100644 --- a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts +++ b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts @@ -16,23 +16,34 @@ * limitations under the License. */ -import {ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges} from '@angular/core'; -import {AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators} from '@angular/forms'; -import {Observable} from 'rxjs/Observable'; -import {Subject} from 'rxjs/Subject'; -import {Observer} from 'rxjs/Observer'; +import { + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnChanges, + OnDestroy, + OnInit, + Output, + SimpleChanges +} from '@angular/core'; +import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { Observer } from 'rxjs/Observer'; import 'rxjs/add/operator/startWith'; -import {CanComponentDeactivate} from '@modules/shared/services/can-deactivate-guard.service'; +import { CanComponentDeactivate } from '@modules/shared/services/can-deactivate-guard.service'; -import {ShipperCluster} from '../../models/shipper-cluster.type'; -import {ShipperClusterService} from '../../models/shipper-cluster-service.type'; -import {ShipperClusterServiceConfigurationInterface} from '../../models/shipper-cluster-service-configuration.interface'; -import {ShipperConfigurationModel} from '../../models/shipper-configuration.model'; +import { ShipperCluster } from '../../models/shipper-cluster.type'; +import { ShipperClusterService } from '../../models/shipper-cluster-service.type'; +import { ShipperClusterServiceConfigurationInterface } from '../../models/shipper-cluster-service-configuration.interface'; +import { ShipperConfigurationModel } from '../../models/shipper-configuration.model'; import * as formValidators from '../../directives/validator.directive'; -import {BehaviorSubject} from 'rxjs/BehaviorSubject'; -import {Subscription} from 'rxjs/Subscription'; -import {ActivatedRoute} from '@angular/router'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Subscription } from 'rxjs/Subscription'; +import { ActivatedRoute } from '@angular/router'; +import { ListItem } from '@app/classes/list-item'; @Component({ selector: 'shipper-configuration-form', @@ -40,7 +51,6 @@ import {ActivatedRoute} from '@angular/router'; styleUrls: ['./shipper-service-configuration-form.component.less'] }) export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestroy, OnChanges, CanComponentDeactivate { - private configurationForm: FormGroup; private validatorForm: FormGroup; @@ -57,7 +67,7 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr existingServiceNames: Observable<ShipperClusterService[]> | ShipperClusterService[]; @Input() - validationResponse: {[key: string]: any}; + validationResponse: { [key: string]: any }; @Input() disabled = false; @@ -69,6 +79,7 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr validationSubmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>(); private configurationComponents$: Observable<string[]>; + private configurationComponentsList$: Observable<ListItem[]>; private isLeavingDirtyForm = false; @@ -94,18 +105,20 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr private canDeactivateModalResult: Subject<boolean> = new Subject<boolean>(); - private canDeactivateObservable$: Observable<boolean> = Observable.create((observer: Observer<boolean>) => { - this.subscriptions.push( - this.canDeactivateModalResult.subscribe((result: boolean) => { - observer.next(result); - }) - ); + private canDeactivateObservable$: Observable<boolean> = Observable.create((observer: Observer<boolean>) => { + this.canDeactivateModalResult.takeUntil(this.destroyed$).subscribe((result: boolean) => { + observer.next(result); + }); }); - private serviceNamesListSubject: BehaviorSubject<ShipperClusterService[]> = new BehaviorSubject<ShipperClusterService[]>([]); + private serviceNamesListSubject: BehaviorSubject<ShipperClusterService[]> = new BehaviorSubject< + ShipperClusterService[] + >([]); private subscriptions: Subscription[] = []; + private destroyed$ = new Subject(); + constructor( private formBuilder: FormBuilder, private activatedRoute: ActivatedRoute, @@ -118,33 +131,52 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr ngOnInit() { this.subscriptions.push( - this.activatedRoute.params.map(params => params.service).subscribe((service) => { - this.serviceName = service; - }) + this.activatedRoute.params + .map(params => params.service) + .subscribe(service => { + this.serviceName = service; + }) ); if (!this.serviceName) { this.configurationForm.controls.serviceName.setValidators([ Validators.required, formValidators.uniqueServiceNameValidator(this.serviceNamesListSubject) ]); - this.changeDetectionRef.detectChanges(); } - this.configurationComponents$ = this.configurationForm.controls.configuration.valueChanges.map((newValue: string): string[] => { - let components: string[]; - try { - const inputs: {[key: string]: any}[] = (newValue ? JSON.parse(newValue) : {}).input; - components = inputs && inputs.length ? inputs.map(input => input.type) : []; - } catch (error) { - components = []; - } - return components; - }).startWith([]); + this.configurationComponents$ = this.configurationForm.controls.configuration.valueChanges + .map( + (newValue: string): string[] => { + let components: string[]; + try { + const inputs: { [key: string]: any }[] = (newValue ? JSON.parse(newValue) : {}).input; + components = inputs && inputs.length ? inputs.map(input => input.type) : []; + } catch (error) { + components = []; + } + return components || []; + } + ) + .startWith([]); + this.configurationComponentsList$ = this.configurationComponents$ + .map( + (components: string[]): ListItem[] => + components + .filter((component: string) => component) + .map( + (component: string, index: number): ListItem => { + return { + value: component, + label: component, + isChecked: index === 0 + }; + } + ) + ) + .startWith([]); if (this.existingServiceNames instanceof Observable) { - this.subscriptions.push( - this.existingServiceNames.subscribe((serviceNames: ShipperClusterService[]) => { - this.serviceNamesListSubject.next(serviceNames); - }) - ); + this.existingServiceNames.takeUntil(this.destroyed$).subscribe((serviceNames: ShipperClusterService[]) => { + this.serviceNamesListSubject.next(serviceNames); + }); } else { this.serviceNamesListSubject.next(this.existingServiceNames); } @@ -168,7 +200,11 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr } }); } - if (this.validatorForm && changes.clusterName && this.validatorForm.controls.clusterName.value !== changes.clusterName.currentValue) { + if ( + this.validatorForm && + changes.clusterName && + this.validatorForm.controls.clusterName.value !== changes.clusterName.currentValue + ) { this.validatorForm.controls.clusterName.setValue(changes.clusterName.currentValue); this.validatorForm.markAsPristine(); } @@ -178,17 +214,18 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr if (this.subscriptions) { this.subscriptions.forEach(subscription => subscription.unsubscribe()); } + this.destroyed$.next(true); } leaveDirtyFormConfirmed = () => { this.canDeactivateModalResult.next(true); this.isLeavingDirtyForm = false; - } + }; leaveDirtyFormCancelled = () => { this.canDeactivateModalResult.next(false); this.isLeavingDirtyForm = false; - } + }; canDeactivate(): Observable<boolean> { if (this.configurationForm.pristine) { @@ -203,39 +240,33 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr } createForms(): void { - const configuration: ShipperClusterServiceConfigurationInterface = this.configuration || ( - this.serviceName ? this.configuration : new ShipperConfigurationModel() - ); + const configuration: ShipperClusterServiceConfigurationInterface = + this.configuration || (this.serviceName ? this.configuration : new ShipperConfigurationModel()); this.configurationForm = this.formBuilder.group({ clusterName: this.formBuilder.control(this.clusterName, Validators.required), - serviceName: this.formBuilder.control( - this.serviceName, - [Validators.required] - ), - configuration: this.formBuilder.control( - this.getConfigurationAsString(configuration), - [Validators.required, formValidators.configurationValidator()] - ) + serviceName: this.formBuilder.control(this.serviceName, [Validators.required]), + configuration: this.formBuilder.control(this.getConfigurationAsString(configuration), [ + Validators.required, + formValidators.configurationValidator() + ]) }); this.validatorForm = this.formBuilder.group({ - clusterName: this.formBuilder.control( - this.clusterName, - [Validators.required] - ), + clusterName: this.formBuilder.control(this.clusterName, [Validators.required]), componentName: this.formBuilder.control('', [ Validators.required, - formValidators.getConfigurationServiceValidator(this.configurationForm.controls.configuration) + formValidators.getConfigurationServiceValidator( + this.configurationForm.controls.configuration, + listItem => listItem && listItem.length && listItem[0].value + ) ]), sampleData: this.formBuilder.control('', Validators.required), configuration: this.formBuilder.control('', Validators.required) }); - this.subscriptions.push( - this.configurationForm.valueChanges.subscribe(() => { - this.validatorForm.controls.componentName.updateValueAndValidity(); - this.validatorForm.controls.configuration.setValue(this.configurationForm.controls.configuration.value); - }) - ); + this.configurationForm.valueChanges.takeUntil(this.destroyed$).subscribe(() => { + this.validatorForm.controls.componentName.updateValueAndValidity(); + this.validatorForm.controls.configuration.setValue(this.configurationForm.controls.configuration.value); + }); } onConfigurationSubmit(): void { @@ -249,5 +280,4 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr this.validationSubmit.emit(this.validatorForm); } } - } diff --git a/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts b/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts index 50c12375a5..a5901250b6 100644 --- a/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts +++ b/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts @@ -16,46 +16,49 @@ * limitations under the License. */ -import {AbstractControl, ValidatorFn} from '@angular/forms'; -import {ShipperClusterService} from '@modules/shipper/models/shipper-cluster-service.type'; -import {ValidationErrors} from '@angular/forms/src/directives/validators'; -import {BehaviorSubject} from 'rxjs/BehaviorSubject'; +import { AbstractControl, ValidatorFn } from '@angular/forms'; +import { ShipperClusterService } from '@modules/shipper/models/shipper-cluster-service.type'; +import { ValidationErrors } from '@angular/forms/src/directives/validators'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; export function configurationValidator(): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { try { - const json: {[key: string]: any} = JSON.parse(control.value); + const json: { [key: string]: any } = JSON.parse(control.value); return null; } catch (error) { return { - invalidJSON: {value: control.value} + invalidJSON: { value: control.value } }; } }; } -export function uniqueServiceNameValidator( - serviceNames: BehaviorSubject<ShipperClusterService[]> -): ValidatorFn { +export function uniqueServiceNameValidator(serviceNames: BehaviorSubject<ShipperClusterService[]>): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { const services: ShipperClusterService[] = serviceNames.getValue(); - return services && services.indexOf(control.value) > -1 ? { - serviceNameExists: {value: control.value} - } : null; + return services && services.indexOf(control.value) > -1 + ? { + serviceNameExists: { value: control.value } + } + : null; }; } -export function getConfigurationServiceValidator(configControl: AbstractControl): ValidatorFn { +export function getConfigurationServiceValidator(configControl: AbstractControl, valueMapper?: Function): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { let components: string[]; try { - const inputs: {[key: string]: any}[] = (configControl.value ? JSON.parse(configControl.value) : {}).input; + const inputs: { [key: string]: any }[] = (configControl.value ? JSON.parse(configControl.value) : {}).input; components = inputs && inputs.length ? inputs.map(input => input.type) : []; } catch (error) { components = []; } - return components.length && components.indexOf(control.value) === -1 ? { - serviceNameDoesNotExistInConfiguration: {value: control.value} - } : null; + const value = valueMapper ? valueMapper(control.value) : control.value; + return components.length && components.indexOf(value) === -1 + ? { + serviceNameDoesNotExistInConfiguration: { value: value } + } + : null; }; } ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services