From 4a47b52c164bc0cb20b2df1cf118da05a7d292b2 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Mon, 3 May 2021 15:42:09 -0700 Subject: [PATCH 1/7] feat: add navigation handling for inactive entities --- .../entity-renderer.component.ts | 6 ++++- .../entity/entity-table-cell-parser.ts | 19 +++++++++++++--- .../entity/entity-table-cell-renderer-util.ts | 22 +++++++++++++++++++ .../entity-table-cell-renderer.component.ts | 10 ++++++--- .../src/shared/constants/entity-metadata.ts | 2 +- .../entity/entity-navigation.service.ts | 12 +++++----- 6 files changed, 57 insertions(+), 14 deletions(-) diff --git a/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.ts b/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.ts index 20acb9462..44867b3a3 100644 --- a/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.ts +++ b/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.ts @@ -32,6 +32,9 @@ export class EntityRendererComponent implements OnChanges { @Input() public entity?: Entity; + @Input() + public inactive: boolean = false; + @Input() public navigable: boolean = true; @@ -59,8 +62,9 @@ export class EntityRendererComponent implements OnChanges { this.setIconType(); } } + public onClickNavigate(): void { - this.navigable && this.entity && this.entityNavService.navigateToEntity(this.entity); + this.navigable && this.entity && this.entityNavService.navigateToEntity(this.entity, this.inactive); } private setName(): void { diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-parser.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-parser.ts index 4997ac6a4..3f767f87e 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-parser.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-parser.ts @@ -1,14 +1,23 @@ import { TableCellParser, TableCellParserBase, TableRow } from '@hypertrace/components'; import { Entity, entityIdKey } from '../../../../graphql/model/schema/entity'; import { ObservabilityTableCellType } from '../../observability-table-cell-type'; -import { parseEntityFromTableRow } from './entity-table-cell-renderer-util'; +import { isInactiveEntity, parseEntityFromTableRow } from './entity-table-cell-renderer-util'; @TableCellParser({ type: ObservabilityTableCellType.Entity }) export class EntityTableCellParser extends TableCellParserBase { public parseValue(cellData: CellData, row: TableRow): CellData { - return parseEntityFromTableRow(cellData, row); + const entity = parseEntityFromTableRow(cellData, row); + + if (entity === undefined) { + return undefined; + } + + return { + ...entity, + isInactive: isInactiveEntity(row) === true + }; } public parseFilterValue(cellData: CellData): string | undefined { @@ -16,4 +25,8 @@ export class EntityTableCellParser extends TableCellParserBase typeof neighbor === 'object'; + +export const isInactiveEntity = (row: TableRow): boolean | undefined => { + const maxEndTimeAggregation = row['max(endTime)']; // Ew. + + if (!includesMaxEndTimeAggregation(maxEndTimeAggregation)) { + // If the aggregation wasn't fetched, we have no way of knowing if this Entity is inactive. + return undefined; + } + + return !hasValidMaxEndTimeTimestamp(maxEndTimeAggregation.value); +}; + +const includesMaxEndTimeAggregation = (maxEndTimeAggregation?: unknown): maxEndTimeAggregation is MetricAggregation => { + return maxEndTimeAggregation !== undefined; +}; + +const hasValidMaxEndTimeTimestamp = (maxEndTime?: unknown): maxEndTime is number => { + return maxEndTime !== undefined && isNumber(maxEndTime); +}; diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.ts index eef1efcbd..49bfeebb3 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { TableCellAlignmentType, TableCellRenderer, TableCellRendererBase } from '@hypertrace/components'; -import { Entity } from '../../../../graphql/model/schema/entity'; import { ObservabilityTableCellType } from '../../observability-table-cell-type'; +import { MaybeInactiveEntity } from './entity-table-cell-parser'; @Component({ selector: 'ht-entity-table-cell-renderer', @@ -9,7 +9,11 @@ import { ObservabilityTableCellType } from '../../observability-table-cell-type' changeDetection: ChangeDetectionStrategy.OnPush, template: `
- +
` }) @@ -18,4 +22,4 @@ import { ObservabilityTableCellType } from '../../observability-table-cell-type' alignment: TableCellAlignmentType.Left, parser: ObservabilityTableCellType.Entity }) -export class EntityTableCellRendererComponent extends TableCellRendererBase {} +export class EntityTableCellRendererComponent extends TableCellRendererBase {} diff --git a/projects/observability/src/shared/constants/entity-metadata.ts b/projects/observability/src/shared/constants/entity-metadata.ts index d8d6b88d4..f886d2b01 100644 --- a/projects/observability/src/shared/constants/entity-metadata.ts +++ b/projects/observability/src/shared/constants/entity-metadata.ts @@ -3,7 +3,7 @@ import { InjectionToken } from '@angular/core'; export interface EntityMetadata { entityType: string; icon: string; - detailPath(id: string, sourceRoute?: string): string[]; + detailPath(id: string, sourceRoute?: string, inactive?: boolean): string[]; listPath?: string[]; apisListPath?(id: string): string[]; sourceRoutes?: string[]; diff --git a/projects/observability/src/shared/services/navigation/entity/entity-navigation.service.ts b/projects/observability/src/shared/services/navigation/entity/entity-navigation.service.ts index b2f4c4301..a54c29e14 100644 --- a/projects/observability/src/shared/services/navigation/entity/entity-navigation.service.ts +++ b/projects/observability/src/shared/services/navigation/entity/entity-navigation.service.ts @@ -11,18 +11,18 @@ export class EntityNavigationService { @Inject(ENTITY_METADATA) private readonly entityMetadata: EntityMetadataMap ) { Array.from(this.entityMetadata.values()).forEach(item => { - this.registerEntityNavigationAction(item.entityType, (id, sourceRoute) => - this.navigationService.navigateWithinApp(item.detailPath(id, sourceRoute)) + this.registerEntityNavigationAction(item.entityType, (id, sourceRoute, inactive) => + this.navigationService.navigateWithinApp(item.detailPath(id, sourceRoute, inactive)) ); }); } private readonly entityNavigationMap: Map< EntityType, - (id: string, sourceRoute?: string) => Observable + (id: string, sourceRoute?: string, inactive?: boolean) => Observable > = new Map(); - public navigateToEntity(entity: Entity): Observable { + public navigateToEntity(entity: Entity, isInactive?: boolean): Observable { const entityType = entity[entityTypeKey]; const entityId = entity[entityIdKey]; const navigationFunction = this.entityNavigationMap.get(entityType); @@ -35,13 +35,13 @@ export class EntityNavigationService { ); return navigationFunction - ? navigationFunction(entityId, sourceRoute) + ? navigationFunction(entityId, sourceRoute, isInactive) : throwError(`Requested entity type not registered for navigation: ${entityType}`); } public registerEntityNavigationAction( entityType: EntityType, - action: (id: string, sourceRoute?: string) => Observable + action: (id: string, sourceRoute?: string, inactive?: boolean) => Observable ): void { this.entityNavigationMap.set(entityType, action); } From 911a4d2e2f6c3c113338dafded7868fe597219fb Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Mon, 3 May 2021 16:24:20 -0700 Subject: [PATCH 2/7] style: linting --- .../entity/entity-table-cell-renderer-util.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts index 9a5717d48..4536b4f21 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts @@ -42,10 +42,8 @@ export const isInactiveEntity = (row: TableRow): boolean | undefined => { return !hasValidMaxEndTimeTimestamp(maxEndTimeAggregation.value); }; -const includesMaxEndTimeAggregation = (maxEndTimeAggregation?: unknown): maxEndTimeAggregation is MetricAggregation => { - return maxEndTimeAggregation !== undefined; -}; +const includesMaxEndTimeAggregation = (maxEndTimeAggregation?: unknown): maxEndTimeAggregation is MetricAggregation => + maxEndTimeAggregation !== undefined; -const hasValidMaxEndTimeTimestamp = (maxEndTime?: unknown): maxEndTime is number => { - return maxEndTime !== undefined && isNumber(maxEndTime); -}; +const hasValidMaxEndTimeTimestamp = (maxEndTime?: unknown): maxEndTime is number => + maxEndTime !== undefined && isNumber(maxEndTime); From 52781dac50db915b66169b236e915354a4990a60 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Tue, 4 May 2021 13:18:57 -0700 Subject: [PATCH 3/7] feat: look at all aggregations to determine inactive --- .../entity/entity-table-cell-renderer-util.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts index 4536b4f21..4ab419827 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts @@ -1,7 +1,7 @@ import { Dictionary } from '@hypertrace/common'; import { TableRow } from '@hypertrace/components'; -import { MetricAggregation } from '@hypertrace/distributed-tracing'; -import { isNumber } from 'lodash-es'; +import { isMetricAggregation, MetricAggregation } from '@hypertrace/distributed-tracing'; +import { isNull } from 'lodash-es'; import { Entity, Interaction } from '../../../../graphql/model/schema/entity'; import { EntitySpecificationBuilder } from '../../../../graphql/request/builders/specification/entity/entity-specification-builder'; @@ -32,18 +32,17 @@ export const parseEntityFromTableRow = (cell: Entity | undefined, row: Dictionar const isInteraction = (neighbor: unknown): neighbor is Interaction => typeof neighbor === 'object'; export const isInactiveEntity = (row: TableRow): boolean | undefined => { - const maxEndTimeAggregation = row['max(endTime)']; // Ew. + const metricAggregations = filterMetricAggregations(row); - if (!includesMaxEndTimeAggregation(maxEndTimeAggregation)) { - // If the aggregation wasn't fetched, we have no way of knowing if this Entity is inactive. + if (metricAggregations.length === 0) { + // If an aggregation wasn't fetched, we have no way of knowing if this Entity is inactive. return undefined; } - return !hasValidMaxEndTimeTimestamp(maxEndTimeAggregation.value); + return metricAggregations.some(metricAggregation => !isValidMetricAggregation(metricAggregation)); }; -const includesMaxEndTimeAggregation = (maxEndTimeAggregation?: unknown): maxEndTimeAggregation is MetricAggregation => - maxEndTimeAggregation !== undefined; +const filterMetricAggregations = (row: TableRow): MetricAggregation[] => + Object.values(row).filter(value => isMetricAggregation(value)) as MetricAggregation[]; -const hasValidMaxEndTimeTimestamp = (maxEndTime?: unknown): maxEndTime is number => - maxEndTime !== undefined && isNumber(maxEndTime); +const isValidMetricAggregation = (metricAggregation: MetricAggregation): boolean => !isNull(metricAggregation.value); From 3fc1d42c61a6436ff0d7b31f00e4a2310f0032b9 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Tue, 4 May 2021 13:27:23 -0700 Subject: [PATCH 4/7] style: linting --- .../table/data-cell/entity/entity-table-cell-renderer-util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts index 4ab419827..3fe94cbfe 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts @@ -43,6 +43,6 @@ export const isInactiveEntity = (row: TableRow): boolean | undefined => { }; const filterMetricAggregations = (row: TableRow): MetricAggregation[] => - Object.values(row).filter(value => isMetricAggregation(value)) as MetricAggregation[]; + Object.values(row).filter(isMetricAggregation); const isValidMetricAggregation = (metricAggregation: MetricAggregation): boolean => !isNull(metricAggregation.value); From 20fafb4f5d04ec56c47a32000ab36bf6a3f31f54 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Tue, 4 May 2021 13:35:53 -0700 Subject: [PATCH 5/7] test: fix --- .../entity-renderer/entity-renderer.component.test.ts | 2 +- .../entity/entity-table-cell-renderer.component.test.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.test.ts b/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.test.ts index c779ab490..327999ebc 100644 --- a/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.test.ts +++ b/projects/observability/src/shared/components/entity-renderer/entity-renderer.component.test.ts @@ -83,7 +83,7 @@ describe('Entity Renderer Component', () => { expect(rendererElement).toHaveClass('navigable'); spectator.dispatchFakeEvent(rendererElement, 'click'); - expect(entityNavService.navigateToEntity).toHaveBeenCalledWith(entity); + expect(entityNavService.navigateToEntity).toHaveBeenCalledWith(entity, false); }); test('renders an entity without icon by default', () => { diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.test.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.test.ts index 5101cb285..c34a09487 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.test.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.test.ts @@ -35,7 +35,10 @@ describe('Entity table cell renderer component', () => { test('should render a milliseconds only value', () => { const spectator = buildComponent(); - expect(spectator.component.value).toEqual(entity); + expect(spectator.component.value).toEqual({ + ...entity, + isInactive: false + }); }); test('should render first column with additional css class', () => { From f33a63598764b9c2a5e811f22a35e4d817f0dadc Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Tue, 4 May 2021 13:48:18 -0700 Subject: [PATCH 6/7] style: prettier --- .../table/data-cell/entity/entity-table-cell-renderer-util.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts index 3fe94cbfe..4c2ad28b8 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts @@ -42,7 +42,6 @@ export const isInactiveEntity = (row: TableRow): boolean | undefined => { return metricAggregations.some(metricAggregation => !isValidMetricAggregation(metricAggregation)); }; -const filterMetricAggregations = (row: TableRow): MetricAggregation[] => - Object.values(row).filter(isMetricAggregation); +const filterMetricAggregations = (row: TableRow): MetricAggregation[] => Object.values(row).filter(isMetricAggregation); const isValidMetricAggregation = (metricAggregation: MetricAggregation): boolean => !isNull(metricAggregation.value); From 03abe7d5ba27546016cd2bd0945406809768d341 Mon Sep 17 00:00:00 2001 From: Jake <45181984+jake-bassett@users.noreply.github.com> Date: Tue, 4 May 2021 14:13:41 -0700 Subject: [PATCH 7/7] Update projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts Co-authored-by: Aaron Steinfeld <45047841+aaron-steinfeld@users.noreply.github.com> --- .../table/data-cell/entity/entity-table-cell-renderer-util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts index 4c2ad28b8..93f1ac4d3 100644 --- a/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts +++ b/projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts @@ -39,7 +39,7 @@ export const isInactiveEntity = (row: TableRow): boolean | undefined => { return undefined; } - return metricAggregations.some(metricAggregation => !isValidMetricAggregation(metricAggregation)); + return metricAggregations.every(metricAggregation => !isValidMetricAggregation(metricAggregation)); }; const filterMetricAggregations = (row: TableRow): MetricAggregation[] => Object.values(row).filter(isMetricAggregation);