cloudfoundry/stratos

View on GitHub
src/frontend/packages/core/src/shared/components/list/list-types/endpoint/base-endpoints-data-source.ts

Summary

Maintainability
A
1 hr
Test Coverage
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { map, pairwise, tap, withLatestFrom } from 'rxjs/operators';

import { GetAllEndpoints } from '../../../../../../../store/src/actions/endpoint.actions';
import { CreatePagination } from '../../../../../../../store/src/actions/pagination.actions';
import { AppState } from '../../../../../../../store/src/app-state';
import { endpointEntityType } from '../../../../../../../store/src/helpers/stratos-entity-factory';
import { EntityMonitorFactory } from '../../../../../../../store/src/monitors/entity-monitor.factory.service';
import { InternalEventMonitorFactory } from '../../../../../../../store/src/monitors/internal-event-monitor.factory';
import { PaginationMonitorFactory } from '../../../../../../../store/src/monitors/pagination-monitor.factory';
import { endpointEntitiesSelector } from '../../../../../../../store/src/selectors/endpoint.selectors';
import { EndpointModel } from '../../../../../../../store/src/types/endpoint.types';
import { PaginationEntityState } from '../../../../../../../store/src/types/pagination.types';
import { DataFunction, DataFunctionDefinition, ListDataSource } from '../../data-sources-controllers/list-data-source';
import { IListDataSourceConfig } from '../../data-sources-controllers/list-data-source-config';
import { RowsState } from '../../data-sources-controllers/list-data-source-types';
import { TableRowStateManager } from '../../list-table/table-row/table-row-state-manager';
import { IListConfig } from '../../list.component.types';
import { ListRowSateHelper } from '../../list.helper';
import { EndpointRowStateSetUpManager } from '../endpoint/endpoint-data-source.helpers';

export function syncPaginationSection(
  store: Store<AppState>,
  action: GetAllEndpoints,
  paginationKey: string
) {
  store.dispatch(new CreatePagination(
    action,
    paginationKey,
    action.paginationKey,
    action.initialParams
  ));
}

export class BaseEndpointsDataSource extends ListDataSource<EndpointModel> {

  public static typeFilterKey = 'endpointType';

  store: Store<AppState>;
  /**
   * Used to distinguish between data sources providing all endpoints or those that only provide endpoints matching this value.
   * Value should match those of an endpoint's `cnsi_type`.
   *
   * Note - Should not be renamed to endpointType to avoid clash with ListDataSource endpointType
   */
  dsEndpointType: string;

  constructor(
    store: Store<AppState>,
    listConfig: IListConfig<EndpointModel>,
    action: GetAllEndpoints,
    dsEndpointType: string = null,
    paginationMonitorFactory: PaginationMonitorFactory,
    entityMonitorFactory: EntityMonitorFactory,
    internalEventMonitorFactory: InternalEventMonitorFactory,
    onlyConnected = true,
    filterByType = false
  ) {
    const rowStateHelper = new ListRowSateHelper();
    const { rowStateManager, sub } = rowStateHelper.getRowStateManager(
      paginationMonitorFactory,
      entityMonitorFactory,
      action.paginationKey,
      action,
      EndpointRowStateSetUpManager,
      false
    );
    const eventSub = BaseEndpointsDataSource.monitorEvents(internalEventMonitorFactory, rowStateManager, store);
    const config = BaseEndpointsDataSource.getEndpointConfig(
      store,
      action,
      listConfig,
      rowStateManager.observable,
      () => {
        eventSub.unsubscribe();
        sub.unsubscribe();
      },
      () => this.store.dispatch(action)
    );

    const transformEntities: (DataFunctionDefinition | DataFunction<EndpointModel>)[] = [{
      type: 'filter',
      field: 'name'
    }];
    if (dsEndpointType || onlyConnected) {
      transformEntities.push((entities: EndpointModel[]) => {
        return dsEndpointType || onlyConnected ? entities.filter(endpoint => {
          return (!onlyConnected || endpoint.connectionStatus === 'connected') &&
            (!dsEndpointType || endpoint.cnsi_type === dsEndpointType);
        }) : entities;
      });
    }
    if (filterByType) {
      transformEntities.push((entities: EndpointModel[], paginationState: PaginationEntityState) =>
        BaseEndpointsDataSource.endpointTypeFilter(entities, paginationState)
      );
    }

    super({
      ...config,
      paginationKey: action.paginationKey,
      transformEntities,
    });
    this.dsEndpointType = dsEndpointType;
  }

  static getEndpointConfig(
    store: Store<AppState>,
    action: GetAllEndpoints,
    listConfig: IListConfig<EndpointModel>,
    rowsState: Observable<RowsState>,
    destroy: () => void,
    refresh: () => void
  ): IListDataSourceConfig<EndpointModel, EndpointModel> {
    return {
      store,
      action,
      schema: action.entity[0],
      getRowUniqueId: (object) => action.entity[0].getId(object),
      getEmptyType: () => ({
        name: '',
        system_shared_token: false,
        metricsAvailable: false,
        sso_allowed: false,
        creator: {
          name: '',
          admin: false,
          system: false
        }
      }),
      paginationKey: action.paginationKey,
      isLocal: true,
      transformEntities: [
        {
          type: 'filter',
          field: 'name'
        },
      ],
      listConfig,
      rowsState,
      destroy,
      refresh
    };
  }
  static monitorEvents(
    internalEventMonitorFactory: InternalEventMonitorFactory,
    rowStateManager: TableRowStateManager,
    store: Store<AppState>
  ) {
    const eventMonitor = internalEventMonitorFactory.getMonitor(endpointEntityType);
    return eventMonitor.hasErroredOverTime().pipe(
      withLatestFrom(store.select(endpointEntitiesSelector)),
      tap(([errored, endpoints]) => Object.keys(errored).forEach(id => {
        if (endpoints[id] && endpoints[id].connectionStatus === 'connected') {
          rowStateManager.updateRowState(id, {
            error: true,
            message: `We've been having trouble communicating with this endpoint`
          });
        }
      }
      )),
      map(([errored]) => errored),
      pairwise(),
      tap(([oldErrored, newErrored]) => Object.keys(oldErrored).forEach(oldId => {
        if (!Object.keys(newErrored).find(newId => newId === oldId)) {
          rowStateManager.updateRowState(oldId, {
            error: false,
            message: ''
          });
        }
      })),
    ).subscribe();
  }

  static endpointTypeFilter: DataFunction<EndpointModel> = (entities: EndpointModel[], paginationState: PaginationEntityState) => {
    if (
      !paginationState.clientPagination ||
      !paginationState.clientPagination.filter ||
      !paginationState.clientPagination.filter.items[BaseEndpointsDataSource.typeFilterKey]
    ) {
      return entities;
    }
    const searchTerm = paginationState.clientPagination.filter.items[BaseEndpointsDataSource.typeFilterKey];
    return searchTerm ?
      entities.filter(endpoint => endpoint.cnsi_type === searchTerm) :
      entities;
  };
}