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/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 metricAggregations = filterMetricAggregations(row); + + if (metricAggregations.length === 0) { + // If an aggregation wasn't fetched, we have no way of knowing if this Entity is inactive. + return undefined; + } + + return metricAggregations.every(metricAggregation => !isValidMetricAggregation(metricAggregation)); +}; + +const filterMetricAggregations = (row: TableRow): MetricAggregation[] => Object.values(row).filter(isMetricAggregation); + +const isValidMetricAggregation = (metricAggregation: MetricAggregation): boolean => !isNull(metricAggregation.value); 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', () => { 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); }