You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by to...@apache.org on 2018/07/19 12:55:13 UTC

[ambari] branch branch-2.7 updated: [AMBARI-24248] [LogSearch UI] There is no user notification on Log Feeder configuration saving (#1779)

This is an automated email from the ASF dual-hosted git repository.

tobiasistvan pushed a commit to branch branch-2.7
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-2.7 by this push:
     new 743cf13  [AMBARI-24248] [LogSearch UI] There is no user notification on Log Feeder configuration saving (#1779)
743cf13 is described below

commit 743cf1337548639c08d27844c45a05bba2be7f86
Author: Istvan Tobias <to...@gmail.com>
AuthorDate: Thu Jul 19 14:55:11 2018 +0200

    [AMBARI-24248] [LogSearch UI] There is no user notification on Log Feeder configuration saving (#1779)
    
    * [AMBARI-24248] [LogSearch UI] There is no user notification on Log Feeder configuration saving
---
 .../src/app/app-routing.module.ts                  |  3 +-
 .../src/app/components/app.component.ts            |  5 +-
 .../cluster-filter/cluster-filter.component.ts     | 51 ++++++--------
 .../src/app/modules/shared/notifications.less      |  6 +-
 .../src/app/modules/shared/variables.less          |  2 +-
 .../shipper-configuration.component.html           |  1 +
 .../shipper-configuration.component.ts             | 54 ++++++++-------
 ...ipper-service-configuration-form.component.html | 80 +++++++++++-----------
 ...shipper-service-configuration-form.component.ts | 36 ++++------
 .../services/shipper-configuration.service.ts      | 35 ++++++----
 .../ambari-logsearch-web/src/assets/i18n/en.json   |  6 +-
 11 files changed, 138 insertions(+), 141 deletions(-)

diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/app-routing.module.ts b/ambari-logsearch/ambari-logsearch-web/src/app/app-routing.module.ts
index d121254..a55e51a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/app-routing.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/app-routing.module.ts
@@ -37,7 +37,8 @@ const appRoutes: Routes = [{
     component: LogsContainerComponent,
     data: {
       breadcrumbs: 'logs.title',
-      multiClusterFilter: true
+      multiClusterFilter: true,
+      clusterParamKey: 'clusters'
     },
     resolve: {
       breadcrumbs: LogsBreadcrumbsResolverService
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts
index 217ff6a..c711a48 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts
@@ -35,12 +35,13 @@ export class AppComponent {
     .map((dataSetState: DataAvailability) => dataSetState === DataAvailabilityValues.AVAILABLE);
 
   private notificationServiceOptions: Options = {
-    timeOut: 5000,
+    timeOut: 2000,
     showProgressBar: true,
     pauseOnHover: true,
     preventLastDuplicates: 'visible',
     theClass: 'app-notification',
-    icons: notificationIcons
+    icons: notificationIcons,
+    position: ['top', 'left']
   };
 
   constructor(
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/cluster-filter/cluster-filter.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/cluster-filter/cluster-filter.component.ts
index 5f11674..366b764 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/cluster-filter/cluster-filter.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/cluster-filter/cluster-filter.component.ts
@@ -17,7 +17,7 @@
 import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
 import 'rxjs/add/operator/switchMap';
 import {Observable} from 'rxjs/Observable';
-import {ActivatedRouteSnapshot, Router, RoutesRecognized} from '@angular/router';
+import {ActivatedRouteSnapshot, Router, NavigationEnd} from '@angular/router';
 import {Subscription} from 'rxjs/Subscription';
 
 import {ClustersService} from '@app/services/storage/clusters.service';
@@ -43,9 +43,21 @@ export class ClusterFilterComponent implements OnInit, OnDestroy {
   })
   filterDropdown: FilterDropdownComponent;
 
-  private clustersAsListItems$: Observable<ListItem[]>;
-
   private clusterSelectionStoreKey: BehaviorSubject<string> = new BehaviorSubject('');
+
+  private clustersAsListItems$: Observable<ListItem[]> = this.clusterSelectionStoreKey.distinctUntilChanged()
+    .switchMap((selectionStoreKey: string) => Observable.combineLatest(
+        this.clusterSelectionStoreService.getParameter(selectionStoreKey),
+        this.clusterStoreService.getAll()
+      ).map(([selections, clusters]) => {
+        const selectedClusters = selections ? (Array.isArray(selections) ? selections : [selections]) : selections;
+        return clusters.map((cluster) => Object.assign(this.utilsService.getListItemFromString(cluster), {
+            isChecked: selectedClusters && selectedClusters.indexOf(cluster) > -1
+          })
+        );
+      })
+    ).startWith([]);
+
   private readonly defaultUseMultiSelection = true;
   private useMultiSelection: BehaviorSubject<boolean> = new BehaviorSubject(false);
 
@@ -62,10 +74,7 @@ export class ClusterFilterComponent implements OnInit, OnDestroy {
 
   ngOnInit() {
     this.subscriptions.push(
-      this.router.events.filter(routes => routes instanceof RoutesRecognized).subscribe(this.onRecognizedActivatedRouteData)
-    );
-    this.subscriptions.push(
-      this.clusterSelectionStoreKey.subscribe(this.onClusterSelectionStoreKeyChange)
+      this.router.events.filter(routes => routes instanceof NavigationEnd).subscribe(this.onNavigationEnd)
     );
     this.actualizeDropdownSelectionByActivatedRouteSnapshot(this.router.routerState.root.snapshot);
   }
@@ -100,7 +109,8 @@ export class ClusterFilterComponent implements OnInit, OnDestroy {
   }
 
   private setDropdownSelectionByActivatedRouteSnapshot(routeSnapshot: ActivatedRouteSnapshot): void {
-    let clusterSelection = this.routingUtilsService.getParamFromActivatedRouteSnapshot(routeSnapshot, 'cluster');
+    const clusterParamKey: string = this.routingUtilsService.getDataFromActivatedRouteSnapshot(routeSnapshot, 'clusterParamKey');
+    let clusterSelection = this.routingUtilsService.getParamFromActivatedRouteSnapshot(routeSnapshot, clusterParamKey || 'cluster');
     if (clusterSelection) {
       clusterSelection = this.useMultiSelection.getValue() ? clusterSelection.split(/[,;]/) : clusterSelection;
       if (Array.isArray(clusterSelection)) {
@@ -121,7 +131,7 @@ export class ClusterFilterComponent implements OnInit, OnDestroy {
           this.filterDropdown.updateSelection(clusterSelection);
         });
     } else {
-      this.filterDropdown.selection = [];
+      this.filterDropdown.updateSelection(null);
     }
   }
 
@@ -131,8 +141,8 @@ export class ClusterFilterComponent implements OnInit, OnDestroy {
     this.setDropdownSelectionByActivatedRouteSnapshot(routeSnapshot);
   }
 
-  private onRecognizedActivatedRouteData = (routes: RoutesRecognized): void => {
-    this.actualizeDropdownSelectionByActivatedRouteSnapshot(routes.state.root);
+  private onNavigationEnd = (): void => {
+    this.actualizeDropdownSelectionByActivatedRouteSnapshot(this.router.routerState.root.snapshot);
   }
 
   onDropDownSelectionChanged = (values): void => {
@@ -148,23 +158,4 @@ export class ClusterFilterComponent implements OnInit, OnDestroy {
       });
   }
 
-  private setListItems(selectionStoreKey: string): void {
-    this.clustersAsListItems$ = Observable.combineLatest(
-      this.clusterSelectionStoreService.getParameter(selectionStoreKey),
-      this.clusterStoreService.getAll()
-    ).map(([selections, clusters]) => {
-      const selectedClusters = selections ? (Array.isArray(selections) ? selections : [selections]) : selections;
-      return clusters.map((cluster) => Object.assign(this.utilsService.getListItemFromString(cluster), {
-          isChecked: selectedClusters && selectedClusters.indexOf(cluster) > -1
-        })
-      );
-    }).startWith([]);
-  }
-
-  private onClusterSelectionStoreKeyChange = (selectionStoreKey: string): void => {
-    if (selectionStoreKey) {
-      this.setListItems(selectionStoreKey);
-    }
-  }
-
 }
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/notifications.less b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/notifications.less
index 7a0a045..9b19562 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/notifications.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/notifications.less
@@ -23,19 +23,19 @@
   border-radius: 2px;
   box-shadow: 0 5px 15px rgba(0,0,0,.5);
   &.success {
-    border-left: 3px solid @form-success-color;
+    border: 1px solid @form-success-color;
     .fa {
       color: @form-success-color;
     }
   }
   &.info {
-    border-left: 3px solid @form-info-color;
+    border: 1px solid @form-info-color;
     .fa {
       color: @form-info-color;
     }
   }
   &.error {
-    border-left: 3px solid @form-error-color;
+    border: 1px solid @form-error-color;
     .fa {
       color: @form-error-color;
     }
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less
index 55db343..9e1c7ff 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less
@@ -88,6 +88,6 @@
 
 // Notifications
 @notification-color: @base-font-color;
-@notification-title-font-size: 16px;
+@notification-title-font-size: 14px;
 @notification-content-font-size: 12px;
 @notification-background-color: #FFF;
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.html
index b2f05fd..610fb3e 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.html
@@ -41,6 +41,7 @@
               [configuration]="configuration$ | async"
               [existingServiceNames]="serviceNamesList$"
               [validationResponse]="validationResponse"
+              [disabled]="requestInProgress$ | async"
               (configurationSubmit)="onConfigurationFormSubmit($event)"
               (validationSubmit)="onValidationFormSubmit($event)"
             ></shipper-configuration-form>
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts
index 8c43a97..d8fccbd 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts
@@ -32,10 +32,10 @@ import {
   ShipperServiceConfigurationFormComponent
 } from '@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component';
 import {TranslateService} from '@ngx-translate/core';
-import {ClustersService} from '@app/services/storage/clusters.service';
-import {ShipperClusterServiceValidationModel} from '@modules/shipper/models/shipper-cluster-service-validation.model';
 import {ClusterSelectionService} from '@app/services/storage/cluster-selection.service';
 import {Subscription} from 'rxjs/Subscription';
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+import { FormGroup } from '@angular/forms';
 
 @Component({
   selector: 'shipper-configuration',
@@ -52,6 +52,8 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On
   @ViewChild(ShipperServiceConfigurationFormComponent)
   configurationFormRef: ShipperServiceConfigurationFormComponent;
 
+  private requestInProgress$: BehaviorSubject<boolean> = new BehaviorSubject(false);
+
   private clusterName$: Observable<ShipperClusterService> = this.activatedRoute.params.map(params => params.cluster);
   private serviceName$: Observable<ShipperClusterService> = this.activatedRoute.params.map(params => params.service);
 
@@ -59,8 +61,10 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On
     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]) => {
+  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);
     });
@@ -76,8 +80,6 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On
     private shipperConfigurationService: ShipperConfigurationService,
     private notificationService: NotificationService,
     private translate: TranslateService,
-
-    private clustersStoreService: ClustersService,
     private clusterSelectionStoreService: ClusterSelectionService
   ) { }
 
@@ -116,38 +118,39 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On
     }
   }
 
-  private onShipperClusterServiceSelected(serviceName: ShipperClusterService) {
-    this.clusterName$.first().subscribe((clusterName: ShipperCluster) => this.router.navigate(
-      [...this.routerPath, clusterName, serviceName]
-    ));
-  }
-
   private getRouterLink(path: string | string[]): string[] {
     return [...this.routerPath, ...(Array.isArray(path) ? path : [path])];
   }
 
-  getResponseHandler(cmd: string, type: string, msgVariables?: {[key: string]: any}) {
+  getResponseHandler(cmd: string, type: string, form?: FormGroup) {
+    const msgVariables = form.getRawValue();
     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 .errorMessage prop
+      // @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: '', ...msgVariables, ...result};
+      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});
+      if (resultType !== NotificationType.ERROR) {
+        form.markAsPristine();
+      }
+      this.requestInProgress$.next(false);
     };
   }
 
-  onConfigurationFormSubmit(rawValue: any): void {
+  onConfigurationFormSubmit(configurationForm: FormGroup): void {
+    const rawValue = configurationForm.getRawValue();
     this.serviceNamesList$.first().subscribe((services: ShipperClusterService[]) => {
-      const cmd: string = services.indexOf(rawValue.service) > -1 ? 'update' : 'add';
+      const cmd: string = services.indexOf(rawValue.serviceName) > -1 ? 'update' : 'add';
+      this.requestInProgress$.next(true);
       this.shipperConfigurationService[`${cmd}Configuration`]({
-        cluster: rawValue.cluster,
-        service: rawValue.service,
-        configuration: rawValue.configuration
+        cluster: rawValue.clusterName,
+        service: rawValue.serviceName,
+        configuration: JSON.parse(rawValue.configuration)
       }).subscribe(
-        this.getResponseHandler(cmd, NotificationType.SUCCESS, rawValue),
-        this.getResponseHandler(cmd, NotificationType.ERROR, rawValue)
+        this.getResponseHandler(cmd, NotificationType.SUCCESS, configurationForm),
+        this.getResponseHandler(cmd, NotificationType.ERROR, configurationForm)
       );
     });
   }
@@ -156,12 +159,13 @@ export class ShipperConfigurationComponent implements CanComponentDeactivate, On
     this.validationResponse = result;
   }
 
-  onValidationFormSubmit(rawValue: ShipperClusterServiceValidationModel): void {
+  onValidationFormSubmit(validationForm: FormGroup): void {
     this.validationResponse = null;
+    const rawValue = validationForm.getRawValue();
     const request$: Observable<Response> = this.shipperConfigurationService.testConfiguration(rawValue);
     request$.subscribe(
-      this.getResponseHandler('validate', NotificationType.SUCCESS, rawValue),
-      this.getResponseHandler('validate', NotificationType.ERROR, rawValue)
+      this.getResponseHandler('validate', NotificationType.SUCCESS, validationForm),
+      this.getResponseHandler('validate', NotificationType.ERROR, validationForm)
     );
     request$
       .filter((response: Response): boolean => response.ok)
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html
index 9ab7194..bb7034a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html
@@ -21,45 +21,47 @@
   <div class="row">
     <div class="shipper-form-configuration col-md-6">
       <form [formGroup]="configurationForm" (ngSubmit)="onConfigurationSubmit($event)">
-        <h2>{{(serviceName ? 'shipperConfiguration.form.titleEdit' : 'shipperConfiguration.form.titleAdd') | translate}}</h2>
-        <div [ngClass]="{'has-error': serviceNameField.invalid, '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}}
-            </span>
-            <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">
-          <ng-container *ngIf="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}">
-          <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}}
-            </span>
-            <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>
-        </div>
-        <button class="btn btn-primary pull-right" type="submit"
-                [disabled]="(!configurationForm.valid || configurationForm.pristine)">
-          {{'shipperConfiguration.form.saveBtn.label' | translate}}
-        </button>
+        <fieldset [disabled]="disabled">
+          <h2>{{(serviceName ? 'shipperConfiguration.form.titleEdit' : 'shipperConfiguration.form.titleAdd') | translate}}</h2>
+          <div [ngClass]="{'has-error': serviceNameField.invalid, '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}}
+              </span>
+              <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">
+            <ng-container *ngIf="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}">
+            <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}}
+              </span>
+              <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>
+          </div>
+          <button class="btn btn-primary pull-right" type="submit"
+                  [disabled]="(!configurationForm.valid || configurationForm.pristine)">
+            {{'shipperConfiguration.form.saveBtn.label' | translate}}
+          </button>
+        </fieldset>
       </form>
     </div>
     <div class="shipper-form-validator col-md-6">
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts
index 2089306..b41b941 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts
@@ -25,8 +25,6 @@ import 'rxjs/add/operator/startWith';
 
 import {CanComponentDeactivate} from '@modules/shared/services/can-deactivate-guard.service';
 
