diff --git a/.snyk b/.snyk index 1e9092fa..154d7794 100644 --- a/.snyk +++ b/.snyk @@ -5,6 +5,6 @@ ignore: SNYK-JAVA-IONETTY-1042268: - '*': reason: No replacement available - expires: 2021-12-31T00:00:00.000Z + expires: 2022-03-31T00:00:00.000Z patch: {} diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/AttributeExpressionDeserializationConfig.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/AttributeExpressionDeserializationConfig.java new file mode 100644 index 00000000..5d6959b8 --- /dev/null +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/AttributeExpressionDeserializationConfig.java @@ -0,0 +1,17 @@ +package org.hypertrace.core.graphql.common.deserialization; + +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; +import org.hypertrace.core.graphql.deserialization.ArgumentDeserializationConfig; + +class AttributeExpressionDeserializationConfig implements ArgumentDeserializationConfig { + + @Override + public String getArgumentKey() { + return AttributeExpression.ARGUMENT_NAME; + } + + @Override + public Class getArgumentSchema() { + return AttributeExpression.class; + } +} diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/CommonDeserializationModule.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/CommonDeserializationModule.java index 9aecb920..f53cc00d 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/CommonDeserializationModule.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/CommonDeserializationModule.java @@ -48,6 +48,9 @@ protected void configure() { .toInstance( ArgumentDeserializationConfig.forPrimitive( SpaceArgument.ARGUMENT_NAME, SpaceArgument.class)); + deserializationConfigMultibinder + .addBinding() + .to(AttributeExpressionDeserializationConfig.class); requireBinding(Key.get(new TypeLiteral>() {})); } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/FilterArgumentDeserializationConfig.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/FilterArgumentDeserializationConfig.java index 1385d4e0..87a42d0a 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/FilterArgumentDeserializationConfig.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/FilterArgumentDeserializationConfig.java @@ -9,6 +9,7 @@ import lombok.Value; import lombok.experimental.Accessors; import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterOperatorType; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterType; @@ -52,6 +53,9 @@ private static class DefaultFilterArgument implements FilterArgument { @JsonProperty(FILTER_ARGUMENT_KEY) String key; + @JsonProperty(FILTER_ARGUMENT_KEY_EXPRESSION) + AttributeExpression keyExpression; + @JsonProperty(FILTER_ARGUMENT_OPERATOR) FilterOperatorType operator; diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfig.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfig.java index 78c22050..5fd92587 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfig.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfig.java @@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; import lombok.Value; import lombok.experimental.Accessors; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderDirection; import org.hypertrace.core.graphql.deserialization.ArgumentDeserializationConfig; @@ -38,5 +39,8 @@ private static class DefaultOrderArgument implements OrderArgument { @JsonProperty(ORDER_KEY_NAME) String key; + + @JsonProperty(ORDER_KEY_EXPRESSION_NAME) + AttributeExpression keyExpression; } } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequest.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequest.java index b37a3b52..64458fc9 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequest.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequest.java @@ -1,10 +1,11 @@ package org.hypertrace.core.graphql.common.request; -import org.hypertrace.core.graphql.attributes.AttributeModel; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; public interface AttributeRequest { + AttributeAssociation attributeExpression(); - AttributeModel attribute(); - - String alias(); + default String asMapKey() { + return attributeExpression().value().asAlias(); + } } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequestBuilder.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequestBuilder.java index c36ff9de..37712d0f 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequestBuilder.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequestBuilder.java @@ -6,6 +6,7 @@ import io.reactivex.rxjava3.core.Single; import java.util.stream.Stream; import org.hypertrace.core.graphql.attributes.AttributeModel; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.context.GraphQlRequestContext; public interface AttributeRequestBuilder { @@ -27,8 +28,10 @@ Observable buildForAttributeQueryableFieldsAndId( String attributeScope, Stream attributeQueryableFields); - Single buildForKey( - GraphQlRequestContext context, String attributeModelScope, String attributeKey); + Single buildForAttributeExpression( + GraphQlRequestContext context, + String attributeScope, + AttributeExpression attributeExpression); AttributeRequest buildForAttribute(AttributeModel attribute); } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilder.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilder.java index 5f6a807f..ed27e43b 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilder.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilder.java @@ -11,7 +11,9 @@ import org.hypertrace.core.graphql.attributes.AttributeModel; import org.hypertrace.core.graphql.attributes.AttributeStore; import org.hypertrace.core.graphql.common.schema.attributes.AttributeQueryable; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeKeyArgument; +import org.hypertrace.core.graphql.common.utils.attributes.AttributeAssociator; import org.hypertrace.core.graphql.context.GraphQlRequestContext; import org.hypertrace.core.graphql.deserialization.ArgumentDeserializer; import org.hypertrace.core.graphql.utils.schema.GraphQlSelectionFinder; @@ -20,15 +22,18 @@ class DefaultAttributeRequestBuilder implements AttributeRequestBuilder { private final AttributeStore attributeStore; + private final AttributeAssociator attributeAssociator; private final ArgumentDeserializer argumentDeserializer; private final GraphQlSelectionFinder selectionFinder; @Inject DefaultAttributeRequestBuilder( AttributeStore attributeStore, + AttributeAssociator attributeAssociator, ArgumentDeserializer argumentDeserializer, GraphQlSelectionFinder selectionFinder) { this.attributeStore = attributeStore; + this.attributeAssociator = attributeAssociator; this.argumentDeserializer = argumentDeserializer; this.selectionFinder = selectionFinder; } @@ -44,8 +49,10 @@ public Observable buildForAttributeQueryableSelectionSet( String attributeScope, DataFetchingFieldSelectionSet attributeQueryableSelectionSet) { return Observable.fromStream( - this.getAttributeKeysForAttributeQueryableSelectionSet(attributeQueryableSelectionSet)) - .flatMapSingle(key -> this.buildForKey(context, attributeScope, key)) + this.getAttributeExpressionsForAttributeQueryableSelectionSet( + attributeQueryableSelectionSet)) + .flatMapSingle( + expression -> this.buildForAttributeExpression(context, attributeScope, expression)) .distinct(); } @@ -73,40 +80,43 @@ public Observable buildForAttributeQueryableFieldsAndId( } @Override - public Single buildForKey( - GraphQlRequestContext context, String requestScope, String attributeKey) { - return this.attributeStore - .get(context, requestScope, attributeKey) - .map(this::buildForAttribute); + public Single buildForAttributeExpression( + GraphQlRequestContext context, + String attributeScope, + AttributeExpression attributeExpression) { + return this.attributeAssociator + .associateAttribute(context, attributeScope, attributeExpression, attributeExpression.key()) + .map(DefaultAttributeRequest::new); } @Override public AttributeRequest buildForAttribute(AttributeModel attribute) { - return new DefaultAttributeRequest(attribute); + return new DefaultAttributeRequest( + AttributeAssociation.of(attribute, AttributeExpression.forAttributeKey(attribute.key()))); } - private Stream getAttributeKeysForAttributeQueryableSelectionSet( + private Stream getAttributeExpressionsForAttributeQueryableSelectionSet( DataFetchingFieldSelectionSet selectionSet) { return this.selectionFinder .findSelections( selectionSet, SelectionQuery.namedChild(AttributeQueryable.ATTRIBUTE_FIELD_NAME)) - .flatMap(this::getArgument); + .flatMap(this::resolveAttributeExpression); } - private Stream getArgument(SelectedField attributeField) { + private Stream resolveAttributeExpression(SelectedField attributeField) { return this.argumentDeserializer - .deserializePrimitive(attributeField.getArguments(), AttributeKeyArgument.class) + .deserializeObject(attributeField.getArguments(), AttributeExpression.class) + .or( + () -> + this.argumentDeserializer + .deserializePrimitive(attributeField.getArguments(), AttributeKeyArgument.class) + .map(AttributeExpression::forAttributeKey)) .stream(); } @Value @Accessors(fluent = true) static class DefaultAttributeRequest implements AttributeRequest { - AttributeModel attribute; - - @Override - public String alias() { - return attribute.id(); - } + AttributeAssociation attributeExpression; } } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilder.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilder.java index 7f1af6b4..cc0976e2 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilder.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilder.java @@ -1,5 +1,7 @@ package org.hypertrace.core.graphql.common.request; +import static java.util.Objects.requireNonNull; + import io.reactivex.rxjava3.core.Maybe; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; @@ -12,6 +14,7 @@ import lombok.experimental.Accessors; import org.hypertrace.core.graphql.attributes.AttributeStore; import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterOperatorType; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterType; @@ -50,12 +53,17 @@ private Single> normalize( GraphQlRequestContext requestContext, String scope, FilterArgument filterArgument) { switch (filterArgument.type()) { case ATTRIBUTE: + AttributeExpression attributeExpression = + Optional.ofNullable(filterArgument.keyExpression()) + .orElseGet( + () -> + AttributeExpression.forAttributeKey(requireNonNull(filterArgument.key()))); return this.attributeAssociator.associateAttribute( requestContext, scope, new NormalizedFilter( - filterArgument.key(), filterArgument.operator(), filterArgument.value()), - FilterArgument::key); + attributeExpression, filterArgument.operator(), filterArgument.value()), + attributeExpression.key()); case ID: return Maybe.fromOptional(Optional.ofNullable(filterArgument.idType())) .map(AttributeScope::getScopeString) @@ -71,7 +79,7 @@ private Single> normalize( AttributeAssociation.of( foreignIdAttribute, new NormalizedFilter( - foreignIdAttribute.key(), + AttributeExpression.forAttributeKey(foreignIdAttribute.key()), filterArgument.operator(), filterArgument.value()))); default: @@ -85,7 +93,8 @@ private Single> normalize( @Accessors(fluent = true) private static class NormalizedFilter implements FilterArgument { FilterType type = FilterType.ATTRIBUTE; - String key; + String key = null; + AttributeExpression keyExpression; FilterOperatorType operator; Object value; String idScope = null; diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultResultSetRequestBuilder.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultResultSetRequestBuilder.java index ec0d83e2..63bbe7c7 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultResultSetRequestBuilder.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultResultSetRequestBuilder.java @@ -17,6 +17,7 @@ import lombok.Value; import lombok.experimental.Accessors; import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.ResultSet; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; @@ -98,7 +99,11 @@ public Single> build( return zip( this.attributeAssociator - .associateAttributes(context, requestScope, requestedOrders, OrderArgument::key) + .associateAttributes( + context, + requestScope, + requestedOrders, + arg -> arg.resolvedKeyExpression().key()) .collect(Collectors.toUnmodifiableList()), this.filterRequestBuilder.build(context, requestScope, requestedFilters), (orders, filters) -> @@ -149,7 +154,7 @@ public Single> build( GraphQlRequestContext context, String requestScope, Map arguments, - List attributes) { + List attributeExpressions) { int limit = this.argumentDeserializer .deserializePrimitive(arguments, LimitArgument.class) @@ -166,7 +171,8 @@ public Single> build( .orElse(Collections.emptyList()); return zip( - this.getAttributeRequests(context, requestScope, attributes).collect(Collectors.toList()), + this.getAttributeRequests(context, requestScope, attributeExpressions) + .collect(Collectors.toList()), this.attributeRequestBuilder.buildForId(context, requestScope), this.filterRequestBuilder.build(context, requestScope, requestedFilters), (attributeRequests, idAttribute, filters) -> @@ -183,12 +189,15 @@ public Single> build( } private Observable getAttributeRequests( - GraphQlRequestContext context, String requestScope, List attributes) { - return Observable.fromIterable(attributes) + GraphQlRequestContext context, + String requestScope, + List attributeExpressions) { + return Observable.fromIterable(attributeExpressions) .distinct() .flatMapSingle( - attributeKey -> - this.attributeRequestBuilder.buildForKey(context, requestScope, attributeKey)); + attributeExpression -> + this.attributeRequestBuilder.buildForAttributeExpression( + context, requestScope, attributeExpression)); } private Stream getAttributeQueryableFields( diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/ResultSetRequestBuilder.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/ResultSetRequestBuilder.java index ce22126f..b8005b0d 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/ResultSetRequestBuilder.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/ResultSetRequestBuilder.java @@ -9,6 +9,7 @@ import java.util.Optional; import java.util.stream.Stream; import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.context.GraphQlRequestContext; @@ -42,5 +43,5 @@ Single> build( GraphQlRequestContext context, String requestScope, Map arguments, - List attributes); + List attributeExpressions); } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/attributes/AttributeQueryable.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/attributes/AttributeQueryable.java index c52da8df..277752e5 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/attributes/AttributeQueryable.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/attributes/AttributeQueryable.java @@ -1,8 +1,12 @@ package org.hypertrace.core.graphql.common.schema.attributes; +import static java.util.Objects.requireNonNull; + import graphql.annotations.annotationTypes.GraphQLField; import graphql.annotations.annotationTypes.GraphQLName; -import graphql.annotations.annotationTypes.GraphQLNonNull; +import java.util.Optional; +import javax.annotation.Nullable; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeKeyArgument; public interface AttributeQueryable { @@ -11,5 +15,14 @@ public interface AttributeQueryable { @GraphQLField @GraphQLName(ATTRIBUTE_FIELD_NAME) - Object attribute(@GraphQLName(AttributeKeyArgument.ARGUMENT_NAME) @GraphQLNonNull String key); + default Object attribute( + @Deprecated @GraphQLName(AttributeKeyArgument.ARGUMENT_NAME) @Nullable String key, + @GraphQLName(AttributeExpression.ARGUMENT_NAME) @Nullable AttributeExpression expression) { + return attribute( + Optional.ofNullable(expression) + .orElseGet(() -> AttributeExpression.forAttributeKey(requireNonNull(key)))); + } + + // Once callers are migrated off using the string, we'll remove it and use this api only + Object attribute(@GraphQLName(AttributeExpression.ARGUMENT_NAME) AttributeExpression expression); } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/attributes/arguments/AttributeExpression.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/attributes/arguments/AttributeExpression.java new file mode 100644 index 00000000..6cae4f39 --- /dev/null +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/attributes/arguments/AttributeExpression.java @@ -0,0 +1,56 @@ +package org.hypertrace.core.graphql.common.schema.attributes.arguments; + +import com.fasterxml.jackson.annotation.JsonProperty; +import graphql.annotations.annotationTypes.GraphQLConstructor; +import graphql.annotations.annotationTypes.GraphQLField; +import graphql.annotations.annotationTypes.GraphQLName; +import graphql.annotations.annotationTypes.GraphQLNonNull; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import lombok.Value; +import lombok.experimental.Accessors; + +@Value +@Accessors(fluent = true) +@GraphQLName(AttributeExpression.TYPE_NAME) +public class AttributeExpression { + public static final String ARGUMENT_NAME = "expression"; + static final String TYPE_NAME = "AttributeExpression"; + + private static final String ATTRIBUTE_KEY = "key"; + private static final String SUBPATH = "subpath"; + + @GraphQLField + @GraphQLNonNull + @GraphQLName(ATTRIBUTE_KEY) + @JsonProperty(ATTRIBUTE_KEY) + String key; + + @GraphQLField + @GraphQLName(SUBPATH) + @JsonProperty(SUBPATH) + Optional subpath; + + @GraphQLConstructor + public AttributeExpression( + @GraphQLName(ATTRIBUTE_KEY) String key, @GraphQLName(SUBPATH) @Nullable String subpath) { + this.key = key; + this.subpath = Optional.ofNullable(subpath); + } + + private AttributeExpression() { + this.key = null; + this.subpath = Optional.empty(); + } + + public String asAlias() { + return subpath() + .map(subpath -> String.format("%s.%s", this.key(), subpath)) + .orElseGet(this::key); + } + + public static AttributeExpression forAttributeKey(@Nonnull String key) { + return new AttributeExpression(key, null); + } +} diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/filter/FilterArgument.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/filter/FilterArgument.java index 2d5be8f7..fcf1e3c0 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/filter/FilterArgument.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/filter/FilterArgument.java @@ -6,6 +6,7 @@ import graphql.annotations.annotationTypes.GraphQLNonNull; import javax.annotation.Nullable; import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; @GraphQLName(FilterArgument.TYPE_NAME) public interface FilterArgument { @@ -13,6 +14,7 @@ public interface FilterArgument { String ARGUMENT_NAME = "filterBy"; String FILTER_ARGUMENT_TYPE = "type"; String FILTER_ARGUMENT_KEY = "key"; + String FILTER_ARGUMENT_KEY_EXPRESSION = "keyExpression"; String FILTER_ARGUMENT_OPERATOR = "operator"; String FILTER_ARGUMENT_VALUE = "value"; @Deprecated String FILTER_ARGUMENT_ID_TYPE = "idType"; @@ -26,8 +28,15 @@ public interface FilterArgument { @GraphQLField @GraphQLName(FILTER_ARGUMENT_KEY) @Nullable + @GraphQLDeprecate + @Deprecated String key(); + @GraphQLField + @GraphQLName(FILTER_ARGUMENT_KEY_EXPRESSION) + @Nullable + AttributeExpression keyExpression(); + @GraphQLField @GraphQLNonNull @GraphQLName(FILTER_ARGUMENT_OPERATOR) diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/order/OrderArgument.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/order/OrderArgument.java index 61937efe..74dcb951 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/order/OrderArgument.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/results/arguments/order/OrderArgument.java @@ -1,22 +1,42 @@ package org.hypertrace.core.graphql.common.schema.results.arguments.order; +import static java.util.Objects.requireNonNull; +import static org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression.forAttributeKey; + +import graphql.annotations.annotationTypes.GraphQLDeprecate; import graphql.annotations.annotationTypes.GraphQLField; import graphql.annotations.annotationTypes.GraphQLName; -import graphql.annotations.annotationTypes.GraphQLNonNull; +import java.util.Optional; +import javax.annotation.Nullable; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; @GraphQLName(OrderArgument.TYPE_NAME) public interface OrderArgument { String ARGUMENT_NAME = "orderBy"; // TODO rename to order String TYPE_NAME = "Order"; String ORDER_KEY_NAME = "key"; + String ORDER_KEY_EXPRESSION_NAME = "keyExpression"; String ORDER_DIRECTION_NAME = "direction"; @GraphQLField - @GraphQLNonNull + @Nullable @GraphQLName(ORDER_KEY_NAME) + @Deprecated + @GraphQLDeprecate String key(); + @GraphQLField + @GraphQLName(ORDER_KEY_EXPRESSION_NAME) + @Nullable + AttributeExpression keyExpression(); + @GraphQLField @GraphQLName(ORDER_DIRECTION_NAME) OrderDirection direction(); + + // Temporary way of getting the expression from either keyExpression or key field + default AttributeExpression resolvedKeyExpression() { + return Optional.ofNullable(keyExpression()) + .orElseGet(() -> forAttributeKey(requireNonNull(key()))); + } } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/AttributeAssociator.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/AttributeAssociator.java index b527111e..bcdf3b08 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/AttributeAssociator.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/AttributeAssociator.java @@ -16,8 +16,5 @@ Observable> associateAttributes( Function attributeKeyMapper); Single> associateAttribute( - GraphQlRequestContext context, - String requestScope, - T input, - Function attributeKeyMapper); + GraphQlRequestContext context, String requestScope, T input, String attributeKey); } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/DefaultAttributeAssociator.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/DefaultAttributeAssociator.java index 48827820..fd2dcd16 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/DefaultAttributeAssociator.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/utils/attributes/DefaultAttributeAssociator.java @@ -26,17 +26,16 @@ public Observable> associateAttributes( Function attributeKeyMapper) { return Observable.fromIterable(inputs) .flatMapSingle( - input -> this.associateAttribute(context, requestScope, input, attributeKeyMapper)); + input -> + this.associateAttribute( + context, requestScope, input, attributeKeyMapper.apply(input))); } @Override public Single> associateAttribute( - GraphQlRequestContext context, - String requestScope, - T input, - Function attributeKeyMapper) { + GraphQlRequestContext context, String requestScope, T input, String attributeKey) { return this.attributeStore - .get(context, requestScope, attributeKeyMapper.apply(input)) + .get(context, requestScope, attributeKey) .map(attribute -> AttributeAssociation.of(attribute, input)); } } diff --git a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfigTest.java b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfigTest.java index 815b46a8..bf7e3261 100644 --- a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfigTest.java +++ b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfigTest.java @@ -7,6 +7,7 @@ import com.google.inject.multibindings.Multibinder; import java.util.List; import java.util.Map; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderDirection; import org.hypertrace.core.graphql.deserialization.ArgumentDeserializationConfig; @@ -54,9 +55,11 @@ void deserializesValueIfPresent() { List result = this.argumentDeserializer.deserializeObjectList(argMap, OrderArgument.class).orElseThrow(); assertEquals(2, result.size()); - assertEquals("fooKey", result.get(0).key()); + assertEquals( + AttributeExpression.forAttributeKey("fooKey"), result.get(0).resolvedKeyExpression()); Assertions.assertEquals(OrderDirection.ASC, result.get(0).direction()); - assertEquals("barKey", result.get(1).key()); + assertEquals( + AttributeExpression.forAttributeKey("barKey"), result.get(1).resolvedKeyExpression()); Assertions.assertEquals(OrderDirection.DESC, result.get(1).direction()); } } diff --git a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilderTest.java b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilderTest.java index df540da4..3026aaf9 100644 --- a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilderTest.java +++ b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilderTest.java @@ -13,7 +13,9 @@ import java.util.stream.Stream; import org.hypertrace.core.graphql.attributes.AttributeModel; import org.hypertrace.core.graphql.attributes.AttributeStore; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeKeyArgument; +import org.hypertrace.core.graphql.common.utils.attributes.AttributeAssociator; import org.hypertrace.core.graphql.context.GraphQlRequestContext; import org.hypertrace.core.graphql.deserialization.ArgumentDeserializer; import org.hypertrace.core.graphql.utils.schema.GraphQlSelectionFinder; @@ -26,6 +28,7 @@ @ExtendWith(MockitoExtension.class) class DefaultAttributeRequestBuilderTest { @Mock AttributeStore mockAttributeStore; + @Mock AttributeAssociator mockAttributeAssociator; @Mock ArgumentDeserializer mockArgumentDeserializer; @Mock GraphQlSelectionFinder mockSelectionFinder; @Mock GraphQlRequestContext mockContext; @@ -39,18 +42,26 @@ class DefaultAttributeRequestBuilderTest { void beforeEach() { this.requestBuilder = new DefaultAttributeRequestBuilder( - mockAttributeStore, mockArgumentDeserializer, mockSelectionFinder); + mockAttributeStore, + mockAttributeAssociator, + mockArgumentDeserializer, + mockSelectionFinder); } @Test void canBuildRequestForSelectionSet() { + AttributeAssociation expectedResultExpression = + AttributeAssociation.of(this.mockAttribute, AttributeExpression.forAttributeKey("fooKey")); when(this.mockSelectionFinder.findSelections(eq(this.mockSelectionSet), any())) .thenReturn(Stream.of(this.mockSelectedField)); when(this.mockArgumentDeserializer.deserializePrimitive(any(), eq(AttributeKeyArgument.class))) .thenReturn(Optional.of("fooKey")); - when(this.mockAttributeStore.get(eq(this.mockContext), eq("SPAN"), eq("fooKey"))) - .thenReturn(Single.just(this.mockAttribute)); - when(this.mockAttribute.id()).thenReturn("fooId"); + when(this.mockAttributeAssociator.associateAttribute( + eq(this.mockContext), + eq("SPAN"), + eq(AttributeExpression.forAttributeKey("fooKey")), + eq("fooKey"))) + .thenReturn(Single.just(expectedResultExpression)); List returned = this.requestBuilder @@ -59,7 +70,6 @@ void canBuildRequestForSelectionSet() { .blockingGet(); assertEquals(1, returned.size()); - assertEquals("fooId", returned.get(0).alias()); - assertEquals(this.mockAttribute, returned.get(0).attribute()); + assertEquals(expectedResultExpression, returned.get(0).attributeExpression()); } } diff --git a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilderTest.java b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilderTest.java index 3451b14c..26eab961 100644 --- a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilderTest.java +++ b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilderTest.java @@ -1,6 +1,9 @@ package org.hypertrace.core.graphql.common.request; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -9,6 +12,7 @@ import org.hypertrace.core.graphql.attributes.AttributeModel; import org.hypertrace.core.graphql.attributes.AttributeStore; import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterOperatorType; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterType; @@ -69,7 +73,9 @@ void canBuildIdFilterWithEnumScope() { .get(0); assertEquals(mockForeignAttribute, builtFilter.attribute()); - assertEquals(filterAttributeKey, builtFilter.value().key()); + assertEquals( + AttributeExpression.forAttributeKey(filterAttributeKey), + builtFilter.value().keyExpression()); assertEquals(filterOperatorType, builtFilter.value().operator()); assertEquals(filterValue, builtFilter.value().value()); assertEquals(FilterType.ATTRIBUTE, builtFilter.value().type()); @@ -105,9 +111,44 @@ void canBuildIdFilterWithStringScope() { .get(0); assertEquals(mockForeignAttribute, builtFilter.attribute()); - assertEquals(filterAttributeKey, builtFilter.value().key()); + assertEquals( + AttributeExpression.forAttributeKey(filterAttributeKey), + builtFilter.value().keyExpression()); assertEquals(filterOperatorType, builtFilter.value().operator()); assertEquals(filterValue, builtFilter.value().value()); assertEquals(FilterType.ATTRIBUTE, builtFilter.value().type()); } + + @Test + void canBuildFilterWithAttributeExpression() { + final FilterType filterType = FilterType.ATTRIBUTE; + final String filterAttributeKey = "filterKey"; + final String currentScope = "scope"; + + final FilterArgument filterArgument = mock(FilterArgument.class); + when(filterArgument.type()).thenReturn(filterType); + when(filterArgument.keyExpression()) + .thenReturn(AttributeExpression.forAttributeKey(filterAttributeKey)); + + final AttributeModel mockAttribute = mock(AttributeModel.class); + when(this.mockAttributeAssociator.associateAttribute( + same(mockRequestContext), + eq(currentScope), + any(FilterArgument.class), + eq(filterAttributeKey))) + .thenAnswer( + invocation -> + Single.just(AttributeAssociation.of(mockAttribute, invocation.getArgument(2)))); + + AttributeAssociation builtFilter = + this.requestBuilder + .build(this.mockRequestContext, currentScope, List.of(filterArgument)) + .blockingGet() + .get(0); + + assertEquals(mockAttribute, builtFilter.attribute()); + assertEquals( + AttributeExpression.forAttributeKey(filterAttributeKey), + builtFilter.value().keyExpression()); + } } diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/AttributeExpressionConverter.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/AttributeExpressionConverter.java new file mode 100644 index 00000000..14cff9a1 --- /dev/null +++ b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/AttributeExpressionConverter.java @@ -0,0 +1,21 @@ +package org.hypertrace.core.graphql.utils.gateway; + +import io.reactivex.rxjava3.core.Single; +import org.hypertrace.core.graphql.common.request.AttributeAssociation; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; +import org.hypertrace.core.graphql.common.utils.Converter; +import org.hypertrace.gateway.service.v1.common.AttributeExpression.Builder; +import org.hypertrace.gateway.service.v1.common.Expression; + +class AttributeExpressionConverter + implements Converter, Expression> { + + @Override + public Single convert(AttributeAssociation attributeExpression) { + Builder builder = org.hypertrace.gateway.service.v1.common.AttributeExpression.newBuilder(); + builder.setAttributeId(attributeExpression.attribute().id()); + attributeExpression.value().subpath().ifPresent(builder::setSubpath); + builder.setAlias(attributeExpression.value().asAlias()); + return Single.just(Expression.newBuilder().setAttributeExpression(builder).build()); + } +} diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/AttributeMapConverter.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/AttributeMapConverter.java index 6ff799ea..8cd41c2a 100644 --- a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/AttributeMapConverter.java +++ b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/AttributeMapConverter.java @@ -9,12 +9,14 @@ import javax.inject.Inject; import org.hypertrace.core.graphql.attributes.AttributeModel; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.utils.BiConverter; import org.hypertrace.core.graphql.common.utils.CollectorUtils; import org.hypertrace.gateway.service.v1.common.Value; class AttributeMapConverter - implements BiConverter, Map, Map> { + implements BiConverter< + Collection, Map, Map> { private final BiConverter valueConverter; @@ -24,20 +26,24 @@ class AttributeMapConverter } @Override - public Single> convert( + public Single> convert( Collection attributes, Map response) { return Observable.fromIterable(attributes) - .filter(attribute -> !Value.getDefaultInstance().equals(response.get(attribute.alias()))) + .filter(attribute -> !Value.getDefaultInstance().equals(response.get(attribute.asMapKey()))) .flatMapSingle(attribute -> this.buildAttributeMapEntry(attribute, response)) .distinct() .collect(CollectorUtils.immutableMapEntryCollector()); } - private Single> buildAttributeMapEntry( + private Single> buildAttributeMapEntry( AttributeRequest attributeRequest, Map response) { // Uses SimpleImmutableEntry to support null values return this.valueConverter - .convert(response.get(attributeRequest.alias()), attributeRequest.attribute()) - .map(value -> new SimpleImmutableEntry<>(attributeRequest.attribute().key(), value)); + .convert( + response.get(attributeRequest.asMapKey()), + attributeRequest.attributeExpression().attribute()) + .map( + value -> + new SimpleImmutableEntry<>(attributeRequest.attributeExpression().value(), value)); } } diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/ColumnIdentifierExpressionConverter.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/ColumnIdentifierExpressionConverter.java deleted file mode 100644 index 8d3976f5..00000000 --- a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/ColumnIdentifierExpressionConverter.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.hypertrace.core.graphql.utils.gateway; - -import io.reactivex.rxjava3.core.Single; -import javax.inject.Inject; -import org.hypertrace.core.graphql.attributes.AttributeModel; -import org.hypertrace.core.graphql.common.utils.Converter; -import org.hypertrace.gateway.service.v1.common.Expression; -import org.hypertrace.gateway.service.v1.common.Expression.Builder; - -class ColumnIdentifierExpressionConverter implements Converter { - - private final ColumnIdentifierConverter columnIdentifierConverter; - - @Inject - ColumnIdentifierExpressionConverter(ColumnIdentifierConverter columnIdentifierConverter) { - this.columnIdentifierConverter = columnIdentifierConverter; - } - - @Override - public Single convert(AttributeModel attribute) { - return this.columnIdentifierConverter - .convert(attribute) - .map(Expression.newBuilder()::setColumnIdentifier) - .map(Builder::build); - } -} diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/FilterConverter.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/FilterConverter.java index beb62be8..fcc66305 100644 --- a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/FilterConverter.java +++ b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/FilterConverter.java @@ -16,16 +16,16 @@ class FilterConverter implements Converter>, Filter> { - private final ColumnIdentifierExpressionConverter columnIdentifierExpressionConverter; + private final AttributeExpressionConverter attributeExpressionConverter; private final OperatorConverter operatorConverter; private final LiteralConstantExpressionConverter literalConstantExpressionConverter; @Inject FilterConverter( - ColumnIdentifierExpressionConverter columnIdentifierExpressionConverter, + AttributeExpressionConverter attributeExpressionConverter, OperatorConverter operatorConverter, LiteralConstantExpressionConverter literalConstantExpressionConverter) { - this.columnIdentifierExpressionConverter = columnIdentifierExpressionConverter; + this.attributeExpressionConverter = attributeExpressionConverter; this.operatorConverter = operatorConverter; this.literalConstantExpressionConverter = literalConstantExpressionConverter; } @@ -49,7 +49,8 @@ public Single convert(Collection> f private Single buildFilter(AttributeAssociation filter) { return zip( - this.columnIdentifierExpressionConverter.convert(filter.attribute()), + this.attributeExpressionConverter.convert( + AttributeAssociation.of(filter.attribute(), filter.value().keyExpression())), this.operatorConverter.convert(filter.value().operator()), this.literalConstantExpressionConverter.convert(filter.value().value()), (key, operator, value) -> diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/GatewayUtilsModule.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/GatewayUtilsModule.java index bc57c149..169e8b52 100644 --- a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/GatewayUtilsModule.java +++ b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/GatewayUtilsModule.java @@ -11,6 +11,7 @@ import org.hypertrace.core.graphql.attributes.AttributeModel; import org.hypertrace.core.graphql.common.request.AttributeAssociation; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderDirection; @@ -32,7 +33,9 @@ protected void configure() { bind(Key.get( new TypeLiteral< BiConverter< - Collection, Map, Map>>() {})) + Collection, + Map, + Map>>() {})) .to(AttributeMapConverter.class); bind(Key.get(new TypeLiteral>() {})).to(UnwrappedValueConverter.class); @@ -54,8 +57,9 @@ protected void configure() { bind(Key.get(new TypeLiteral>() {})) .to(ColumnIdentifierConverter.class); - bind(Key.get(new TypeLiteral>() {})) - .to(ColumnIdentifierExpressionConverter.class); + bind(Key.get( + new TypeLiteral, Expression>>() {})) + .to(AttributeExpressionConverter.class); bind(Key.get(new TypeLiteral>() {})) .to(LiteralConstantConverter.class); diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/OrderByExpressionListConverter.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/OrderByExpressionListConverter.java index aad30a28..7c637c67 100644 --- a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/OrderByExpressionListConverter.java +++ b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/OrderByExpressionListConverter.java @@ -15,14 +15,14 @@ class OrderByExpressionListConverter implements Converter>, List> { - private final ColumnIdentifierExpressionConverter columnExpressionConverter; + private final AttributeExpressionConverter attributeExpressionConverter; private final SortOrderConverter sortOrderConverter; @Inject OrderByExpressionListConverter( - ColumnIdentifierExpressionConverter columnExpressionConverter, + AttributeExpressionConverter attributeExpressionConverter, SortOrderConverter sortOrderConverter) { - this.columnExpressionConverter = columnExpressionConverter; + this.attributeExpressionConverter = attributeExpressionConverter; this.sortOrderConverter = sortOrderConverter; } @@ -37,7 +37,9 @@ private Single buildOrderByExpression( AttributeAssociation orderArgument) { return zip( this.sortOrderConverter.convert(orderArgument.value().direction()), - this.columnExpressionConverter.convert(orderArgument.attribute()), + this.attributeExpressionConverter.convert( + AttributeAssociation.of( + orderArgument.attribute(), orderArgument.value().resolvedKeyExpression())), (sortOrder, columnExpression) -> OrderByExpression.newBuilder() .setOrder(sortOrder) diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/SelectionExpressionSetConverter.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/SelectionExpressionSetConverter.java index a85645f2..bc9a8644 100644 --- a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/SelectionExpressionSetConverter.java +++ b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/SelectionExpressionSetConverter.java @@ -14,11 +14,11 @@ class SelectionExpressionSetConverter implements Converter, Set> { - private final ColumnIdentifierExpressionConverter columnExpressionConverter; + private final AttributeExpressionConverter attributeExpressionConverter; @Inject - SelectionExpressionSetConverter(ColumnIdentifierExpressionConverter columnExpressionConverter) { - this.columnExpressionConverter = columnExpressionConverter; + SelectionExpressionSetConverter(AttributeExpressionConverter attributeExpressionConverter) { + this.attributeExpressionConverter = attributeExpressionConverter; } @Override @@ -30,11 +30,9 @@ public Single> convert(Collection attributeReq } private Single buildAliasedSelectionExpression(AttributeRequest attributeRequest) { - return this.columnExpressionConverter - .convert(attributeRequest.attribute()) + return this.attributeExpressionConverter + .convert(attributeRequest.attributeExpression()) .map(Expression::toBuilder) - .doOnSuccess( - builder -> builder.getColumnIdentifierBuilder().setAlias(attributeRequest.alias())) .map(Builder::build); } } diff --git a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/UnwrappedValueConverter.java b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/UnwrappedValueConverter.java index ebc99fa6..0f064c52 100644 --- a/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/UnwrappedValueConverter.java +++ b/hypertrace-core-graphql-gateway-service-utils/src/main/java/org/hypertrace/core/graphql/utils/gateway/UnwrappedValueConverter.java @@ -62,7 +62,7 @@ private Single handleTimestamp(Value value, AttributeModel attributeMode if ("ns".equals(attributeModel.units())) { return Single.just(Instant.ofEpochSecond(0, value.getTimestamp())); } - return Single.just((Instant.ofEpochMilli(value.getTimestamp()))); + return Single.just(Instant.ofEpochMilli(value.getTimestamp())); } catch (Throwable t) { return Single.error(t); } diff --git a/hypertrace-core-graphql-gateway-service-utils/src/test/java/org/hypertrace/core/graphql/utils/gateway/AttributeExpressionConverterTest.java b/hypertrace-core-graphql-gateway-service-utils/src/test/java/org/hypertrace/core/graphql/utils/gateway/AttributeExpressionConverterTest.java new file mode 100644 index 00000000..49d63259 --- /dev/null +++ b/hypertrace-core-graphql-gateway-service-utils/src/test/java/org/hypertrace/core/graphql/utils/gateway/AttributeExpressionConverterTest.java @@ -0,0 +1,59 @@ +package org.hypertrace.core.graphql.utils.gateway; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import org.hypertrace.core.graphql.attributes.AttributeModel; +import org.hypertrace.core.graphql.common.request.AttributeAssociation; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; +import org.hypertrace.gateway.service.v1.common.Expression; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AttributeExpressionConverterTest { + @Mock AttributeModel mockAttribute; + + @Test + void convertsAttributeExpressionWithSubpath() { + org.hypertrace.gateway.service.v1.common.AttributeExpression expectedAttributeExpression = + org.hypertrace.gateway.service.v1.common.AttributeExpression.newBuilder() + .setAttributeId("EVENT.tags") + .setAlias("tags.spantag") + .setSubpath("spantag") + .build(); + + when(mockAttribute.id()).thenReturn("EVENT.tags"); + AttributeExpressionConverter attributeExpressionConverter = new AttributeExpressionConverter(); + + assertEquals( + Expression.newBuilder().setAttributeExpression(expectedAttributeExpression).build(), + attributeExpressionConverter + .convert( + AttributeAssociation.of( + this.mockAttribute, new AttributeExpression("tags", "spantag"))) + .blockingGet()); + } + + @Test + void convertsAttributeExpressionWithoutSubpath() { + org.hypertrace.gateway.service.v1.common.AttributeExpression expectedAttributeExpression = + org.hypertrace.gateway.service.v1.common.AttributeExpression.newBuilder() + .setAttributeId("EVENT.tags") + .setAlias("tags") + .build(); + + when(mockAttribute.id()).thenReturn("EVENT.tags"); + AttributeExpressionConverter attributeExpressionConverter = new AttributeExpressionConverter(); + + assertEquals( + Expression.newBuilder().setAttributeExpression(expectedAttributeExpression).build(), + attributeExpressionConverter + .convert( + AttributeAssociation.of( + this.mockAttribute, AttributeExpression.forAttributeKey("tags"))) + .blockingGet()); + } +} diff --git a/hypertrace-core-graphql-gateway-service-utils/src/test/java/org/hypertrace/core/graphql/utils/gateway/ColumnIdentifierExpressionConverterTest.java b/hypertrace-core-graphql-gateway-service-utils/src/test/java/org/hypertrace/core/graphql/utils/gateway/ColumnIdentifierExpressionConverterTest.java deleted file mode 100644 index e0ae38d8..00000000 --- a/hypertrace-core-graphql-gateway-service-utils/src/test/java/org/hypertrace/core/graphql/utils/gateway/ColumnIdentifierExpressionConverterTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.hypertrace.core.graphql.utils.gateway; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -import io.reactivex.rxjava3.core.Single; -import org.hypertrace.core.graphql.attributes.AttributeModel; -import org.hypertrace.gateway.service.v1.common.ColumnIdentifier; -import org.hypertrace.gateway.service.v1.common.Expression; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class ColumnIdentifierExpressionConverterTest { - @Mock ColumnIdentifierConverter mockColumnIdentifierConverter; - @Mock AttributeModel mockAttribute; - - @Test - void convertsColumnBasedOnAttributeId() { - ColumnIdentifier expectedColumnIdentifier = - ColumnIdentifier.newBuilder().setColumnName("foo").build(); - ColumnIdentifierExpressionConverter columnIdentifierExpressionConverter = - new ColumnIdentifierExpressionConverter(this.mockColumnIdentifierConverter); - - when(this.mockColumnIdentifierConverter.convert(any())) - .thenReturn(Single.just(expectedColumnIdentifier)); - - assertEquals( - Expression.newBuilder().setColumnIdentifier(expectedColumnIdentifier).build(), - columnIdentifierExpressionConverter.convert(this.mockAttribute).blockingGet()); - } -} diff --git a/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverter.java b/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverter.java index 4c98fe09..a5aa2981 100644 --- a/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverter.java +++ b/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverter.java @@ -8,6 +8,7 @@ import javax.inject.Inject; import lombok.experimental.Accessors; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.utils.BiConverter; import org.hypertrace.core.graphql.log.event.request.LogEventRequest; import org.hypertrace.core.graphql.log.event.schema.LogEvent; @@ -17,12 +18,14 @@ class GatewayServiceLogEventsResponseConverter { - private final BiConverter, Map, Map> + private final BiConverter< + Collection, Map, Map> attributeMapConverter; @Inject GatewayServiceLogEventsResponseConverter( - BiConverter, Map, Map> + BiConverter< + Collection, Map, Map> attributeMapConverter) { this.attributeMapConverter = attributeMapConverter; } @@ -47,11 +50,11 @@ private Single convert( @lombok.Value @Accessors(fluent = true) private static class ConvertedLogEvent implements LogEvent { - Map attributeValues; + Map attributeValues; @Override - public Object attribute(String key) { - return this.attributeValues.get(key); + public Object attribute(AttributeExpression attributeExpression) { + return this.attributeValues.get(attributeExpression); } } diff --git a/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/LogEventDaoModule.java b/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/LogEventDaoModule.java index e71ecb86..e221a361 100644 --- a/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/LogEventDaoModule.java +++ b/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/dao/LogEventDaoModule.java @@ -10,6 +10,7 @@ import java.util.Set; import org.hypertrace.core.graphql.common.request.AttributeAssociation; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.common.utils.BiConverter; @@ -51,6 +52,8 @@ protected void configure() { Key.get( new TypeLiteral< BiConverter< - Collection, Map, Map>>() {})); + Collection, + Map, + Map>>() {})); } } diff --git a/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/request/DefaultLogEventRequestBuilder.java b/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/request/DefaultLogEventRequestBuilder.java index 68d87883..afb74e78 100644 --- a/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/request/DefaultLogEventRequestBuilder.java +++ b/hypertrace-core-graphql-log-event-schema/src/main/java/org/hypertrace/core/graphql/log/event/request/DefaultLogEventRequestBuilder.java @@ -105,7 +105,11 @@ Single build( context, requestScope, getAttributeQueryableFields(selectionSet)) .collect(Collectors.toUnmodifiableSet()), this.attributeAssociator - .associateAttributes(context, requestScope, requestedOrders, OrderArgument::key) + .associateAttributes( + context, + requestScope, + requestedOrders, + arg -> arg.resolvedKeyExpression().key()) .collect(Collectors.toUnmodifiableList()), this.filterRequestBuilder.build(context, requestScope, requestedFilters), (attributeRequests, orders, filters) -> diff --git a/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/BaseDaoTest.java b/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/BaseDaoTest.java index 6493d6d5..04c02b8c 100644 --- a/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/BaseDaoTest.java +++ b/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/BaseDaoTest.java @@ -12,6 +12,7 @@ import org.hypertrace.core.graphql.common.request.AttributeAssociation; import org.hypertrace.core.graphql.common.request.AttributeRequest; import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.context.GraphQlRequestContext; @@ -35,18 +36,12 @@ static class DefaultLogEventRequest implements LogEventRequest { @Value @Accessors(fluent = true) static class DefaultAttributeRequest implements AttributeRequest { - - AttributeModel attribute; - - @Override - public String alias() { - return attribute.id(); - } + AttributeAssociation attributeExpression; } @Value @Accessors(fluent = true) - class DefaultAttributeModel implements AttributeModel { + static class DefaultAttributeModel implements AttributeModel { String id; String scope; @@ -62,7 +57,7 @@ class DefaultAttributeModel implements AttributeModel { @Value @Accessors(fluent = true) - class DefaultTimeRange implements TimeRangeArgument { + static class DefaultTimeRange implements TimeRangeArgument { @JsonProperty(TIME_RANGE_ARGUMENT_START_TIME) Instant startTime; diff --git a/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsRequestBuilderTest.java b/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsRequestBuilderTest.java index 53aa4bff..3f07d071 100644 --- a/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsRequestBuilderTest.java +++ b/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsRequestBuilderTest.java @@ -18,6 +18,7 @@ import org.hypertrace.core.graphql.attributes.AttributeModelType; import org.hypertrace.core.graphql.common.request.AttributeAssociation; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.common.utils.Converter; @@ -77,29 +78,33 @@ void testBuildRequest() { Collection attributeRequests = List.of( new DefaultAttributeRequest( - new DefaultAttributeModel( - "traceId", - "LOG_EVENT", - "traceId", - "Trace Id", - AttributeModelType.STRING, - "", - false, - false, - Collections.emptyList(), - false)), + AttributeAssociation.of( + new DefaultAttributeModel( + "traceId", + "LOG_EVENT", + "traceId", + "Trace Id", + AttributeModelType.STRING, + "", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("traceId"))), new DefaultAttributeRequest( - new DefaultAttributeModel( - "timestamp", - "LOG_EVENT", - "timestamp", - "Timestamp", - AttributeModelType.TIMESTAMP, - "", - false, - false, - Collections.emptyList(), - false))); + AttributeAssociation.of( + new DefaultAttributeModel( + "timestamp", + "LOG_EVENT", + "timestamp", + "Timestamp", + AttributeModelType.TIMESTAMP, + "", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("timestamp")))); DefaultLogEventRequest defaultLogEventRequest = new DefaultLogEventRequest( null, diff --git a/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverterTest.java b/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverterTest.java index f8255252..a73b9669 100644 --- a/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverterTest.java +++ b/hypertrace-core-graphql-log-event-schema/src/test/java/org/hypertrace/core/graphql/log/event/dao/GatewayServiceLogEventsResponseConverterTest.java @@ -16,7 +16,9 @@ import java.util.List; import java.util.Map; import org.hypertrace.core.graphql.attributes.AttributeModelType; +import org.hypertrace.core.graphql.common.request.AttributeAssociation; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.utils.BiConverter; import org.hypertrace.core.graphql.log.event.schema.LogEventResultSet; import org.hypertrace.core.graphql.spi.config.GraphQlServiceConfig; @@ -46,7 +48,7 @@ protected void configure() { bind(GrpcChannelRegistry.class).toInstance(mock(GrpcChannelRegistry.class)); } }); - BiConverter, Map, Map> + BiConverter, Map, Map> attributeMapConverter = injector.getInstance( Key.get( @@ -54,7 +56,7 @@ protected void configure() { BiConverter< Collection, Map, - Map>>() {})); + Map>>() {})); responseConverter = new GatewayServiceLogEventsResponseConverter(attributeMapConverter); } @@ -82,29 +84,33 @@ void testConvert() { Collection attributeRequests = List.of( new DefaultAttributeRequest( - new DefaultAttributeModel( - "traceId", - "LOG_EVENT", - "traceId", - "Trace Id", - AttributeModelType.STRING, - "", - false, - false, - Collections.emptyList(), - false)), + AttributeAssociation.of( + new DefaultAttributeModel( + "traceId", + "LOG_EVENT", + "traceId", + "Trace Id", + AttributeModelType.STRING, + "", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("traceId"))), new DefaultAttributeRequest( - new DefaultAttributeModel( - "timestamp", - "LOG_EVENT", - "timestamp", - "Timestamp", - AttributeModelType.TIMESTAMP, - "ns", - false, - false, - Collections.emptyList(), - false))); + AttributeAssociation.of( + new DefaultAttributeModel( + "timestamp", + "LOG_EVENT", + "timestamp", + "Timestamp", + AttributeModelType.TIMESTAMP, + "ns", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("timestamp")))); DefaultLogEventRequest defaultLogEventRequest = new DefaultLogEventRequest( null, @@ -117,9 +123,17 @@ void testConvert() { LogEventResultSet logEventResultSet = responseConverter.convert(defaultLogEventRequest, logEventsResponse).blockingGet(); assertEquals(1, logEventResultSet.results().size()); - assertEquals("trace1", logEventResultSet.results().get(0).attribute("traceId")); + assertEquals( + "trace1", + logEventResultSet + .results() + .get(0) + .attribute(AttributeExpression.forAttributeKey("traceId"))); assertEquals( Instant.ofEpochSecond(0, Duration.ofMillis(startTime).toNanos()), - logEventResultSet.results().get(0).attribute("timestamp")); + logEventResultSet + .results() + .get(0) + .attribute(AttributeExpression.forAttributeKey("timestamp"))); } } diff --git a/hypertrace-core-graphql-platform/build.gradle.kts b/hypertrace-core-graphql-platform/build.gradle.kts index b1da87bd..e53fb0ae 100644 --- a/hypertrace-core-graphql-platform/build.gradle.kts +++ b/hypertrace-core-graphql-platform/build.gradle.kts @@ -5,10 +5,10 @@ plugins { dependencies { constraints { - api("org.hypertrace.core.grpcutils:grpc-context-utils:0.6.2") - api("org.hypertrace.core.grpcutils:grpc-client-utils:0.6.2") - api("org.hypertrace.core.grpcutils:grpc-client-rx-utils:0.6.2") - api("org.hypertrace.gateway.service:gateway-service-api:0.1.170") + api("org.hypertrace.core.grpcutils:grpc-context-utils:0.7.0") + api("org.hypertrace.core.grpcutils:grpc-client-utils:0.7.0") + api("org.hypertrace.core.grpcutils:grpc-client-rx-utils:0.7.0") + api("org.hypertrace.gateway.service:gateway-service-api:0.2.0") api("org.hypertrace.core.attribute.service:caching-attribute-service-client:0.13.6") api("com.google.inject:guice:4.2.3") @@ -21,12 +21,12 @@ dependencies { api("org.projectlombok:lombok:1.18.18") api("com.google.code.findbugs:jsr305:3.0.2") api("com.typesafe:config:1.4.1") - api("com.google.guava:guava:30.1-jre") + api("com.google.guava:guava:31.0.1-jre") api("com.graphql-java-kickstart:graphql-java-servlet:10.1.0") - api("io.grpc:grpc-api:1.42.0") - api("io.grpc:grpc-core:1.42.0") - api("io.grpc:grpc-stub:1.42.0") - api("io.grpc:grpc-context:1.42.0") + api("io.grpc:grpc-api:1.43.1") + api("io.grpc:grpc-core:1.43.1") + api("io.grpc:grpc-stub:1.43.1") + api("io.grpc:grpc-context:1.43.1") api("com.fasterxml.jackson.core:jackson-databind:2.12.6") api("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.6") api("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.12.6") @@ -37,7 +37,7 @@ dependencies { } runtime("org.apache.logging.log4j:log4j-slf4j-impl:2.17.1") - runtime("io.grpc:grpc-netty:1.42.0") + runtime("io.grpc:grpc-netty:1.43.1") runtime("io.netty:netty-codec-http2:4.1.71.Final") runtime("io.netty:netty-handler-proxy:4.1.71.Final") } diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/GatewayServiceSpanConverter.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/GatewayServiceSpanConverter.java index 25f43029..64587153 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/GatewayServiceSpanConverter.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/GatewayServiceSpanConverter.java @@ -9,6 +9,7 @@ import javax.inject.Inject; import lombok.experimental.Accessors; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.utils.BiConverter; import org.hypertrace.core.graphql.log.event.schema.LogEvent; import org.hypertrace.core.graphql.log.event.schema.LogEventResultSet; @@ -20,12 +21,14 @@ class GatewayServiceSpanConverter { - private final BiConverter, Map, Map> + private final BiConverter< + Collection, Map, Map> attributeMapConverter; @Inject GatewayServiceSpanConverter( - BiConverter, Map, Map> + BiConverter< + Collection, Map, Map> attributeMapConverter) { this.attributeMapConverter = attributeMapConverter; } @@ -47,7 +50,8 @@ private Single convert( attrMap -> new ConvertedSpan( attrMap - .get(request.spanEventsRequest().idAttribute().attribute().key()) + .get( + request.spanEventsRequest().idAttribute().attributeExpression().value()) .toString(), attrMap, spanIdToLogEvents)); @@ -57,12 +61,12 @@ private Single convert( @Accessors(fluent = true) private static class ConvertedSpan implements Span { String id; - Map attributeValues; + Map attributeValues; Map> spanIdToLogEvents; @Override - public Object attribute(String key) { - return this.attributeValues.get(key); + public Object attribute(AttributeExpression attributeExpression) { + return this.attributeValues.get(attributeExpression); } @Override diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanDaoModule.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanDaoModule.java index b3048f2a..ce172161 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanDaoModule.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanDaoModule.java @@ -11,7 +11,9 @@ import org.hypertrace.core.graphql.attributes.AttributeStore; import org.hypertrace.core.graphql.common.request.AttributeAssociation; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.request.AttributeRequestBuilder; import org.hypertrace.core.graphql.common.request.FilterRequestBuilder; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.common.utils.BiConverter; @@ -42,6 +44,7 @@ protected void configure() { requireBinding(ArgumentDeserializer.class); requireBinding(AttributeStore.class); requireBinding(RequestTransformer.class); + requireBinding(AttributeRequestBuilder.class); requireBinding( Key.get(new TypeLiteral, Set>>() {})); @@ -61,6 +64,8 @@ protected void configure() { Key.get( new TypeLiteral< BiConverter< - Collection, Map, Map>>() {})); + Collection, + Map, + Map>>() {})); } } diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventDao.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventDao.java index f714ddec..f49d92e0 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventDao.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventDao.java @@ -50,7 +50,8 @@ Single fetchLogEvents( SpanRequest gqlRequest, SpansResponse spansResponse) { if (null == gqlRequest.spanEventsRequest().idAttribute() || null == gqlRequest.logEventAttributes() - || gqlRequest.logEventAttributes().isEmpty()) { + || gqlRequest.logEventAttributes().isEmpty() + || spansResponse.getSpansList().isEmpty()) { return Single.just(new SpanLogEventsResponse(spansResponse, Map.of())); } return spanLogEventRequestBuilder diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilder.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilder.java index 6f51c80e..ddeaaa97 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilder.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilder.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; +import lombok.Value; import lombok.experimental.Accessors; import org.hypertrace.core.graphql.attributes.AttributeStore; import org.hypertrace.core.graphql.atttributes.scopes.HypertraceCoreAttributeScopeString; @@ -18,6 +19,7 @@ import org.hypertrace.core.graphql.common.request.AttributeRequestBuilder; import org.hypertrace.core.graphql.common.request.FilterRequestBuilder; import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterOperatorType; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterType; @@ -92,7 +94,7 @@ private Single>> buildLogEventsQueryFi spanEvent -> spanEvent .getAttributesMap() - .get(gqlRequest.spanEventsRequest().idAttribute().attribute().id()) + .get(gqlRequest.spanEventsRequest().idAttribute().asMapKey()) .getString()) .collect(Collectors.toList()); @@ -102,12 +104,12 @@ private Single>> buildLogEventsQueryFi Set.of(new LogEventFilter(spanIds))); } - @lombok.Value + @Value @Accessors(fluent = true) private static class LogEventFilter implements FilterArgument { - FilterType type = FilterType.ID; String key = null; + AttributeExpression keyExpression = null; FilterOperatorType operator = FilterOperatorType.IN; Collection value; AttributeScope idType = null; diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverter.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverter.java index 938c84df..3da5cc1b 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverter.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverter.java @@ -7,10 +7,11 @@ import java.util.stream.Collectors; import javax.inject.Inject; import lombok.experimental.Accessors; -import org.hypertrace.core.graphql.attributes.AttributeModel; import org.hypertrace.core.graphql.attributes.AttributeStore; import org.hypertrace.core.graphql.atttributes.scopes.HypertraceCoreAttributeScopeString; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.request.AttributeRequestBuilder; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.utils.BiConverter; import org.hypertrace.core.graphql.context.GraphQlRequestContext; import org.hypertrace.core.graphql.log.event.schema.LogEvent; @@ -20,17 +21,22 @@ class SpanLogEventResponseConverter { - private final BiConverter, Map, Map> + private final BiConverter< + Collection, Map, Map> attributeMapConverter; private final AttributeStore attributeStore; + private final AttributeRequestBuilder attributeRequestBuilder; @Inject SpanLogEventResponseConverter( - BiConverter, Map, Map> + BiConverter< + Collection, Map, Map> attributeMapConverter, - AttributeStore attributeStore) { + AttributeStore attributeStore, + AttributeRequestBuilder attributeRequestBuilder) { this.attributeMapConverter = attributeMapConverter; this.attributeStore = attributeStore; + this.attributeRequestBuilder = attributeRequestBuilder; } Single buildResponse( @@ -43,19 +49,21 @@ Single buildResponse( graphQlRequestContext, HypertraceCoreAttributeScopeString.LOG_EVENT, HypertraceCoreAttributeScopeString.SPAN) + .map(this.attributeRequestBuilder::buildForAttribute) .flatMap( - spanId -> buildResponse(spanId, attributeRequests, spansResponse, logEventsResponse)); + spanIdRequest -> + buildResponse(spanIdRequest, attributeRequests, spansResponse, logEventsResponse)); } private Single buildResponse( - AttributeModel foreignIdAttribute, + AttributeRequest foreignIdAttributeRequest, Collection attributeRequests, SpansResponse spansResponse, LogEventsResponse logEventsResponse) { return Observable.fromIterable(logEventsResponse.getLogEventsList()) .concatMapSingle( logEventsResponseVar -> - this.convert(foreignIdAttribute, attributeRequests, logEventsResponseVar)) + this.convert(foreignIdAttributeRequest, attributeRequests, logEventsResponseVar)) .collect( Collectors.groupingBy( SpanLogEventPair::spanId, @@ -65,7 +73,7 @@ private Single buildResponse( } private Single convert( - AttributeModel foreignIdAttribute, + AttributeRequest foreignIdAttributeRequest, Collection request, org.hypertrace.gateway.service.v1.log.events.LogEvent logEvent) { return this.attributeMapConverter @@ -73,7 +81,10 @@ private Single convert( .map( attributeMap -> new SpanLogEventPair( - logEvent.getAttributesMap().get(foreignIdAttribute.id()).getString(), + logEvent + .getAttributesMap() + .get(foreignIdAttributeRequest.asMapKey()) + .getString(), new ConvertedLogEvent(attributeMap))); } @@ -89,11 +100,11 @@ private static class SpanLogEventPair { private static class ConvertedLogEvent implements org.hypertrace.core.graphql.log.event.schema.LogEvent { - Map attributeValues; + Map attributeValues; @Override - public Object attribute(String key) { - return this.attributeValues.get(key); + public Object attribute(AttributeExpression attributeExpression) { + return this.attributeValues.get(attributeExpression); } } } diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpan.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpan.java index 5d83537e..68ed97dd 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpan.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpan.java @@ -1,5 +1,6 @@ package org.hypertrace.core.graphql.span.export; +import static org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression.forAttributeKey; import static org.hypertrace.core.graphql.span.export.ExportSpanConstants.SpanTagsKey.SERVICE_NAME_KEY; import static org.hypertrace.core.graphql.span.export.ExportSpanConstants.SpanTagsKey.SPAN_KIND; @@ -17,24 +18,27 @@ import io.opentelemetry.proto.trace.v1.Status.StatusCode; import java.time.Duration; import java.time.Instant; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import lombok.Value; import lombok.experimental.Accessors; import org.hypertrace.core.graphql.log.event.schema.LogEvent; import org.hypertrace.core.graphql.span.export.ExportSpanConstants.LogEventAttributes; import org.hypertrace.core.graphql.span.export.ExportSpanConstants.SpanAttributes; import org.hypertrace.core.graphql.span.export.ExportSpanConstants.SpanTagsKey; -@lombok.Value +@Value @Accessors(fluent = true) @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class ExportSpan { - private final ResourceSpans resourceSpans; + ResourceSpans resourceSpans; public static class Builder { @@ -45,46 +49,49 @@ public Builder(org.hypertrace.core.graphql.span.schema.Span span) { } private void setResourceServiceName(Resource.Builder resourceBuilder) { - if (span.attribute(SpanAttributes.SERVICE_NAME) != null) { - String serviceName = span.attribute(SpanAttributes.SERVICE_NAME).toString(); - KeyValue keyValue = - KeyValue.newBuilder() - .setKey(SERVICE_NAME_KEY) - .setValue(AnyValue.newBuilder().setStringValue(serviceName).build()) - .build(); - resourceBuilder.addAttributes(keyValue); - } + Optional.ofNullable(span.attribute(forAttributeKey(SpanAttributes.SERVICE_NAME))) + .map(Object::toString) + .map( + serviceName -> + KeyValue.newBuilder() + .setKey(SERVICE_NAME_KEY) + .setValue(AnyValue.newBuilder().setStringValue(serviceName))) + .map(KeyValue.Builder::build) + .ifPresent(resourceBuilder::addAttributes); } private void setBytesFields(Span.Builder spanBuilder) { byte[] spanIdBytes = BaseEncoding.base64().decode(span.id()); spanBuilder.setSpanId(ByteString.copyFrom(spanIdBytes)); - String traceId = span.attribute(SpanAttributes.TRACE_ID).toString(); + String traceId = span.attribute(forAttributeKey(SpanAttributes.TRACE_ID)).toString(); byte[] traceIdBytes = BaseEncoding.base64().decode(traceId); spanBuilder.setTraceId(ByteString.copyFrom(traceIdBytes)); byte[] parentSpanIdBytes = BaseEncoding.base64().decode(""); - if (span.attribute(SpanAttributes.PARENT_SPAN_ID) != null) { + if (span.attribute(forAttributeKey(SpanAttributes.PARENT_SPAN_ID)) != null) { parentSpanIdBytes = - BaseEncoding.base64().decode(span.attribute(SpanAttributes.PARENT_SPAN_ID).toString()); + BaseEncoding.base64() + .decode(span.attribute(forAttributeKey(SpanAttributes.PARENT_SPAN_ID)).toString()); } spanBuilder.setParentSpanId(ByteString.copyFrom(parentSpanIdBytes)); } private void setTimeFields(Span.Builder spanBuilder) { - long startTime = Long.parseLong(span.attribute(SpanAttributes.START_TIME).toString()); + long startTime = + Long.parseLong(span.attribute(forAttributeKey(SpanAttributes.START_TIME)).toString()); spanBuilder.setStartTimeUnixNano( TimeUnit.NANOSECONDS.convert(startTime, TimeUnit.MILLISECONDS)); - long endTime = Long.parseLong(span.attribute(SpanAttributes.END_TIME).toString()); + long endTime = + Long.parseLong(span.attribute(forAttributeKey(SpanAttributes.END_TIME)).toString()); spanBuilder.setEndTimeUnixNano(TimeUnit.NANOSECONDS.convert(endTime, TimeUnit.MILLISECONDS)); } private void setName(Span.Builder spanBuilder) { - if (span.attribute(SpanAttributes.NAME) != null) { - spanBuilder.setName(span.attribute(SpanAttributes.NAME).toString()); - } + Optional.ofNullable(span.attribute(forAttributeKey(SpanAttributes.NAME))) + .map(Object::toString) + .ifPresent(spanBuilder::setName); } private static void setAttributes(Span.Builder spanBuilder, Map tags) { @@ -104,7 +111,7 @@ private static void setAttributes(Span.Builder spanBuilder, Map private static void setStatusCode(Span.Builder spanBuilder, Map tags) { int statusCode = SpanTagsKey.STATUS_CODE_KEYS.stream() - .filter(e -> tags.containsKey(e)) + .filter(tags::containsKey) .map(e -> Integer.parseInt(tags.get(e))) .findFirst() .orElse(0); @@ -130,14 +137,17 @@ private static void setLogEvent(Span.Builder spanBuilder, List logEven Duration.between( Instant.EPOCH, Instant.parse( - logEvent.attribute(LogEventAttributes.TIMESTAMP).toString())) + logEvent + .attribute(forAttributeKey(LogEventAttributes.TIMESTAMP)) + .toString())) .toNanos(); eventBuilder.setTimeUnixNano(timeNanos); Map logEventAttributes = - logEvent.attribute(LogEventAttributes.ATTRIBUTES) != null - ? (Map) logEvent.attribute(LogEventAttributes.ATTRIBUTES) - : Map.of(); + Optional.ofNullable( + logEvent.attribute(forAttributeKey(LogEventAttributes.ATTRIBUTES))) + .map(value -> (Map) value) + .orElseGet(Collections::emptyMap); List attributes = logEventAttributes.entrySet().stream() .map( @@ -167,9 +177,9 @@ public ExportSpan build() { setName(spanBuilder); Map tags = - span.attribute(SpanAttributes.TAGS) != null - ? (Map) span.attribute(SpanAttributes.TAGS) - : Map.of(); + Optional.ofNullable(span.attribute(forAttributeKey(SpanAttributes.TAGS))) + .map(value -> (Map) value) + .orElseGet(Collections::emptyMap); setStatusCode(spanBuilder, tags); setSpanKind(spanBuilder, tags); setAttributes(spanBuilder, tags); diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpanConstants.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpanConstants.java index 36c8658d..2db0b47d 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpanConstants.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/export/ExportSpanConstants.java @@ -1,6 +1,7 @@ package org.hypertrace.core.graphql.span.export; import java.util.List; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; public interface ExportSpanConstants { interface SpanAttributes { @@ -13,16 +14,16 @@ interface SpanAttributes { String NAME = "displaySpanName"; String TAGS = "spanTags"; - List SPAN_ATTRIBUTES = + List SPAN_ATTRIBUTES = List.of( - SpanAttributes.ID, - SpanAttributes.SERVICE_NAME, - SpanAttributes.TRACE_ID, - SpanAttributes.PARENT_SPAN_ID, - SpanAttributes.START_TIME, - SpanAttributes.END_TIME, - SpanAttributes.NAME, - SpanAttributes.TAGS); + AttributeExpression.forAttributeKey(SpanAttributes.ID), + AttributeExpression.forAttributeKey(SpanAttributes.SERVICE_NAME), + AttributeExpression.forAttributeKey(SpanAttributes.TRACE_ID), + AttributeExpression.forAttributeKey(SpanAttributes.PARENT_SPAN_ID), + AttributeExpression.forAttributeKey(SpanAttributes.START_TIME), + AttributeExpression.forAttributeKey(SpanAttributes.END_TIME), + AttributeExpression.forAttributeKey(SpanAttributes.NAME), + AttributeExpression.forAttributeKey(SpanAttributes.TAGS)); } interface SpanTagsKey { @@ -39,7 +40,9 @@ interface LogEventAttributes { String TIMESTAMP = "timestamp"; String ATTRIBUTES = "attributes"; - List LOG_EVENT_ATTRIBUTES = - List.of(LogEventAttributes.TIMESTAMP, LogEventAttributes.ATTRIBUTES); + List LOG_EVENT_ATTRIBUTES = + List.of( + AttributeExpression.forAttributeKey(LogEventAttributes.TIMESTAMP), + AttributeExpression.forAttributeKey(LogEventAttributes.ATTRIBUTES)); } } diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/DefaultSpanRequestBuilder.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/DefaultSpanRequestBuilder.java index 812a9a38..2cae9188 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/DefaultSpanRequestBuilder.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/DefaultSpanRequestBuilder.java @@ -14,6 +14,7 @@ import org.hypertrace.core.graphql.common.request.AttributeRequest; import org.hypertrace.core.graphql.common.request.ResultSetRequest; import org.hypertrace.core.graphql.common.request.ResultSetRequestBuilder; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.context.GraphQlRequestContext; @@ -51,12 +52,12 @@ public Single build( public Single build( GraphQlRequestContext context, Map arguments, - List spanAttributes, - List logAttributes) { + List spanAttributeExpressions, + List logAttributeExpressions) { return zip( resultSetRequestBuilder.build( - context, HypertraceCoreAttributeScopeString.SPAN, arguments, spanAttributes), - logEventAttributeRequestBuilder.buildAttributeRequest(context, logAttributes), + context, HypertraceCoreAttributeScopeString.SPAN, arguments, spanAttributeExpressions), + logEventAttributeRequestBuilder.buildAttributeRequest(context, logAttributeExpressions), (resultSetRequest, logEventAttributeRequest) -> new DefaultSpanRequest(context, resultSetRequest, logEventAttributeRequest)); } diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/LogEventAttributeRequestBuilder.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/LogEventAttributeRequestBuilder.java index d43a5ad5..7956f2ae 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/LogEventAttributeRequestBuilder.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/LogEventAttributeRequestBuilder.java @@ -15,6 +15,7 @@ import org.hypertrace.core.graphql.atttributes.scopes.HypertraceCoreAttributeScopeString; import org.hypertrace.core.graphql.common.request.AttributeRequest; import org.hypertrace.core.graphql.common.request.AttributeRequestBuilder; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.context.GraphQlRequestContext; import org.hypertrace.core.graphql.utils.schema.GraphQlSelectionFinder; import org.hypertrace.core.graphql.utils.schema.SelectionQuery; @@ -42,13 +43,13 @@ Single> buildAttributeRequest( } Single> buildAttributeRequest( - GraphQlRequestContext context, List attributes) { - return Observable.fromIterable(attributes) + GraphQlRequestContext context, List attributeExpressions) { + return Observable.fromIterable(attributeExpressions) .distinct() .flatMapSingle( - attributeKey -> - this.attributeRequestBuilder.buildForKey( - context, HypertraceCoreAttributeScopeString.LOG_EVENT, attributeKey)) + attributeExpression -> + this.attributeRequestBuilder.buildForAttributeExpression( + context, HypertraceCoreAttributeScopeString.LOG_EVENT, attributeExpression)) .collect(Collectors.toUnmodifiableSet()); } diff --git a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/SpanRequestBuilder.java b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/SpanRequestBuilder.java index 64cf189b..86f107f1 100644 --- a/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/SpanRequestBuilder.java +++ b/hypertrace-core-graphql-span-schema/src/main/java/org/hypertrace/core/graphql/span/request/SpanRequestBuilder.java @@ -4,6 +4,7 @@ import io.reactivex.rxjava3.core.Single; import java.util.List; import java.util.Map; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.context.GraphQlRequestContext; public interface SpanRequestBuilder { @@ -16,6 +17,6 @@ Single build( Single build( GraphQlRequestContext context, Map arguments, - List spanAttributes, - List logAttributes); + List spanAttributeExpressions, + List logAttributeExpressions); } diff --git a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/DaoTestUtil.java b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/DaoTestUtil.java index d733c1e2..e793d0bd 100644 --- a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/DaoTestUtil.java +++ b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/DaoTestUtil.java @@ -17,6 +17,7 @@ import org.hypertrace.core.graphql.common.request.ResultSetRequest; import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument; import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterOperatorType; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterType; @@ -34,19 +35,12 @@ class DaoTestUtil { @Value @Accessors(fluent = true) static class DefaultAttributeRequest implements AttributeRequest { - - AttributeModel attribute; - - @Override - public String alias() { - return attribute.id(); - } + AttributeAssociation attributeExpression; } @Value @Accessors(fluent = true) static class DefaultAttributeModel implements AttributeModel { - String id; String scope; String key; @@ -62,9 +56,9 @@ static class DefaultAttributeModel implements AttributeModel { @Value @Accessors(fluent = true) static class NormalizedFilter implements FilterArgument { - FilterType type = FilterType.ATTRIBUTE; - String key; + String key = null; + AttributeExpression keyExpression; FilterOperatorType operator; Object value; String idScope = null; @@ -82,7 +76,6 @@ static class DefaultSpanRequest implements SpanRequest { @Value @Accessors(fluent = true) static class DefaultResultSetRequest implements ResultSetRequest { - GraphQlRequestContext context; Collection attributes; TimeRangeArgument timeRange; @@ -107,59 +100,67 @@ static class DefaultTimeRange implements TimeRangeArgument { static AttributeRequest traceIdAttribute = new DefaultAttributeRequest( - new DefaultAttributeModel( - "traceId", - "LOG_EVENT", - "traceId", - "Trace Id", - AttributeModelType.STRING, - "", - false, - false, - Collections.emptyList(), - false)); + AttributeAssociation.of( + new DefaultAttributeModel( + "traceId", + "LOG_EVENT", + "traceId", + "Trace Id", + AttributeModelType.STRING, + "", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("traceId"))); static AttributeRequest spanIdAttribute = new DefaultAttributeRequest( - new DefaultAttributeModel( - "spanId", - "LOG_EVENT", - "spanId", - "Span Id", - AttributeModelType.STRING, - "", - false, - false, - Collections.emptyList(), - false)); + AttributeAssociation.of( + new DefaultAttributeModel( + "spanId", + "LOG_EVENT", + "spanId", + "Span Id", + AttributeModelType.STRING, + "", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("spanId"))); static AttributeRequest attributesAttribute = new DefaultAttributeRequest( - new DefaultAttributeModel( - "attributes", - "LOG_EVENT", - "attributes", - "Attributes", - AttributeModelType.STRING, - "", - false, - false, - Collections.emptyList(), - false)); + AttributeAssociation.of( + new DefaultAttributeModel( + "attributes", + "LOG_EVENT", + "attributes", + "Attributes", + AttributeModelType.STRING, + "", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("attributes"))); static AttributeRequest eventIdAttribute = new DefaultAttributeRequest( - new DefaultAttributeModel( - "id", - "EVENT", - "id", - "Id", - AttributeModelType.STRING, - "", - false, - false, - Collections.emptyList(), - false)); + AttributeAssociation.of( + new DefaultAttributeModel( + "id", + "EVENT", + "id", + "Id", + AttributeModelType.STRING, + "", + false, + false, + Collections.emptyList(), + false), + AttributeExpression.forAttributeKey("id"))); static SpansResponse spansResponse = SpansResponse.newBuilder() diff --git a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilderTest.java b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilderTest.java index c525371b..a7140512 100644 --- a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilderTest.java +++ b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventRequestBuilderTest.java @@ -34,6 +34,7 @@ import org.hypertrace.core.graphql.common.request.AttributeRequest; import org.hypertrace.core.graphql.common.request.AttributeRequestBuilder; import org.hypertrace.core.graphql.common.request.FilterRequestBuilder; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.utils.Converter; import org.hypertrace.core.graphql.span.dao.DaoTestUtil.DefaultAttributeRequest; @@ -45,7 +46,6 @@ import org.hypertrace.core.graphql.spi.config.GraphQlServiceConfig; import org.hypertrace.core.graphql.utils.gateway.GatewayUtilsModule; import org.hypertrace.core.graphql.utils.grpc.GrpcChannelRegistry; -import org.hypertrace.gateway.service.v1.common.ColumnIdentifier; import org.hypertrace.gateway.service.v1.common.Expression; import org.hypertrace.gateway.service.v1.common.Filter; import org.hypertrace.gateway.service.v1.common.LiteralConstant; @@ -109,9 +109,10 @@ protected void configure() { return Single.just( List.of( AttributeAssociation.of( - spanIdAttribute.attribute(), + spanIdAttribute.attributeExpression().attribute(), new NormalizedFilter( - spanIdAttribute.attribute().key(), + AttributeExpression.forAttributeKey( + spanIdAttribute.attributeExpression().value().key()), filterArgument.operator(), filterArgument.value())))); }) @@ -119,12 +120,14 @@ protected void configure() { .build(any(), any(), anyCollection()); when(attributeStore.getForeignIdAttribute(any(), anyString(), anyString())) - .thenReturn(Single.just(spanIdAttribute.attribute())); + .thenReturn(Single.just(spanIdAttribute.attributeExpression().attribute())); doAnswer( invocation -> { AttributeModel attributeModel = invocation.getArgument(0, AttributeModel.class); - return new DefaultAttributeRequest(attributeModel); + return new DefaultAttributeRequest( + AttributeAssociation.of( + attributeModel, AttributeExpression.forAttributeKey(attributeModel.key()))); }) .when(attributeRequestBuilder) .buildForAttribute(any()); @@ -164,7 +167,7 @@ void testBuildRequest() { .setOperator(AND) .addChildFilter( Filter.newBuilder() - .setLhs(buildUnaliasedSelection("spanId")) + .setLhs(buildAliasedSelection("spanId")) .setOperator(IN) .setRhs(buildStringList("span1", "span2", "span3")))) .build(); @@ -204,7 +207,7 @@ void testBuildRequest_addSpanId() { .setOperator(AND) .addChildFilter( Filter.newBuilder() - .setLhs(buildUnaliasedSelection("spanId")) + .setLhs(buildAliasedSelection("spanId")) .setOperator(IN) .setRhs(buildStringList("span1", "span2", "span3")))) .build(); @@ -215,13 +218,10 @@ void testBuildRequest_addSpanId() { Expression buildAliasedSelection(String name) { return Expression.newBuilder() - .setColumnIdentifier(ColumnIdentifier.newBuilder().setColumnName(name).setAlias(name)) - .build(); - } - - Expression buildUnaliasedSelection(String name) { - return Expression.newBuilder() - .setColumnIdentifier(ColumnIdentifier.newBuilder().setColumnName(name)) + .setAttributeExpression( + org.hypertrace.gateway.service.v1.common.AttributeExpression.newBuilder() + .setAttributeId(name) + .setAlias(name)) .build(); } diff --git a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverterTest.java b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverterTest.java index 26de96d1..d1cf3c62 100644 --- a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverterTest.java +++ b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/dao/SpanLogEventResponseConverterTest.java @@ -17,11 +17,12 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import org.hypertrace.core.graphql.attributes.AttributeStore; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.request.AttributeRequestBuilder; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.utils.BiConverter; import org.hypertrace.core.graphql.context.GraphQlRequestContext; import org.hypertrace.gateway.service.v1.common.Value; @@ -35,18 +36,20 @@ class SpanLogEventResponseConverterTest { @Mock - BiConverter, Map, Map> + BiConverter, Map, Map> attributeMapConverter; @Mock AttributeStore attributeStore; @Mock GraphQlRequestContext requestContext; + @Mock AttributeRequestBuilder attributeRequestBuilder; private SpanLogEventResponseConverter spanLogEventResponseConverter; @BeforeEach void beforeEach() { spanLogEventResponseConverter = - new SpanLogEventResponseConverter(attributeMapConverter, attributeStore); + new SpanLogEventResponseConverter( + attributeMapConverter, attributeStore, attributeRequestBuilder); } @Test @@ -55,7 +58,10 @@ void testBuildResponse() { List.of(spanIdAttribute, traceIdAttribute, attributesAttribute); when(attributeStore.getForeignIdAttribute(any(), anyString(), anyString())) - .thenReturn(Single.just(spanIdAttribute.attribute())); + .thenReturn(Single.just(spanIdAttribute.attributeExpression().attribute())); + when(attributeRequestBuilder.buildForAttribute( + spanIdAttribute.attributeExpression().attribute())) + .thenReturn(spanIdAttribute); doAnswer( invocation -> { @@ -64,7 +70,9 @@ void testBuildResponse() { map.entrySet().stream() .collect( Collectors.toMap( - Entry::getKey, valueEntry -> valueEntry.getValue().getString()))); + valueEntry -> + AttributeExpression.forAttributeKey(valueEntry.getKey()), + valueEntry -> valueEntry.getValue().getString()))); }) .when(attributeMapConverter) .convert(anyCollection(), anyMap()); @@ -83,7 +91,10 @@ void testBuildResponse_spanIdNotRequested() { Collection attributeRequests = List.of(traceIdAttribute, attributesAttribute); when(attributeStore.getForeignIdAttribute(any(), anyString(), anyString())) - .thenReturn(Single.just(spanIdAttribute.attribute())); + .thenReturn(Single.just(spanIdAttribute.attributeExpression().attribute())); + when(attributeRequestBuilder.buildForAttribute( + spanIdAttribute.attributeExpression().attribute())) + .thenReturn(spanIdAttribute); doAnswer( invocation -> { @@ -92,7 +103,9 @@ void testBuildResponse_spanIdNotRequested() { map.entrySet().stream() .collect( Collectors.toMap( - Entry::getKey, valueEntry -> valueEntry.getValue().getString()))); + valueEntry -> + AttributeExpression.forAttributeKey(valueEntry.getKey()), + valueEntry -> valueEntry.getValue().getString()))); }) .when(attributeMapConverter) .convert(anyCollection(), anyMap()); diff --git a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/export/ExportSpanTest.java b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/export/ExportSpanTest.java index 981acdcc..884facdc 100644 --- a/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/export/ExportSpanTest.java +++ b/hypertrace-core-graphql-span-schema/src/test/java/org/hypertrace/core/graphql/span/export/ExportSpanTest.java @@ -6,7 +6,11 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import lombok.Value; import lombok.experimental.Accessors; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.log.event.schema.LogEvent; import org.hypertrace.core.graphql.log.event.schema.LogEventResultSet; import org.hypertrace.core.graphql.span.export.ExportSpan.Builder; @@ -18,16 +22,30 @@ public class ExportSpanTest { - @lombok.Value + @Value @Accessors(fluent = true) - static class ConvertedSpan implements Span { + private static class ConvertedSpan implements Span { String id; - Map attributeValues; + Map attributeValues; Map> spanIdToLogEvents; + ConvertedSpan( + String id, + Map attributeMap, + Map> spanIdToLogEvents) { + this.id = id; + this.spanIdToLogEvents = spanIdToLogEvents; + this.attributeValues = + attributeMap.entrySet().stream() + .collect( + Collectors.toUnmodifiableMap( + entry -> AttributeExpression.forAttributeKey(entry.getKey()), + Entry::getValue)); + } + @Override - public Object attribute(String key) { - return this.attributeValues.get(key); + public Object attribute(AttributeExpression attributeExpression) { + return this.attributeValues.get(attributeExpression); } @Override @@ -37,23 +55,31 @@ public LogEventResultSet logEvents() { } } - @lombok.Value + @Value @Accessors(fluent = true) - static class ConvertedLogEventResultSet implements LogEventResultSet { + private static class ConvertedLogEventResultSet implements LogEventResultSet { List results; long total; long count; } - @lombok.Value + @Value @Accessors(fluent = true) - static class ConvertedLogEvent implements org.hypertrace.core.graphql.log.event.schema.LogEvent { + private static class ConvertedLogEvent implements LogEvent { + ConvertedLogEvent(Map attributeMap) { + this.attributeValues = + attributeMap.entrySet().stream() + .collect( + Collectors.toUnmodifiableMap( + entry -> AttributeExpression.forAttributeKey(entry.getKey()), + Entry::getValue)); + } - Map attributeValues; + Map attributeValues; @Override - public Object attribute(String key) { - return this.attributeValues.get(key); + public Object attribute(AttributeExpression attributeExpression) { + return this.attributeValues.get(attributeExpression); } } diff --git a/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/GatewayServiceTraceConverter.java b/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/GatewayServiceTraceConverter.java index 93f469c6..181f2904 100644 --- a/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/GatewayServiceTraceConverter.java +++ b/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/GatewayServiceTraceConverter.java @@ -9,6 +9,7 @@ import lombok.experimental.Accessors; import org.hypertrace.core.graphql.common.request.AttributeRequest; import org.hypertrace.core.graphql.common.request.ResultSetRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.utils.BiConverter; import org.hypertrace.core.graphql.trace.schema.Trace; import org.hypertrace.core.graphql.trace.schema.TraceResultSet; @@ -16,12 +17,14 @@ import org.hypertrace.gateway.service.v1.trace.TracesResponse; public class GatewayServiceTraceConverter { - private final BiConverter, Map, Map> + private final BiConverter< + Collection, Map, Map> attributeMapConverter; @Inject GatewayServiceTraceConverter( - BiConverter, Map, Map> + BiConverter< + Collection, Map, Map> attributeMapConverter) { this.attributeMapConverter = attributeMapConverter; } @@ -42,18 +45,19 @@ private Single convert( .map( attrMap -> new ConvertedTrace( - attrMap.get(request.idAttribute().attribute().key()).toString(), attrMap)); + attrMap.get(request.idAttribute().attributeExpression().value()).toString(), + attrMap)); } @lombok.Value @Accessors(fluent = true) private static class ConvertedTrace implements Trace { String id; - Map attributeValues; + Map attributeValues; @Override - public Object attribute(String key) { - return this.attributeValues.get(key); + public Object attribute(AttributeExpression attributeExpression) { + return this.attributeValues.get(attributeExpression); } } diff --git a/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/TraceDaoModule.java b/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/TraceDaoModule.java index 711dddd7..77ca9524 100644 --- a/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/TraceDaoModule.java +++ b/hypertrace-core-graphql-trace-schema/src/main/java/org/hypertrace/core/graphql/trace/dao/TraceDaoModule.java @@ -10,6 +10,7 @@ import java.util.Set; import org.hypertrace.core.graphql.common.request.AttributeAssociation; import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression; import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument; import org.hypertrace.core.graphql.common.utils.BiConverter; @@ -52,6 +53,8 @@ protected void configure() { Key.get( new TypeLiteral< BiConverter< - Collection, Map, Map>>() {})); + Collection, + Map, + Map>>() {})); } }