cloudfoundry/stratos

View on GitHub
src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/tabs/gitscm-tab/gitscm-tab.component.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import { DatePipe } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
import { Store } from '@ngrx/store';
import { GitCommit, gitEntityCatalog, GitMeta, GitRepo, GitSCMService, GitSCMType, SCMIcon } from '@stratosui/git';
import { Observable, Subscription } from 'rxjs';
import {
  distinctUntilChanged,
  filter,
  first,
  map,
  publishReplay,
  refCount,
  startWith,
  switchMap,
  withLatestFrom,
} from 'rxjs/operators';

import { ListConfig } from '../../../../../../../../core/src/shared/components/list/list.component.types';
import {
  NoContentMessageLine,
} from '../../../../../../../../core/src/shared/components/no-content-message/no-content-message.component';
import { CFAppState } from '../../../../../../cf-app-state';
import {
  GithubCommitsListConfigServiceAppTab,
} from '../../../../../../shared/components/list/list-types/github-commits/github-commits-list-config-app-tab.service';
import { ApplicationService } from '../../../../application.service';
import { EnvVarStratosProject } from '../build-tab/application-env-vars.service';

@Component({
  selector: 'app-gitscm-tab',
  templateUrl: './gitscm-tab.component.html',
  styleUrls: ['./gitscm-tab.component.scss'],
  providers: [
    {
      provide: ListConfig,
      useFactory: (
        store: Store<CFAppState>,
        datePipe: DatePipe,
        scmService: GitSCMService,
        applicationService: ApplicationService) => {
        return new GithubCommitsListConfigServiceAppTab(store, datePipe, scmService, applicationService);
      },
      deps: [Store, DatePipe, GitSCMService, ApplicationService]
    }
  ],
})
export class GitSCMTabComponent implements OnInit, OnDestroy {

  public hasRepo$: Observable<boolean>;
  public isLoading$: Observable<boolean>;

  public gitSCMRepo$: Observable<GitRepo>;
  public commit$: Observable<GitCommit>;
  public isHead$: Observable<boolean>;

  private gitSCMRepoErrorSub: Subscription;
  private snackBarRef: MatSnackBarRef<SimpleSnackBar>;

  public noContentFirstLine = 'Unable to fetch details';
  public noContentSecondLine: NoContentMessageLine = {
    text: 'This repository may be private or has been removed.'
  };
  public noContentOtherLines: NoContentMessageLine[] = [{
    text: 'Alternatively this may be due to a communication issue.'
  }];
  public icon$: Observable<SCMIcon>;

  ngOnDestroy(): void {
    if (this.snackBarRef) {
      this.snackBarRef.dismiss();
    }
    if (this.gitSCMRepoErrorSub) {
      this.gitSCMRepoErrorSub.unsubscribe();
    }
  }

  constructor(
    public appService: ApplicationService,
    private snackBar: MatSnackBar,
    private scmService: GitSCMService
  ) { }

  private createBaseGitMeta(stProject: EnvVarStratosProject): GitMeta {
    // Fallback to type if scm is not set (legacy support)
    const scmType = stProject.deploySource.scm || stProject.deploySource.type;
    const scm = this.scmService.getSCM(scmType as GitSCMType, stProject.deploySource.endpointGuid);

    return { projectName: stProject.deploySource.project, scm };
  }

  ngOnInit() {
    const coreInfo$: Observable<[EnvVarStratosProject, GitMeta]> = this.appService.applicationStratProject$.pipe(
      first(),
      map(stProject => [stProject, this.createBaseGitMeta(stProject)])
    );

    this.icon$ = this.appService.applicationStratProject$.pipe(
      first(),
      map((stProject: EnvVarStratosProject) => {
        const meta: GitMeta = this.createBaseGitMeta(stProject);
        return meta.scm.getIcon();
      })
    );

    this.hasRepo$ = this.appService.applicationStratProject$.pipe(
      first(),
      switchMap((stProject: EnvVarStratosProject) => {
        const gitRepInfoMeta: GitMeta = this.createBaseGitMeta(stProject);
        return gitEntityCatalog.repo.store.getRepoInfo.getEntityService(gitRepInfoMeta).entityObs$;
      }),
      map(entity => entity.entity ? true : entity.entityRequestInfo.error ? false : undefined),
      startWith(undefined),
      publishReplay(1),
      refCount()
    );

    this.isLoading$ = this.hasRepo$.pipe(
      filter(hasRepo => hasRepo !== undefined),
      map(() => false),
      startWith(true)
    );

    const blockedOnRepo$: Observable<[EnvVarStratosProject, GitMeta]> = this.hasRepo$.pipe(
      filter(hasRepo => hasRepo),
      switchMap(() => coreInfo$)
    );

    this.gitSCMRepo$ = blockedOnRepo$.pipe(
      map(([, baseGitMeta]) => gitEntityCatalog.repo.store.getRepoInfo.getEntityService(baseGitMeta)),
      switchMap(repoService => repoService.waitForEntity$),
      map(p => p.entity)
    );

    this.gitSCMRepoErrorSub = this.hasRepo$.pipe(
      filter(hasRepo => hasRepo === false),
      switchMap(() => coreInfo$),
      switchMap(([, baseGitMeta]) => gitEntityCatalog.repo.store.getRepoInfo.getEntityService(baseGitMeta).entityMonitor.entityRequest$),
      map(request => request.message),
      distinctUntilChanged(),
      withLatestFrom(coreInfo$)
    ).subscribe(([errorMessage, [, baseGitMeta]]) => {
      if (this.snackBarRef) {
        this.snackBarRef.dismiss();
      }
      this.snackBarRef = this.snackBar.open(`Unable to fetch ${baseGitMeta.scm.getLabel()} project: ${errorMessage}`, 'Dismiss');
    });

    this.commit$ = blockedOnRepo$.pipe(
      map(([stProject, baseGitMeta]) => gitEntityCatalog.commit.store.getEntityService(null, null, {
        ...baseGitMeta,
        commitSha: stProject.deploySource.commit.trim()
      })),
      switchMap(commitService => commitService.waitForEntity$),
      map(p => p.entity)
    );
    this.isHead$ = blockedOnRepo$.pipe(
      map(([stProject, baseGitMeta]) => gitEntityCatalog.branch.store.getEntityService(undefined, undefined, {
        ...baseGitMeta,
        branchName: stProject.deploySource.branch
      })),
      switchMap(branchService => branchService.waitForEntity$),
      withLatestFrom(blockedOnRepo$),
      map(([p, [stProject]]) => p.entity.commit.sha === stProject.deploySource.commit.trim()),
    );
  }
}