Skip to content

Commit e25a66c

Browse files
Merge remote-tracking branch 'origin/log-icon' into log-icon
2 parents 01b9bbc + 929f90b commit e25a66c

File tree

10 files changed

+69
-26
lines changed

10 files changed

+69
-26
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM nginx:1.15-alpine
1+
FROM nginx:1.20-alpine
22
COPY dist/hypertrace-ui /usr/share/nginx/html
33
COPY conf/default.conf /etc/nginx/conf.d/default.conf
44
EXPOSE 2020

projects/components/src/popover/popover.component.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
OnDestroy,
1010
Output
1111
} from '@angular/core';
12-
import { Subscription } from 'rxjs';
1312
import { PopoverBackdrop, PopoverPositionType, PopoverRelativePositionLocation } from './popover';
1413
import { PopoverContentComponent } from './popover-content.component';
1514
import { PopoverRef } from './popover-ref';
@@ -51,12 +50,14 @@ export class PopoverComponent implements OnDestroy {
5150
@ContentChild(PopoverContentComponent, { static: true })
5251
public content!: PopoverContentComponent;
5352

54-
private subscription?: Subscription;
53+
private popover?: PopoverRef;
5554

5655
public constructor(private readonly popoverService: PopoverService, private readonly popoverElement: ElementRef) {}
5756

5857
public ngOnDestroy(): void {
59-
this.subscription?.unsubscribe();
58+
if (!this.popover?.closed) {
59+
this.popover?.close();
60+
}
6061
}
6162

6263
@HostListener('click')
@@ -65,7 +66,7 @@ export class PopoverComponent implements OnDestroy {
6566
return;
6667
}
6768

68-
const popover = this.popoverService.drawPopover({
69+
this.popover = this.popoverService.drawPopover({
6970
position: {
7071
type: PopoverPositionType.Relative,
7172
origin: this.popoverElement,
@@ -76,17 +77,17 @@ export class PopoverComponent implements OnDestroy {
7677
});
7778

7879
// Closing can happen internal to the Popover for things like closeOnBackdropClick. Let the consumer know.
79-
this.subscription = popover.closed$.subscribe(() => this.popoverClose.emit());
80+
this.popover.closed$.subscribe(() => this.popoverClose.emit());
8081

81-
popover.closeOnBackdropClick();
82+
this.popover.closeOnBackdropClick();
8283

8384
if (this.closeOnClick) {
84-
popover.closeOnPopoverContentClick();
85+
this.popover.closeOnPopoverContentClick();
8586
}
8687
if (this.closeOnNavigate) {
87-
popover.closeOnNavigation();
88+
this.popover.closeOnNavigation();
8889
}
8990

90-
this.popoverOpen.emit(popover);
91+
this.popoverOpen.emit(this.popover);
9192
}
9293
}

projects/observability/src/shared/components/entity-renderer/entity-renderer.component.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ describe('Entity Renderer Component', () => {
8383

8484
expect(rendererElement).toHaveClass('navigable');
8585
spectator.dispatchFakeEvent(rendererElement, 'click');
86-
expect(entityNavService.navigateToEntity).toHaveBeenCalledWith(entity);
86+
expect(entityNavService.navigateToEntity).toHaveBeenCalledWith(entity, false);
8787
});
8888

8989
test('renders an entity without icon by default', () => {

projects/observability/src/shared/components/entity-renderer/entity-renderer.component.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export class EntityRendererComponent implements OnChanges {
3232
@Input()
3333
public entity?: Entity;
3434

35+
@Input()
36+
public inactive: boolean = false;
37+
3538
@Input()
3639
public navigable: boolean = true;
3740

@@ -59,8 +62,9 @@ export class EntityRendererComponent implements OnChanges {
5962
this.setIconType();
6063
}
6164
}
65+
6266
public onClickNavigate(): void {
63-
this.navigable && this.entity && this.entityNavService.navigateToEntity(this.entity);
67+
this.navigable && this.entity && this.entityNavService.navigateToEntity(this.entity, this.inactive);
6468
}
6569

6670
private setName(): void {
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
import { TableCellParser, TableCellParserBase, TableRow } from '@hypertrace/components';
22
import { Entity, entityIdKey } from '../../../../graphql/model/schema/entity';
33
import { ObservabilityTableCellType } from '../../observability-table-cell-type';
4-
import { parseEntityFromTableRow } from './entity-table-cell-renderer-util';
4+
import { isInactiveEntity, parseEntityFromTableRow } from './entity-table-cell-renderer-util';
55

66
@TableCellParser({
77
type: ObservabilityTableCellType.Entity
88
})
99
export class EntityTableCellParser extends TableCellParserBase<CellData, CellData, string | undefined> {
1010
public parseValue(cellData: CellData, row: TableRow): CellData {
11-
return parseEntityFromTableRow(cellData, row);
11+
const entity = parseEntityFromTableRow(cellData, row);
12+
13+
if (entity === undefined) {
14+
return undefined;
15+
}
16+
17+
return {
18+
...entity,
19+
isInactive: isInactiveEntity(row) === true
20+
};
1221
}
1322

1423
public parseFilterValue(cellData: CellData): string | undefined {
1524
return cellData !== undefined ? cellData[entityIdKey] : undefined;
1625
}
1726
}
1827

19-
type CellData = Entity | undefined;
28+
type CellData = MaybeInactiveEntity | undefined;
29+
30+
export interface MaybeInactiveEntity extends Entity {
31+
isInactive: boolean;
32+
}

projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer-util.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { Dictionary } from '@hypertrace/common';
2+
import { TableRow } from '@hypertrace/components';
3+
import { isMetricAggregation, MetricAggregation } from '@hypertrace/distributed-tracing';
4+
import { isNull } from 'lodash-es';
25
import { Entity, Interaction } from '../../../../graphql/model/schema/entity';
36
import { EntitySpecificationBuilder } from '../../../../graphql/request/builders/specification/entity/entity-specification-builder';
47

@@ -27,3 +30,18 @@ export const parseEntityFromTableRow = (cell: Entity | undefined, row: Dictionar
2730
};
2831

2932
const isInteraction = (neighbor: unknown): neighbor is Interaction => typeof neighbor === 'object';
33+
34+
export const isInactiveEntity = (row: TableRow): boolean | undefined => {
35+
const metricAggregations = filterMetricAggregations(row);
36+
37+
if (metricAggregations.length === 0) {
38+
// If an aggregation wasn't fetched, we have no way of knowing if this Entity is inactive.
39+
return undefined;
40+
}
41+
42+
return metricAggregations.every(metricAggregation => !isValidMetricAggregation(metricAggregation));
43+
};
44+
45+
const filterMetricAggregations = (row: TableRow): MetricAggregation[] => Object.values(row).filter(isMetricAggregation);
46+
47+
const isValidMetricAggregation = (metricAggregation: MetricAggregation): boolean => !isNull(metricAggregation.value);

projects/observability/src/shared/components/table/data-cell/entity/entity-table-cell-renderer.component.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ describe('Entity table cell renderer component', () => {
3535

3636
test('should render a milliseconds only value', () => {
3737
const spectator = buildComponent();
38-
expect(spectator.component.value).toEqual(entity);
38+
expect(spectator.component.value).toEqual({
39+
...entity,
40+
isInactive: false
41+
});
3942
});
4043

4144
test('should render first column with additional css class', () => {
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { ChangeDetectionStrategy, Component } from '@angular/core';
22
import { TableCellAlignmentType, TableCellRenderer, TableCellRendererBase } from '@hypertrace/components';
3-
import { Entity } from '../../../../graphql/model/schema/entity';
43
import { ObservabilityTableCellType } from '../../observability-table-cell-type';
4+
import { MaybeInactiveEntity } from './entity-table-cell-parser';
55

66
@Component({
77
selector: 'ht-entity-table-cell-renderer',
88
styleUrls: ['./entity-table-cell-renderer.component.scss'],
99
changeDetection: ChangeDetectionStrategy.OnPush,
1010
template: `
1111
<div class="fill-container entity-cell" [ngClass]="{ 'first-column': this.isFirstColumn }">
12-
<ht-entity-renderer [entity]="this.value" [navigable]="true"></ht-entity-renderer>
12+
<ht-entity-renderer
13+
[entity]="this.value"
14+
[inactive]="this.value?.isInactive === true"
15+
[navigable]="true"
16+
></ht-entity-renderer>
1317
</div>
1418
`
1519
})
@@ -18,4 +22,4 @@ import { ObservabilityTableCellType } from '../../observability-table-cell-type'
1822
alignment: TableCellAlignmentType.Left,
1923
parser: ObservabilityTableCellType.Entity
2024
})
21-
export class EntityTableCellRendererComponent extends TableCellRendererBase<Entity | undefined> {}
25+
export class EntityTableCellRendererComponent extends TableCellRendererBase<MaybeInactiveEntity | undefined> {}

projects/observability/src/shared/constants/entity-metadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { InjectionToken } from '@angular/core';
33
export interface EntityMetadata {
44
entityType: string;
55
icon: string;
6-
detailPath(id: string, sourceRoute?: string): string[];
6+
detailPath(id: string, sourceRoute?: string, inactive?: boolean): string[];
77
listPath?: string[];
88
apisListPath?(id: string): string[];
99
sourceRoutes?: string[];

projects/observability/src/shared/services/navigation/entity/entity-navigation.service.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ export class EntityNavigationService {
1111
@Inject(ENTITY_METADATA) private readonly entityMetadata: EntityMetadataMap
1212
) {
1313
Array.from(this.entityMetadata.values()).forEach(item => {
14-
this.registerEntityNavigationAction(item.entityType, (id, sourceRoute) =>
15-
this.navigationService.navigateWithinApp(item.detailPath(id, sourceRoute))
14+
this.registerEntityNavigationAction(item.entityType, (id, sourceRoute, inactive) =>
15+
this.navigationService.navigateWithinApp(item.detailPath(id, sourceRoute, inactive))
1616
);
1717
});
1818
}
1919

2020
private readonly entityNavigationMap: Map<
2121
EntityType,
22-
(id: string, sourceRoute?: string) => Observable<boolean>
22+
(id: string, sourceRoute?: string, inactive?: boolean) => Observable<boolean>
2323
> = new Map();
2424

25-
public navigateToEntity(entity: Entity): Observable<boolean> {
25+
public navigateToEntity(entity: Entity, isInactive?: boolean): Observable<boolean> {
2626
const entityType = entity[entityTypeKey];
2727
const entityId = entity[entityIdKey];
2828
const navigationFunction = this.entityNavigationMap.get(entityType);
@@ -35,13 +35,13 @@ export class EntityNavigationService {
3535
);
3636

3737
return navigationFunction
38-
? navigationFunction(entityId, sourceRoute)
38+
? navigationFunction(entityId, sourceRoute, isInactive)
3939
: throwError(`Requested entity type not registered for navigation: ${entityType}`);
4040
}
4141

4242
public registerEntityNavigationAction(
4343
entityType: EntityType,
44-
action: (id: string, sourceRoute?: string) => Observable<boolean>
44+
action: (id: string, sourceRoute?: string, inactive?: boolean) => Observable<boolean>
4545
): void {
4646
this.entityNavigationMap.set(entityType, action);
4747
}

0 commit comments

Comments
 (0)