From 45a1ef0a26314ef76f1ffa0e9445a2ff6148294d Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Tue, 8 Feb 2022 19:11:11 -0800 Subject: [PATCH 1/4] feat: filter changes --- .../filter-chip/filter-chip.service.ts | 9 +-- .../filter-button/filter-button.component.ts | 4 +- .../filter-modal/in-filter-modal.component.ts | 6 +- .../builder/filter-builder-lookup.service.ts | 3 +- .../builder/types/abstract-filter-builder.ts | 4 +- .../src/filtering/filter/filter-operators.ts | 20 +++++- .../components/src/filtering/filter/filter.ts | 9 ++- .../parser/filter-parser-lookup.service.ts | 5 +- projects/components/src/table/table-api.ts | 4 +- ...ies-attribute-options-data-source.model.ts | 4 +- .../graphql/table/table-data-source.model.ts | 2 +- .../model/metadata/attribute-metadata.ts | 1 + .../graphql-filter-builder.service.ts | 65 ++++++++++++++++++- 13 files changed, 110 insertions(+), 26 deletions(-) diff --git a/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts b/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts index 66ae2dd20..b9bd99115 100644 --- a/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts +++ b/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts @@ -1,3 +1,4 @@ +import { FilterValue } from './../../filter/filter'; import { Injectable } from '@angular/core'; import { isEmpty } from 'lodash-es'; import { FilterBuilderLookupService } from '../../filter/builder/filter-builder-lookup.service'; @@ -61,7 +62,7 @@ export class FilterChipService { private buildIncompleteFiltersForAttribute( text: string, - filterBuilder: AbstractFilterBuilder, + filterBuilder: AbstractFilterBuilder, attributeExpression: FilterAttributeExpression ): IncompleteFilter[] { const topLevelOperatorFilters = filterBuilder.supportedTopLevelOperators().map(operator => ({ @@ -95,8 +96,8 @@ export class FilterChipService { } private buildIncompleteFilterForAttributeAndOperator( - filterBuilder: AbstractFilterBuilder, - filterParser: AbstractFilterParser, + filterBuilder: AbstractFilterBuilder, + filterParser: AbstractFilterParser, splitFilter: SplitFilter, text: string ): IncompleteFilter { @@ -132,7 +133,7 @@ export class FilterChipService { } private buildIncompleteFilterForPartialAttributeMatch( - filterBuilder: AbstractFilterBuilder, + filterBuilder: AbstractFilterBuilder, attribute: FilterAttribute ): IncompleteFilter { return { diff --git a/projects/components/src/filtering/filter-button/filter-button.component.ts b/projects/components/src/filtering/filter-button/filter-button.component.ts index 8eb5f54e5..0c4876661 100644 --- a/projects/components/src/filtering/filter-button/filter-button.component.ts +++ b/projects/components/src/filtering/filter-button/filter-button.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Out import { IconType } from '@hypertrace/assets-library'; import { IconSize } from '../../icon/icon-size'; import { FilterBuilderLookupService } from '../filter/builder/filter-builder-lookup.service'; -import { Filter } from '../filter/filter'; +import { Filter, FilterValue } from '../filter/filter'; import { FilterAttribute } from '../filter/filter-attribute'; import { FilterUrlService } from '../filter/filter-url.service'; @@ -45,7 +45,7 @@ export class FilterButtonComponent implements OnChanges { public attribute?: FilterAttribute; @Input() - public value?: unknown; + public value?: FilterValue; @Output() public readonly popoverOpen: EventEmitter = new EventEmitter(); diff --git a/projects/components/src/filtering/filter-modal/in-filter-modal.component.ts b/projects/components/src/filtering/filter-modal/in-filter-modal.component.ts index 693ac2077..d1033f5db 100644 --- a/projects/components/src/filtering/filter-modal/in-filter-modal.component.ts +++ b/projects/components/src/filtering/filter-modal/in-filter-modal.component.ts @@ -3,7 +3,7 @@ import { sortUnknown } from '@hypertrace/common'; import { ButtonRole } from '../../button/button'; import { ModalRef, MODAL_DATA } from '../../modal/modal'; import { FilterBuilderLookupService } from '../filter/builder/filter-builder-lookup.service'; -import { IncompleteFilter } from '../filter/filter'; +import { FilterValue, IncompleteFilter } from '../filter/filter'; import { FilterAttribute } from '../filter/filter-attribute'; import { FilterOperator } from '../filter/filter-operators'; import { FilterUrlService } from '../filter/filter-url.service'; @@ -43,7 +43,7 @@ import { FilterUrlService } from '../filter/filter-url.service'; }) export class InFilterModalComponent { public isSupported: boolean = false; - public selected: Set = new Set(); + public selected: Set = new Set(); public constructor( private readonly modalRef: ModalRef, @@ -91,7 +91,7 @@ export class InFilterModalComponent { this.modalRef.close(); } - public onChecked(checked: boolean, value: unknown): void { + public onChecked(checked: boolean, value: FilterValue): void { checked ? this.selected.add(value) : this.selected.delete(value); } } diff --git a/projects/components/src/filtering/filter/builder/filter-builder-lookup.service.ts b/projects/components/src/filtering/filter/builder/filter-builder-lookup.service.ts index 3f73184f1..e9bd3f7b8 100644 --- a/projects/components/src/filtering/filter/builder/filter-builder-lookup.service.ts +++ b/projects/components/src/filtering/filter/builder/filter-builder-lookup.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { assertUnreachable } from '@hypertrace/common'; +import { FilterValue } from '../filter'; import { FilterAttributeType } from '../filter-attribute-type'; import { AbstractFilterBuilder } from './types/abstract-filter-builder'; import { BooleanFilterBuilder } from './types/boolean-filter-builder'; @@ -11,7 +12,7 @@ import { StringMapFilterBuilder } from './types/string-map-filter-builder'; providedIn: 'root' }) export class FilterBuilderLookupService { - public lookup(type: FilterAttributeType): AbstractFilterBuilder { + public lookup(type: FilterAttributeType): AbstractFilterBuilder { switch (type) { case FilterAttributeType.Boolean: return new BooleanFilterBuilder(); diff --git a/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts b/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts index b981692d4..d24b37d78 100644 --- a/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts +++ b/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts @@ -1,13 +1,13 @@ import { collapseWhitespace } from '@hypertrace/common'; import { isEmpty } from 'lodash-es'; -import { Filter } from '../../filter'; +import { Filter, FilterValue } from '../../filter'; import { FilterAttribute } from '../../filter-attribute'; import { FilterAttributeType } from '../../filter-attribute-type'; import { MAP_LHS_DELIMITER } from '../../filter-delimiters'; import { FilterOperator, toUrlFilterOperator } from '../../filter-operators'; import { FilterAttributeExpression } from '../../parser/parsed-filter'; -export abstract class AbstractFilterBuilder { +export abstract class AbstractFilterBuilder { public abstract supportedAttributeType(): FilterAttributeType; public abstract supportedSubpathOperators(): FilterOperator[]; diff --git a/projects/components/src/filtering/filter/filter-operators.ts b/projects/components/src/filtering/filter/filter-operators.ts index 36c92ecb1..3cbe8de30 100644 --- a/projects/components/src/filtering/filter/filter-operators.ts +++ b/projects/components/src/filtering/filter/filter-operators.ts @@ -9,6 +9,7 @@ export const enum FilterOperator { GreaterThanOrEqualTo = '>=', Like = '~', In = 'IN', + NotIn = 'NOT_IN', ContainsKey = 'CONTAINS_KEY' } @@ -21,6 +22,7 @@ export const enum UrlFilterOperator { GreaterThanOrEqualTo = '_gte_', Like = '_lk_', In = '_in_', + NotIn = '_nin_', ContainsKey = '_ck_' } @@ -44,6 +46,8 @@ export const toUrlFilterOperator = (operator: FilterOperator): UrlFilterOperator return UrlFilterOperator.In; case FilterOperator.ContainsKey: return UrlFilterOperator.ContainsKey; + case FilterOperator.NotIn: + return UrlFilterOperator.ContainsKey; default: return assertUnreachable(operator); } @@ -67,6 +71,8 @@ export const fromUrlFilterOperator = (operator: UrlFilterOperator): FilterOperat return FilterOperator.Like; case UrlFilterOperator.In: return FilterOperator.In; + case UrlFilterOperator.NotIn: + return FilterOperator.NotIn; case UrlFilterOperator.ContainsKey: return FilterOperator.ContainsKey; default: @@ -78,6 +84,7 @@ export const incompatibleOperators = (operator: FilterOperator): FilterOperator[ switch (operator) { case FilterOperator.In: case FilterOperator.Equals: + case FilterOperator.NotIn: return [ FilterOperator.In, FilterOperator.Equals, @@ -92,17 +99,24 @@ export const incompatibleOperators = (operator: FilterOperator): FilterOperator[ case FilterOperator.ContainsKey: case FilterOperator.NotEquals: case FilterOperator.Like: - return [FilterOperator.In, FilterOperator.Equals]; + return [FilterOperator.In, FilterOperator.Equals, FilterOperator.NotIn]; case FilterOperator.LessThan: case FilterOperator.LessThanOrEqualTo: - return [FilterOperator.In, FilterOperator.Equals, FilterOperator.LessThan, FilterOperator.LessThanOrEqualTo]; + return [ + FilterOperator.In, + FilterOperator.Equals, + FilterOperator.LessThan, + FilterOperator.LessThanOrEqualTo, + FilterOperator.NotIn + ]; case FilterOperator.GreaterThan: case FilterOperator.GreaterThanOrEqualTo: return [ FilterOperator.In, FilterOperator.Equals, FilterOperator.GreaterThan, - FilterOperator.GreaterThanOrEqualTo + FilterOperator.GreaterThanOrEqualTo, + FilterOperator.NotIn ]; default: assertUnreachable(operator); diff --git a/projects/components/src/filtering/filter/filter.ts b/projects/components/src/filtering/filter/filter.ts index 1f9cafaa8..a5f384a23 100644 --- a/projects/components/src/filtering/filter/filter.ts +++ b/projects/components/src/filtering/filter/filter.ts @@ -1,23 +1,26 @@ +import { Dictionary } from '@hypertrace/common'; import { FilterAttribute } from './filter-attribute'; import { FilterOperator, incompatibleOperators } from './filter-operators'; -export interface Filter extends IncompleteFilter { +export interface Filter extends IncompleteFilter { operator: FilterOperator; value: TValue; urlString: string; } -export interface IncompleteFilter extends FieldFilter { +export interface IncompleteFilter extends FieldFilter { metadata: FilterAttribute; userString: string; } -export interface FieldFilter { +export interface FieldFilter { field: string; subpath?: string; operator?: FilterOperator; value?: TValue; } +export type FilterValue = string | number | boolean | Date | Dictionary | FilterValue[]; + export const areCompatibleFilters = (f1: Filter, f2: Filter) => f1.field !== f2.field || f1.subpath !== f2.subpath || !incompatibleOperators(f1.operator).includes(f2.operator); diff --git a/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts b/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts index b1b215261..e2daaae0b 100644 --- a/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts +++ b/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { assertUnreachable } from '@hypertrace/common'; +import { FilterValue } from '../filter'; import { FilterAttributeType } from '../filter-attribute-type'; import { FilterOperator } from '../filter-operators'; import { AbstractFilterParser } from './types/abstract-filter-parser'; @@ -14,7 +15,7 @@ export class FilterParserLookupService { // TODO remove the separate parsers entirely. // There's next to no logic left in them, and they duplicate (incorrectly) supported operators, // Which should be based on attribute type (as defined in filter builders) - public lookup(operator: FilterOperator): AbstractFilterParser { + public lookup(operator: FilterOperator): AbstractFilterParser { switch (operator) { case FilterOperator.Equals: case FilterOperator.NotEquals: @@ -28,6 +29,8 @@ export class FilterParserLookupService { return new InFilterParser(); case FilterOperator.ContainsKey: return new ContainsFilterParser(); + case FilterOperator.NotIn: + throw new Error('NotIn is not supported'); default: return assertUnreachable(operator); } diff --git a/projects/components/src/table/table-api.ts b/projects/components/src/table/table-api.ts index 764a93624..2bc448e10 100644 --- a/projects/components/src/table/table-api.ts +++ b/projects/components/src/table/table-api.ts @@ -1,6 +1,6 @@ import { Dictionary } from '@hypertrace/common'; import { Observable } from 'rxjs'; -import { FieldFilter } from '../filtering/filter/filter'; +import { FieldFilter, FilterValue } from '../filtering/filter/filter'; import { FilterOperator } from '../filtering/filter/filter-operators'; import { TableCellAlignmentType } from './cells/types/table-cell-alignment-type'; @@ -60,7 +60,7 @@ export interface RowStateChange { export interface TableFilter extends FieldFilter { operator: FilterOperator; - value: unknown; + value: FilterValue; } export const enum TableSortDirection { diff --git a/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts b/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts index c4a13ca65..74771874b 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts @@ -1,4 +1,4 @@ -import { FilterOperator, TableControlOptionType, TableSelectControlOption } from '@hypertrace/components'; +import { FilterOperator, FilterValue, TableControlOptionType, TableSelectControlOption } from '@hypertrace/components'; import { Model } from '@hypertrace/hyperdash'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -17,7 +17,7 @@ export class EntitiesAttributeOptionsDataSourceModel extends EntitiesAttributeDa metaValue: { field: this.specification.name, operator: FilterOperator.Equals, - value: value + value: value as FilterValue } })) ) diff --git a/projects/observability/src/shared/dashboard/data/graphql/table/table-data-source.model.ts b/projects/observability/src/shared/dashboard/data/graphql/table/table-data-source.model.ts index e0f421062..e4b98f8b5 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/table/table-data-source.model.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/table/table-data-source.model.ts @@ -47,6 +47,6 @@ export abstract class TableDataSourceModel extends GraphQlDataSourceModel; protected toGraphQlFilters(tableFilters: TableFilter[] = []): GraphQlFilter[] { - return this.graphQlFilterBuilderService.buildGraphQlFilters(tableFilters); + return this.graphQlFilterBuilderService.buildGraphQlFiltersFromTableFilters(tableFilters); } } diff --git a/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts b/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts index b1717a42b..4b2b63741 100644 --- a/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts +++ b/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts @@ -7,6 +7,7 @@ export interface AttributeMetadata { units: string; type: AttributeMetadataType; scope: string; + category?: string; onlySupportsAggregation: boolean; onlySupportsGrouping: boolean; allowedAggregations: MetricAggregationType[]; diff --git a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts index cc5d0bf47..cec29e66f 100644 --- a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts +++ b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts @@ -1,13 +1,36 @@ +import { FieldFilter, FilterValue } from './../../../../../components/src/filtering/filter/filter'; +import { TableFilter } from './../../../../../components/src/table/table-api'; import { Injectable } from '@angular/core'; import { assertUnreachable } from '@hypertrace/common'; -import { FilterOperator, TableFilter } from '@hypertrace/components'; +import { FilterOperator } from '@hypertrace/components'; import { GraphQlArgumentValue } from '@hypertrace/graphql-client'; import { GraphQlFieldFilter } from '../../graphql/model/schema/filter/field/graphql-field-filter'; import { GraphQlFilter, GraphQlOperatorType } from '../../graphql/model/schema/filter/graphql-filter'; @Injectable({ providedIn: 'root' }) export class GraphQlFilterBuilderService { - public buildGraphQlFilters(filters: TableFilter[]): GraphQlFilter[] { + public buildFiltersFromGraphQlFieldFilters(filters: GraphQlFieldFilter[]): FieldFilter[] { + return filters.map(filter => ({ + field: typeof filter.keyOrExpression === 'string' ? filter.keyOrExpression : filter.keyOrExpression.key, + subpath: typeof filter.keyOrExpression === 'string' ? undefined : filter.keyOrExpression.subpath, + operator: toFilterOperator(filter.operator), + value: filter.value as FilterValue, + urlString: '' + })); + } + + public buildGraphQlFilters(filters: FieldFilter[]): GraphQlFilter[] { + return filters.map( + filter => + new GraphQlFieldFilter( + { key: filter.field, subpath: filter.subpath }, + toGraphQlOperator(filter.operator!), // Todo : Very weird + filter.value as GraphQlArgumentValue + ) + ); + } + + public buildGraphQlFiltersFromTableFilters(filters: TableFilter[]): GraphQlFilter[] { return filters.map( filter => new GraphQlFieldFilter( @@ -37,9 +60,47 @@ export const toGraphQlOperator = (operator: FilterOperator): GraphQlOperatorType return GraphQlOperatorType.Like; case FilterOperator.In: return GraphQlOperatorType.In; + case FilterOperator.NotIn: + return GraphQlOperatorType.NotIn; case FilterOperator.ContainsKey: return GraphQlOperatorType.ContainsKey; default: return assertUnreachable(operator); } }; + +export const toFilterOperator = (operator: GraphQlOperatorType): FilterOperator => { + switch (operator) { + case GraphQlOperatorType.Equals: + return FilterOperator.Equals; + + case GraphQlOperatorType.NotEquals: + return FilterOperator.NotEquals; + + case GraphQlOperatorType.LessThan: + return FilterOperator.LessThan; + + case GraphQlOperatorType.LessThanOrEqualTo: + return FilterOperator.LessThanOrEqualTo; + + case GraphQlOperatorType.GreaterThan: + return FilterOperator.GreaterThan; + + case GraphQlOperatorType.GreaterThanOrEqualTo: + return FilterOperator.GreaterThanOrEqualTo; + + case GraphQlOperatorType.Like: + return FilterOperator.Like; + + case GraphQlOperatorType.In: + return FilterOperator.In; + + case GraphQlOperatorType.NotIn: + return FilterOperator.NotIn; + + case GraphQlOperatorType.ContainsKey: + return FilterOperator.ContainsKey; + default: + return assertUnreachable(operator); + } +}; From 817f97c7d62eb15a694ef6c3048b8c6bbcd2ee3d Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Tue, 15 Feb 2022 01:03:39 -0800 Subject: [PATCH 2/4] refactor: fixing existing code --- .../filter-chip/filter-chip.service.ts | 2 +- .../builder/types/abstract-filter-builder.ts | 26 ++++++++++++++++++- .../src/filtering/filter/filter-operators.ts | 20 +++----------- .../filtering/filter/filter-url.service.ts | 10 +++++++ .../parser/filter-parser-lookup.service.ts | 2 -- .../explore-visualization-builder.ts | 6 ++--- .../navigable-dashboard.component.ts | 5 +++- .../table/table-widget-renderer.component.ts | 2 +- .../graphql-filter-builder.service.test.ts | 2 +- .../graphql-filter-builder.service.ts | 13 +++++----- 10 files changed, 54 insertions(+), 34 deletions(-) diff --git a/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts b/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts index b9bd99115..eea4123c4 100644 --- a/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts +++ b/projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.ts @@ -1,4 +1,3 @@ -import { FilterValue } from './../../filter/filter'; import { Injectable } from '@angular/core'; import { isEmpty } from 'lodash-es'; import { FilterBuilderLookupService } from '../../filter/builder/filter-builder-lookup.service'; @@ -14,6 +13,7 @@ import { tryParseStringForAttribute } from '../../filter/parser/parsed-filter'; import { AbstractFilterParser } from '../../filter/parser/types/abstract-filter-parser'; +import { FilterValue } from './../../filter/filter'; @Injectable({ providedIn: 'root' diff --git a/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts b/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts index d24b37d78..1ca7c796a 100644 --- a/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts +++ b/projects/components/src/filtering/filter/builder/types/abstract-filter-builder.ts @@ -1,6 +1,6 @@ import { collapseWhitespace } from '@hypertrace/common'; import { isEmpty } from 'lodash-es'; -import { Filter, FilterValue } from '../../filter'; +import { Filter, FilterValue, IncompleteFilter } from '../../filter'; import { FilterAttribute } from '../../filter-attribute'; import { FilterAttributeType } from '../../filter-attribute-type'; import { MAP_LHS_DELIMITER } from '../../filter-delimiters'; @@ -47,6 +47,30 @@ export abstract class AbstractFilterBuilder { }; } + public buildPartialFilter( + attribute: FilterAttribute, + operator?: FilterOperator, + value?: TValue, + subpath?: string + ): IncompleteFilter { + if ( + operator !== undefined && + ((isEmpty(subpath) && !this.supportedTopLevelOperators().includes(operator)) || + (!isEmpty(subpath) && !this.supportedSubpathOperators().includes(operator))) + ) { + throw Error(`Operator '${operator}' not supported for filter attribute type '${attribute.type}'`); + } + + return { + metadata: attribute, + field: attribute.name, + subpath: subpath, + operator: operator, + value: value, + userString: this.buildUserFilterString(attribute, subpath, operator, value) + }; + } + public buildUserFilterString( attribute: FilterAttribute, subpath?: string, diff --git a/projects/components/src/filtering/filter/filter-operators.ts b/projects/components/src/filtering/filter/filter-operators.ts index 3cbe8de30..36c92ecb1 100644 --- a/projects/components/src/filtering/filter/filter-operators.ts +++ b/projects/components/src/filtering/filter/filter-operators.ts @@ -9,7 +9,6 @@ export const enum FilterOperator { GreaterThanOrEqualTo = '>=', Like = '~', In = 'IN', - NotIn = 'NOT_IN', ContainsKey = 'CONTAINS_KEY' } @@ -22,7 +21,6 @@ export const enum UrlFilterOperator { GreaterThanOrEqualTo = '_gte_', Like = '_lk_', In = '_in_', - NotIn = '_nin_', ContainsKey = '_ck_' } @@ -46,8 +44,6 @@ export const toUrlFilterOperator = (operator: FilterOperator): UrlFilterOperator return UrlFilterOperator.In; case FilterOperator.ContainsKey: return UrlFilterOperator.ContainsKey; - case FilterOperator.NotIn: - return UrlFilterOperator.ContainsKey; default: return assertUnreachable(operator); } @@ -71,8 +67,6 @@ export const fromUrlFilterOperator = (operator: UrlFilterOperator): FilterOperat return FilterOperator.Like; case UrlFilterOperator.In: return FilterOperator.In; - case UrlFilterOperator.NotIn: - return FilterOperator.NotIn; case UrlFilterOperator.ContainsKey: return FilterOperator.ContainsKey; default: @@ -84,7 +78,6 @@ export const incompatibleOperators = (operator: FilterOperator): FilterOperator[ switch (operator) { case FilterOperator.In: case FilterOperator.Equals: - case FilterOperator.NotIn: return [ FilterOperator.In, FilterOperator.Equals, @@ -99,24 +92,17 @@ export const incompatibleOperators = (operator: FilterOperator): FilterOperator[ case FilterOperator.ContainsKey: case FilterOperator.NotEquals: case FilterOperator.Like: - return [FilterOperator.In, FilterOperator.Equals, FilterOperator.NotIn]; + return [FilterOperator.In, FilterOperator.Equals]; case FilterOperator.LessThan: case FilterOperator.LessThanOrEqualTo: - return [ - FilterOperator.In, - FilterOperator.Equals, - FilterOperator.LessThan, - FilterOperator.LessThanOrEqualTo, - FilterOperator.NotIn - ]; + return [FilterOperator.In, FilterOperator.Equals, FilterOperator.LessThan, FilterOperator.LessThanOrEqualTo]; case FilterOperator.GreaterThan: case FilterOperator.GreaterThanOrEqualTo: return [ FilterOperator.In, FilterOperator.Equals, FilterOperator.GreaterThan, - FilterOperator.GreaterThanOrEqualTo, - FilterOperator.NotIn + FilterOperator.GreaterThanOrEqualTo ]; default: assertUnreachable(operator); diff --git a/projects/components/src/filtering/filter/filter-url.service.ts b/projects/components/src/filtering/filter/filter-url.service.ts index 54373b267..960d6b08e 100644 --- a/projects/components/src/filtering/filter/filter-url.service.ts +++ b/projects/components/src/filtering/filter/filter-url.service.ts @@ -25,6 +25,16 @@ export class FilterUrlService { return this.navigationService.navigation$.pipe(map(() => this.getUrlFilters(attributes))); } + public getUrlFiltersForAttributes(attributes: FilterAttribute[]): (Filter | IncompleteFilter)[] { + const urlFilters = this.getUrlFilters(attributes); + + return attributes.map( + attribute => + urlFilters.find(f => f.field === attribute.name) ?? + this.filterBuilderLookupService.lookup(attribute.type).buildPartialFilter(attribute) + ); + } + public getUrlFilters(attributes: FilterAttribute[]): Filter[] { return this.navigationService .getAllValuesForQueryParameter(FilterUrlService.FILTER_QUERY_PARAM) diff --git a/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts b/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts index e2daaae0b..39fa96e14 100644 --- a/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts +++ b/projects/components/src/filtering/filter/parser/filter-parser-lookup.service.ts @@ -29,8 +29,6 @@ export class FilterParserLookupService { return new InFilterParser(); case FilterOperator.ContainsKey: return new ContainsFilterParser(); - case FilterOperator.NotIn: - throw new Error('NotIn is not supported'); default: return assertUnreachable(operator); } diff --git a/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.ts b/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.ts index ccddaba83..38522a5e7 100644 --- a/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.ts +++ b/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.ts @@ -128,7 +128,7 @@ export class ExploreVisualizationBuilder implements OnDestroy { selections: state.series.map(series => series.specification), context: state.context, interval: this.resolveInterval(state.interval), - filters: state.filters && this.graphQlFilterBuilderService.buildGraphQlFilters(state.filters), + filters: state.filters && this.graphQlFilterBuilderService.buildGraphQlFieldFilters(state.filters), groupBy: state.groupBy, limit: state.resultLimit }); @@ -178,7 +178,7 @@ export class ExploreVisualizationBuilder implements OnDestroy { traceType: traceType, properties: specifications, limit: 100, - filters: filters && this.graphQlFilterBuilderService.buildGraphQlFilters(filters) + filters: filters && this.graphQlFilterBuilderService.buildGraphQlFieldFilters(filters) }; } @@ -190,7 +190,7 @@ export class ExploreVisualizationBuilder implements OnDestroy { requestType: SPANS_GQL_REQUEST, properties: specifications, limit: 100, - filters: filters && this.graphQlFilterBuilderService.buildGraphQlFilters(filters) + filters: filters && this.graphQlFilterBuilderService.buildGraphQlFieldFilters(filters) }; } diff --git a/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.ts b/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.ts index 97a88f121..1b08d84e7 100644 --- a/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.ts +++ b/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.ts @@ -110,7 +110,10 @@ export class NavigableDashboardComponent implements OnChanges { const rootDataSource = dashboard.getRootDataSource(); rootDataSource ?.clearFilters() - .addFilters(...this.implicitFilters, ...this.graphQlFilterBuilderService.buildGraphQlFilters(explicitFilters)); + .addFilters( + ...this.implicitFilters, + ...this.graphQlFilterBuilderService.buildGraphQlFieldFilters(explicitFilters) + ); dashboard.refresh(); } } diff --git a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts index b10f70950..6ffa3e233 100644 --- a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts +++ b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts @@ -615,7 +615,7 @@ export class TableWidgetRendererComponent private mergeFilters(tableFilter: TableFilter): TableFilter[] { const existingSelectFiltersWithChangedRemoved = this.removeFilters(tableFilter.field); - return [...existingSelectFiltersWithChangedRemoved, tableFilter].filter(f => f.value !== undefined); // Remove filters that are unset + return [...existingSelectFiltersWithChangedRemoved, tableFilter]; } private removeFilters(field: string): TableFilter[] { diff --git a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.test.ts b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.test.ts index 213376efe..bbe24424f 100644 --- a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.test.ts +++ b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.test.ts @@ -32,7 +32,7 @@ describe('Graphql filter builder service', () => { const spectator = serviceFactory(); expect( - spectator.service.buildGraphQlFilters([ + spectator.service.buildGraphQlFieldFilters([ buildFilter(attribute2, FilterOperator.Equals, 'foo'), buildFilter(attribute2, FilterOperator.NotEquals, 'bar'), buildFilter(attribute1, FilterOperator.GreaterThan, 5), diff --git a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts index cec29e66f..81ffdaced 100644 --- a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts +++ b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts @@ -1,14 +1,15 @@ -import { FieldFilter, FilterValue } from './../../../../../components/src/filtering/filter/filter'; -import { TableFilter } from './../../../../../components/src/table/table-api'; import { Injectable } from '@angular/core'; import { assertUnreachable } from '@hypertrace/common'; -import { FilterOperator } from '@hypertrace/components'; +import { FieldFilter, FilterOperator, FilterValue, TableFilter } from '@hypertrace/components'; import { GraphQlArgumentValue } from '@hypertrace/graphql-client'; import { GraphQlFieldFilter } from '../../graphql/model/schema/filter/field/graphql-field-filter'; import { GraphQlFilter, GraphQlOperatorType } from '../../graphql/model/schema/filter/graphql-filter'; @Injectable({ providedIn: 'root' }) export class GraphQlFilterBuilderService { + /** + * This is a temporary method to convert the GraphQL Field filters to its UI counterpart Filter Field Object. + */ public buildFiltersFromGraphQlFieldFilters(filters: GraphQlFieldFilter[]): FieldFilter[] { return filters.map(filter => ({ field: typeof filter.keyOrExpression === 'string' ? filter.keyOrExpression : filter.keyOrExpression.key, @@ -19,7 +20,7 @@ export class GraphQlFilterBuilderService { })); } - public buildGraphQlFilters(filters: FieldFilter[]): GraphQlFilter[] { + public buildGraphQlFieldFilters(filters: FieldFilter[]): GraphQlFieldFilter[] { return filters.map( filter => new GraphQlFieldFilter( @@ -60,8 +61,6 @@ export const toGraphQlOperator = (operator: FilterOperator): GraphQlOperatorType return GraphQlOperatorType.Like; case FilterOperator.In: return GraphQlOperatorType.In; - case FilterOperator.NotIn: - return GraphQlOperatorType.NotIn; case FilterOperator.ContainsKey: return GraphQlOperatorType.ContainsKey; default: @@ -96,7 +95,7 @@ export const toFilterOperator = (operator: GraphQlOperatorType): FilterOperator return FilterOperator.In; case GraphQlOperatorType.NotIn: - return FilterOperator.NotIn; + throw new Error('NotIn operator is not supported'); case GraphQlOperatorType.ContainsKey: return FilterOperator.ContainsKey; From 559564acc23ec517f1d96cd46226a4fc58ebca42 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Tue, 15 Feb 2022 01:32:18 -0800 Subject: [PATCH 3/4] refactor: revert code --- .../src/shared/graphql/model/metadata/attribute-metadata.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts b/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts index 4b2b63741..b1717a42b 100644 --- a/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts +++ b/projects/observability/src/shared/graphql/model/metadata/attribute-metadata.ts @@ -7,7 +7,6 @@ export interface AttributeMetadata { units: string; type: AttributeMetadataType; scope: string; - category?: string; onlySupportsAggregation: boolean; onlySupportsGrouping: boolean; allowedAggregations: MetricAggregationType[]; From c8f834a6fa9c664e616b544bb1bf832ba23307b3 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Tue, 15 Feb 2022 20:12:28 -0800 Subject: [PATCH 4/4] refactor: removing filter which is already matched --- .../src/filtering/filter/filter-url.service.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/projects/components/src/filtering/filter/filter-url.service.ts b/projects/components/src/filtering/filter/filter-url.service.ts index 960d6b08e..2de20d590 100644 --- a/projects/components/src/filtering/filter/filter-url.service.ts +++ b/projects/components/src/filtering/filter/filter-url.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { NavigationService } from '@hypertrace/common'; +import { remove } from 'lodash-es'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { FilterBuilderLookupService } from './builder/filter-builder-lookup.service'; @@ -28,11 +29,16 @@ export class FilterUrlService { public getUrlFiltersForAttributes(attributes: FilterAttribute[]): (Filter | IncompleteFilter)[] { const urlFilters = this.getUrlFilters(attributes); - return attributes.map( - attribute => - urlFilters.find(f => f.field === attribute.name) ?? - this.filterBuilderLookupService.lookup(attribute.type).buildPartialFilter(attribute) - ); + return attributes.map(attribute => { + const match = urlFilters.find(f => f.field === attribute.name); + if (match !== undefined) { + remove(urlFilters, f => f === match); + + return match; + } + + return this.filterBuilderLookupService.lookup(attribute.type).buildPartialFilter(attribute); + }); } public getUrlFilters(attributes: FilterAttribute[]): Filter[] {