diff --git a/projects/observability/src/pages/explorer/explorer.component.test.ts b/projects/observability/src/pages/explorer/explorer.component.test.ts index a77f92535..617e82718 100644 --- a/projects/observability/src/pages/explorer/explorer.component.test.ts +++ b/projects/observability/src/pages/explorer/explorer.component.test.ts @@ -360,7 +360,7 @@ describe('Explorer component', () => { spectator.query(ExploreQueryEditorComponent)!.setInterval(new TimeDuration(30, TimeUnit.Second)); spectator.query(ExploreQueryEditorComponent)!.updateGroupByKey( { - keys: ['apiName'], + keyExpressions: [{ key: 'apiName' }], limit: 6, includeRest: true }, @@ -370,7 +370,7 @@ describe('Explorer component', () => { expect(queryParamChangeSpy).toHaveBeenLastCalledWith({ scope: 'spans', series: ['column:avg(second)'], - group: 'apiName', + group: ['apiName'], limit: 6, other: true, interval: '30s' diff --git a/projects/observability/src/pages/explorer/explorer.component.ts b/projects/observability/src/pages/explorer/explorer.component.ts index 185d1a348..9a379decc 100644 --- a/projects/observability/src/pages/explorer/explorer.component.ts +++ b/projects/observability/src/pages/explorer/explorer.component.ts @@ -9,7 +9,7 @@ import { TimeDurationService } from '@hypertrace/common'; import { Filter, ToggleItem } from '@hypertrace/components'; -import { isNil } from 'lodash-es'; +import { isEmpty, isNil } from 'lodash-es'; import { concat, EMPTY, Observable, Subject } from 'rxjs'; import { map, take } from 'rxjs/operators'; import { CartesianSeriesVisualizationType } from '../../shared/components/cartesian/chart'; @@ -19,6 +19,7 @@ import { ExploreVisualizationRequest } from '../../shared/components/explore-query-editor/explore-visualization-builder'; import { IntervalValue } from '../../shared/components/interval-select/interval-select.component'; +import { AttributeExpression } from '../../shared/graphql/model/attribute/attribute-expression'; import { AttributeMetadata } from '../../shared/graphql/model/metadata/attribute-metadata'; import { MetricAggregationType } from '../../shared/graphql/model/metrics/metric-aggregation'; import { GraphQlGroupBy } from '../../shared/graphql/model/schema/groupby/graphql-group-by'; @@ -216,8 +217,8 @@ export class ExplorerComponent { } private getGroupByQueryParams(groupBy?: GraphQlGroupBy): QueryParamObject { - const key = groupBy?.keys[0]; - if (key === undefined) { + const keyExpressions = groupBy?.keyExpressions ?? []; + if (keyExpressions.length === 0) { return { // Clear existing selection [ExplorerQueryParam.Group]: undefined, @@ -227,7 +228,7 @@ export class ExplorerComponent { } return { - [ExplorerQueryParam.Group]: key, + [ExplorerQueryParam.Group]: keyExpressions.map(expression => this.encodeAttributeExpression(expression)), [ExplorerQueryParam.OtherGroup]: groupBy?.includeRest || undefined, // No param needed for false [ExplorerQueryParam.GroupLimit]: groupBy?.limit }; @@ -238,7 +239,9 @@ export class ExplorerComponent { contextToggle: this.getOrDefaultContextItemFromQueryParam(param.get(ExplorerQueryParam.Scope) as ScopeQueryParam), groupBy: param.has(ExplorerQueryParam.Group) ? { - keys: param.getAll(ExplorerQueryParam.Group), + keyExpressions: param + .getAll(ExplorerQueryParam.Group) + .flatMap(expressionString => this.tryDecodeAttributeExpression(expressionString)), includeRest: param.get(ExplorerQueryParam.OtherGroup) === 'true', // tslint:disable-next-line: strict-boolean-expressions limit: parseInt(param.get(ExplorerQueryParam.GroupLimit)!) || 5 @@ -294,6 +297,19 @@ export class ExplorerComponent { } ]; } + + private encodeAttributeExpression(attributeExpression: AttributeExpression): string { + if (isEmpty(attributeExpression.subpath)) { + return attributeExpression.key; + } + + return `${attributeExpression.key}__${attributeExpression.subpath}`; + } + private tryDecodeAttributeExpression(expressionString: string): [AttributeExpression] | [] { + const [key, subpath] = expressionString.split('__'); + + return [{ key: key, ...(isEmpty(subpath) ? { subpath: subpath } : {}) }]; + } } interface ContextToggleItem extends ToggleItem { value: ExplorerContextScope; diff --git a/projects/observability/src/public-api.ts b/projects/observability/src/public-api.ts index 030e8fbe9..634484dbb 100644 --- a/projects/observability/src/public-api.ts +++ b/projects/observability/src/public-api.ts @@ -64,6 +64,7 @@ export * from './shared/services/metadata/metadata.service'; export * from './shared/services/metadata/metadata.service.module'; export * from './shared/graphql/model/metadata/attribute-metadata'; +export * from './shared/graphql/model/attribute/attribute-expression'; export * from './shared/graphql/model/metrics/metric-aggregation'; export * from './shared/graphql/model/metrics/metric-health'; diff --git a/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.test.ts b/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.test.ts index 984ed8188..3b63a3e12 100644 --- a/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.test.ts +++ b/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.test.ts @@ -176,7 +176,7 @@ describe('Explore query editor', () => { expect.objectContaining({ series: [defaultSeries], groupBy: { - keys: ['first groupable'], + keyExpressions: [{ key: 'first groupable' }], limit: 5 // Default group by limit } }) @@ -215,7 +215,7 @@ describe('Explore query editor', () => { expect.objectContaining({ series: [defaultSeries], groupBy: { - keys: ['first groupable'], + keyExpressions: [{ key: 'first groupable' }], limit: 6 } }) diff --git a/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.ts b/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.ts index cca51a6fd..0a2a1b99d 100644 --- a/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.ts +++ b/projects/observability/src/shared/components/explore-query-editor/explore-query-editor.component.ts @@ -39,7 +39,7 @@ import { @@ -103,8 +103,8 @@ export class ExploreQueryEditorComponent implements OnChanges, OnInit { this.setInterval(this.interval); } - if (changeObject.groupBy && this.groupBy?.keys.length) { - this.updateGroupByKey(this.groupBy, this.groupBy.keys[0]); + if (changeObject.groupBy && this.groupBy?.keyExpressions.length) { + this.updateGroupByKey(this.groupBy, this.groupBy.keyExpressions[0]?.key); } } @@ -117,7 +117,9 @@ export class ExploreQueryEditorComponent implements OnChanges, OnInit { this.visualizationBuilder.groupBy(); } else { this.visualizationBuilder.groupBy( - groupBy ? { ...groupBy, keys: [key] } : { keys: [key], limit: ExploreQueryEditorComponent.DEFAULT_GROUP_LIMIT } + groupBy + ? { ...groupBy, keyExpressions: [{ key: key }] } + : { keyExpressions: [{ key: key }], limit: ExploreQueryEditorComponent.DEFAULT_GROUP_LIMIT } ); } } diff --git a/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.test.ts b/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.test.ts index f71afa668..d46c5bd84 100644 --- a/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.test.ts +++ b/projects/observability/src/shared/components/explore-query-editor/explore-visualization-builder.test.ts @@ -84,7 +84,7 @@ describe('Explore visualization builder', () => { spectator.service .setSeries([buildSeries('test1')]) .groupBy({ - keys: ['testGroupBy'], + keyExpressions: [{ key: 'testGroupBy' }], limit: 15 }) .setSeries([buildSeries('test2')]); @@ -92,7 +92,7 @@ describe('Explore visualization builder', () => { expectObservable(recordedRequests).toBe('10ms x', { x: expectedQuery({ series: [matchSeriesWithName('test2')], - groupBy: { keys: ['testGroupBy'], limit: 15 } + groupBy: { keyExpressions: [{ key: 'testGroupBy' }], limit: 15 } }) }); }); diff --git a/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.test.ts b/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.test.ts index beb17daaa..a80fd0f27 100644 --- a/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.test.ts +++ b/projects/observability/src/shared/dashboard/dashboard-wrapper/navigable-dashboard.component.test.ts @@ -144,7 +144,7 @@ describe('Navigable dashboard component', () => { }; spectator.query(FilterBarComponent)?.filtersChange.next([explicitFilter]); expect(mockDataSource.addFilters).toHaveBeenCalledWith( - expect.objectContaining({ key: 'foo', operator: GraphQlOperatorType.Equals, value: 'bar' }) + expect.objectContaining({ keyOrExpression: 'foo', operator: GraphQlOperatorType.Equals, value: 'bar' }) ); }); diff --git a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.test.ts b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.test.ts index b901b1ba7..55d4195be 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.test.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.test.ts @@ -155,7 +155,7 @@ describe('Explore cartesian data source model', () => { ]; model.groupBy = { - keys: ['baz'], + keyExpressions: [{ key: 'baz' }], includeRest: true, limit: 5 }; @@ -220,7 +220,7 @@ describe('Explore cartesian data source model', () => { ]; model.groupBy = { - keys: ['baz'], + keyExpressions: [{ key: 'baz' }], limit: 5 }; diff --git a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.ts b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.ts index 27fa2e81e..092bcd828 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-cartesian-data-source.model.ts @@ -5,6 +5,7 @@ import { NEVER, Observable, of } from 'rxjs'; import { map, mergeMap } from 'rxjs/operators'; import { Series } from '../../../../components/cartesian/chart'; import { ExploreRequestState } from '../../../../components/explore-query-editor/explore-visualization-builder'; +import { AttributeExpression } from '../../../../graphql/model/attribute/attribute-expression'; import { MetricTimeseriesInterval } from '../../../../graphql/model/metric/metric-timeseries'; import { GraphQlFilter } from '../../../../graphql/model/schema/filter/graphql-filter'; import { ExploreSpecification } from '../../../../graphql/model/schema/specifications/explore-specification'; @@ -107,19 +108,19 @@ export abstract class ExploreCartesianDataSourceModel extends GraphQlDataSourceM (selection): selection is RequireBy => selection.aggregation !== undefined ); - const groupByKeys = request.groupBy?.keys ?? []; - const isGroupBy = groupByKeys.length > 0; + const groupByExpressions = request.groupBy?.keyExpressions ?? []; + const isGroupBy = groupByExpressions.length > 0; if (!isGroupBy && request.interval) { return aggregatableSpecs.map(spec => this.buildTimeseriesData(spec, result)); } if (isGroupBy && !request.interval) { - return aggregatableSpecs.map(spec => this.buildGroupedSeriesData(spec, groupByKeys, result)); + return aggregatableSpecs.map(spec => this.buildGroupedSeriesData(spec, groupByExpressions, result)); } if (isGroupBy && request.interval) { - return aggregatableSpecs.map(spec => this.buildGroupedTimeseriesData(spec, groupByKeys, result)).flat(); + return aggregatableSpecs.map(spec => this.buildGroupedTimeseriesData(spec, groupByExpressions, result)).flat(); } return []; @@ -149,10 +150,14 @@ export abstract class ExploreCartesianDataSourceModel extends GraphQlDataSourceM }; } - public buildGroupedSeriesData(spec: AggregatableSpec, groupByKeys: string[], result: ExploreResult): SeriesData { + public buildGroupedSeriesData( + spec: AggregatableSpec, + groupByExpressions: AttributeExpression[], + result: ExploreResult + ): SeriesData { return { data: result - .getGroupedSeriesData(groupByKeys, spec.name, spec.aggregation) + .getGroupedSeriesData(groupByExpressions, spec.name, spec.aggregation) .map(({ keys, value }) => [this.buildGroupedSeriesName(keys), value]), spec: spec }; @@ -160,10 +165,10 @@ export abstract class ExploreCartesianDataSourceModel extends GraphQlDataSourceM public buildGroupedTimeseriesData( spec: AggregatableSpec, - groupByKeys: string[], + groupByExpressions: AttributeExpression[], result: ExploreResult ): SeriesData[] { - return Array.from(result.getGroupedTimeSeriesData(groupByKeys, spec.name, spec.aggregation).entries()).map( + return Array.from(result.getGroupedTimeSeriesData(groupByExpressions, spec.name, spec.aggregation).entries()).map( ([groupNames, data]) => ({ data: data, groupName: this.buildGroupedSeriesName(groupNames), diff --git a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.test.ts b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.test.ts index 87afdff75..c562600e9 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.test.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.test.ts @@ -83,7 +83,7 @@ describe('Explore result', () => { ] }); - expect(result.getGroupedSeriesData(['group'], 'foo', MetricAggregationType.Sum)).toEqual([ + expect(result.getGroupedSeriesData([{ key: 'group' }], 'foo', MetricAggregationType.Sum)).toEqual([ { keys: ['first'], value: 10 }, { keys: ['second'], value: 15 }, { keys: ['third'], value: 20 } @@ -116,7 +116,7 @@ describe('Explore result', () => { ] }); - expect(result.getGroupedSeriesData(['group'], 'foo', MetricAggregationType.Sum)).toEqual([ + expect(result.getGroupedSeriesData([{ key: 'group' }], 'foo', MetricAggregationType.Sum)).toEqual([ { keys: ['first'], value: 10 }, { keys: ['Others'], value: 15 } ]); @@ -172,7 +172,7 @@ describe('Explore result', () => { ] }); - expect(result.getGroupedTimeSeriesData(['group'], 'foo', MetricAggregationType.Sum)).toEqual( + expect(result.getGroupedTimeSeriesData([{ key: 'group' }], 'foo', MetricAggregationType.Sum)).toEqual( new Map([ [ ['first'], diff --git a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.ts b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.ts index 005a22a47..732df3187 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/explore/explore-result.ts @@ -1,5 +1,6 @@ import { TimeDuration } from '@hypertrace/common'; import { groupBy } from 'lodash-es'; +import { AttributeExpression } from '../../../../graphql/model/attribute/attribute-expression'; import { MetricTimeseriesInterval } from '../../../../graphql/model/metric/metric-timeseries'; import { MetricAggregationType } from '../../../../graphql/model/metrics/metric-aggregation'; import { ExploreSpecification } from '../../../../graphql/model/schema/specifications/explore-specification'; @@ -27,19 +28,25 @@ export class ExploreResult { return this.extractTimeseriesForSpec(this.specBuilder.exploreSpecificationForKey(metricKey, aggregation)); } - public getGroupedSeriesData(groupKeys: string[], metricKey: string, aggregation: MetricAggregationType): GroupData[] { + public getGroupedSeriesData( + groupExpressions: AttributeExpression[], + metricKey: string, + aggregation: MetricAggregationType + ): GroupData[] { return this.extractGroupSeriesForSpec( - groupKeys.map(key => this.specBuilder.exploreSpecificationForKey(key)), + groupExpressions.map(expression => this.specBuilder.exploreSpecificationForAttributeExpression(expression)), this.specBuilder.exploreSpecificationForKey(metricKey, aggregation) ); } public getGroupedTimeSeriesData( - groupKeys: string[], + groupExpressions: AttributeExpression[], metricKey: string, aggregation: MetricAggregationType ): Map { - const groupSpecs = groupKeys.map(key => this.specBuilder.exploreSpecificationForKey(key)); + const groupSpecs = groupExpressions.map(expression => + this.specBuilder.exploreSpecificationForAttributeExpression(expression) + ); const spec = this.specBuilder.exploreSpecificationForKey(metricKey, aggregation); const groupedResults = groupBy(this.response.results, result => this.getGroupNamesFromResult(result, groupSpecs).join(',') diff --git a/projects/observability/src/shared/dashboard/data/graphql/explorer-visualization/explorer-visualization-cartesian-data-source.model.test.ts b/projects/observability/src/shared/dashboard/data/graphql/explorer-visualization/explorer-visualization-cartesian-data-source.model.test.ts index 836deda98..621fad703 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/explorer-visualization/explorer-visualization-cartesian-data-source.model.test.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/explorer-visualization/explorer-visualization-cartesian-data-source.model.test.ts @@ -167,7 +167,7 @@ describe('Explorer Visualization cartesian data source model', () => { model.request = buildVisualizationRequest({ interval: undefined, groupBy: { - keys: ['baz'], + keyExpressions: [{ key: 'baz' }], limit: 10 }, series: [ @@ -233,7 +233,7 @@ describe('Explorer Visualization cartesian data source model', () => { model.request = buildVisualizationRequest({ interval: new TimeDuration(5, TimeUnit.Minute), groupBy: { - keys: ['baz'], + keyExpressions: [{ key: 'baz' }], limit: 5 }, series: [ diff --git a/projects/observability/src/shared/dashboard/data/graphql/graphql-data-source.module.ts b/projects/observability/src/shared/dashboard/data/graphql/graphql-data-source.module.ts index b5150e6df..2eba63755 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/graphql-data-source.module.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/graphql-data-source.module.ts @@ -12,7 +12,6 @@ import { SpanDataSourceModel } from './span/span-data-source.model'; import { AttributeSpecificationModel } from './specifiers/attribute-specification.model'; import { CompositeSpecificationModel } from './specifiers/composite-specification.model'; import { EnrichedAttributeSpecificationModel } from './specifiers/enriched-attribute-specification.model'; -import { FieldSpecificationModel } from './specifiers/field-specification.model'; import { MappedAttributeSpecificationModel } from './specifiers/mapped-attribute-specification.model'; import { TraceStatusSpecificationModel } from './specifiers/trace-status-specification.model'; import { SpansTableDataSourceModel } from './table/spans/spans-table-data-source.model'; @@ -40,7 +39,6 @@ import { TraceWaterfallDataSourceModel } from './waterfall/trace-waterfall-data- TracesDataSourceModel, CompositeSpecificationModel, AttributeSpecificationModel, - FieldSpecificationModel, TraceStatusSpecificationModel, EnrichedAttributeSpecificationModel, MappedAttributeSpecificationModel diff --git a/projects/observability/src/shared/dashboard/data/graphql/metric-aggregation/metric-aggregation-data-source.model.test.ts b/projects/observability/src/shared/dashboard/data/graphql/metric-aggregation/metric-aggregation-data-source.model.test.ts index 9ea16c3ef..46c38fb26 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/metric-aggregation/metric-aggregation-data-source.model.test.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/metric-aggregation/metric-aggregation-data-source.model.test.ts @@ -76,7 +76,7 @@ describe('Metric aggregation data source model', () => { }), filters: [ expect.objectContaining({ - key: 'duration', + keyOrExpression: 'duration', operator: GraphQlOperatorType.GreaterThan, value: 500 }) diff --git a/projects/observability/src/shared/dashboard/data/graphql/specifiers/field-specification.model.ts b/projects/observability/src/shared/dashboard/data/graphql/specifiers/field-specification.model.ts deleted file mode 100644 index bdf175e99..000000000 --- a/projects/observability/src/shared/dashboard/data/graphql/specifiers/field-specification.model.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Dictionary } from '@hypertrace/common'; -import { Model, ModelProperty, STRING_PROPERTY } from '@hypertrace/hyperdash'; -import { Specification } from '../../../../graphql/model/schema/specifier/specification'; -import { SpecificationBuilder } from '../../../../graphql/request/builders/specification/specification-builder'; -import { SpecificationModel } from './specification.model'; - -@Model({ - type: 'field-specification', - displayName: 'Field' -}) -export class FieldSpecificationModel extends SpecificationModel { - @ModelProperty({ - key: 'field', - displayName: 'Field', - type: STRING_PROPERTY.type, - required: true - }) - public field!: string; - - protected buildInnerSpec(): Specification { - return new SpecificationBuilder().fieldSpecificationForKey(this.field); - } - - public extractFromServerData(resultContainer: Dictionary): unknown { - return this.innerSpec.extractFromServerData(resultContainer); - } -} diff --git a/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.test.ts b/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.test.ts index 689c07f08..b75ae910a 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.test.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.test.ts @@ -187,7 +187,7 @@ describe('Explore table data source model', () => { limit: 100, offset: 0, groupBy: expect.objectContaining({ - keys: ['name'], + keyExpressions: [{ key: 'name' }], includeRest: false }), orderBy: [ diff --git a/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.ts b/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.ts index 808fd0fe1..240548e67 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/table/explore/explore-table-data-source.model.ts @@ -87,7 +87,7 @@ export class ExploreTableDataSourceModel extends TableDataSourceModel { filters: [...filters, ...this.toGraphQlFilters(request.filters)], timeRange: this.getTimeRangeOrThrow(), groupBy: { - keys: this.groupBy, + keyExpressions: this.groupBy.map(key => ({ key: key })), includeRest: this.groupByIncludeRest, limit: this.groupLimit } diff --git a/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.test.ts b/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.test.ts index c74c1b8e4..cc6c05381 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.test.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.test.ts @@ -63,7 +63,7 @@ describe('Donut data source model', () => { timeRange: GraphQlTimeRange.fromTimeRange(mockTimeRange), filters: [], groupBy: { - keys: ['bar'], + keyExpressions: [{ key: 'bar' }], limit: 3 }, limit: 3, diff --git a/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.ts b/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.ts index c272b9c68..0bcfc1005 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/trace/donut/trace-donut-data-source.model.ts @@ -65,7 +65,7 @@ export class TraceDonutDataSourceModel extends GraphQlDataSourceModel this.buildDonutResults(exploreResponse, this.metric))); @@ -78,7 +78,7 @@ export class TraceDonutDataSourceModel extends GraphQlDataSourceModel { total = total + seriesTuple.value; diff --git a/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.test.ts b/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.test.ts index f242c5ca6..d95da0557 100644 --- a/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.test.ts +++ b/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.test.ts @@ -61,7 +61,7 @@ describe('Top N Data Source Model', () => { ]), filters: [], groupBy: expect.objectContaining({ - keys: ['nameKey', 'idKey'] + keyExpressions: [{ key: 'nameKey' }, { key: 'idKey' }] }) }) ); diff --git a/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.ts b/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.ts index 27d408307..45bfbccd2 100644 --- a/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.ts +++ b/projects/observability/src/shared/dashboard/widgets/top-n/data/top-n-data-source.model.ts @@ -65,7 +65,7 @@ export class TopNDataSourceModel extends GraphQlDataSourceModel; type: GraphQlEnumArgument; diff --git a/projects/observability/src/shared/graphql/model/schema/filter/graphql-filter.ts b/projects/observability/src/shared/graphql/model/schema/filter/graphql-filter.ts index 7688a5939..c19cff0c7 100644 --- a/projects/observability/src/shared/graphql/model/schema/filter/graphql-filter.ts +++ b/projects/observability/src/shared/graphql/model/schema/filter/graphql-filter.ts @@ -2,7 +2,6 @@ import { GraphQlArgumentObject } from '@hypertrace/graphql-client'; export interface GraphQlFilter { asArgumentObjects(): GraphQlArgumentObject[]; - key: string; } export interface GraphQlFilterable { diff --git a/projects/observability/src/shared/graphql/model/schema/groupby/graphql-group-by.ts b/projects/observability/src/shared/graphql/model/schema/groupby/graphql-group-by.ts index a209a7368..342bc60ed 100644 --- a/projects/observability/src/shared/graphql/model/schema/groupby/graphql-group-by.ts +++ b/projects/observability/src/shared/graphql/model/schema/groupby/graphql-group-by.ts @@ -1,5 +1,7 @@ +import { AttributeExpression } from '../../attribute/attribute-expression'; + export interface GraphQlGroupBy { - keys: string[]; + keyExpressions: AttributeExpression[]; includeRest?: boolean; limit: number; } diff --git a/projects/observability/src/shared/graphql/model/schema/sort/graphql-sort-argument.ts b/projects/observability/src/shared/graphql/model/schema/sort/graphql-sort-argument.ts index a944605c8..ad8c8bef6 100644 --- a/projects/observability/src/shared/graphql/model/schema/sort/graphql-sort-argument.ts +++ b/projects/observability/src/shared/graphql/model/schema/sort/graphql-sort-argument.ts @@ -1,7 +1,8 @@ import { GraphQlEnumArgument } from '@hypertrace/graphql-client'; +import { AttributeExpression } from '../../attribute/attribute-expression'; import { GraphQlSortDirection } from './graphql-sort-direction'; export interface GraphQlSortArgument { direction: GraphQlEnumArgument; - key: string; + expression: AttributeExpression; } diff --git a/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.test.ts b/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.test.ts index c3773912d..a48b0faed 100644 --- a/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.test.ts +++ b/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.test.ts @@ -15,7 +15,9 @@ describe('Graphql Argument Builder', () => { }); test('construct arguments for attribute key correctly', () => { - expect(argBuilder.forAttributeKey('test-key')).toEqual(expect.objectContaining({ name: 'key', value: 'test-key' })); + expect(argBuilder.forAttributeKey('test-key')).toEqual( + expect.objectContaining({ name: 'expression', value: { key: 'test-key' } }) + ); }); test('construct arguments for limit correctly', () => { @@ -26,7 +28,11 @@ describe('Graphql Argument Builder', () => { expect(argBuilder.forOrderBy(undefined)).toEqual(expect.arrayContaining([])); expect( argBuilder.forOrderBy({ direction: 'ASC', key: specBuilder.attributeSpecificationForKey('test-key') }) - ).toEqual(expect.arrayContaining([{ name: 'orderBy', value: [{ direction: { value: 'ASC' }, key: 'test-key' }] }])); + ).toEqual( + expect.arrayContaining([ + { name: 'orderBy', value: [{ direction: { value: 'ASC' }, keyExpression: { key: 'test-key' } }] } + ]) + ); }); test('construct arguments for orderBys correctly', () => { @@ -41,8 +47,8 @@ describe('Graphql Argument Builder', () => { { name: 'orderBy', value: [ - { direction: { value: 'ASC' }, key: 'test-key' }, - { direction: { value: 'ASC' }, key: 'test-key2' } + { direction: { value: 'ASC' }, keyExpression: { key: 'test-key' } }, + { direction: { value: 'ASC' }, keyExpression: { key: 'test-key2' } } ] } ]) @@ -82,7 +88,12 @@ describe('Graphql Argument Builder', () => { { name: 'filterBy', value: [ - { key: 'filter-key', operator: { value: 'EQUALS' }, type: { value: 'ATTRIBUTE' }, value: 'filter-value' } + { + keyExpression: { key: 'filter-key' }, + operator: { value: 'EQUALS' }, + type: { value: 'ATTRIBUTE' }, + value: 'filter-value' + } ] } ]) diff --git a/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.ts b/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.ts index a55a06143..abc21b9d4 100644 --- a/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/argument/graphql-argument-builder.ts @@ -1,4 +1,6 @@ -import { GraphQlArgument, GraphQlEnumArgument } from '@hypertrace/graphql-client'; +import { GraphQlArgument, GraphQlArgumentObject, GraphQlEnumArgument } from '@hypertrace/graphql-client'; +import { isEmpty, omit } from 'lodash-es'; +import { AttributeExpression } from '../../../model/attribute/attribute-expression'; import { GraphQlFilter } from '../../../model/schema/filter/graphql-filter'; import { GraphQlSortBySpecification } from '../../../model/schema/sort/graphql-sort-by-specification'; import { GraphQlTimeRange } from '../../../model/schema/timerange/graphql-time-range'; @@ -10,9 +12,13 @@ export class GraphQlArgumentBuilder { } public forAttributeKey(key: string): GraphQlArgument { + return this.forAttributeExpression({ key: key }); + } + + public forAttributeExpression(attributeExpression: AttributeExpression): GraphQlArgument { return { - name: 'key', - value: key + name: 'expression', + value: this.buildAttributeExpression(attributeExpression) }; } @@ -32,10 +38,7 @@ export class GraphQlArgumentBuilder { return [ { name: 'orderBy', - value: orderBys.map(orderBy => ({ - ...orderBy.key.asGraphQlOrderByFragment(), - direction: new GraphQlEnumArgument(orderBy.direction) - })) + value: orderBys.map(orderBy => this.buildOrderByArgumentValue(orderBy)) } ]; } @@ -77,4 +80,24 @@ export class GraphQlArgumentBuilder { public forScope(scope: string): GraphQlArgument { return { name: 'scope', value: scope }; } + + protected buildAttributeExpression( + attributeExpression: AttributeExpression + ): AttributeExpression & GraphQlArgumentObject { + return { + key: attributeExpression.key, + ...(!isEmpty(attributeExpression.subpath) ? { subpath: attributeExpression.subpath } : {}) + }; + } + + protected buildOrderByArgumentValue(orderBy: GraphQlSortBySpecification): GraphQlArgumentObject { + const orderByFragment = orderBy.key.asGraphQlOrderByFragment(); + const unknownFields = omit(orderByFragment, 'direction', 'expression'); + + return { + ...unknownFields, + direction: new GraphQlEnumArgument(orderBy.direction), + keyExpression: this.buildAttributeExpression(orderByFragment.expression) + }; + } } diff --git a/projects/observability/src/shared/graphql/request/builders/argument/graphql-observability-argument-builder.ts b/projects/observability/src/shared/graphql/request/builders/argument/graphql-observability-argument-builder.ts index 4aebf06d1..1f0801c3b 100644 --- a/projects/observability/src/shared/graphql/request/builders/argument/graphql-observability-argument-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/argument/graphql-observability-argument-builder.ts @@ -1,11 +1,11 @@ import { TimeDuration } from '@hypertrace/common'; import { GraphQlArgument, GraphQlEnumArgument } from '@hypertrace/graphql-client'; +import { isNil } from 'lodash-es'; import { MetricAggregationType } from '../../../model/metrics/metric-aggregation'; import { EntityType } from '../../../model/schema/entity'; import { GraphQlGroupBy } from '../../../model/schema/groupby/graphql-group-by'; import { GraphQlIntervalUnit } from '../../../model/schema/interval/graphql-interval-unit'; import { convertToGraphQlMetricAggregationType } from '../../../model/schema/metrics/graphql-metric-aggregation-type'; -import { GraphQlSortBySpecification } from '../../../model/schema/sort/graphql-sort-by-specification'; import { TraceType } from '../../../model/schema/trace'; import { convertToGraphQlIntervalUnit } from '../specification/metric/metric-interval-unit-converter'; import { GraphQlArgumentBuilder } from './graphql-argument-builder'; @@ -101,26 +101,6 @@ export class GraphQlObservabilityArgumentBuilder extends GraphQlArgumentBuilder : [{ name: 'aggregation', value: new GraphQlEnumArgument(convertToGraphQlMetricAggregationType(aggregation)) }]; } - public forOrderBy(orderBy?: GraphQlSortBySpecification): GraphQlArgument[] { - return this.forOrderBys(orderBy && [orderBy]); - } - - public forOrderBys(orderBys: GraphQlSortBySpecification[] = []): GraphQlArgument[] { - if (orderBys.length === 0) { - return []; - } - - return [ - { - name: 'orderBy', - value: orderBys.map(orderBy => ({ - ...orderBy.key.asGraphQlOrderByFragment(), - direction: new GraphQlEnumArgument(orderBy.direction) - })) - } - ]; - } - public forGroupBy(groupBy?: GraphQlGroupBy): GraphQlArgument[] { if (!groupBy) { return []; @@ -129,18 +109,11 @@ export class GraphQlObservabilityArgumentBuilder extends GraphQlArgumentBuilder return [ { name: 'groupBy', - value: - // Remove includeRest key if undefined - groupBy.includeRest === undefined - ? { - keys: groupBy.keys, - groupLimit: groupBy.limit - } - : { - keys: groupBy.keys, - groupLimit: groupBy.limit, - includeRest: groupBy.includeRest - } + value: { + expressions: groupBy.keyExpressions.map(expression => this.buildAttributeExpression(expression)), + groupLimit: groupBy.limit, + ...(isNil(groupBy.includeRest) ? {} : { includeRest: groupBy.includeRest }) + } } ]; } diff --git a/projects/observability/src/shared/graphql/request/builders/selections/observability-specification-builder.ts b/projects/observability/src/shared/graphql/request/builders/selections/observability-specification-builder.ts index 47183bf2d..86b43b1f0 100644 --- a/projects/observability/src/shared/graphql/request/builders/selections/observability-specification-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/selections/observability-specification-builder.ts @@ -297,7 +297,7 @@ export class ObservabilitySpecificationBuilder extends SpecificationBuilder { name: metric, aggregation: aggregation, asGraphQlOrderByFragment: () => ({ - key: metric, + expression: { key: metric }, aggregation: this.aggregationAsEnum(aggregation) }) }; diff --git a/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.test.ts b/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.test.ts index 161465c74..ad85f6c30 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.test.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.test.ts @@ -11,8 +11,8 @@ describe('EnrichedAttributeSpecificationBuilder', () => { alias: 'duration', arguments: [ { - name: 'key', - value: 'duration' + name: 'expression', + value: { key: 'duration' } } ] }); diff --git a/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.ts b/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.ts index 3dcaefcf0..aafcb5834 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/attribute/enriched-attribute-specification-builder.ts @@ -13,7 +13,7 @@ export class EnrichedAttributeSpecificationBuilder { asGraphQlSelections: () => this.buildGraphQlSelections(attributeKey), extractFromServerData: serverData => this.extractFromServerData(attributeKey, units, serverData), asGraphQlOrderByFragment: () => ({ - key: attributeKey + expression: { key: attributeKey } }) }; } diff --git a/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.test.ts b/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.test.ts index b4faf9711..16bbc89b8 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.test.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.test.ts @@ -18,8 +18,8 @@ describe('Entity Specification Builder', () => { alias: 'id', arguments: [ { - name: 'key', - value: 'id' + name: 'expression', + value: { key: 'id' } } ] }, @@ -28,8 +28,8 @@ describe('Entity Specification Builder', () => { alias: 'name', arguments: [ { - name: 'key', - value: 'name' + name: 'expression', + value: { key: 'name' } } ] }, @@ -38,8 +38,8 @@ describe('Entity Specification Builder', () => { alias: 'attribute1', arguments: [ { - name: 'key', - value: 'attribute1' + name: 'expression', + value: { key: 'attribute1' } } ] }, @@ -48,8 +48,8 @@ describe('Entity Specification Builder', () => { alias: 'attribute2', arguments: [ { - name: 'key', - value: 'attribute2' + name: 'expression', + value: { key: 'attribute2' } } ] } @@ -89,8 +89,8 @@ describe('Entity Specification Builder', () => { alias: 'id', arguments: [ { - name: 'key', - value: 'id' + name: 'expression', + value: { key: 'id' } } ] }, @@ -99,8 +99,8 @@ describe('Entity Specification Builder', () => { alias: 'name', arguments: [ { - name: 'key', - value: 'name' + name: 'expression', + value: { key: 'name' } } ] }, @@ -109,8 +109,8 @@ describe('Entity Specification Builder', () => { alias: 'attribute1', arguments: [ { - name: 'key', - value: 'attribute1' + name: 'expression', + value: { key: 'attribute1' } } ] }, @@ -119,8 +119,8 @@ describe('Entity Specification Builder', () => { alias: 'attribute2', arguments: [ { - name: 'key', - value: 'attribute2' + name: 'expression', + value: { key: 'attribute2' } } ] }, @@ -129,8 +129,8 @@ describe('Entity Specification Builder', () => { alias: 'status', arguments: [ { - name: 'key', - value: 'status' + name: 'expression', + value: { key: 'status' } } ] }, @@ -139,8 +139,8 @@ describe('Entity Specification Builder', () => { alias: 'statusCode', arguments: [ { - name: 'key', - value: 'statusCode' + name: 'expression', + value: { key: 'statusCode' } } ] }, @@ -149,8 +149,8 @@ describe('Entity Specification Builder', () => { alias: 'statusMessage', arguments: [ { - name: 'key', - value: 'statusMessage' + name: 'expression', + value: { key: 'statusMessage' } } ] } @@ -189,8 +189,8 @@ describe('Entity Specification Builder', () => { alias: 'id', arguments: [ { - name: 'key', - value: 'id' + name: 'expression', + value: { key: 'id' } } ] }, @@ -199,8 +199,8 @@ describe('Entity Specification Builder', () => { alias: 'name', arguments: [ { - name: 'key', - value: 'name' + name: 'expression', + value: { key: 'name' } } ] }, @@ -231,8 +231,8 @@ describe('Entity Specification Builder', () => { alias: 'id', arguments: [ { - name: 'key', - value: 'id' + name: 'expression', + value: { key: 'id' } } ] }, @@ -241,8 +241,8 @@ describe('Entity Specification Builder', () => { alias: 'name', arguments: [ { - name: 'key', - value: 'name' + name: 'expression', + value: { key: 'name' } } ] }, diff --git a/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.ts b/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.ts index 0f46ffad4..0c38ab0bf 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/entity/entity-specification-builder.ts @@ -44,7 +44,7 @@ export class EntitySpecificationBuilder { additionalSpecifications ), asGraphQlOrderByFragment: () => ({ - key: nameKey + expression: { key: nameKey } }) }; } diff --git a/projects/observability/src/shared/graphql/request/builders/specification/explore/explore-specification-builder.ts b/projects/observability/src/shared/graphql/request/builders/specification/explore/explore-specification-builder.ts index 627e3a41d..e1742a94d 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/explore/explore-specification-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/explore/explore-specification-builder.ts @@ -1,5 +1,7 @@ import { DateCoercer, Dictionary } from '@hypertrace/common'; import { GraphQlEnumArgument } from '@hypertrace/graphql-client'; +import { isEmpty } from 'lodash-es'; +import { AttributeExpression } from '../../../../model/attribute/attribute-expression'; import { AttributeMetadataType } from '../../../../model/metadata/attribute-metadata'; import { MetricAggregationType } from '../../../../model/metrics/metric-aggregation'; import { INTERVAL_START_QUERY_KEY } from '../../../../model/schema/explore'; @@ -25,23 +27,31 @@ export class ExploreSpecificationBuilder { type: AttributeMetadataType.Timestamp }), asGraphQlOrderByFragment: () => ({ - key: 'intervalStart' + expression: { key: 'intervalStart' } }) }; } public exploreSpecificationForKey(key: string, aggregation?: MetricAggregationType): ExploreSpecification { - const queryAlias = aggregation === undefined ? key : `${aggregation}_${key}`; + return this.exploreSpecificationForAttributeExpression({ key: key }, aggregation); + } + + public exploreSpecificationForAttributeExpression( + expression: AttributeExpression, + aggregation?: MetricAggregationType + ): ExploreSpecification { + const expressionString = this.attributeExpressionAsString(expression); + const queryAlias = aggregation === undefined ? expressionString : `${aggregation}_${expressionString}`; return { - resultAlias: () => this.buildResultAlias(key, aggregation), - name: key, + resultAlias: () => this.buildResultAlias(expression, aggregation), + name: expressionString, aggregation: aggregation, asGraphQlSelections: () => ({ path: 'selection', alias: queryAlias, arguments: [ - this.argBuilder.forAttributeKey(key), + this.argBuilder.forAttributeExpression(expression), ...this.argBuilder.forAggregation(aggregation), ...this.argBuilder.forAggregationArgs(aggregation) ], @@ -50,7 +60,7 @@ export class ExploreSpecificationBuilder { extractFromServerData: serverData => serverData[queryAlias], asGraphQlOrderByFragment: () => { const fragment: GraphQlSortWithoutDirection & Dictionary = { - key: key + expression: expression }; if (aggregation !== undefined) { @@ -62,8 +72,14 @@ export class ExploreSpecificationBuilder { }; } - protected buildResultAlias(key: string, aggregation?: MetricAggregationType): string { - return aggregation === undefined ? key : `${aggregation}(${key})`; + protected attributeExpressionAsString(expression: AttributeExpression): string { + return isEmpty(expression.subpath) ? expression.key : `${expression.key}.${expression.subpath}`; + } + + protected buildResultAlias(expression: AttributeExpression, aggregation?: MetricAggregationType): string { + const expressionString = this.attributeExpressionAsString(expression); + + return aggregation === undefined ? expressionString : `${aggregation}(${expressionString})`; } protected aggregationAsEnum(aggregation: MetricAggregationType): GraphQlEnumArgument { diff --git a/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.test.ts b/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.test.ts index eb0859c0f..a60d4faf9 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.test.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.test.ts @@ -9,18 +9,4 @@ describe('Specification Builder', () => { }) ).toBeUndefined(); }); - - test('field specs should build correct selection and response', () => { - const fieldSpec = specBuilder.fieldSpecificationForKey('test'); - - expect(fieldSpec.resultAlias()).toEqual('test'); - expect(fieldSpec.name).toEqual('test'); - - expect(fieldSpec.asGraphQlSelections()).toEqual({ path: 'test' }); - expect(fieldSpec.extractFromServerData({ test: 'null' })).toBeUndefined(); - - expect(fieldSpec.extractFromServerData({ test: 'test value' })).toEqual('test value'); - - expect(fieldSpec.asGraphQlOrderByFragment()).toEqual({ key: 'test' }); - }); }); diff --git a/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.ts b/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.ts index 924080935..e1519addf 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/specification-builder.ts @@ -20,7 +20,7 @@ export class SpecificationBuilder { extractFromServerData: serverData => specifications.map(specification => specification.extractFromServerData(serverData)), asGraphQlOrderByFragment: () => ({ - key: orderByKey + expression: { key: orderByKey } }) }; } @@ -44,25 +44,7 @@ export class SpecificationBuilder { return serverValue === 'null' ? undefined : serverValue; }, asGraphQlOrderByFragment: () => ({ - key: attributeKey - }) - }; - } - - public fieldSpecificationForKey(field: string): Specification { - return { - resultAlias: () => field, - name: field, - asGraphQlSelections: () => ({ - path: field - }), - extractFromServerData: serverData => { - const serverValue = serverData[field]; - - return serverValue === 'null' ? undefined : serverValue; - }, - asGraphQlOrderByFragment: () => ({ - key: field + expression: { key: attributeKey } }) }; } diff --git a/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.test.ts b/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.test.ts index b9eb6f6d5..d52f31832 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.test.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.test.ts @@ -13,8 +13,8 @@ describe('TraceStatusSpecificationBuilder', () => { alias: 'status', arguments: [ { - name: 'key', - value: 'status' + name: 'expression', + value: { key: 'status' } } ] }, @@ -23,8 +23,8 @@ describe('TraceStatusSpecificationBuilder', () => { alias: 'statusCode', arguments: [ { - name: 'key', - value: 'statusCode' + name: 'expression', + value: { key: 'statusCode' } } ] }, @@ -33,8 +33,8 @@ describe('TraceStatusSpecificationBuilder', () => { alias: 'statusMessage', arguments: [ { - name: 'key', - value: 'statusMessage' + name: 'expression', + value: { key: 'statusMessage' } } ] } diff --git a/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.ts b/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.ts index 025d557f0..e57e83f40 100644 --- a/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.ts +++ b/projects/observability/src/shared/graphql/request/builders/specification/trace/trace-status/trace-status-specification-builder.ts @@ -17,7 +17,7 @@ export class TraceStatusSpecificationBuilder { name: TraceStatusSpecificationBuilder.STATUS_CODE_FIELD, asGraphQlSelections: () => this.buildGraphQlSelections(), extractFromServerData: serverData => this.extractFromServerData(serverData), - asGraphQlOrderByFragment: () => ({ key: TraceStatusSpecificationBuilder.STATUS_CODE_FIELD }) + asGraphQlOrderByFragment: () => ({ expression: { key: TraceStatusSpecificationBuilder.STATUS_CODE_FIELD } }) }; } diff --git a/projects/observability/src/shared/graphql/request/handlers/entities/query/entities-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/entities/query/entities-graphql-query-handler.service.test.ts index 1ad28b8aa..a3a5d1c95 100644 --- a/projects/observability/src/shared/graphql/request/handlers/entities/query/entities-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/entities/query/entities-graphql-query-handler.service.test.ts @@ -94,7 +94,7 @@ describe('Entities graphql query handler', () => { { path: 'attribute', alias: 'name', - arguments: [{ name: 'key', value: 'name' }] + arguments: [{ name: 'expression', value: { key: 'name' } }] } ] }, @@ -217,7 +217,7 @@ describe('Entities graphql query handler', () => { { path: 'metric', alias: 'some_metric', - arguments: [{ name: 'key', value: 'some_metric' }], + arguments: [{ name: 'expression', value: { key: 'some_metric' } }], children: [ { alias: 'avgrate_min', @@ -234,7 +234,7 @@ describe('Entities graphql query handler', () => { { path: 'metric', alias: 'some_metric', - arguments: [{ name: 'key', value: 'some_metric' }], + arguments: [{ name: 'expression', value: { key: 'some_metric' } }], children: [ { alias: 'avgrate_sec', @@ -343,7 +343,7 @@ describe('Entities graphql query handler', () => { { path: 'metric', alias: 'a_metric', - arguments: [{ name: 'key', value: 'a_metric' }], + arguments: [{ name: 'expression', value: { key: 'a_metric' } }], children: [ { alias: 'min_series_1m', @@ -362,7 +362,7 @@ describe('Entities graphql query handler', () => { { path: 'metric', alias: 'a_metric', - arguments: [{ name: 'key', value: 'a_metric' }], + arguments: [{ name: 'expression', value: { key: 'a_metric' } }], children: [ { alias: 'max_series_1m', @@ -382,7 +382,7 @@ describe('Entities graphql query handler', () => { { path: 'metric', alias: 'a_metric', - arguments: [{ name: 'key', value: 'a_metric' }], + arguments: [{ name: 'expression', value: { key: 'a_metric' } }], children: [ { alias: 'min_series_5m', diff --git a/projects/observability/src/shared/graphql/request/handlers/entities/query/interactions/interactions-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/entities/query/interactions/interactions-graphql-query-handler.service.test.ts index 9daeb73ed..b6b5d8433 100644 --- a/projects/observability/src/shared/graphql/request/handlers/entities/query/interactions/interactions-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/entities/query/interactions/interactions-graphql-query-handler.service.test.ts @@ -110,7 +110,7 @@ describe('Interactions graphql query handler', () => { { path: 'metric', alias: 'duration', - arguments: [{ name: 'key', value: 'duration' }], + arguments: [{ name: 'expression', value: { key: 'duration' } }], children: [ { path: 'avg', @@ -124,7 +124,11 @@ describe('Interactions graphql query handler', () => { path: 'neighbor', children: [ { path: 'id' }, - { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] } + { + path: 'attribute', + alias: 'name', + arguments: [{ name: 'expression', value: { key: 'name' } }] + } ] } ] diff --git a/projects/observability/src/shared/graphql/request/handlers/entities/query/topology/entity-topology-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/entities/query/topology/entity-topology-graphql-query-handler.service.test.ts index b61a9f3f4..19d3139b8 100644 --- a/projects/observability/src/shared/graphql/request/handlers/entities/query/topology/entity-topology-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/entities/query/topology/entity-topology-graphql-query-handler.service.test.ts @@ -259,11 +259,11 @@ describe('Entity topology graphql query handler', () => { path: 'results', children: [ { path: 'id' }, - { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }, + { path: 'attribute', alias: 'name', arguments: [{ name: 'expression', value: { key: 'name' } }] }, { path: 'metric', alias: 'duration', - arguments: [{ name: 'key', value: 'duration' }], + arguments: [{ name: 'expression', value: { key: 'duration' } }], children: [ { path: 'avg', @@ -284,7 +284,7 @@ describe('Entity topology graphql query handler', () => { { path: 'metric', alias: 'duration', - arguments: [{ name: 'key', value: 'duration' }], + arguments: [{ name: 'expression', value: { key: 'duration' } }], children: [ { path: 'avg', @@ -298,12 +298,20 @@ describe('Entity topology graphql query handler', () => { path: 'neighbor', children: [ { path: 'id' }, - { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }, - { path: 'attribute', alias: 'type', arguments: [{ name: 'key', value: 'type' }] }, + { + path: 'attribute', + alias: 'name', + arguments: [{ name: 'expression', value: { key: 'name' } }] + }, + { + path: 'attribute', + alias: 'type', + arguments: [{ name: 'expression', value: { key: 'type' } }] + }, { path: 'metric', alias: 'numCalls', - arguments: [{ name: 'key', value: 'numCalls' }], + arguments: [{ name: 'expression', value: { key: 'numCalls' } }], children: [ { path: 'avgrate', @@ -333,7 +341,7 @@ describe('Entity topology graphql query handler', () => { { path: 'metric', alias: 'duration', - arguments: [{ name: 'key', value: 'duration' }], + arguments: [{ name: 'expression', value: { key: 'duration' } }], children: [ { path: 'avg', @@ -347,11 +355,15 @@ describe('Entity topology graphql query handler', () => { path: 'neighbor', children: [ { path: 'id' }, - { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }, + { + path: 'attribute', + alias: 'name', + arguments: [{ name: 'expression', value: { key: 'name' } }] + }, { path: 'metric', alias: 'duration', - arguments: [{ name: 'key', value: 'duration' }], + arguments: [{ name: 'expression', value: { key: 'duration' } }], children: [ { path: 'avg', @@ -378,7 +390,7 @@ describe('Entity topology graphql query handler', () => { { path: 'metric', alias: 'duration', - arguments: [{ name: 'key', value: 'duration' }], + arguments: [{ name: 'expression', value: { key: 'duration' } }], children: [ { path: 'avg', @@ -392,11 +404,15 @@ describe('Entity topology graphql query handler', () => { path: 'neighbor', children: [ { path: 'id' }, - { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }, + { + path: 'attribute', + alias: 'name', + arguments: [{ name: 'expression', value: { key: 'name' } }] + }, { path: 'metric', alias: 'duration', - arguments: [{ name: 'key', value: 'duration' }], + arguments: [{ name: 'expression', value: { key: 'duration' } }], children: [ { path: 'avg', diff --git a/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-builder.service.ts b/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-builder.service.ts index 8337583d2..eb00e23d8 100644 --- a/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-builder.service.ts +++ b/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-builder.service.ts @@ -102,6 +102,8 @@ export class ExploreGraphqlQueryBuilderService { } private groupByAsSpecifications(groupBy?: GraphQlGroupBy): ExploreSpecification[] { - return (groupBy?.keys ?? []).map(key => this.specBuilder.exploreSpecificationForKey(key)); + return (groupBy?.keyExpressions ?? []).map(expression => + this.specBuilder.exploreSpecificationForAttributeExpression(expression) + ); } } diff --git a/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-handler.service.test.ts index 8b51596b6..4a240fc89 100644 --- a/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/explore/explore-graphql-query-handler.service.test.ts @@ -57,7 +57,7 @@ describe('Explore graphql query handler', () => { groupBy: { limit: 2, includeRest: true, - keys: ['serviceName'] + keyExpressions: [{ key: 'serviceName' }] }, orderBy: [ { @@ -98,13 +98,13 @@ describe('Explore graphql query handler', () => { name: 'filterBy', value: [ { - key: 'duration', + keyExpression: { key: 'duration' }, operator: new GraphQlEnumArgument(GraphQlOperatorType.GreaterThan), value: 0, type: new GraphQlEnumArgument(GraphQlFilterType.Attribute) }, { - key: 'duration', + keyExpression: { key: 'duration' }, operator: new GraphQlEnumArgument(GraphQlOperatorType.LessThan), value: 100, type: new GraphQlEnumArgument(GraphQlFilterType.Attribute) @@ -115,7 +115,7 @@ describe('Explore graphql query handler', () => { name: 'groupBy', value: { includeRest: true, - keys: ['serviceName'], + expressions: [{ key: 'serviceName' }], groupLimit: 2 } }, @@ -124,7 +124,7 @@ describe('Explore graphql query handler', () => { value: [ { direction: new GraphQlEnumArgument('ASC'), - key: 'duration', + keyExpression: { key: 'duration' }, aggregation: new GraphQlEnumArgument(GraphQlMetricAggregationType.Average) } ] @@ -138,14 +138,14 @@ describe('Explore graphql query handler', () => { { path: 'selection', alias: 'serviceName', - arguments: [{ name: 'key', value: 'serviceName' }], + arguments: [{ name: 'expression', value: { key: 'serviceName' } }], children: [{ path: 'value' }, { path: 'type' }] }, { path: 'selection', alias: 'avg_duration', arguments: [ - { name: 'key', value: 'duration' }, + { name: 'expression', value: { key: 'duration' } }, { name: 'aggregation', value: new GraphQlEnumArgument(GraphQlMetricAggregationType.Average) } ], children: [{ path: 'value' }, { path: 'type' }] @@ -154,7 +154,7 @@ describe('Explore graphql query handler', () => { path: 'selection', alias: 'avgrate_min_duration', arguments: [ - { name: 'key', value: 'duration' }, + { name: 'expression', value: { key: 'duration' } }, { name: 'aggregation', value: new GraphQlEnumArgument(GraphQlMetricAggregationType.Avgrate) }, { name: 'units', value: new GraphQlEnumArgument(GraphQlIntervalUnit.Minutes) }, { name: 'size', value: 1 } @@ -193,13 +193,13 @@ describe('Explore graphql query handler', () => { name: 'filterBy', value: [ { - key: 'duration', + keyExpression: { key: 'duration' }, operator: new GraphQlEnumArgument(GraphQlOperatorType.GreaterThan), value: 0, type: new GraphQlEnumArgument(GraphQlFilterType.Attribute) }, { - key: 'duration', + keyExpression: { key: 'duration' }, operator: new GraphQlEnumArgument(GraphQlOperatorType.LessThan), value: 100, type: new GraphQlEnumArgument(GraphQlFilterType.Attribute) @@ -210,7 +210,7 @@ describe('Explore graphql query handler', () => { name: 'groupBy', value: { includeRest: true, - keys: ['serviceName'], + expressions: [{ key: 'serviceName' }], groupLimit: 2 } }, @@ -219,7 +219,7 @@ describe('Explore graphql query handler', () => { value: [ { direction: new GraphQlEnumArgument('ASC'), - key: 'duration', + keyExpression: { key: 'duration' }, aggregation: new GraphQlEnumArgument(GraphQlMetricAggregationType.Average) } ] @@ -233,14 +233,14 @@ describe('Explore graphql query handler', () => { { path: 'selection', alias: 'serviceName', - arguments: [{ name: 'key', value: 'serviceName' }], + arguments: [{ name: 'expression', value: { key: 'serviceName' } }], children: [{ path: 'value' }, { path: 'type' }] }, { path: 'selection', alias: 'avg_duration', arguments: [ - { name: 'key', value: 'duration' }, + { name: 'expression', value: { key: 'duration' } }, { name: 'aggregation', value: new GraphQlEnumArgument(GraphQlMetricAggregationType.Average) } ], children: [{ path: 'value' }, { path: 'type' }] @@ -249,7 +249,7 @@ describe('Explore graphql query handler', () => { path: 'selection', alias: 'avgrate_min_duration', arguments: [ - { name: 'key', value: 'duration' }, + { name: 'expression', value: { key: 'duration' } }, { name: 'aggregation', value: new GraphQlEnumArgument(GraphQlMetricAggregationType.Avgrate) }, { name: 'units', value: new GraphQlEnumArgument(GraphQlIntervalUnit.Minutes) }, { name: 'size', value: 1 } diff --git a/projects/observability/src/shared/graphql/request/handlers/spans/span-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/spans/span-graphql-query-handler.service.test.ts index a8216410d..60049401d 100644 --- a/projects/observability/src/shared/graphql/request/handlers/spans/span-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/spans/span-graphql-query-handler.service.test.ts @@ -101,7 +101,7 @@ describe('SpanGraphQlQueryHandlerService', () => { operator: new GraphQlEnumArgument('EQUALS'), value: 'test-id', type: new GraphQlEnumArgument(GraphQlFilterType.Attribute), - key: 'id' + keyExpression: { key: 'id' } } ] } @@ -111,8 +111,8 @@ describe('SpanGraphQlQueryHandlerService', () => { path: 'results', children: [ { path: 'id' }, - { path: 'attribute', alias: 'apiName', arguments: [{ name: 'key', value: 'apiName' }] }, - { path: 'attribute', alias: 'duration', arguments: [{ name: 'key', value: 'duration' }] } + { path: 'attribute', alias: 'apiName', arguments: [{ name: 'expression', value: { key: 'apiName' } }] }, + { path: 'attribute', alias: 'duration', arguments: [{ name: 'expression', value: { key: 'duration' } }] } ] }, { path: 'total' } @@ -145,7 +145,7 @@ describe('SpanGraphQlQueryHandlerService', () => { operator: new GraphQlEnumArgument('EQUALS'), value: 'test-id', type: new GraphQlEnumArgument(GraphQlFilterType.Attribute), - key: 'id' + keyExpression: { key: 'id' } } ] } @@ -155,8 +155,8 @@ describe('SpanGraphQlQueryHandlerService', () => { path: 'results', children: [ { path: 'id' }, - { path: 'attribute', alias: 'apiName', arguments: [{ name: 'key', value: 'apiName' }] }, - { path: 'attribute', alias: 'duration', arguments: [{ name: 'key', value: 'duration' }] } + { path: 'attribute', alias: 'apiName', arguments: [{ name: 'expression', value: { key: 'apiName' } }] }, + { path: 'attribute', alias: 'duration', arguments: [{ name: 'expression', value: { key: 'duration' } }] } ] }, { path: 'total' } diff --git a/projects/observability/src/shared/graphql/request/handlers/spans/spans-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/spans/spans-graphql-query-handler.service.test.ts index f91e6a988..dbb0a0cb1 100644 --- a/projects/observability/src/shared/graphql/request/handlers/spans/spans-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/spans/spans-graphql-query-handler.service.test.ts @@ -97,7 +97,7 @@ describe('SpansGraphQlQueryHandlerService', () => { direction: { value: 'ASC' }, - key: 'apiName' + keyExpression: { key: 'apiName' } } ] } @@ -109,22 +109,12 @@ describe('SpansGraphQlQueryHandlerService', () => { { path: 'id' }, { alias: 'apiName', - arguments: [ - { - name: 'key', - value: 'apiName' - } - ], + arguments: [{ name: 'expression', value: { key: 'apiName' } }], path: 'attribute' }, { alias: 'duration', - arguments: [ - { - name: 'key', - value: 'duration' - } - ], + arguments: [{ name: 'expression', value: { key: 'duration' } }], path: 'attribute' } ] diff --git a/projects/observability/src/shared/graphql/request/handlers/traces/trace-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/traces/trace-graphql-query-handler.service.test.ts index 4112f5846..a0a076ffd 100644 --- a/projects/observability/src/shared/graphql/request/handlers/traces/trace-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/traces/trace-graphql-query-handler.service.test.ts @@ -79,7 +79,10 @@ describe('TraceGraphQlQueryHandlerService', () => { children: [ { path: 'results', - children: [{ path: 'id' }, { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }] + children: [ + { path: 'id' }, + { path: 'attribute', alias: 'name', arguments: [{ name: 'expression', value: { key: 'name' } }] } + ] } ] }); @@ -113,7 +116,10 @@ describe('TraceGraphQlQueryHandlerService', () => { children: [ { path: 'results', - children: [{ path: 'id' }, { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }] + children: [ + { path: 'id' }, + { path: 'attribute', alias: 'name', arguments: [{ name: 'expression', value: { key: 'name' } }] } + ] } ] }); @@ -156,7 +162,10 @@ describe('TraceGraphQlQueryHandlerService', () => { children: [ { path: 'results', - children: [{ path: 'id' }, { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }] + children: [ + { path: 'id' }, + { path: 'attribute', alias: 'name', arguments: [{ name: 'expression', value: { key: 'name' } }] } + ] } ] }); @@ -190,7 +199,10 @@ describe('TraceGraphQlQueryHandlerService', () => { children: [ { path: 'results', - children: [{ path: 'id' }, { path: 'attribute', alias: 'name', arguments: [{ name: 'key', value: 'name' }] }] + children: [ + { path: 'id' }, + { path: 'attribute', alias: 'name', arguments: [{ name: 'expression', value: { key: 'name' } }] } + ] } ] }); diff --git a/projects/observability/src/shared/graphql/request/handlers/traces/traces-graphql-query-handler.service.test.ts b/projects/observability/src/shared/graphql/request/handlers/traces/traces-graphql-query-handler.service.test.ts index a35a92502..a16752d56 100644 --- a/projects/observability/src/shared/graphql/request/handlers/traces/traces-graphql-query-handler.service.test.ts +++ b/projects/observability/src/shared/graphql/request/handlers/traces/traces-graphql-query-handler.service.test.ts @@ -74,12 +74,7 @@ describe('TracesGraphQlQueryHandlerService', () => { { alias: 'name', path: 'attribute', - arguments: [ - { - name: 'key', - value: 'name' - } - ] + arguments: [{ name: 'expression', value: { key: 'name' } }] } ] },