diff --git a/projects/observability/src/shared/components/status-display/status-display-icon.pipe.ts b/projects/observability/src/shared/components/status-display/status-display-icon.pipe.ts new file mode 100644 index 000000000..2bf822eb0 --- /dev/null +++ b/projects/observability/src/shared/components/status-display/status-display-icon.pipe.ts @@ -0,0 +1,25 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { IconType } from '@hypertrace/assets-library'; + +@Pipe({ + name: 'htStatusDisplayIcon' +}) +export class StatusDisplayIconPipe implements PipeTransform { + public transform(statusCode: string | number, status?: string): IconType | undefined { + if (status === 'FAIL') { + return IconType.AlertFill; + } + if (status === 'SUCCESS') { + return IconType.CheckCircleFill; + } + /** + * GRPC Success -> 0 + * HTTP Success -> 100-300 + */ + if (+statusCode === 0 || (+statusCode >= 100 && +statusCode <= 399)) { + return IconType.CheckCircleFill; + } + + return IconType.AlertFill; + } +} diff --git a/projects/observability/src/shared/components/status-display/status-display-text.pipe.ts b/projects/observability/src/shared/components/status-display/status-display-text.pipe.ts new file mode 100644 index 000000000..732d5a5e2 --- /dev/null +++ b/projects/observability/src/shared/components/status-display/status-display-text.pipe.ts @@ -0,0 +1,24 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { displayString } from '@hypertrace/common'; +import { isNil } from 'lodash-es'; + +@Pipe({ + name: 'htStatusDisplayText' +}) +export class StatusDisplayTextPipe implements PipeTransform { + /** + * Appends the `statusMessage` with a `-` if it's available. + * If not just show the `statusCode` + * + * @param statusCode - status code + * @param statusMessage - status message + */ + public transform(statusCode: string | number, statusMessage?: string): string { + let statusDisplayText = displayString(statusCode); + if (!isNil(statusMessage) && statusMessage !== 'null') { + statusDisplayText = `${statusDisplayText} - ${statusMessage}`; + } + + return statusDisplayText; + } +} diff --git a/projects/observability/src/shared/components/status-display/status-display.component.scss b/projects/observability/src/shared/components/status-display/status-display.component.scss new file mode 100644 index 000000000..754f4dd37 --- /dev/null +++ b/projects/observability/src/shared/components/status-display/status-display.component.scss @@ -0,0 +1,12 @@ +@import 'mixins'; + +.status { + display: flex; + align-items: center; + gap: 4px; + padding: 0 4px; + background-color: $gray-1; + border-radius: 4px; + font-size: 12px; + color: $gray-5; +} diff --git a/projects/observability/src/shared/components/status-display/status-display.component.test.ts b/projects/observability/src/shared/components/status-display/status-display.component.test.ts new file mode 100644 index 000000000..0c3203424 --- /dev/null +++ b/projects/observability/src/shared/components/status-display/status-display.component.test.ts @@ -0,0 +1,81 @@ +import { IconType } from '@hypertrace/assets-library'; +import { IconComponent } from '@hypertrace/components'; +import { createHostFactory } from '@ngneat/spectator/jest'; +import { MockComponent } from 'ng-mocks'; +import { StatusDisplayIconPipe } from './status-display-icon.pipe'; +import { StatusDisplayTextPipe } from './status-display-text.pipe'; +import { StatusDisplayComponent } from './status-display.component'; + +describe('Status Display Component', () => { + const createHost = createHostFactory({ + component: StatusDisplayComponent, + shallow: true, + declarations: [MockComponent(IconComponent), StatusDisplayTextPipe, StatusDisplayIconPipe] + }); + + test('should display the status code properly with check icon', () => { + const spectator = createHost(``, { + hostProps: { + statusCode: 200 + } + }); + + expect(spectator.query(IconComponent)?.icon).toBe(IconType.CheckCircleFill); + expect(spectator.query('.text')?.textContent).toBe('200'); + }); + + test('should display the fail icon', () => { + const spectator = createHost( + ``, + { + hostProps: { + status: 'FAIL', + statusCode: 400 + } + } + ); + + expect(spectator.query(IconComponent)?.icon).toBe(IconType.AlertFill); + }); + + test('should display the fail icon', () => { + const spectator = createHost( + ``, + { + hostProps: { + statusCode: 400 + } + } + ); + + expect(spectator.query(IconComponent)?.icon).toBe(IconType.AlertFill); + }); + + test('should display the check icon if unknown status is send', () => { + const spectator = createHost( + ``, + { + hostProps: { + status: 'SOMETHING', + statusCode: 200 + } + } + ); + + expect(spectator.query(IconComponent)?.icon).toBe(IconType.CheckCircleFill); + }); + + test('should display the status code with status message', () => { + const spectator = createHost( + ``, + { + hostProps: { + statusCode: 200, + statusMessage: 'OK' + } + } + ); + + expect(spectator.query('.text')?.textContent).toBe('200 - OK'); + }); +}); diff --git a/projects/observability/src/shared/components/status-display/status-display.component.ts b/projects/observability/src/shared/components/status-display/status-display.component.ts new file mode 100644 index 000000000..081137bc8 --- /dev/null +++ b/projects/observability/src/shared/components/status-display/status-display.component.ts @@ -0,0 +1,26 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { IconSize } from '@hypertrace/components'; + +@Component({ + selector: 'ht-status-display', + template: `
+ + + + {{ this.statusCode | htStatusDisplayText: this.statusMessage }} +
`, + styleUrls: ['./status-display.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class StatusDisplayComponent { + @Input() + public statusCode?: number | string; + + @Input() + public status?: string; + + @Input() + public statusMessage?: string; + + // TODO: Add display for different styles if required +} diff --git a/projects/observability/src/shared/components/status-display/status-display.module.ts b/projects/observability/src/shared/components/status-display/status-display.module.ts new file mode 100644 index 000000000..7ddae040a --- /dev/null +++ b/projects/observability/src/shared/components/status-display/status-display.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; + +import { CommonModule } from '@angular/common'; +import { FormattingModule, MemoizeModule } from '@hypertrace/common'; +import { IconModule } from '@hypertrace/components'; +import { StatusDisplayIconPipe } from './status-display-icon.pipe'; +import { StatusDisplayTextPipe } from './status-display-text.pipe'; +import { StatusDisplayComponent } from './status-display.component'; + +@NgModule({ + imports: [IconModule, MemoizeModule, CommonModule, FormattingModule], + exports: [StatusDisplayComponent], + declarations: [StatusDisplayComponent, StatusDisplayTextPipe, StatusDisplayIconPipe] +}) +export class StatusDisplayModule {}