cloudfoundry/stratos

View on GitHub
src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-credential/edit-autoscaler-credential.component.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material/core';
import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
import { Store } from '@ngrx/store';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { delay, filter, first, map, pairwise, publishReplay, refCount, tap } from 'rxjs/operators';

import { ApplicationService } from '../../../../cloud-foundry/src/features/applications/application.service';
import { safeUnsubscribe } from '../../../../core/src/core/utils.service';
import { ConfirmationDialogConfig } from '../../../../core/src/shared/components/confirmation-dialog.config';
import { ConfirmationDialogService } from '../../../../core/src/shared/components/confirmation-dialog.service';
import { AppState } from '../../../../store/src/app-state';
import { entityCatalog } from '../../../../store/src/entity-catalog/entity-catalog';
import { EntityService } from '../../../../store/src/entity-service';
import { EntityServiceFactory } from '../../../../store/src/entity-service-factory.service';
import { ActionState } from '../../../../store/src/reducers/api-request-reducer/types';
import { selectDeletionInfo } from '../../../../store/src/selectors/api.selectors';
import {
  DeleteAppAutoscalerCredentialAction,
  UpdateAppAutoscalerCredentialAction,
} from '../../store/app-autoscaler.actions';
import { AppAutoscalerCredential } from '../../store/app-autoscaler.types';

@Component({
  selector: 'app-edit-autoscaler-credential',
  templateUrl: './edit-autoscaler-credential.component.html',
  styleUrls: ['./edit-autoscaler-credential.component.scss'],
  providers: [
    { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher },
  ]
})
export class EditAutoscalerCredentialComponent implements OnInit, OnDestroy {

  parentUrl = `/applications/${this.applicationService.cfGuid}/${this.applicationService.appGuid}/autoscale`;
  applicationName$: Observable<string>;

  public editCredentialForm: FormGroup;
  public appAutoscalerCredential$: Observable<AppAutoscalerCredential>;

  private appAutoscalerCredentialErrorSub: Subscription;
  private appAutoscalerCredentialSnackBarRef: MatSnackBarRef<SimpleSnackBar>;


  private creating = new BehaviorSubject(false);
  public creating$ = this.creating.asObservable();
  private deleting = new BehaviorSubject(false);
  public deleting$ = this.deleting.asObservable();

  constructor(
    public applicationService: ApplicationService,
    private fb: FormBuilder,
    private store: Store<AppState>,
    private entityServiceFactory: EntityServiceFactory,
    private appAutoscalerCredentialSnackBar: MatSnackBar,
    private confirmDialog: ConfirmationDialogService,
  ) {
    this.editCredentialForm = this.fb.group({
      actype: new FormControl({ value: true }),
      acusername: new FormControl({ value: '', disabled: true }, Validators.required),
      acpassword: new FormControl({ value: '', disabled: true }, Validators.required),
    });
  }

  ngOnInit() {
    this.applicationName$ = this.applicationService.app$.pipe(
      map(({ entity }) => entity ? entity.entity.name : null),
      publishReplay(1),
      refCount()
    );
  }

  ngOnDestroy(): void {
    if (this.appAutoscalerCredentialSnackBarRef) {
      this.appAutoscalerCredentialSnackBarRef.dismiss();
    }
    safeUnsubscribe(this.appAutoscalerCredentialErrorSub);
  }

  toggleChange() {
    if (this.editCredentialForm.controls.actype.value) {
      this.editCredentialForm.controls.acusername.setValue('');
      this.editCredentialForm.controls.acpassword.setValue('');
      this.editCredentialForm.controls.acusername.disable();
      this.editCredentialForm.controls.acpassword.disable();
    } else {
      this.editCredentialForm.controls.acusername.setValue('');
      this.editCredentialForm.controls.acpassword.setValue('');
      this.editCredentialForm.controls.acusername.enable();
      this.editCredentialForm.controls.acpassword.enable();
    }
  }

  createCredential() {
    this.creating.next(true);
    let action: UpdateAppAutoscalerCredentialAction;
    if (this.editCredentialForm.controls.actype.value) {
      action = new UpdateAppAutoscalerCredentialAction(this.applicationService.appGuid, this.applicationService.cfGuid);
    } else {
      const credential: AppAutoscalerCredential = {
        username: this.editCredentialForm.controls.acusername.value,
        password: this.editCredentialForm.controls.acpassword.value,
      };
      action = new UpdateAppAutoscalerCredentialAction(this.applicationService.appGuid, this.applicationService.cfGuid, credential);
    }
    const updateAppAutoscalerCredentialService: EntityService = this.entityServiceFactory.create(
      this.applicationService.appGuid,
      action,
    );
    this.appAutoscalerCredential$ = updateAppAutoscalerCredentialService.entityObs$.pipe(
      filter(({ entity, entityRequestInfo }) => {
        return entityRequestInfo && !entityRequestInfo.creating && !entityRequestInfo.deleting.busy;
      }),
      map(({ entity }) => entity ? entity.entity : null),
      map(creds => {
        if (!creds) {
          return;
        }
        return {
          ...creds,
          authHeader: 'basic ' + btoa(`${creds.username}:${creds.password}`),
          fullUrl: `${creds.url}/v1/apps/${creds.app_id}/metrics`
        };
      }),
      publishReplay(1),
      refCount()
    );
    updateAppAutoscalerCredentialService.entityMonitor.getUpdatingSection(action.updatingKey).pipe(
      delay(150),
      pairwise(),
      filter(([oldV, newV]) => oldV.busy && !newV.busy),
      map(([, newV]) => newV),
      first(),
    ).subscribe(actionState => {
      this.creating.next(false);
      if (actionState.error) {
        if (this.appAutoscalerCredentialSnackBarRef) {
          this.appAutoscalerCredentialSnackBarRef.dismiss();
        }
        this.appAutoscalerCredentialSnackBarRef =
          this.appAutoscalerCredentialSnackBar.open(`Failed to create credentials: ${actionState.message}`, 'Dismiss');
      }

    });
    this.store.dispatch(action);
  }

  deleteCredentialConfirm() {
    const confirmation = new ConfirmationDialogConfig(
      'Delete Credentials',
      'Are you sure you want to delete the credentials?',
      'Delete',
      true
    );
    this.confirmDialog.open(confirmation, () => {
      this.deleteCredential().pipe(
        first(),
      ).subscribe(actionState => {
        if (actionState.error) {
          if (this.appAutoscalerCredentialSnackBarRef) {
            this.appAutoscalerCredentialSnackBarRef.dismiss();
          }
          this.appAutoscalerCredentialSnackBarRef =
            this.appAutoscalerCredentialSnackBar.open(`Failed to delete credential: ${actionState.message}`, 'Dismiss');
        }
      });
    });
  }

  deleteCredential(): Observable<ActionState> {
    this.deleting.next(true);
    const action = new DeleteAppAutoscalerCredentialAction(this.applicationService.appGuid, this.applicationService.cfGuid);
    this.store.dispatch(action);
    const entityKey = entityCatalog.getEntityKey(action);

    return this.store.select(selectDeletionInfo(entityKey, this.applicationService.appGuid)).pipe(
      delay(250),
      pairwise(),
      filter(([oldV, newV]) => oldV.busy && !newV.busy),
      map(([, newV]) => newV),
      tap(() => this.deleting.next(false))
    );
  }

}