Skip to content

Commit 7be2e98

Browse files
Attribute expressions (#91)
* feat: add support for attribute expressions * feat: expression support in filter and sorting * refactor: move stuff around * refactor: more refactoring * chore: temp for testing integration * chore: more cleanup * chore: clenaup dependencies, fix tests
1 parent a89535a commit 7be2e98

File tree

53 files changed

+737
-388
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+737
-388
lines changed

.snyk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ ignore:
55
SNYK-JAVA-IONETTY-1042268:
66
- '*':
77
reason: No replacement available
8-
expires: 2021-12-31T00:00:00.000Z
8+
expires: 2022-03-31T00:00:00.000Z
99
patch: {}
1010

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.hypertrace.core.graphql.common.deserialization;
2+
3+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
4+
import org.hypertrace.core.graphql.deserialization.ArgumentDeserializationConfig;
5+
6+
class AttributeExpressionDeserializationConfig implements ArgumentDeserializationConfig {
7+
8+
@Override
9+
public String getArgumentKey() {
10+
return AttributeExpression.ARGUMENT_NAME;
11+
}
12+
13+
@Override
14+
public Class<AttributeExpression> getArgumentSchema() {
15+
return AttributeExpression.class;
16+
}
17+
}

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/CommonDeserializationModule.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ protected void configure() {
4848
.toInstance(
4949
ArgumentDeserializationConfig.forPrimitive(
5050
SpaceArgument.ARGUMENT_NAME, SpaceArgument.class));
51+
deserializationConfigMultibinder
52+
.addBinding()
53+
.to(AttributeExpressionDeserializationConfig.class);
5154

5255
requireBinding(Key.get(new TypeLiteral<Class<? extends AttributeScope>>() {}));
5356
}

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/FilterArgumentDeserializationConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import lombok.Value;
1010
import lombok.experimental.Accessors;
1111
import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope;
12+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
1213
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
1314
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterOperatorType;
1415
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterType;
@@ -52,6 +53,9 @@ private static class DefaultFilterArgument implements FilterArgument {
5253
@JsonProperty(FILTER_ARGUMENT_KEY)
5354
String key;
5455

56+
@JsonProperty(FILTER_ARGUMENT_KEY_EXPRESSION)
57+
AttributeExpression keyExpression;
58+
5559
@JsonProperty(FILTER_ARGUMENT_OPERATOR)
5660
FilterOperatorType operator;
5761

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/deserialization/OrderArgumentDeserializationConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import lombok.NoArgsConstructor;
88
import lombok.Value;
99
import lombok.experimental.Accessors;
10+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
1011
import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument;
1112
import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderDirection;
1213
import org.hypertrace.core.graphql.deserialization.ArgumentDeserializationConfig;
@@ -38,5 +39,8 @@ private static class DefaultOrderArgument implements OrderArgument {
3839

3940
@JsonProperty(ORDER_KEY_NAME)
4041
String key;
42+
43+
@JsonProperty(ORDER_KEY_EXPRESSION_NAME)
44+
AttributeExpression keyExpression;
4145
}
4246
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package org.hypertrace.core.graphql.common.request;
22

3-
import org.hypertrace.core.graphql.attributes.AttributeModel;
3+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
44

55
public interface AttributeRequest {
6+
AttributeAssociation<AttributeExpression> attributeExpression();
67

7-
AttributeModel attribute();
8-
9-
String alias();
8+
default String asMapKey() {
9+
return attributeExpression().value().asAlias();
10+
}
1011
}

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/AttributeRequestBuilder.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.reactivex.rxjava3.core.Single;
77
import java.util.stream.Stream;
88
import org.hypertrace.core.graphql.attributes.AttributeModel;
9+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
910
import org.hypertrace.core.graphql.context.GraphQlRequestContext;
1011

1112
public interface AttributeRequestBuilder {
@@ -27,8 +28,10 @@ Observable<AttributeRequest> buildForAttributeQueryableFieldsAndId(
2728
String attributeScope,
2829
Stream<SelectedField> attributeQueryableFields);
2930

30-
Single<AttributeRequest> buildForKey(
31-
GraphQlRequestContext context, String attributeModelScope, String attributeKey);
31+
Single<AttributeRequest> buildForAttributeExpression(
32+
GraphQlRequestContext context,
33+
String attributeScope,
34+
AttributeExpression attributeExpression);
3235

3336
AttributeRequest buildForAttribute(AttributeModel attribute);
3437
}

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultAttributeRequestBuilder.java

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import org.hypertrace.core.graphql.attributes.AttributeModel;
1212
import org.hypertrace.core.graphql.attributes.AttributeStore;
1313
import org.hypertrace.core.graphql.common.schema.attributes.AttributeQueryable;
14+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
1415
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeKeyArgument;
16+
import org.hypertrace.core.graphql.common.utils.attributes.AttributeAssociator;
1517
import org.hypertrace.core.graphql.context.GraphQlRequestContext;
1618
import org.hypertrace.core.graphql.deserialization.ArgumentDeserializer;
1719
import org.hypertrace.core.graphql.utils.schema.GraphQlSelectionFinder;
@@ -20,15 +22,18 @@
2022
class DefaultAttributeRequestBuilder implements AttributeRequestBuilder {
2123

2224
private final AttributeStore attributeStore;
25+
private final AttributeAssociator attributeAssociator;
2326
private final ArgumentDeserializer argumentDeserializer;
2427
private final GraphQlSelectionFinder selectionFinder;
2528

2629
@Inject
2730
DefaultAttributeRequestBuilder(
2831
AttributeStore attributeStore,
32+
AttributeAssociator attributeAssociator,
2933
ArgumentDeserializer argumentDeserializer,
3034
GraphQlSelectionFinder selectionFinder) {
3135
this.attributeStore = attributeStore;
36+
this.attributeAssociator = attributeAssociator;
3237
this.argumentDeserializer = argumentDeserializer;
3338
this.selectionFinder = selectionFinder;
3439
}
@@ -44,8 +49,10 @@ public Observable<AttributeRequest> buildForAttributeQueryableSelectionSet(
4449
String attributeScope,
4550
DataFetchingFieldSelectionSet attributeQueryableSelectionSet) {
4651
return Observable.fromStream(
47-
this.getAttributeKeysForAttributeQueryableSelectionSet(attributeQueryableSelectionSet))
48-
.flatMapSingle(key -> this.buildForKey(context, attributeScope, key))
52+
this.getAttributeExpressionsForAttributeQueryableSelectionSet(
53+
attributeQueryableSelectionSet))
54+
.flatMapSingle(
55+
expression -> this.buildForAttributeExpression(context, attributeScope, expression))
4956
.distinct();
5057
}
5158

@@ -73,40 +80,43 @@ public Observable<AttributeRequest> buildForAttributeQueryableFieldsAndId(
7380
}
7481

7582
@Override
76-
public Single<AttributeRequest> buildForKey(
77-
GraphQlRequestContext context, String requestScope, String attributeKey) {
78-
return this.attributeStore
79-
.get(context, requestScope, attributeKey)
80-
.map(this::buildForAttribute);
83+
public Single<AttributeRequest> buildForAttributeExpression(
84+
GraphQlRequestContext context,
85+
String attributeScope,
86+
AttributeExpression attributeExpression) {
87+
return this.attributeAssociator
88+
.associateAttribute(context, attributeScope, attributeExpression, attributeExpression.key())
89+
.map(DefaultAttributeRequest::new);
8190
}
8291

8392
@Override
8493
public AttributeRequest buildForAttribute(AttributeModel attribute) {
85-
return new DefaultAttributeRequest(attribute);
94+
return new DefaultAttributeRequest(
95+
AttributeAssociation.of(attribute, AttributeExpression.forAttributeKey(attribute.key())));
8696
}
8797

88-
private Stream<String> getAttributeKeysForAttributeQueryableSelectionSet(
98+
private Stream<AttributeExpression> getAttributeExpressionsForAttributeQueryableSelectionSet(
8999
DataFetchingFieldSelectionSet selectionSet) {
90100
return this.selectionFinder
91101
.findSelections(
92102
selectionSet, SelectionQuery.namedChild(AttributeQueryable.ATTRIBUTE_FIELD_NAME))
93-
.flatMap(this::getArgument);
103+
.flatMap(this::resolveAttributeExpression);
94104
}
95105

96-
private Stream<String> getArgument(SelectedField attributeField) {
106+
private Stream<AttributeExpression> resolveAttributeExpression(SelectedField attributeField) {
97107
return this.argumentDeserializer
98-
.deserializePrimitive(attributeField.getArguments(), AttributeKeyArgument.class)
108+
.deserializeObject(attributeField.getArguments(), AttributeExpression.class)
109+
.or(
110+
() ->
111+
this.argumentDeserializer
112+
.deserializePrimitive(attributeField.getArguments(), AttributeKeyArgument.class)
113+
.map(AttributeExpression::forAttributeKey))
99114
.stream();
100115
}
101116

102117
@Value
103118
@Accessors(fluent = true)
104119
static class DefaultAttributeRequest implements AttributeRequest {
105-
AttributeModel attribute;
106-
107-
@Override
108-
public String alias() {
109-
return attribute.id();
110-
}
120+
AttributeAssociation<AttributeExpression> attributeExpression;
111121
}
112122
}

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultFilterRequestBuilder.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.hypertrace.core.graphql.common.request;
22

3+
import static java.util.Objects.requireNonNull;
4+
35
import io.reactivex.rxjava3.core.Maybe;
46
import io.reactivex.rxjava3.core.Observable;
57
import io.reactivex.rxjava3.core.Single;
@@ -12,6 +14,7 @@
1214
import lombok.experimental.Accessors;
1315
import org.hypertrace.core.graphql.attributes.AttributeStore;
1416
import org.hypertrace.core.graphql.common.schema.attributes.AttributeScope;
17+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
1518
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
1619
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterOperatorType;
1720
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterType;
@@ -50,12 +53,17 @@ private Single<AttributeAssociation<FilterArgument>> normalize(
5053
GraphQlRequestContext requestContext, String scope, FilterArgument filterArgument) {
5154
switch (filterArgument.type()) {
5255
case ATTRIBUTE:
56+
AttributeExpression attributeExpression =
57+
Optional.ofNullable(filterArgument.keyExpression())
58+
.orElseGet(
59+
() ->
60+
AttributeExpression.forAttributeKey(requireNonNull(filterArgument.key())));
5361
return this.attributeAssociator.associateAttribute(
5462
requestContext,
5563
scope,
5664
new NormalizedFilter(
57-
filterArgument.key(), filterArgument.operator(), filterArgument.value()),
58-
FilterArgument::key);
65+
attributeExpression, filterArgument.operator(), filterArgument.value()),
66+
attributeExpression.key());
5967
case ID:
6068
return Maybe.fromOptional(Optional.ofNullable(filterArgument.idType()))
6169
.map(AttributeScope::getScopeString)
@@ -71,7 +79,7 @@ private Single<AttributeAssociation<FilterArgument>> normalize(
7179
AttributeAssociation.of(
7280
foreignIdAttribute,
7381
new NormalizedFilter(
74-
foreignIdAttribute.key(),
82+
AttributeExpression.forAttributeKey(foreignIdAttribute.key()),
7583
filterArgument.operator(),
7684
filterArgument.value())));
7785
default:
@@ -85,7 +93,8 @@ private Single<AttributeAssociation<FilterArgument>> normalize(
8593
@Accessors(fluent = true)
8694
private static class NormalizedFilter implements FilterArgument {
8795
FilterType type = FilterType.ATTRIBUTE;
88-
String key;
96+
String key = null;
97+
AttributeExpression keyExpression;
8998
FilterOperatorType operator;
9099
Object value;
91100
String idScope = null;

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/DefaultResultSetRequestBuilder.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import lombok.Value;
1818
import lombok.experimental.Accessors;
1919
import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument;
20+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
2021
import org.hypertrace.core.graphql.common.schema.results.ResultSet;
2122
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
2223
import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument;
@@ -98,7 +99,11 @@ public <O extends OrderArgument> Single<ResultSetRequest<O>> build(
9899

99100
return zip(
100101
this.attributeAssociator
101-
.associateAttributes(context, requestScope, requestedOrders, OrderArgument::key)
102+
.associateAttributes(
103+
context,
104+
requestScope,
105+
requestedOrders,
106+
arg -> arg.resolvedKeyExpression().key())
102107
.collect(Collectors.toUnmodifiableList()),
103108
this.filterRequestBuilder.build(context, requestScope, requestedFilters),
104109
(orders, filters) ->
@@ -149,7 +154,7 @@ public Single<ResultSetRequest<OrderArgument>> build(
149154
GraphQlRequestContext context,
150155
String requestScope,
151156
Map<String, Object> arguments,
152-
List<String> attributes) {
157+
List<AttributeExpression> attributeExpressions) {
153158
int limit =
154159
this.argumentDeserializer
155160
.deserializePrimitive(arguments, LimitArgument.class)
@@ -166,7 +171,8 @@ public Single<ResultSetRequest<OrderArgument>> build(
166171
.orElse(Collections.emptyList());
167172

168173
return zip(
169-
this.getAttributeRequests(context, requestScope, attributes).collect(Collectors.toList()),
174+
this.getAttributeRequests(context, requestScope, attributeExpressions)
175+
.collect(Collectors.toList()),
170176
this.attributeRequestBuilder.buildForId(context, requestScope),
171177
this.filterRequestBuilder.build(context, requestScope, requestedFilters),
172178
(attributeRequests, idAttribute, filters) ->
@@ -183,12 +189,15 @@ public Single<ResultSetRequest<OrderArgument>> build(
183189
}
184190

185191
private Observable<AttributeRequest> getAttributeRequests(
186-
GraphQlRequestContext context, String requestScope, List<String> attributes) {
187-
return Observable.fromIterable(attributes)
192+
GraphQlRequestContext context,
193+
String requestScope,
194+
List<AttributeExpression> attributeExpressions) {
195+
return Observable.fromIterable(attributeExpressions)
188196
.distinct()
189197
.flatMapSingle(
190-
attributeKey ->
191-
this.attributeRequestBuilder.buildForKey(context, requestScope, attributeKey));
198+
attributeExpression ->
199+
this.attributeRequestBuilder.buildForAttributeExpression(
200+
context, requestScope, attributeExpression));
192201
}
193202

194203
private Stream<SelectedField> getAttributeQueryableFields(

hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/request/ResultSetRequestBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Optional;
1010
import java.util.stream.Stream;
1111
import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument;
12+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
1213
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
1314
import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument;
1415
import org.hypertrace.core.graphql.context.GraphQlRequestContext;
@@ -42,5 +43,5 @@ Single<ResultSetRequest<OrderArgument>> build(
4243
GraphQlRequestContext context,
4344
String requestScope,
4445
Map<String, Object> arguments,
45-
List<String> attributes);
46+
List<AttributeExpression> attributeExpressions);
4647
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package org.hypertrace.core.graphql.common.schema.attributes;
22

3+
import static java.util.Objects.requireNonNull;
4+
35
import graphql.annotations.annotationTypes.GraphQLField;
46
import graphql.annotations.annotationTypes.GraphQLName;
5-
import graphql.annotations.annotationTypes.GraphQLNonNull;
7+
import java.util.Optional;
8+
import javax.annotation.Nullable;
9+
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
610
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeKeyArgument;
711

812
public interface AttributeQueryable {
@@ -11,5 +15,14 @@ public interface AttributeQueryable {
1115

1216
@GraphQLField
1317
@GraphQLName(ATTRIBUTE_FIELD_NAME)
14-
Object attribute(@GraphQLName(AttributeKeyArgument.ARGUMENT_NAME) @GraphQLNonNull String key);
18+
default Object attribute(
19+
@Deprecated @GraphQLName(AttributeKeyArgument.ARGUMENT_NAME) @Nullable String key,
20+
@GraphQLName(AttributeExpression.ARGUMENT_NAME) @Nullable AttributeExpression expression) {
21+
return attribute(
22+
Optional.ofNullable(expression)
23+
.orElseGet(() -> AttributeExpression.forAttributeKey(requireNonNull(key))));
24+
}
25+
26+
// Once callers are migrated off using the string, we'll remove it and use this api only
27+
Object attribute(@GraphQLName(AttributeExpression.ARGUMENT_NAME) AttributeExpression expression);
1528
}

0 commit comments

Comments
 (0)