From a710c40ba5da1c39f31c9558d70b527e8eb40072 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:50:32 -0800 Subject: [PATCH 1/9] refactor: breadcrumb to support additional specifications --- package.json | 2 +- .../api-detail-breadcrumb.resolver.ts | 69 +++++++------------ .../apis/api-detail/api-detail.service.ts | 4 +- .../service-detail-breadcrumb.resolver.ts | 61 ++++------------ .../service-detail/service-detail.service.ts | 3 +- projects/observability/src/public-api.ts | 1 + .../entity-breadcrumb.resolver.ts | 59 ++++++++++++++++ 7 files changed, 106 insertions(+), 93 deletions(-) create mode 100644 projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts diff --git a/package.json b/package.json index d546d307d..a218ce02c 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "build:components": "ng build components", "build:dashboards": "ng build dashboards", "build:ci": "node --max_old_space_size=3584 node_modules/@angular/cli/bin/ng build --configuration production --no-progress", - "test": "ng test hypertrace-ui --cache", + "test": "ng test hypertrace-ui --cache --maxWorkers=2", "lint": "ng lint hypertrace-ui", "lint:fix": "ng lint --fix hypertrace-ui", "prettier:check": "prettier --check '**'", diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts index 2af9fde7e..c0bd1a51f 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts @@ -1,43 +1,47 @@ import { Inject, Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Breadcrumb, NavigationService, TimeRangeService } from '@hypertrace/common'; import { BreadcrumbsService } from '@hypertrace/components'; -import { GraphQlRequestCacheability, GraphQlRequestService } from '@hypertrace/graphql-client'; +import { GraphQlRequestService } from '@hypertrace/graphql-client'; import { Observable } from 'rxjs'; -import { map, switchMap, take } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { EntityMetadata, EntityMetadataMap, ENTITY_METADATA } from '../../../shared/constants/entity-metadata'; import { Entity, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; -import { GraphQlTimeRange } from '../../../shared/graphql/model/schema/timerange/graphql-time-range'; -import { SpecificationBuilder } from '../../../shared/graphql/request/builders/specification/specification-builder'; -import { - EntityGraphQlQueryHandlerService, - ENTITY_GQL_REQUEST -} from '../../../shared/graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; +import { EntityBreadcrumbResolver } from '../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; +import { EntityIconLookupService } from './../../../shared/services/entity/entity-icon-lookup.service'; +import { ApiEntity } from './api-detail.service'; @Injectable({ providedIn: 'root' }) -export class ApiDetailBreadcrumbResolver implements Resolve> { - private readonly specificationBuilder: SpecificationBuilder = new SpecificationBuilder(); +export class ApiDetailBreadcrumbResolver extends EntityBreadcrumbResolver { protected readonly apiEntityMetadata: EntityMetadata | undefined; public constructor( + timeRangeService: TimeRangeService, + graphQlQueryService: GraphQlRequestService, + iconLookupService: EntityIconLookupService, private readonly navigationService: NavigationService, - private readonly timeRangeService: TimeRangeService, - private readonly graphQlQueryService: GraphQlRequestService, protected readonly breadcrumbService: BreadcrumbsService, @Inject(ENTITY_METADATA) private readonly entityMetadataMap: EntityMetadataMap ) { + super(timeRangeService, graphQlQueryService, iconLookupService); this.apiEntityMetadata = this.entityMetadataMap.get(ObservabilityEntityType.Api); } public async resolve(activatedRouteSnapshot: ActivatedRouteSnapshot): Promise> { const id = activatedRouteSnapshot.paramMap.get('id') as string; - const parentType = this.resolveParentType(); + const parentEntityMetadata = this.resolveParentType(); return Promise.resolve( - this.fetchEntity(id, parentType).pipe( - take(1), + this.fetchEntity(id, ObservabilityEntityType.Api).pipe( + map( + apiEntity => + ({ + ...apiEntity, + ...this.getParentPartial(apiEntity, parentEntityMetadata) + } as ApiBreadcrumbDetails) + ), switchMap(api => [ - ...this.getParentBreadcrumbs(api, parentType), + ...this.getParentBreadcrumbs(api, parentEntityMetadata), this.createBreadcrumbForEntity(api, activatedRouteSnapshot) ]) ) @@ -47,8 +51,9 @@ export class ApiDetailBreadcrumbResolver implements Resolve { - return this.timeRangeService.getTimeRangeAndChanges().pipe( - switchMap(timeRange => - this.graphQlQueryService.query( - { - requestType: ENTITY_GQL_REQUEST, - entityType: ObservabilityEntityType.Api, - id: id, - properties: this.getAttributeKeys(parentEntityMetadata).map(attributeKey => - this.specificationBuilder.attributeSpecificationForKey(attributeKey) - ), - timeRange: new GraphQlTimeRange(timeRange.startTime, timeRange.endTime) - }, - { cacheability: GraphQlRequestCacheability.NotCacheable } - ) - ), - map(apiEntity => ({ - ...apiEntity, - ...this.getParentPartial(apiEntity, parentEntityMetadata) - })) - ); - } - - private getAttributeKeys(parentTypeMetadata?: EntityMetadata): string[] { + protected getAttributeKeys(): string[] { + const parentTypeMetadata = this.resolveParentType(); const parentAttributes = parentTypeMetadata ? [this.getParentNameAttribute(parentTypeMetadata), this.getParentIdAttribute(parentTypeMetadata)] : []; @@ -142,7 +125,7 @@ export class ApiDetailBreadcrumbResolver implements Resolve { +export interface ApiBreadcrumbDetails extends ApiEntity { name: string; parentName: string; parentId: string; diff --git a/projects/observability/src/pages/apis/api-detail/api-detail.service.ts b/projects/observability/src/pages/apis/api-detail/api-detail.service.ts index 1d5a1aca9..86664c72c 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail.service.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { Breadcrumb } from '@hypertrace/common'; import { Entity, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; import { EntityDetailService } from '../../../shared/services/entity/entity-detail.service'; @@ -18,8 +19,9 @@ export class ApiDetailService extends EntityDetailService { } } -export interface ApiEntity extends Entity { +export interface ApiEntity extends Entity, Breadcrumb { apiType: ApiType; + name: string; } export const enum ApiType { diff --git a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts index eb5c0d848..2b62d16c5 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts @@ -1,60 +1,27 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; -import { Breadcrumb, TimeRangeService } from '@hypertrace/common'; -import { GraphQlRequestCacheability, GraphQlRequestService } from '@hypertrace/graphql-client'; +import { ActivatedRouteSnapshot } from '@angular/router'; +import { TimeRangeService } from '@hypertrace/common'; +import { GraphQlRequestService } from '@hypertrace/graphql-client'; import { Observable } from 'rxjs'; -import { map, switchMap, take } from 'rxjs/operators'; import { ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; -import { GraphQlTimeRange } from '../../../shared/graphql/model/schema/timerange/graphql-time-range'; -import { SpecificationBuilder } from '../../../shared/graphql/request/builders/specification/specification-builder'; -import { - EntityGraphQlQueryHandlerService, - ENTITY_GQL_REQUEST -} from '../../../shared/graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; -import { EntityIconLookupService } from '../../../shared/services/entity/entity-icon-lookup.service'; +import { EntityBreadcrumbResolver } from '../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; +import { EntityIconLookupService } from './../../../shared/services/entity/entity-icon-lookup.service'; import { ServiceEntity } from './service-detail.service'; @Injectable({ providedIn: 'root' }) -export class ServiceDetailBreadcrumbResolver implements Resolve> { - private readonly specificationBuilder: SpecificationBuilder = new SpecificationBuilder(); - +export class ServiceDetailBreadcrumbResolver extends EntityBreadcrumbResolver { public constructor( - private readonly timeRangeService: TimeRangeService, - private readonly graphQlQueryService: GraphQlRequestService, - protected readonly iconLookupService: EntityIconLookupService - ) {} + timeRangeService: TimeRangeService, + graphQlQueryService: GraphQlRequestService, + iconLookupService: EntityIconLookupService + ) { + super(timeRangeService, graphQlQueryService, iconLookupService); + } - public async resolve(activatedRouteSnapshot: ActivatedRouteSnapshot): Promise> { + public async resolve(activatedRouteSnapshot: ActivatedRouteSnapshot): Promise> { const id = activatedRouteSnapshot.paramMap.get('id'); - return Promise.resolve( - this.fetchEntity(id as string).pipe( - take(1), - map(service => ({ - label: service.name, - icon: this.iconLookupService.forEntity(service) - })) - ) - ); - } - - protected fetchEntity(id: string): Observable { - return this.timeRangeService.getTimeRangeAndChanges().pipe( - switchMap(timeRange => - this.graphQlQueryService.query( - { - requestType: ENTITY_GQL_REQUEST, - entityType: ObservabilityEntityType.Service, - id: id, - properties: this.getAttributeKeys().map(attributeKey => - this.specificationBuilder.attributeSpecificationForKey(attributeKey) - ), - timeRange: new GraphQlTimeRange(timeRange.startTime, timeRange.endTime) - }, - { cacheability: GraphQlRequestCacheability.NotCacheable } - ) - ) - ); + return Promise.resolve(this.fetchEntity(id as string, ObservabilityEntityType.Service)); } protected getAttributeKeys(): string[] { diff --git a/projects/observability/src/pages/apis/service-detail/service-detail.service.ts b/projects/observability/src/pages/apis/service-detail/service-detail.service.ts index 4a82b62c2..636326bd2 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail.service.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { Breadcrumb } from '@hypertrace/common'; import { Entity, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; import { EntityDetailService } from '../../../shared/services/entity/entity-detail.service'; @@ -19,6 +20,6 @@ export class ServiceDetailService extends EntityDetailService { } } -export interface ServiceEntity extends Entity { +export interface ServiceEntity extends Entity, Breadcrumb { name: string; } diff --git a/projects/observability/src/public-api.ts b/projects/observability/src/public-api.ts index e4e9d0bae..5b746d229 100644 --- a/projects/observability/src/public-api.ts +++ b/projects/observability/src/public-api.ts @@ -92,6 +92,7 @@ export * from './shared/graphql/model/schema/trace'; // Services export * from './pages/trace-detail/trace-detail.service'; export * from './shared/services/log-events/log-events.service'; +export * from './shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; // Span Detail export { SpanData } from './shared/components/span-detail/span-data'; diff --git a/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts new file mode 100644 index 000000000..533a6838c --- /dev/null +++ b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts @@ -0,0 +1,59 @@ +import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { Breadcrumb, TimeRangeService } from '@hypertrace/common'; +import { GraphQlRequestCacheability, GraphQlRequestService } from '@hypertrace/graphql-client'; +import { Observable } from 'rxjs'; +import { map, switchMap, take } from 'rxjs/operators'; +import { Entity } from '../../graphql/model/schema/entity'; +import { GraphQlTimeRange } from '../../graphql/model/schema/timerange/graphql-time-range'; +import { SpecificationBuilder } from '../../graphql/request/builders/specification/specification-builder'; +import { EntityGraphQlQueryHandlerService } from '../../graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; +import { EntityIconLookupService } from '../entity/entity-icon-lookup.service'; +import { Specification } from './../../graphql/model/schema/specifier/specification'; +import { ENTITY_GQL_REQUEST } from './../../graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; + +export abstract class EntityBreadcrumbResolver implements Resolve> { + private readonly specificationBuilder: SpecificationBuilder = new SpecificationBuilder(); + + public constructor( + protected readonly timeRangeService: TimeRangeService, + protected readonly graphQlQueryService: GraphQlRequestService, + protected readonly iconLookupService: EntityIconLookupService + ) {} + + public abstract resolve(route: ActivatedRouteSnapshot): Promise>; + + protected abstract getAttributeKeys(): string[]; + + protected getAdditionalSpecifications(): Specification[] { + return []; + } + + protected fetchEntity(id: string, entityType: string): Observable { + return this.timeRangeService.getTimeRangeAndChanges().pipe( + switchMap(timeRange => + this.graphQlQueryService.query( + { + requestType: ENTITY_GQL_REQUEST, + entityType: entityType, + id: id, + properties: [...this.getAttributeSpecification(), ...this.getAdditionalSpecifications()], + timeRange: new GraphQlTimeRange(timeRange.startTime, timeRange.endTime) + }, + { cacheability: GraphQlRequestCacheability.NotCacheable } + ) + ), + map(entity => ({ + ...entity, + label: entity.name as string, + icon: this.iconLookupService.forEntity(entity) + })), + take(1) + ); + } + + private getAttributeSpecification(): Specification[] { + return this.getAttributeKeys().map(attributeKey => + this.specificationBuilder.attributeSpecificationForKey(attributeKey) + ); + } +} From 39e81b6452f0bac42281aadad9486025fbc2a7b6 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 10 Nov 2021 16:11:25 -0800 Subject: [PATCH 2/9] refactor: fixing lint --- .../api-detail-breadcrumb.resolver.ts | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts index c0bd1a51f..863dbd83f 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts @@ -33,13 +33,10 @@ export class ApiDetailBreadcrumbResolver extends EntityBrea return Promise.resolve( this.fetchEntity(id, ObservabilityEntityType.Api).pipe( - map( - apiEntity => - ({ - ...apiEntity, - ...this.getParentPartial(apiEntity, parentEntityMetadata) - } as ApiBreadcrumbDetails) - ), + map(apiEntity => ({ + ...apiEntity, + ...this.getParentPartial(apiEntity, parentEntityMetadata) + })), switchMap(api => [ ...this.getParentBreadcrumbs(api, parentEntityMetadata), this.createBreadcrumbForEntity(api, activatedRouteSnapshot) @@ -48,10 +45,7 @@ export class ApiDetailBreadcrumbResolver extends EntityBrea ); } - protected createBreadcrumbForEntity( - api: ApiBreadcrumbDetails, - activatedRouteSnapshot: ActivatedRouteSnapshot - ): ApiEntity { + protected createBreadcrumbForEntity(api: T & Breadcrumb, activatedRouteSnapshot: ActivatedRouteSnapshot): ApiEntity { return { ...api, label: api.name, @@ -60,18 +54,18 @@ export class ApiDetailBreadcrumbResolver extends EntityBrea }; } - protected getParentBreadcrumbs(api: ApiBreadcrumbDetails, parentEntityMetadata?: EntityMetadata): Breadcrumb[] { + protected getParentBreadcrumbs(api: T & Breadcrumb, parentEntityMetadata?: EntityMetadata): Breadcrumb[] { return parentEntityMetadata !== undefined ? [ { - label: api.parentName, + label: api.parentName as string, icon: parentEntityMetadata?.icon, - url: parentEntityMetadata?.detailPath(api.parentId) + url: parentEntityMetadata?.detailPath(api.parentId as string) }, { label: 'Endpoints', icon: this.apiEntityMetadata?.icon, - url: parentEntityMetadata?.apisListPath?.(api.parentId) + url: parentEntityMetadata?.apisListPath?.(api.parentId as string) } ] : []; From f054311f97884696841eefb4af92082826eb6397 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 10 Nov 2021 16:32:18 -0800 Subject: [PATCH 3/9] refactor: fix test --- .../apis/api-detail/api-detail-breadcrumb.resolver.test.ts | 3 ++- .../service-detail/service-detail-breadcrumb.resolver.test.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts index f71accb58..b1b449d62 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts @@ -12,9 +12,10 @@ import { entityIdKey, entityTypeKey, ObservabilityEntityType } from '../../../sh import { ENTITY_GQL_REQUEST } from '../../../shared/graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; import { ObservabilityIconType } from '../../../shared/icons/observability-icon-type'; import { ApiDetailBreadcrumbResolver } from './api-detail-breadcrumb.resolver'; +import { ApiEntity } from './api-detail.service'; describe('Api detail breadcrumb resolver', () => { - let spectator: SpectatorService; + let spectator: SpectatorService>; let activatedRouteSnapshot: ActivatedRouteSnapshot; const buildResolver = createServiceFactory({ service: ApiDetailBreadcrumbResolver, diff --git a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts index d8da9b2f9..7c0b14269 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts @@ -11,9 +11,10 @@ import { ENTITY_GQL_REQUEST } from '../../../shared/graphql/request/handlers/ent import { ObservabilityIconType } from '../../../shared/icons/observability-icon-type'; import { EntityIconLookupService } from '../../../shared/services/entity/entity-icon-lookup.service'; import { ServiceDetailBreadcrumbResolver } from './service-detail-breadcrumb.resolver'; +import { ServiceEntity } from './service-detail.service'; describe('Service detail breadcrumb resolver', () => { - let spectator: SpectatorService; + let spectator: SpectatorService>; let activatedRouteSnapshot: ActivatedRouteSnapshot; const buildResolver = createServiceFactory({ service: ServiceDetailBreadcrumbResolver, From 305e1f14aacec132c0bff64f96e0e64bec720cdf Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 10 Nov 2021 22:53:28 -0800 Subject: [PATCH 4/9] refactor: addressing review comments --- .../api-detail-breadcrumb.resolver.test.ts | 4 ++-- .../api-detail-breadcrumb.resolver.ts | 22 +++++++++++-------- .../apis/api-detail/api-detail.service.ts | 3 +-- .../service-detail-breadcrumb.resolver.ts | 8 ++++--- .../service-detail/service-detail.service.ts | 3 +-- .../entity-breadcrumb.resolver.ts | 9 +++++--- 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts index b1b449d62..1b65d1daa 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts @@ -4,6 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { NavigationService } from '@hypertrace/common'; import { GraphQlRequestCacheability, GraphQlRequestService } from '@hypertrace/graphql-client'; import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/jest'; +import { EntityBreadcrumb } from './../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; import { patchRouterNavigateForTest, runFakeRxjs } from '@hypertrace/test-utils'; import { of } from 'rxjs'; @@ -12,10 +13,9 @@ import { entityIdKey, entityTypeKey, ObservabilityEntityType } from '../../../sh import { ENTITY_GQL_REQUEST } from '../../../shared/graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; import { ObservabilityIconType } from '../../../shared/icons/observability-icon-type'; import { ApiDetailBreadcrumbResolver } from './api-detail-breadcrumb.resolver'; -import { ApiEntity } from './api-detail.service'; describe('Api detail breadcrumb resolver', () => { - let spectator: SpectatorService>; + let spectator: SpectatorService>; let activatedRouteSnapshot: ActivatedRouteSnapshot; const buildResolver = createServiceFactory({ service: ApiDetailBreadcrumbResolver, diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts index 863dbd83f..b7092c8cf 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts @@ -1,18 +1,20 @@ import { Inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot } from '@angular/router'; -import { Breadcrumb, NavigationService, TimeRangeService } from '@hypertrace/common'; +import { NavigationService, TimeRangeService } from '@hypertrace/common'; import { BreadcrumbsService } from '@hypertrace/components'; import { GraphQlRequestService } from '@hypertrace/graphql-client'; import { Observable } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { EntityMetadata, EntityMetadataMap, ENTITY_METADATA } from '../../../shared/constants/entity-metadata'; import { Entity, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; -import { EntityBreadcrumbResolver } from '../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; +import { + EntityBreadcrumb, + EntityBreadcrumbResolver +} from '../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; import { EntityIconLookupService } from './../../../shared/services/entity/entity-icon-lookup.service'; -import { ApiEntity } from './api-detail.service'; @Injectable({ providedIn: 'root' }) -export class ApiDetailBreadcrumbResolver extends EntityBreadcrumbResolver { +export class ApiDetailBreadcrumbResolver extends EntityBreadcrumbResolver { protected readonly apiEntityMetadata: EntityMetadata | undefined; public constructor( @@ -27,7 +29,7 @@ export class ApiDetailBreadcrumbResolver extends EntityBrea this.apiEntityMetadata = this.entityMetadataMap.get(ObservabilityEntityType.Api); } - public async resolve(activatedRouteSnapshot: ActivatedRouteSnapshot): Promise> { + public async resolve(activatedRouteSnapshot: ActivatedRouteSnapshot): Promise> { const id = activatedRouteSnapshot.paramMap.get('id') as string; const parentEntityMetadata = this.resolveParentType(); @@ -45,24 +47,26 @@ export class ApiDetailBreadcrumbResolver extends EntityBrea ); } - protected createBreadcrumbForEntity(api: T & Breadcrumb, activatedRouteSnapshot: ActivatedRouteSnapshot): ApiEntity { + protected createBreadcrumbForEntity(api: Entity, activatedRouteSnapshot: ActivatedRouteSnapshot): EntityBreadcrumb { return { ...api, - label: api.name, + label: api.name as string, icon: this.apiEntityMetadata?.icon, url: this.breadcrumbService.getPath(activatedRouteSnapshot) }; } - protected getParentBreadcrumbs(api: T & Breadcrumb, parentEntityMetadata?: EntityMetadata): Breadcrumb[] { + protected getParentBreadcrumbs(api: EntityBreadcrumb, parentEntityMetadata?: EntityMetadata): EntityBreadcrumb[] { return parentEntityMetadata !== undefined ? [ { + ...api, label: api.parentName as string, icon: parentEntityMetadata?.icon, url: parentEntityMetadata?.detailPath(api.parentId as string) }, { + ...api, label: 'Endpoints', icon: this.apiEntityMetadata?.icon, url: parentEntityMetadata?.apisListPath?.(api.parentId as string) @@ -119,7 +123,7 @@ export class ApiDetailBreadcrumbResolver extends EntityBrea } } -export interface ApiBreadcrumbDetails extends ApiEntity { +export interface ApiBreadcrumbDetails extends EntityBreadcrumb { name: string; parentName: string; parentId: string; diff --git a/projects/observability/src/pages/apis/api-detail/api-detail.service.ts b/projects/observability/src/pages/apis/api-detail/api-detail.service.ts index 86664c72c..4c3326743 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail.service.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Breadcrumb } from '@hypertrace/common'; import { Entity, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; import { EntityDetailService } from '../../../shared/services/entity/entity-detail.service'; @@ -19,7 +18,7 @@ export class ApiDetailService extends EntityDetailService { } } -export interface ApiEntity extends Entity, Breadcrumb { +export interface ApiEntity extends Entity { apiType: ApiType; name: string; } diff --git a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts index 2b62d16c5..7a1667d8b 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.ts @@ -4,12 +4,14 @@ import { TimeRangeService } from '@hypertrace/common'; import { GraphQlRequestService } from '@hypertrace/graphql-client'; import { Observable } from 'rxjs'; import { ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; -import { EntityBreadcrumbResolver } from '../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; +import { + EntityBreadcrumb, + EntityBreadcrumbResolver +} from '../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; import { EntityIconLookupService } from './../../../shared/services/entity/entity-icon-lookup.service'; -import { ServiceEntity } from './service-detail.service'; @Injectable({ providedIn: 'root' }) -export class ServiceDetailBreadcrumbResolver extends EntityBreadcrumbResolver { +export class ServiceDetailBreadcrumbResolver extends EntityBreadcrumbResolver { public constructor( timeRangeService: TimeRangeService, graphQlQueryService: GraphQlRequestService, diff --git a/projects/observability/src/pages/apis/service-detail/service-detail.service.ts b/projects/observability/src/pages/apis/service-detail/service-detail.service.ts index 636326bd2..4a82b62c2 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail.service.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Breadcrumb } from '@hypertrace/common'; import { Entity, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; import { EntityDetailService } from '../../../shared/services/entity/entity-detail.service'; @@ -20,6 +19,6 @@ export class ServiceDetailService extends EntityDetailService { } } -export interface ServiceEntity extends Entity, Breadcrumb { +export interface ServiceEntity extends Entity { name: string; } diff --git a/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts index 533a6838c..1b8b8c7a2 100644 --- a/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts +++ b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts @@ -11,7 +11,8 @@ import { EntityIconLookupService } from '../entity/entity-icon-lookup.service'; import { Specification } from './../../graphql/model/schema/specifier/specification'; import { ENTITY_GQL_REQUEST } from './../../graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; -export abstract class EntityBreadcrumbResolver implements Resolve> { +export abstract class EntityBreadcrumbResolver + implements Resolve> { private readonly specificationBuilder: SpecificationBuilder = new SpecificationBuilder(); public constructor( @@ -20,7 +21,7 @@ export abstract class EntityBreadcrumbResolver implements Reso protected readonly iconLookupService: EntityIconLookupService ) {} - public abstract resolve(route: ActivatedRouteSnapshot): Promise>; + public abstract resolve(route: ActivatedRouteSnapshot): Promise>; protected abstract getAttributeKeys(): string[]; @@ -28,7 +29,7 @@ export abstract class EntityBreadcrumbResolver implements Reso return []; } - protected fetchEntity(id: string, entityType: string): Observable { + protected fetchEntity(id: string, entityType: string): Observable { return this.timeRangeService.getTimeRangeAndChanges().pipe( switchMap(timeRange => this.graphQlQueryService.query( @@ -57,3 +58,5 @@ export abstract class EntityBreadcrumbResolver implements Reso ); } } + +export interface EntityBreadcrumb extends Breadcrumb, Entity {} From f3e855dcd1aa2a2fba6c9728579cad57445cd8bf Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 10 Nov 2021 23:24:07 -0800 Subject: [PATCH 5/9] refactor: fixing test --- .../service-detail/service-detail-breadcrumb.resolver.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts index 7c0b14269..a19ff7c6c 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts @@ -1,3 +1,4 @@ +import { EntityBreadcrumb } from './../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; import { fakeAsync, flushMicrotasks } from '@angular/core/testing'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -11,10 +12,9 @@ import { ENTITY_GQL_REQUEST } from '../../../shared/graphql/request/handlers/ent import { ObservabilityIconType } from '../../../shared/icons/observability-icon-type'; import { EntityIconLookupService } from '../../../shared/services/entity/entity-icon-lookup.service'; import { ServiceDetailBreadcrumbResolver } from './service-detail-breadcrumb.resolver'; -import { ServiceEntity } from './service-detail.service'; describe('Service detail breadcrumb resolver', () => { - let spectator: SpectatorService>; + let spectator: SpectatorService>; let activatedRouteSnapshot: ActivatedRouteSnapshot; const buildResolver = createServiceFactory({ service: ServiceDetailBreadcrumbResolver, From e6895e8df734ad9beb140b83dccdc3ff11b386f5 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 10 Nov 2021 23:48:44 -0800 Subject: [PATCH 6/9] refactor: fixing lint --- .../service-detail/service-detail-breadcrumb.resolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts index a19ff7c6c..082140726 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts @@ -1,4 +1,3 @@ -import { EntityBreadcrumb } from './../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; import { fakeAsync, flushMicrotasks } from '@angular/core/testing'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -11,6 +10,7 @@ import { entityIdKey, entityTypeKey, ObservabilityEntityType } from '../../../sh import { ENTITY_GQL_REQUEST } from '../../../shared/graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; import { ObservabilityIconType } from '../../../shared/icons/observability-icon-type'; import { EntityIconLookupService } from '../../../shared/services/entity/entity-icon-lookup.service'; +import { EntityBreadcrumb } from './../../../shared/services/entity-breadcrumb/entity-breadcrumb.resolver'; import { ServiceDetailBreadcrumbResolver } from './service-detail-breadcrumb.resolver'; describe('Service detail breadcrumb resolver', () => { From ca25f5c09d4215b72f483f07ac02e15b8709e7f5 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Thu, 11 Nov 2021 11:36:20 -0800 Subject: [PATCH 7/9] refactor: addressing review comments --- .../entity-breadcrumb/entity-breadcrumb.resolver.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts index 1b8b8c7a2..ebb303993 100644 --- a/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts +++ b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts @@ -1,6 +1,6 @@ import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { Breadcrumb, TimeRangeService } from '@hypertrace/common'; -import { GraphQlRequestCacheability, GraphQlRequestService } from '@hypertrace/graphql-client'; +import { GraphQlRequestCacheability, GraphQlRequestOptions, GraphQlRequestService } from '@hypertrace/graphql-client'; import { Observable } from 'rxjs'; import { map, switchMap, take } from 'rxjs/operators'; import { Entity } from '../../graphql/model/schema/entity'; @@ -29,6 +29,10 @@ export abstract class EntityBreadcrumbResolver { return this.timeRangeService.getTimeRangeAndChanges().pipe( switchMap(timeRange => @@ -40,7 +44,7 @@ export abstract class EntityBreadcrumbResolver ({ From 9c61b3d065859f4c32ab331deca1982541e8035d Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:30:18 -0800 Subject: [PATCH 8/9] refactor: fixing test --- .../api-detail-breadcrumb.resolver.test.ts | 38 ++++++++++++++++--- ...service-detail-breadcrumb.resolver.test.ts | 7 +++- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts index 1b65d1daa..982cf9b7d 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts @@ -84,19 +84,40 @@ describe('Api detail breadcrumb resolver', () => { runFakeRxjs(({ expectObservable }) => { expectObservable(breadcrumb$).toBe('(abc|)', { a: { + [entityIdKey]: 'test-id', + [entityTypeKey]: ObservabilityEntityType.Api, label: 'test service', icon: ObservabilityIconType.Service, - url: ['services', 'service', 'test-service-id'] + url: ['services', 'service', 'test-service-id'], + name: 'test api', + parentId: 'test-service-id', + parentName: 'test service', + serviceName: 'test service', + serviceId: 'test-service-id' }, b: { + [entityIdKey]: 'test-id', + [entityTypeKey]: ObservabilityEntityType.Api, label: 'Endpoints', icon: ObservabilityIconType.Api, - url: ['services', 'service', 'test-service-id', 'endpoints'] + url: ['services', 'service', 'test-service-id', 'endpoints'], + name: 'test api', + parentId: 'test-service-id', + parentName: 'test service', + serviceName: 'test service', + serviceId: 'test-service-id' }, c: { + [entityIdKey]: 'test-id', + [entityTypeKey]: ObservabilityEntityType.Api, label: 'test api', icon: ObservabilityIconType.Api, - url: ['api', 'test-id'] + url: ['api', 'test-id'], + name: 'test api', + parentId: 'test-service-id', + parentName: 'test service', + serviceName: 'test service', + serviceId: 'test-service-id' } }); }); @@ -109,7 +130,7 @@ describe('Api detail breadcrumb resolver', () => { entityType: ObservabilityEntityType.Api, id: 'test-id' }), - { cacheability: GraphQlRequestCacheability.NotCacheable } + { cacheability: GraphQlRequestCacheability.Cacheable } ); })); @@ -123,9 +144,14 @@ describe('Api detail breadcrumb resolver', () => { runFakeRxjs(({ expectObservable }) => { expectObservable(breadcrumb$).toBe('(y|)', { y: { + [entityIdKey]: 'test-id', + [entityTypeKey]: ObservabilityEntityType.Api, label: 'test api', icon: ObservabilityIconType.Api, - url: ['api', 'test-id'] + url: ['api', 'test-id'], + name: 'test api', + serviceName: 'test service', + serviceId: 'test-service-id' } }); }); @@ -138,7 +164,7 @@ describe('Api detail breadcrumb resolver', () => { entityType: ObservabilityEntityType.Api, id: 'test-id' }), - { cacheability: GraphQlRequestCacheability.NotCacheable } + { cacheability: GraphQlRequestCacheability.Cacheable } ); })); }); diff --git a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts index 082140726..0dba0f384 100644 --- a/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/service-detail/service-detail-breadcrumb.resolver.test.ts @@ -56,8 +56,11 @@ describe('Service detail breadcrumb resolver', () => { runFakeRxjs(({ expectObservable }) => { expectObservable(breadcrumb$).toBe('(x|)', { x: { + [entityTypeKey]: ObservabilityEntityType.Service, + [entityIdKey]: 'test-id', label: 'test service', - icon: ObservabilityIconType.Service + icon: ObservabilityIconType.Service, + name: 'test service' } }); }); @@ -70,7 +73,7 @@ describe('Service detail breadcrumb resolver', () => { entityType: ObservabilityEntityType.Service, id: 'test-id' }), - { cacheability: GraphQlRequestCacheability.NotCacheable } + { cacheability: GraphQlRequestCacheability.Cacheable } ); })); }); From 8a0bdef5691af358de52617ded8267584b73bd05 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Thu, 11 Nov 2021 15:49:44 -0800 Subject: [PATCH 9/9] refactor: update breadcrumb and fix tests --- .../api-detail-breadcrumb.resolver.test.ts | 20 ++++--------------- .../api-detail-breadcrumb.resolver.ts | 16 +++++++++------ .../entity-breadcrumb.resolver.ts | 4 ++-- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts index 982cf9b7d..c401bfa55 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.test.ts @@ -84,28 +84,16 @@ describe('Api detail breadcrumb resolver', () => { runFakeRxjs(({ expectObservable }) => { expectObservable(breadcrumb$).toBe('(abc|)', { a: { - [entityIdKey]: 'test-id', - [entityTypeKey]: ObservabilityEntityType.Api, + [entityIdKey]: 'test-service-id', + [entityTypeKey]: ObservabilityEntityType.Service, label: 'test service', icon: ObservabilityIconType.Service, - url: ['services', 'service', 'test-service-id'], - name: 'test api', - parentId: 'test-service-id', - parentName: 'test service', - serviceName: 'test service', - serviceId: 'test-service-id' + url: ['services', 'service', 'test-service-id'] }, b: { - [entityIdKey]: 'test-id', - [entityTypeKey]: ObservabilityEntityType.Api, label: 'Endpoints', icon: ObservabilityIconType.Api, - url: ['services', 'service', 'test-service-id', 'endpoints'], - name: 'test api', - parentId: 'test-service-id', - parentName: 'test service', - serviceName: 'test service', - serviceId: 'test-service-id' + url: ['services', 'service', 'test-service-id', 'endpoints'] }, c: { [entityIdKey]: 'test-id', diff --git a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts index b7092c8cf..190ffae8e 100644 --- a/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts +++ b/projects/observability/src/pages/apis/api-detail/api-detail-breadcrumb.resolver.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot } from '@angular/router'; -import { NavigationService, TimeRangeService } from '@hypertrace/common'; +import { Breadcrumb, NavigationService, TimeRangeService } from '@hypertrace/common'; import { BreadcrumbsService } from '@hypertrace/components'; import { GraphQlRequestService } from '@hypertrace/graphql-client'; +import { entityIdKey } from '@hypertrace/observability'; import { Observable } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { EntityMetadata, EntityMetadataMap, ENTITY_METADATA } from '../../../shared/constants/entity-metadata'; -import { Entity, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; +import { Entity, entityTypeKey, ObservabilityEntityType } from '../../../shared/graphql/model/schema/entity'; import { EntityBreadcrumb, EntityBreadcrumbResolver @@ -29,7 +30,7 @@ export class ApiDetailBreadcrumbResolver extends Ent this.apiEntityMetadata = this.entityMetadataMap.get(ObservabilityEntityType.Api); } - public async resolve(activatedRouteSnapshot: ActivatedRouteSnapshot): Promise> { + public async resolve(activatedRouteSnapshot: ActivatedRouteSnapshot): Promise> { const id = activatedRouteSnapshot.paramMap.get('id') as string; const parentEntityMetadata = this.resolveParentType(); @@ -56,17 +57,20 @@ export class ApiDetailBreadcrumbResolver extends Ent }; } - protected getParentBreadcrumbs(api: EntityBreadcrumb, parentEntityMetadata?: EntityMetadata): EntityBreadcrumb[] { + protected getParentBreadcrumbs( + api: EntityBreadcrumb, + parentEntityMetadata?: EntityMetadata + ): (EntityBreadcrumb | Breadcrumb)[] { return parentEntityMetadata !== undefined ? [ { - ...api, + [entityIdKey]: api.parentId as string, + [entityTypeKey]: parentEntityMetadata.entityType, label: api.parentName as string, icon: parentEntityMetadata?.icon, url: parentEntityMetadata?.detailPath(api.parentId as string) }, { - ...api, label: 'Endpoints', icon: this.apiEntityMetadata?.icon, url: parentEntityMetadata?.apisListPath?.(api.parentId as string) diff --git a/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts index ebb303993..36b64fa28 100644 --- a/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts +++ b/projects/observability/src/shared/services/entity-breadcrumb/entity-breadcrumb.resolver.ts @@ -12,7 +12,7 @@ import { Specification } from './../../graphql/model/schema/specifier/specificat import { ENTITY_GQL_REQUEST } from './../../graphql/request/handlers/entities/query/entity/entity-graphql-query-handler.service'; export abstract class EntityBreadcrumbResolver - implements Resolve> { + implements Resolve> { private readonly specificationBuilder: SpecificationBuilder = new SpecificationBuilder(); public constructor( @@ -21,7 +21,7 @@ export abstract class EntityBreadcrumbResolver>; + public abstract resolve(route: ActivatedRouteSnapshot): Promise>; protected abstract getAttributeKeys(): string[];