From 3697588520cac9a39ead9906c6bf05eece0119c9 Mon Sep 17 00:00:00 2001 From: Kshitiz Saxena <84707889+saxenakshitiz@users.noreply.github.com> Date: Tue, 11 Jun 2024 22:07:54 +0530 Subject: [PATCH] chore: Add support for dynamic expression in label application rules (#233) --- .../graphql/label/schema/rule/Action.java | 9 ++- .../schema/rule/DynamicLabelExpression.java | 24 ++++++++ .../schema/rule/TokenExtractionRule.java | 30 ++++++++++ .../LabelApplicationRuleRequestConverter.java | 24 ++++++++ ...LabelApplicationRuleResponseConverter.java | 57 ++++++++++++++++++- ...lApplicationRuleDeserializationConfig.java | 33 +++++++++++ 6 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/DynamicLabelExpression.java create mode 100644 hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/TokenExtractionRule.java diff --git a/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/Action.java b/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/Action.java index 1491cf8a..e98c4dee 100644 --- a/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/Action.java +++ b/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/Action.java @@ -14,6 +14,7 @@ public interface Action { String OPERATION_KEY = "operation"; String STATIC_LABELS = "staticLabels"; String DYNAMIC_LABEL_KEY_KEY = "dynamicLabelKey"; + String DYNAMIC_LABEL_EXPRESSION_KEY = "dynamicLabelExpression"; String ACTION_TYPE_KEY = "type"; @GraphQLName(Operation.TYPE_NAME) @@ -25,7 +26,8 @@ enum Operation { @GraphQLName(ActionType.TYPE_NAME) enum ActionType { STATIC_LABELS, - DYNAMIC_LABEL_KEY; + DYNAMIC_LABEL_KEY, + DYNAMIC_LABEL_EXPRESSION; private static final String TYPE_NAME = "LabelApplicationActionType"; } @@ -49,6 +51,11 @@ enum ActionType { @GraphQLName(DYNAMIC_LABEL_KEY_KEY) String dynamicLabelKey(); + @GraphQLField + @Nullable + @GraphQLName(DYNAMIC_LABEL_EXPRESSION_KEY) + DynamicLabelExpression dynamicLabelExpression(); + @GraphQLField @GraphQLNonNull @GraphQLName(ACTION_TYPE_KEY) diff --git a/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/DynamicLabelExpression.java b/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/DynamicLabelExpression.java new file mode 100644 index 00000000..9852d6d7 --- /dev/null +++ b/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/DynamicLabelExpression.java @@ -0,0 +1,24 @@ +package org.hypertrace.graphql.label.schema.rule; + +import graphql.annotations.annotationTypes.GraphQLField; +import graphql.annotations.annotationTypes.GraphQLName; +import graphql.annotations.annotationTypes.GraphQLNonNull; +import java.util.List; + +@GraphQLName(DynamicLabelExpression.TYPE_NAME) +public interface DynamicLabelExpression { + String TYPE_NAME = "DynamicLabelExpression"; + + String LABEL_EXPRESSION_KEY = "labelExpression"; + String TOKEN_EXTRACTION_RULES_KEY = "tokenExtractionRules"; + + @GraphQLField + @GraphQLName(LABEL_EXPRESSION_KEY) + @GraphQLNonNull + String labelExpression(); + + @GraphQLField + @GraphQLNonNull + @GraphQLName(TOKEN_EXTRACTION_RULES_KEY) + List tokenExtractionRules(); +} diff --git a/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/TokenExtractionRule.java b/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/TokenExtractionRule.java new file mode 100644 index 00000000..3af675e9 --- /dev/null +++ b/hypertrace-graphql-labels-schema-api/src/main/java/org/hypertrace/graphql/label/schema/rule/TokenExtractionRule.java @@ -0,0 +1,30 @@ +package org.hypertrace.graphql.label.schema.rule; + +import graphql.annotations.annotationTypes.GraphQLField; +import graphql.annotations.annotationTypes.GraphQLName; +import graphql.annotations.annotationTypes.GraphQLNonNull; +import javax.annotation.Nullable; + +@GraphQLName(TokenExtractionRule.TYPE_NAME) +public interface TokenExtractionRule { + String TYPE_NAME = "TokenExtractionRule"; + + String KEY_NAME = "key"; + String ALIAS_KEY = "alias"; + String REGEX_CAPTURE_KEY = "regexCapture"; + + @GraphQLField + @GraphQLName(KEY_NAME) + @GraphQLNonNull + String key(); + + @GraphQLField + @GraphQLName(ALIAS_KEY) + @Nullable + String alias(); + + @GraphQLField + @GraphQLName(REGEX_CAPTURE_KEY) + @Nullable + String regexCapture(); +} diff --git a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleRequestConverter.java b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleRequestConverter.java index a1eee5c3..7f5cfb60 100644 --- a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleRequestConverter.java +++ b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleRequestConverter.java @@ -12,6 +12,7 @@ import org.hypertrace.label.application.rule.config.service.v1.DeleteLabelApplicationRuleRequest; import org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData; import org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData.Action; +import org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData.Action.DynamicLabel.TokenExtractionRule; import org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData.CompositeCondition; import org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData.Condition; import org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData.LeafCondition; @@ -82,11 +83,34 @@ private Action convertLabelAction(org.hypertrace.graphql.label.schema.rule.Actio .build(); case DYNAMIC_LABEL_KEY: return actionBuilder.setDynamicLabelKey(action.dynamicLabelKey()).build(); + case DYNAMIC_LABEL_EXPRESSION: + return actionBuilder + .setDynamicLabelExpression( + Action.DynamicLabel.newBuilder() + .setLabelExpression(action.dynamicLabelExpression().labelExpression()) + .addAllTokenExtractionRules( + convertTokenExtractionRules( + action.dynamicLabelExpression().tokenExtractionRules()))) + .build(); default: throw new IllegalArgumentException("Unsupported action value"); } } + private List convertTokenExtractionRules( + List tokenExtractionRules) { + return tokenExtractionRules.stream() + .map( + rule -> { + TokenExtractionRule.Builder builder = + TokenExtractionRule.newBuilder().setKey(rule.key()); + Optional.ofNullable(rule.alias()).ifPresent(builder::setAlias); + Optional.ofNullable(rule.regexCapture()).ifPresent(builder::setRegexCapture); + return builder.build(); + }) + .collect(Collectors.toList()); + } + Condition convertConditionList( List conditionList) { if (conditionList.size() == 1) { diff --git a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleResponseConverter.java b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleResponseConverter.java index 7cea9c4f..f72dd86e 100644 --- a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleResponseConverter.java +++ b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/dao/LabelApplicationRuleResponseConverter.java @@ -12,12 +12,14 @@ import lombok.extern.slf4j.Slf4j; import org.hypertrace.graphql.label.schema.rule.Action; import org.hypertrace.graphql.label.schema.rule.Condition; +import org.hypertrace.graphql.label.schema.rule.DynamicLabelExpression; import org.hypertrace.graphql.label.schema.rule.LabelApplicationRule; import org.hypertrace.graphql.label.schema.rule.LabelApplicationRuleData; import org.hypertrace.graphql.label.schema.rule.LabelApplicationRuleResultSet; import org.hypertrace.graphql.label.schema.rule.LeafCondition; import org.hypertrace.graphql.label.schema.rule.StaticLabels; import org.hypertrace.graphql.label.schema.rule.StringCondition; +import org.hypertrace.graphql.label.schema.rule.TokenExtractionRule; import org.hypertrace.graphql.label.schema.rule.UnaryCondition; import org.hypertrace.graphql.label.schema.rule.ValueCondition; import org.hypertrace.label.application.rule.config.service.v1.CreateLabelApplicationRuleResponse; @@ -105,7 +107,7 @@ private Optional convertAction( return operation.map( op -> new ConvertedAction( - entityTypes, op, staticLabels, null, Action.ActionType.STATIC_LABELS)); + entityTypes, op, staticLabels, null, null, Action.ActionType.STATIC_LABELS)); case DYNAMIC_LABEL_KEY: return operation.map( op -> @@ -114,7 +116,20 @@ private Optional convertAction( op, null, action.getDynamicLabelKey(), + null, Action.ActionType.DYNAMIC_LABEL_KEY)); + case DYNAMIC_LABEL_EXPRESSION: + DynamicLabelExpression dynamicLabelExpression = + convertDynamicLabelExpression(action.getDynamicLabelExpression()); + return operation.map( + op -> + new ConvertedAction( + entityTypes, + op, + null, + null, + dynamicLabelExpression, + Action.ActionType.DYNAMIC_LABEL_EXPRESSION)); default: log.error("Unrecognized Value type in Action {}", action.getValueCase().name()); return Optional.empty(); @@ -128,6 +143,30 @@ private StaticLabels convertStaticLabels( return new ConvertedStaticLabels(staticLabels.getIdsList()); } + private DynamicLabelExpression convertDynamicLabelExpression( + org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData.Action + .DynamicLabel + dynamicLabel) { + return new ConvertedDynamicLabelExpression( + dynamicLabel.getLabelExpression(), + convertTokenExtractionRules(dynamicLabel.getTokenExtractionRulesList())); + } + + private List convertTokenExtractionRules( + List< + org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData + .Action.DynamicLabel.TokenExtractionRule> + tokenExtractionRules) { + return tokenExtractionRules.stream() + .map( + rule -> + new ConvertedTokenExtractionRule( + rule.getKey(), + rule.hasAlias() ? rule.getAlias() : null, + rule.hasRegexCapture() ? rule.getRegexCapture() : null)) + .collect(Collectors.toList()); + } + private List convertCondition( org.hypertrace.label.application.rule.config.service.v1.LabelApplicationRuleData.Condition condition) { @@ -300,6 +339,7 @@ private static class ConvertedAction implements Action { Operation operation; StaticLabels staticLabels; String dynamicLabelKey; + DynamicLabelExpression dynamicLabelExpression; ActionType type; } @@ -309,6 +349,21 @@ private static class ConvertedStaticLabels implements StaticLabels { List ids; } + @Value + @Accessors(fluent = true) + private static class ConvertedDynamicLabelExpression implements DynamicLabelExpression { + String labelExpression; + List tokenExtractionRules; + } + + @Value + @Accessors(fluent = true) + private static class ConvertedTokenExtractionRule implements TokenExtractionRule { + String key; + String alias; + String regexCapture; + } + @Value @Accessors(fluent = true) private static class ConvertedCondition implements Condition { diff --git a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/deserialization/LabelApplicationRuleDeserializationConfig.java b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/deserialization/LabelApplicationRuleDeserializationConfig.java index 98d7723c..96a51799 100644 --- a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/deserialization/LabelApplicationRuleDeserializationConfig.java +++ b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/deserialization/LabelApplicationRuleDeserializationConfig.java @@ -10,11 +10,13 @@ import org.hypertrace.core.graphql.deserialization.ArgumentDeserializationConfig; import org.hypertrace.graphql.label.schema.rule.Action; import org.hypertrace.graphql.label.schema.rule.Condition; +import org.hypertrace.graphql.label.schema.rule.DynamicLabelExpression; import org.hypertrace.graphql.label.schema.rule.LabelApplicationRule; import org.hypertrace.graphql.label.schema.rule.LabelApplicationRuleData; import org.hypertrace.graphql.label.schema.rule.LeafCondition; import org.hypertrace.graphql.label.schema.rule.StaticLabels; import org.hypertrace.graphql.label.schema.rule.StringCondition; +import org.hypertrace.graphql.label.schema.rule.TokenExtractionRule; import org.hypertrace.graphql.label.schema.rule.UnaryCondition; import org.hypertrace.graphql.label.schema.rule.ValueCondition; @@ -38,6 +40,9 @@ public List jacksonModules() { LabelApplicationRuleData.class, LabelApplicationRuleDataArgument.class) .addAbstractTypeMapping(Action.class, ActionArgument.class) .addAbstractTypeMapping(StaticLabels.class, StaticLabelsArgument.class) + .addAbstractTypeMapping( + DynamicLabelExpression.class, DynamicLabelExpressionArgument.class) + .addAbstractTypeMapping(TokenExtractionRule.class, TokenExtractionRuleArgument.class) .addAbstractTypeMapping(Condition.class, ConditionArgument.class) .addAbstractTypeMapping(LeafCondition.class, LeafConditionArgument.class) .addAbstractTypeMapping(ValueCondition.class, ValueConditionArgument.class) @@ -92,6 +97,9 @@ private static class ActionArgument implements Action { @JsonProperty(DYNAMIC_LABEL_KEY_KEY) String dynamicLabelKey; + @JsonProperty(DYNAMIC_LABEL_EXPRESSION_KEY) + DynamicLabelExpression dynamicLabelExpression; + @JsonProperty(ACTION_TYPE_KEY) ActionType type; } @@ -104,6 +112,31 @@ private static class StaticLabelsArgument implements StaticLabels { List ids; } + @Value + @Accessors(fluent = true) + @NoArgsConstructor(force = true) + private static class DynamicLabelExpressionArgument implements DynamicLabelExpression { + @JsonProperty(LABEL_EXPRESSION_KEY) + String labelExpression; + + @JsonProperty(TOKEN_EXTRACTION_RULES_KEY) + List tokenExtractionRules; + } + + @Value + @Accessors(fluent = true) + @NoArgsConstructor(force = true) + private static class TokenExtractionRuleArgument implements TokenExtractionRule { + @JsonProperty(KEY_NAME) + String key; + + @JsonProperty(ALIAS_KEY) + String alias; + + @JsonProperty(REGEX_CAPTURE_KEY) + String regexCapture; + } + @Value @Accessors(fluent = true) @NoArgsConstructor(force = true)