-import {UtilsService} from '@app/services/utils.service';
-import {ShipperClusterServiceConfigurationModel} from '../../models/shipper-cluster-service-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';
@@ -34,7 +32,6 @@ import {ShipperConfigurationModel} from '../../models/shipper-configuration.mode
 import * as formValidators from '../../directives/validator.directive';
 import {BehaviorSubject} from 'rxjs/BehaviorSubject';
 import {Subscription} from 'rxjs/Subscription';
-import {ShipperClusterServiceValidationModel} from '@modules/shipper/models/shipper-cluster-service-validation.model';
 import {ActivatedRoute} from '@angular/router';
 
 @Component({
@@ -62,11 +59,14 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr
   @Input()
   validationResponse: {[key: string]: any};
 
+  @Input()
+  disabled = false;
+
   @Output()
-  configurationSubmit: EventEmitter<ShipperClusterServiceConfigurationModel> = new EventEmitter<ShipperClusterServiceConfigurationModel>();
+  configurationSubmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
 
   @Output()
-  validationSubmit: EventEmitter<ShipperClusterServiceValidationModel> = new EventEmitter<ShipperClusterServiceValidationModel>();
+  validationSubmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
 
   private configurationComponents$: Observable<string[]>;
 
@@ -107,7 +107,6 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr
   private subscriptions: Subscription[] = [];
 
   constructor(
-    private utilsService: UtilsService,
     private formBuilder: FormBuilder,
     private activatedRoute: ActivatedRoute,
     private changeDetectionRef: ChangeDetectorRef
@@ -181,10 +180,6 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr
     }
   }
 
-  onServiceParamsChange = (service): void => {
-
-  }
-
   leaveDirtyFormConfirmed = () => {
     this.canDeactivateModalResult.next(true);
     this.isLeavingDirtyForm = false;
@@ -232,33 +227,26 @@ export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestr
         Validators.required,
         formValidators.getConfigurationServiceValidator(this.configurationForm.controls.configuration)
       ]),
