Skip to content
This repository has been archived by the owner on Jan 24, 2023. It is now read-only.

Commit

Permalink
Filter endpoints by type (#434)
Browse files Browse the repository at this point in the history
* Add endpoint type filter to endpoint page list
- Need to decide - always show filter or only when above certain number of endpoint types?
- Need to decide - show base enpdoints or endpoint with subtypes

* Changes to suse extensions

* Fix issue where endpoint list sort/filter was lost on unregister/disconnect/connect

* Changes following review
- only filter by type, ignore sub type
- only show filter if there are more than two types of endpoint registered

* Tidy up

* Changes following review, fix empty sort drop down on cf endpoints list
  • Loading branch information
richard-cox committed Sep 10, 2020
1 parent aa675d3 commit 3d4829d
Show file tree
Hide file tree
Showing 14 changed files with 185 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class CFEndpointsListConfigService implements IListConfig<EndpointModel>
paginationMonitorFactory: PaginationMonitorFactory,
entityMonitorFactory: EntityMonitorFactory,
internalEventMonitorFactory: InternalEventMonitorFactory,
endpointsListConfigService: EndpointsListConfigService
endpointsListConfigService: EndpointsListConfigService,
) {
this.columns = endpointsListConfigService.columns.filter(column => {
return column.columnId !== 'type';
Expand All @@ -46,7 +46,8 @@ export class CFEndpointsListConfigService implements IListConfig<EndpointModel>
this,
paginationMonitorFactory,
entityMonitorFactory,
internalEventMonitorFactory);
internalEventMonitorFactory,
);
}
public getColumns = () => this.columns;
public getGlobalActions = () => [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { InternalEventMonitorFactory } from '../../../../../../../store/src/moni
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 { ListDataSource } from '../../data-sources-controllers/list-data-source';
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';
Expand All @@ -27,11 +28,15 @@ export function syncPaginationSection(
store.dispatch(new CreatePagination(
action,
paginationKey,
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.
Expand All @@ -49,7 +54,8 @@ export class BaseEndpointsDataSource extends ListDataSource<EndpointModel> {
paginationMonitorFactory: PaginationMonitorFactory,
entityMonitorFactory: EntityMonitorFactory,
internalEventMonitorFactory: InternalEventMonitorFactory,
onlyConnected = true
onlyConnected = true,
filterByType = false
) {
const rowStateHelper = new ListRowSateHelper();
const { rowStateManager, sub } = rowStateHelper.getRowStateManager(
Expand All @@ -73,21 +79,28 @@ export class BaseEndpointsDataSource extends ListDataSource<EndpointModel> {
() => 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: [
(entities: EndpointModel[]) => {
return dsEndpointType || onlyConnected ? entities.filter(endpoint => {
return (!onlyConnected || endpoint.connectionStatus === 'connected') &&
(!dsEndpointType || endpoint.cnsi_type === dsEndpointType);
}) : entities;
},
{
type: 'filter',
field: 'name'
},
],
transformEntities,
});
this.dsEndpointType = dsEndpointType;
}
Expand Down Expand Up @@ -154,4 +167,18 @@ export class BaseEndpointsDataSource extends ListDataSource<EndpointModel> {
})),
).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;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export class EndpointsDataSource extends BaseEndpointsDataSource {
listConfig: IListConfig<EndpointModel>,
paginationMonitorFactory: PaginationMonitorFactory,
entityMonitorFactory: EntityMonitorFactory,
internalEventMonitorFactory: InternalEventMonitorFactory
internalEventMonitorFactory: InternalEventMonitorFactory,
filterByType = false
) {
super(
store,
Expand All @@ -28,7 +29,8 @@ export class EndpointsDataSource extends BaseEndpointsDataSource {
paginationMonitorFactory,
entityMonitorFactory,
internalEventMonitorFactory,
false
false,
filterByType
);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { filter } from 'rxjs/operators';
import { BehaviorSubject, combineLatest, of } from 'rxjs';
import { debounceTime, filter, map } from 'rxjs/operators';

import { ListView } from '../../../../../../../store/src/actions/list.actions';
import { SetClientFilter } from '../../../../../../../store/src/actions/pagination.actions';
import { AppState } from '../../../../../../../store/src/app-state';
import { entityCatalog } from '../../../../../../../store/src/entity-catalog/entity-catalog';
import { FavoritesConfigMapper } from '../../../../../../../store/src/favorite-config-mapper';
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 { stratosEntityCatalog } from '../../../../../../../store/src/stratos-entity-catalog';
import { EndpointModel } from '../../../../../../../store/src/types/endpoint.types';
import { PaginationEntityState } from '../../../../../../../store/src/types/pagination.types';
import { createTableColumnFavorite } from '../../list-table/table-cell-favorite/table-cell-favorite.component';
import { ITableColumn } from '../../list-table/table.types';
import { IListAction, IListConfig, ListViewTypes } from '../../list.component.types';
import {
IListAction,
IListConfig,
IListMultiFilterConfig,
IListMultiFilterConfigItem,
ListViewTypes,
} from '../../list.component.types';
import { BaseEndpointsDataSource } from './base-endpoints-data-source';
import { EndpointCardComponent } from './endpoint-card/endpoint-card.component';
import { EndpointListHelper } from './endpoint-list.helpers';
import { EndpointsDataSource } from './endpoints-data-source';
Expand Down Expand Up @@ -110,12 +121,14 @@ export class EndpointsListConfigService implements IListConfig<EndpointModel> {
(row: EndpointModel) => favoritesConfigMapper.getFavoriteEndpointFromEntity(row)
);
this.columns.push(favoriteCell);

this.dataSource = new EndpointsDataSource(
this.store,
this,
paginationMonitorFactory,
entityMonitorFactory,
internalEventMonitorFactory
internalEventMonitorFactory,
true
);
}

Expand All @@ -124,9 +137,68 @@ export class EndpointsListConfigService implements IListConfig<EndpointModel> {
public getSingleActions = () => this.singleActions;
public getColumns = () => this.columns;
public getDataSource = () => this.dataSource;
public getMultiFiltersConfigs = () => [];

public getMultiFiltersConfigs = (): IListMultiFilterConfig[] => [this.createEndpointTypeFilter()];

private getEndpointTypeString(endpoint: EndpointModel): string {
return entityCatalog.getEndpoint(endpoint.cnsi_type, endpoint.sub_type).definition.label;
}

private createEndpointTypeFilter(): IListMultiFilterConfig {
return {
key: BaseEndpointsDataSource.typeFilterKey,
label: 'Endpoint Type',
list$: combineLatest([
stratosEntityCatalog.endpoint.store.getPaginationMonitor().currentPage$,
stratosEntityCatalog.endpoint.store.getPaginationMonitor().pagination$
]).pipe(
debounceTime(100),// This can get pretty spammy, to help protect resetEndpointTypeFilter allow a pause
filter(([endpoints, pagination]) => !!endpoints),
map(([endpoints, pagination]) => {
// Provide a list of endpoint types only if there are more than two registered endpoint types
const types: { [type: string]: boolean; } = {};
for (const endpoint of endpoints) {
types[endpoint.cnsi_type] = true;
}
if (Object.values(types).filter(type => type).length < 2) {
// If we're going to hid the endpoint filter ensure any existing filter value is reset
this.resetEndpointTypeFilter(pagination);
return [];
}
return entityCatalog.getAllBaseEndpointTypes()
.sort((a, b) => a.definition.renderPriority - b.definition.renderPriority)
.filter(et => types[et.type])
.map(et => {
const res: IListMultiFilterConfigItem = {
label: et.definition.label,
item: et,
value: et.type
};
return res;
});
})
),
loading$: of(false),
select: new BehaviorSubject(undefined)
};
}

private resetEndpointTypeFilter(pagination: PaginationEntityState) {
if (
pagination.clientPagination &&
pagination.clientPagination.filter &&
pagination.clientPagination.filter.items[BaseEndpointsDataSource.typeFilterKey]
) {
const clientPaginationFilter = {
...pagination.clientPagination.filter,
items: {
...pagination.clientPagination.filter.items,
[BaseEndpointsDataSource.typeFilterKey]: null
}
};
this.store.dispatch(
new SetClientFilter(this.dataSource.masterAction, this.dataSource.paginationKey, clientPaginationFilter)
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
<div class="list-component__header__left--multi-filters"
[hidden]="(!(hasRows$ | async) && !filter) || (isAddingOrSelecting$ | async)">
<ng-container *ngFor="let multiFilterManager of multiFilterManagers; first as isFirst">
<mat-form-field *ngIf="!isFirst || !(multiFilterManager.hasOneItem$ | async)" [floatLabel]="'never'">
<mat-form-field *ngIf="!isFirst || !(multiFilterManager.hasOneOrLessItems$ | async)"
[floatLabel]="'never'">
<mat-select id="{{multiFilterManager.filterKey}}" matInput [(value)]="multiFilterManager.value"
[disabled]="!(multiFilterManager.filterIsReady$ | async)"
(selectionChange)="multiFilterManager.selectItem($event.value)">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Type } from '@angular/core';
import { Injectable, Type } from '@angular/core';
import * as moment from 'moment';
import { BehaviorSubject, combineLatest, Observable, of as observableOf } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
Expand All @@ -13,7 +13,6 @@ import { ListDataSource } from './data-sources-controllers/list-data-source';
import { IListDataSource } from './data-sources-controllers/list-data-source-types';
import { CardTypes } from './list-cards/card/card.component';
import { ITableColumn, ITableText } from './list-table/table.types';
import { Injectable } from "@angular/core";
import { CardCell } from './list.types';

export enum ListViewTypes {
Expand Down Expand Up @@ -204,7 +203,7 @@ export class MultiFilterManager<T> {
public filterIsReady$: Observable<boolean>;
public filterItems$: Observable<IListMultiFilterConfigItem[]>;
public hasItems$: Observable<boolean>;
public hasOneItem$: Observable<boolean>;
public hasOneOrLessItems$: Observable<boolean>;
public value: string;

public filterKey: string;
Expand All @@ -217,7 +216,7 @@ export class MultiFilterManager<T> {
this.filterKey = this.multiFilterConfig.key;
this.allLabel = multiFilterConfig.allLabel || 'All';
this.filterItems$ = this.getItemObservable(multiFilterConfig);
this.hasOneItem$ = this.filterItems$.pipe(map(items => items.length === 1));
this.hasOneOrLessItems$ = this.filterItems$.pipe(map(items => items.length <= 1));
this.hasItems$ = this.filterItems$.pipe(map(items => !!items.length));
this.filterIsReady$ = this.getReadyObservable(multiFilterConfig, dataSource, this.hasItems$);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ export class CreatePagination extends BasePaginationAction implements Action {
/**
* @param seed The pagination key for the section we should use as a seed when creating the new pagination section.
*/
constructor(pEntityConfig: Partial<EntityCatalogEntityConfig>, public paginationKey: string, public seed?: string) {
constructor(
pEntityConfig: Partial<EntityCatalogEntityConfig>,
public paginationKey: string,
public seed?: string,
public initialParams?: PaginationParam
) {
super(pEntityConfig);
}
type = CREATE_PAGINATION;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { EndpointActionComplete } from '../../actions/endpoint.actions';
import { entityCatalog } from '../../entity-catalog/entity-catalog';
import { PaginationState } from '../../types/pagination.types';
import { getDefaultPaginationEntityState } from './pagination-reducer-reset-pagination';

Expand All @@ -22,14 +20,3 @@ export function paginationClearAllTypes(state: PaginationState, entityKeys: stri
return prevState;
}, state);
}

export function clearEndpointEntities(state: PaginationState, action: EndpointActionComplete) {
const entityKeys = entityCatalog.getAllEntitiesForEndpointType(action.endpointType).map(entity => entity.entityKey);
if (entityKeys.length > 0) {
return paginationClearAllTypes(
state,
entityKeys
);
}
return state;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { CreatePagination } from '../../actions/pagination.actions';
import { entityCatalog } from '../../entity-catalog/entity-catalog';
import { EntityCatalogEntityConfig } from '../../entity-catalog/entity-catalog.types';
import { PaginationEntityState, PaginationState } from '../../types/pagination.types';
import {
PaginationEntityState,
PaginationEntityTypeState,
PaginationParam,
PaginationState,
} from '../../types/pagination.types';
import { spreadClientPagination } from './pagination-reducer.helper';

function getPaginationKey(entityConfig: EntityCatalogEntityConfig) {
Expand Down Expand Up @@ -42,15 +47,18 @@ function mergeWithSeed(state: PaginationState, action: CreatePagination, default
const currentPagination = state[entityKey][action.paginationKey] || defaultState;
const seeded = action.seed && state[entityKey] && state[entityKey][action.seed];
const seedPagination = seeded ? state[entityKey][action.seed] : defaultState;
const entityState = {
const entityState: PaginationEntityTypeState = {
...newState[entityKey],
[action.paginationKey]: {
...seedPagination,
// If we already have a pagination section, retain these values.
pageCount: currentPagination.pageCount,
currentPage: currentPagination.currentPage,
clientPagination: mergePaginationSections(currentPagination, seedPagination, defaultState),
seed: seeded ? action.seed : null
seed: seeded ? action.seed : null,
// Ensure any filters from seed are not carried into new list
// For example, sort by type on endpoints page, go to cf endpoint page and type is not shown
params: mergeParamsSections(currentPagination, action.initialParams)
}
};
return {
Expand All @@ -59,6 +67,16 @@ function mergeWithSeed(state: PaginationState, action: CreatePagination, default
};
}

function mergeParamsSections(
currentPagination: PaginationEntityState,
initialSeedParams: PaginationParam = {},
): PaginationParam {
return {
...currentPagination.params,
...initialSeedParams,
};
}

function mergePaginationSections(
currentPagination: PaginationEntityState,
seedPagination: PaginationEntityState,
Expand Down
Loading

0 comments on commit 3d4829d

Please sign in to comment.