Skip to content

Attribute expressions #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .snyk
Original file line number Diff line number Diff line change
Expand Up @@ -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: {}

Original file line number Diff line number Diff line change
@@ -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<AttributeExpression> getArgumentSchema() {
return AttributeExpression.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Class<? extends AttributeScope>>() {}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -38,5 +39,8 @@ private static class DefaultOrderArgument implements OrderArgument {

@JsonProperty(ORDER_KEY_NAME)
String key;

@JsonProperty(ORDER_KEY_EXPRESSION_NAME)
AttributeExpression keyExpression;
}
}
Original file line number Diff line number Diff line change
@@ -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> attributeExpression();

AttributeModel attribute();

String alias();
default String asMapKey() {
return attributeExpression().value().asAlias();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -27,8 +28,10 @@ Observable<AttributeRequest> buildForAttributeQueryableFieldsAndId(
String attributeScope,
Stream<SelectedField> attributeQueryableFields);

Single<AttributeRequest> buildForKey(
GraphQlRequestContext context, String attributeModelScope, String attributeKey);
Single<AttributeRequest> buildForAttributeExpression(
GraphQlRequestContext context,
String attributeScope,
AttributeExpression attributeExpression);

AttributeRequest buildForAttribute(AttributeModel attribute);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -44,8 +49,10 @@ public Observable<AttributeRequest> 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();
}

Expand Down Expand Up @@ -73,40 +80,43 @@ public Observable<AttributeRequest> buildForAttributeQueryableFieldsAndId(
}

@Override
public Single<AttributeRequest> buildForKey(
GraphQlRequestContext context, String requestScope, String attributeKey) {
return this.attributeStore
.get(context, requestScope, attributeKey)
.map(this::buildForAttribute);
public Single<AttributeRequest> 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<String> getAttributeKeysForAttributeQueryableSelectionSet(
private Stream<AttributeExpression> getAttributeExpressionsForAttributeQueryableSelectionSet(
DataFetchingFieldSelectionSet selectionSet) {
return this.selectionFinder
.findSelections(
selectionSet, SelectionQuery.namedChild(AttributeQueryable.ATTRIBUTE_FIELD_NAME))
.flatMap(this::getArgument);
.flatMap(this::resolveAttributeExpression);
}

private Stream<String> getArgument(SelectedField attributeField) {
private Stream<AttributeExpression> 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> attributeExpression;
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -50,12 +53,17 @@ private Single<AttributeAssociation<FilterArgument>> 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)
Expand All @@ -71,7 +79,7 @@ private Single<AttributeAssociation<FilterArgument>> normalize(
AttributeAssociation.of(
foreignIdAttribute,
new NormalizedFilter(
foreignIdAttribute.key(),
AttributeExpression.forAttributeKey(foreignIdAttribute.key()),
filterArgument.operator(),
filterArgument.value())));
default:
Expand All @@ -85,7 +93,8 @@ private Single<AttributeAssociation<FilterArgument>> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -98,7 +99,11 @@ public <O extends OrderArgument> Single<ResultSetRequest<O>> 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) ->
Expand Down Expand Up @@ -149,7 +154,7 @@ public Single<ResultSetRequest<OrderArgument>> build(
GraphQlRequestContext context,
String requestScope,
Map<String, Object> arguments,
List<String> attributes) {
List<AttributeExpression> attributeExpressions) {
int limit =
this.argumentDeserializer
.deserializePrimitive(arguments, LimitArgument.class)
Expand All @@ -166,7 +171,8 @@ public Single<ResultSetRequest<OrderArgument>> 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) ->
Expand All @@ -183,12 +189,15 @@ public Single<ResultSetRequest<OrderArgument>> build(
}

private Observable<AttributeRequest> getAttributeRequests(
GraphQlRequestContext context, String requestScope, List<String> attributes) {
return Observable.fromIterable(attributes)
GraphQlRequestContext context,
String requestScope,
List<AttributeExpression> attributeExpressions) {
return Observable.fromIterable(attributeExpressions)
.distinct()
.flatMapSingle(
attributeKey ->
this.attributeRequestBuilder.buildForKey(context, requestScope, attributeKey));
attributeExpression ->
this.attributeRequestBuilder.buildForAttributeExpression(
context, requestScope, attributeExpression));
}

private Stream<SelectedField> getAttributeQueryableFields(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -42,5 +43,5 @@ Single<ResultSetRequest<OrderArgument>> build(
GraphQlRequestContext context,
String requestScope,
Map<String, Object> arguments,
List<String> attributes);
List<AttributeExpression> attributeExpressions);
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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);
}
Loading