Skip to content

Commit

Permalink
chore: optimize filters (#1269)
Browse files Browse the repository at this point in the history
  • Loading branch information
ngruelaneo authored Jan 8, 2025
2 parents 0f9f354 + ff08c00 commit fd08bc3
Show file tree
Hide file tree
Showing 35 changed files with 299 additions and 360 deletions.
4 changes: 2 additions & 2 deletions src/app/applications/index.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { MatToolbarModule } from '@angular/material/toolbar';
import { DashboardIndexService } from '@app/dashboard/services/dashboard-index.service';
import { DashboardStorageService } from '@app/dashboard/services/dashboard-storage.service';
import { TasksStatusesService } from '@app/tasks/services/tasks-statuses.service';
import { DATA_FILTERS_SERVICE } from '@app/tokens/filters.token';
import { TableHandler } from '@app/types/components';
import { DataFilterService } from '@app/types/services/data-filter.service';
import { TableType } from '@app/types/table';
import { FiltersToolbarComponent } from '@components/filters/filters-toolbar.component';
import { PageHeaderComponent } from '@components/page-header.component';
Expand Down Expand Up @@ -52,7 +52,7 @@ import { ApplicationRaw } from './types';
TasksStatusesService,
ApplicationsFiltersService,
{
provide: DATA_FILTERS_SERVICE,
provide: DataFilterService,
useExisting: ApplicationsFiltersService
},
DashboardIndexService,
Expand Down
4 changes: 2 additions & 2 deletions src/app/applications/services/applications-filters.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { ApplicationRawEnumField } from '@aneoconsultingfr/armonik.api.angular';
import { Injectable } from '@angular/core';
import { Scope } from '@app/types/config';
import { FilterFor } from '@app/types/filter-definition';
import { AbstractFilterService } from '@app/types/services/filtersService';
import { DataFilterService } from '@app/types/services/data-filter.service';
import { ApplicationFilterField, ApplicationRawFilters, ApplicationsFiltersDefinition } from '../types';

@Injectable({
providedIn: 'root'
})
export class ApplicationsFiltersService extends AbstractFilterService<ApplicationRawEnumField> {
export class ApplicationsFiltersService extends DataFilterService<ApplicationRawEnumField> {
protected readonly scope: Scope = 'applications';
readonly rootField: Record<ApplicationRawEnumField, string> = {
[ApplicationRawEnumField.APPLICATION_RAW_ENUM_FIELD_NAME]: $localize`Name`,
Expand Down
8 changes: 2 additions & 6 deletions src/app/components/filters/filters-chips.component.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<mat-chip-listbox>
@for(filter of filtersAnd; track filter.field) {
@for(filter of filters(); track $index) {
<mat-chip class="mat-mdc-standard-chip mat-primary mat-mdc-chip-selected">
@if(filter.field) {
<span>{{ content(filter) }}</span>
} @else {
<span i18n>No field</span>
}
<span>{{ filter }}</span>
</mat-chip>
@if(!$last) {
<span class="text" i18n>AND</span>
Expand Down
235 changes: 123 additions & 112 deletions src/app/components/filters/filters-chips.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DATA_FILTERS_SERVICE } from '@app/tokens/filters.token';
import { FilterDateOperator, FilterDurationOperator, FilterStatusOperator, FilterStringOperator, TaskOptionEnumField, TaskSummaryEnumField } from '@aneoconsultingfr/armonik.api.angular';
import { TestBed } from '@angular/core/testing';
import { FilterDefinition } from '@app/types/filter-definition';
import { Filter } from '@app/types/filters';
import { DataFilterService } from '@app/types/services/data-filter.service';
import { FiltersService } from '@services/filters.service';
import { UtilsService } from '@services/utils.service';
import { FiltersChipsComponent } from './filters-chips.component';

describe('FiltersChipsComponent', () => {
let component: FiltersChipsComponent<number, number | null>;
let fixture: ComponentFixture<FiltersChipsComponent<number, number | null>>;
const mockDataFilterService = {
retrieveFiltersDefinitions: jest.fn(() => {
return filterDefinitions;
}),
retrieveLabel: jest.fn()
};
const filterDefinitions: FilterDefinition<number, number>[] = [

const labels = {
[TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_TASK_ID]: 'TaskId',
[TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_STATUS]: 'Status',
[TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_CREATED_AT]: 'CreatedAt',
} as Record<TaskSummaryEnumField, string>;

const optionsLabels = {
[TaskOptionEnumField.TASK_OPTION_ENUM_FIELD_MAX_DURATION]: 'Max Duration',
} as Record<TaskOptionEnumField, string>;

const filtersDefinitions: FilterDefinition<TaskSummaryEnumField, TaskOptionEnumField>[] = [
{
field: 4,
type: 'number',
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_TASK_ID,
type: 'string',
for: 'root'
},
{
field: 1,
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_STATUS,
type: 'status',
for: 'root',
statuses: [
Expand All @@ -31,127 +35,134 @@ describe('FiltersChipsComponent', () => {
]
},
{
field: 2,
type: 'string',
field: TaskOptionEnumField.TASK_OPTION_ENUM_FIELD_MAX_DURATION,
type: 'duration',
for: 'options'
},
{
field: 3,
type: 'array',
for: 'root'
},
{
field: 6,
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_CREATED_AT,
type: 'date',
for: 'root'
},
({
field: 5,
type: 'unknownType',
for: 'root'
} as unknown as FilterDefinition<number, number>),
{
field: 1,
type: 'duration',
for: 'options'
}
];

beforeEach(async () => {
await TestBed.configureTestingModule({
const mockDataFilterService = {
filtersDefinitions: filtersDefinitions,
retrieveLabel: jest.fn((for_: string, field: TaskSummaryEnumField | TaskOptionEnumField) => {
if (for_ === 'root') {
return labels[field as TaskSummaryEnumField];
} else {
return optionsLabels[field as TaskOptionEnumField];
}
})
};

beforeAll(() => {
component = TestBed.configureTestingModule({
providers: [
FiltersChipsComponent,
FiltersService,
UtilsService,
{ provide: DATA_FILTERS_SERVICE, useValue: mockDataFilterService }
{ provide: DataFilterService, useValue: mockDataFilterService }
]
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(FiltersChipsComponent);
component = fixture.componentInstance;
fixture.detectChanges();

mockDataFilterService.retrieveLabel.mockImplementation((for_: string, field: number) => {
if (for_ === 'root' && field === 4) return 'number';
else if (for_ === 'root' && field === 1) return 'status';
else if (for_ === 'root' && field === 6) return 'date';
else return 'other';
});
}).inject(FiltersChipsComponent);
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should create the content of the component', () => {
const filter: Filter<number, number> = {
field: 4,
for: 'root',
operator: 1,
value: 2
};
expect(component.content(filter)).toEqual('number Not Equal 2');
});

it('should create the content of the component with statuses', () => {
const filter: Filter<number, number> = {
field: 1,
for: 'root',
operator: 0,
value: 'status1'
};
expect(component.content(filter)).toEqual('status Equal dispatched');
});

it('should create the label of the date filter', () => {
const filter: Filter<number, number> = {
field: 6,
for: 'root',
operator: 0,
value: 0
};
expect(component.content(filter)).toEqual('date Equal Thu, 01 Jan 1970 00:00:00 GMT');
describe('Filters cases', () => {
it('should create the label of a filter', () => {
component.filtersAnd = [{
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_TASK_ID,
for: 'root',
operator: FilterStringOperator.FILTER_STRING_OPERATOR_EQUAL,
value: '0',
}];
expect(component.filters()).toEqual(['TaskId Equal 0']);
});

it('should create the label of a status filter', () => {
component.filtersAnd = [{
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_STATUS,
for: 'root',
operator: FilterStatusOperator.FILTER_STATUS_OPERATOR_NOT_EQUAL,
value: 'status1',
}];
expect(component.filters()).toEqual(['Status Not Equal dispatched']);
});

it('should create the label of a date filter', () => {
component.filtersAnd = [{
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_CREATED_AT,
for: 'root',
operator: FilterDateOperator.FILTER_DATE_OPERATOR_AFTER_OR_EQUAL,
value: '0',
}];
expect(component.filters()).toEqual(['CreatedAt After or Equal Thu, 01 Jan 1970 00:00:00 GMT']);
});

it('should create the label of a duration filter', () => {
component.filtersAnd = [{
field: TaskOptionEnumField.TASK_OPTION_ENUM_FIELD_MAX_DURATION,
for: 'options',
operator: FilterDurationOperator.FILTER_DURATION_OPERATOR_LONGER_THAN_OR_EQUAL,
value: '94350',
}];
expect(component.filters()).toEqual(['Max Duration Longer or Equal 26h 12m 30s']);
});

it('should create the label of a custom filter', () => {
component.filtersAnd = [{
field: 'CustomField',
for: 'custom',
operator: FilterStringOperator.FILTER_STRING_OPERATOR_CONTAINS,
value: 'a'
}];
expect(component.filters()).toEqual(['CustomField Contains a']);
});
});

it('should create the content of the component even with no for', () => {
const filter: Filter<number, number> = {
field: 1,
for: null,
operator: 0,
value: 'status1'
};
expect(component.content(filter)).toEqual('status Equal dispatched');
});
describe('Error cases', () => {
it('should create the label if there is no field', () => {
component.filtersAnd = [{
field: null,
for: 'root',
operator: FilterDateOperator.FILTER_DATE_OPERATOR_BEFORE_OR_EQUAL,
value: 'abc'
}];
expect(component.filters()).toEqual(['No field']);
});

it('should create the content of the component with a custom filter', () => {
const filter: Filter<number, number> = {
field: 'test',
operator: 0,
for: 'custom',
value: 'True'
};
expect(component.content(filter)).toEqual('test Equal True');
});
it('should create the label if there is no operator', () => {
component.filtersAnd = [{
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_TASK_ID,
for: 'root',
operator: null,
value: 'a'
}];
expect(component.filters()).toEqual(['No operator']);
});

it('should not create the content if there is no values', () => {
const filter: Filter<number, number> = {
field: 2,
for: 'options',
operator: 1,
value: null
};
expect(component.content(filter)).toEqual('other has no value');
});
it('should create the label if there is no value', () => {
component.filtersAnd = [{
field: TaskOptionEnumField.TASK_OPTION_ENUM_FIELD_MAX_DURATION,
for: 'options',
operator: FilterDurationOperator.FILTER_DURATION_OPERATOR_EQUAL,
value: null
}];
expect(component.filters()).toEqual(['Max Duration has no value']);
});

it('should show a duration appropriately', () => {
const filter: Filter<number, number> = {
field: 1,
for: 'options',
operator: 1,
value: 94350
};
expect(component.content(filter)).toEqual('other Not Equal 26h 12m 30s');
it('should create the label if the field is invalid', () => {
component.filtersAnd = [{
field: TaskSummaryEnumField.TASK_SUMMARY_ENUM_FIELD_FETCHED_AT,
for: 'root',
operator: FilterDateOperator.FILTER_DATE_OPERATOR_BEFORE_OR_EQUAL,
value: '12349000'
}];
expect(component.filters()).toEqual(['Invalid Filter Field']);
});
});
});
Loading

1 comment on commit fd08bc3

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines Statements Branches Functions
Coverage: 100%
99.95% (4783/4785) 100% (875/875) 99.84% (1285/1287)

JUnit

Tests Skipped Failures Errors Time
1858 0 💤 0 ❌ 0 🔥 1m 2s ⏱️
Files coverage (100%)
File% Stmts% Branch% Funcs% LinesUncovered Line #s
All files99.9510099.84100 
applications100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
applications/components100100100100 
   table.component.html100100100100 
   table.component.ts100100100100 
applications/services100100100100 
   applications-data.service.ts100100100100 
   applications-filters.service.ts100100100100 
   applications-grpc.service.ts100100100100 
   applications-index.service.ts100100100100 
components100100100100 
   actions-toolbar-group.component.ts100100100100 
   actions-toolbar.component.ts100100100100 
   auto-complete.component.html100100100100 
   auto-complete.component.ts100100100100 
   auto-refresh-button.component.html100100100100 
   auto-refresh-button.component.ts100100100100 
   auto-refresh-dialog.component.html100100100100 
   auto-refresh-dialog.component.ts100100100100 
   columns-button.component.ts100100100100 
   columns-modify-dialog.component.html100100100100 
   columns-modify-dialog.component.ts100100100100 
   count-tasks-by-status.component.ts100100100100 
   icon-picker-dialog.component.html100100100100 
   icon-picker-dialog.component.ts100100100100 
   inspect-list.component.html100100100100 
   inspect-list.component.ts100100100100 
   inspection-header.component.html100100100100 
   inspection-header.component.ts100100100100 
   inspection-toolbar.component.html100100100100 
   inspection-toolbar.component.ts100100100100 
   manage-custom-dialog.component.html100100100100 
   manage-custom-dialog.component.ts100100100100 
   page-header.component.html100100100100 
   page-header.component.ts100100100100 
   page-section-header.component.ts100100100100 
   page-section.component.ts100100100100 
   refresh-button.component.ts100100100100 
   share-url.component.ts100100100100 
   show-action-area.component.html100100100100 
   show-action-area.component.ts100100100100 
   show-actions.component.html100100100100 
   show-actions.component.ts100100100100 
   show-card-content.component.html100100100100 
   show-card-content.component.ts100100100100 
   show-card.component.html100100100100 
   show-card.component.ts100100100100 
   show-page.component.html100100100100 
   show-page.component.ts100100100100 
   spinner.component.ts100100100100 
   table-actions-toolbar.component.html100100100100 
   table-actions-toolbar.component.ts100100100100 
   table-container.component.ts100100100100 
   table-dashboard-actions-toolbar.component.html100100100100 
   table-dashboard-actions-toolbar.component.ts100100100100 
   table-index-actions-toolbar.component.html100100100100 
   table-index-actions-toolbar.component.ts100100100100 
   view-tasks-by-status.component.ts100100100100 
components/filters100100100100 
   filters-chips.component.html100100100100 
   filters-chips.component.ts100100100100 
   filters-dialog-and.component.html100100100100 
   filters-dialog-and.component.ts100100100100 
   filters-dialog-filter-field.component.html100100100100 
   filters-dialog-filter-field.component.ts100100100100 
   filters-dialog-input.component.html100100100100 
   filters-dialog-input.component.ts100100100100 
   filters-dialog-or.component.html100100100100 
   filters-dialog-or.component.ts100100100100 
   filters-dialog.component.html100100100100 
   filters-dialog.component.ts100100100100 
   filters-toolbar.component.html100100100100 
   filters-toolbar.component.ts100100100100 
components/inspection100100100100 
   field-content.component.html100100100100 
   field-content.component.ts100100100100 
   inspection-card.component.html100100100100 
   inspection-card.component.ts100100100100 
   inspection-json.component.html100100100100 
   inspection-json.component.ts100100100100 
   inspection-list-grid.component.html100100100100 
   inspection-list-grid.component.ts100100100100 
   inspection-object.component.html100100100100 
   inspection-object.component.ts100100100100 
   inspection.component.html100100100100 
   inspection.component.ts100100100100 
   json.component.html100100100100 
   json.component.ts100100100100 
   message.component.html100100100100 
   message.component.ts100100100100 
components/navigation100100100100 
   change-language-button.component.html100100100100 
   change-language-button.component.ts100100100100 
   navigation.component.html100100100100 
   navigation.component.ts100100100100 
   theme-selector.component.html100100100100 
   theme-selector.component.ts100100100100 
components/navigation/external-services100100100100 
   external-services.component.html100100100100 
   external-services.component.ts100100100100 
   form-external-service.component.html100100100100 
   form-external-service.component.ts100100100100 
   manage-external-services-dialog.component.html100100100100 
   manage-external-services-dialog.component.ts100100100100 
components/navigation/version-menu100100100100 
   repository-version.component.html100100100100 
   repository-version.component.ts100100100100 
   versions-menu.component.html100100100100 
   versions-menu.component.ts100100100100 
components/statuses100100100100 
   add-statuses-group-dialog.component.ts100100100100 
   edit-status-group-dialog.component.ts100100100100 
   form-statuses-group.component.html100100100100 
   form-statuses-group.component.ts100100100100 
   manage-groups-dialog.component.html100100100100 
   manage-groups-dialog.component.ts100100100100 
components/table100100100100 
   table-actions.component.html100100100100 
   table-actions.component.ts100100100100 
   table-cell.component.html100100100100 
   table-cell.component.ts100100100100 
   table-column-header.component.html100100100100 
   table-column-header.component.ts100100100100 
   table-empty-data.component.ts100100100100 
   table-inspect-message-dialog.component.html100100100100 
   table-inspect-message-dialog.component.ts100100100100 
   table-inspect-message.component.html100100100100 
   table-inspect-message.component.ts100100100100 
   table-inspect-object-dialog.component.html100100100100 
   table-inspect-object-dialog.component.ts100100100100 
   table-inspect-object.component.html100100100100 
   table-inspect-object.component.ts100100100100 
   table.component.html100100100100 
   table.component.ts100100100100 
dashboard100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
dashboard/components100100100100 
   add-line-dialog.component.html100100100100 
   add-line-dialog.component.ts100100100100 
   edit-name-line-dialog.component.html100100100100 
   edit-name-line-dialog.component.ts100100100100 
   reorganize-lines-dialog.component.html100100100100 
   reorganize-lines-dialog.component.ts100100100100 
   split-lines-dialog.component.ts100100100100 
   statuses-group-card.component.html100100100100 
   statuses-group-card.component.ts100100100100 
dashboard/components/lines100100100100 
   applications-line.component.html100100100100 
   applications-line.component.ts100100100100 
   partitions-line.component.html100100100100 
   partitions-line.component.ts100100100100 
   results-line.component.html100100100100 
   results-line.component.ts100100100100 
   sessions-line.component.html100100100100 
   sessions-line.component.ts100100100100 
   task-by-status-line.component.html100100100100 
   task-by-status-line.component.ts100100100100 
   tasks-line.component.html100100100100 
   tasks-line.component.ts100100100100 
dashboard/services100100100100 
   dashboard-index.service.ts100100100100 
   dashboard-storage.service.ts100100100100 
healthcheck100100100100 
   healthcheck.component.html100100100100 
   healthcheck.component.ts100100100100 
healthcheck/services100100100100 
   healthcheck-grpc.service.ts100100100100 
partitions100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
   show.component.html100100100100 
   show.component.ts100100100100 
partitions/components100100100100 
   table.component.html100100100100 
   table.component.ts100100100100 
partitions/services100100100100 
   partitions-data.service.ts100100100100 
   partitions-filters.service.ts100100100100 
   partitions-grpc.service.ts100100100100 
   partitions-index.service.ts100100100100 
   partitions-inspection.service.ts100100100100 
pipes100100100100 
   duration.pipe.ts100100100100 
   empty-cell.pipe.ts100100100100 
   pretty.pipe.ts100100100100 
profile100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
   types.ts100100100100 
profile/guards100100100100 
   user-connected.guard.ts100100100100 
results100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
   show.component.html100100100100 
   show.component.ts100100100100 
results/components100100100100 
   table.component.html100100100100 
   table.component.ts100100100100 
results/services100100100100 
   results-data.service.ts100100100100 
   results-filters.service.ts100100100100 
   results-grpc.service.ts100100100100 
   results-index.service.ts100100100100 
   results-inspection.service.ts100100100100 
   results-statuses.service.ts100100100100 
services100100100100 
   auto-refresh.service.ts100100100100 
   cache.service.ts100100100100 
   default-config.service.ts100100100100 
   environment.service.ts100100100100 
   filters-cache.service.ts100100100100 
   filters.service.ts100100100100 
   grpc-build-request.service.ts100100100100 
   grpc-sort-field.service.ts100100100100 
   icons.service.ts100100100100 
   navigation.service.ts100100100100 
   notification.service.ts100100100100 
   query-params.service.ts100100100100 
   share-url.service.ts100100100100 
   storage.service.ts100100100100 
   table-storage.service.ts100100100100 
   table-url.service.ts100100100100 
   table.service.ts100100100100 
   tasks-by-status.service.ts100100100100 
   user-grpc.service.ts100100100100 
   user.service.ts100100100100 
   utils.service.ts100100100100 
   versions-grpc.service.ts100100100100 
   versions.service.ts100100100100 
sessions100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
   show.component.html100100100100 
   show.component.ts100100100100 
sessions/components100100100100 
   table.component.html100100100100 
   table.component.ts100100100100 
sessions/services100100100100 
   sessions-data.service.ts100100100100 
   sessions-filters.service.ts100100100100 
   sessions-grpc.service.ts100100100100 
   sessions-index.service.ts100100100100 
   sessions-inspection.service.ts100100100100 
   sessions-statuses.service.ts100100100100 
settings100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
settings/component100100100100 
   clear-all-dialog.component.html100100100100 
   clear-all-dialog.component.ts100100100100 
tasks100100100100 
   index.component.html100100100100 
   index.component.ts100100100100 
   show.component.html100100100100 
   show.component.ts100100100100 
tasks/components100100100100 
   manage-view-in-logs-dialog.component.html100100100100 
   manage-view-in-logs-dialog.component.ts100100100100 
   table.component.html100100100100 
   table.component.ts100100100100 
tasks/services100100100100 
   tasks-data.service.ts100100100100 
   tasks-filters.service.ts100100100100 
   tasks-grpc.service.ts100100100100 
   tasks-index.service.ts100100100100 
   tasks-inspection.service.ts100100100100 
   tasks-statuses.service.ts100100100100 
types100100100100 
   navigation.ts100100100100 
types/components99.3510098.18100 
   dashboard-line-table.ts99.1910097.56100 
   index.ts99.1210097.5100 
   show.ts100100100100 
   table.ts100100100100 
types/services100100100100 
   data-filter.service.ts100100100100 
   grpcService.ts100100100100 
   inspectionService.ts100100100100 
   table-data.service.ts100100100100 

Please sign in to comment.