-      sampleData: this.formBuilder.control('', Validators.required)
+      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);
       })
     );
   }
 
-  onConfigurationSubmit(configurationForm: FormGroup): void {
+  onConfigurationSubmit(): void {
     if (this.configurationForm.valid) {
-      const rawValue = this.configurationForm.getRawValue();
-      const serviceName: string = Array.isArray(rawValue.serviceName) ? rawValue.serviceName.shift().value : rawValue.serviceName;
-      this.configurationSubmit.emit({
-        cluster: rawValue.clusterName,
-        service: serviceName,
-        configuration: JSON.parse(rawValue.configuration)
-      });
+      this.configurationSubmit.emit(this.configurationForm);
     }
   }
 
   onValidationSubmit(): void {
-    if (this.validatorForm) {
-      this.validationSubmit.emit({
-        configuration: this.configurationForm.controls.configuration.value,
-        ...this.validatorForm.getRawValue()
-      });
+    if (this.validatorForm.valid) {
+      this.validationSubmit.emit(this.validatorForm);
     }
   }
 
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/services/shipper-configuration.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/services/shipper-configuration.service.ts
index fd9ac2c..b538c0c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/services/shipper-configuration.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shipper/services/shipper-configuration.service.ts
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 import { Injectable } from '@angular/core';
-import {Response} from '@angular/http';
+import {Response, ResponseOptions, ResponseType} from '@angular/http';
 import {Observable} from 'rxjs/Observable';
 import 'rxjs/add/operator/catch';
 
@@ -31,7 +31,20 @@ export class ShipperConfigurationService {
     private httpClientService: HttpClientService
   ) { }
 
-  addConfiguration(configuration: ShipperClusterServiceConfigurationModel): Observable<ShipperClusterServiceConfigurationModel | Error> {
+  createResponseWithConfigBody(configuration: ShipperClusterServiceConfigurationModel, originalResponse?: Response): Response {
+    return new Response(
+      new ResponseOptions({
+        body: configuration,
+        status: originalResponse ? originalResponse.status : null,
+        statusText: originalResponse ? originalResponse.statusText : null,
+        headers: originalResponse ? originalResponse.headers : null,
+        type: originalResponse ? originalResponse.type : ResponseType.Basic,
+        url: originalResponse ? originalResponse.url : ''
+      })
+    );
+  }
+
+  addConfiguration(configuration: ShipperClusterServiceConfigurationModel): Observable<Response | Error> {
     return this.httpClientService.post(
       'shipperClusterServiceConfiguration',
       configuration.configuration,
@@ -40,15 +53,13 @@ export class ShipperConfigurationService {
         cluster: configuration.cluster,
         service: configuration.service
       })
-      .map((response: Response): ShipperClusterServiceConfigurationModel => {
-        return configuration;
-      })
-      .catch((error: Response): Observable<Error> => {
-        return Observable.of(new Error(error.json().message || ''));
+      .map((response: Response): Response => this.createResponseWithConfigBody(configuration, response))
+      .catch((error: Response): Observable<Response> => {
+        return Observable.of(error);
       });
   }
 
-  updateConfiguration(configuration: ShipperClusterServiceConfigurationModel): Observable<ShipperClusterServiceConfigurationModel | Error> {
+  updateConfiguration(configuration: ShipperClusterServiceConfigurationModel): Observable<Response> {
     return this.httpClientService.put(
       'shipperClusterServiceConfiguration',
       configuration.configuration,
@@ -57,11 +68,9 @@ export class ShipperConfigurationService {
         cluster: configuration.cluster,
         service: configuration.service
       })
-      .map((response: Response): ShipperClusterServiceConfigurationModel => {
-        return configuration;
-      })
-      .catch((error: Response): Observable<Error> => {
-        return Observable.of(new Error(error.json().message || ''));
+      .map((response: Response): Response => this.createResponseWithConfigBody(configuration, response))
+      .catch((error: Response): Observable<Response> => {
+        return Observable.of(error);
       });
   }
 
diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
index da7fa62..9f6c772 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
+++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
@@ -255,10 +255,10 @@
 
   "shipperConfiguration.action.add.title": "New Configuration",
   "shipperConfiguration.action.add.success.message": "New configuration has been added successfully.",
-  "shipperConfiguration.action.add.error.message": "Error at adding new configuration.<div class='cluster-name'>Cluster: {{clusterName}}</div><div class='service-name'>Service: {{componentName}}</div>",
+  "shipperConfiguration.action.add.error.message": "Error at adding new configuration.<div class='cluster-name'>Cluster: {{cluster}}</div><div class='service-name'>Service: {{service}}</div>",
   "shipperConfiguration.action.update.title": "Update Configuration",
-  "shipperConfiguration.action.update.success.message": "The configuration has been updated successfully.<div class='cluster-name'>Cluster: {{clusterName}}</div><div class='service-name'>Service: {{componentName}}</div>",
-  "shipperConfiguration.action.update.error.message": "Error at updating the configuration.<div class='cluster-name'>Cluster: {{clusterName}}</div><div class='service-name'>Service: {{componentName}}</div>",
+  "shipperConfiguration.action.update.success.message": "The configuration has been updated successfully.<div class='cluster-name'>Cluster: {{cluster}}</div><div class='service-name'>Service: {{service}}</div>",
+  "shipperConfiguration.action.update.error.message": "Error at updating the configuration.<div class='cluster-name'>Cluster: {{cluster}}</div><div class='service-name'>Service: {{service}}</div>",
   "shipperConfiguration.action.validate.title": "Validate Configuration",
   "shipperConfiguration.action.validate.success.message": "The configuration is valid.",
   "shipperConfiguration.action.validate.error.message": "The configuration is not valid.<div class='cluster-name'>Cluster: {{clusterName}}</div><div class='service-name'>Service: {{componentName}}</div><div class='error-message'>{{errorMessage}}</div>",