Skip to content

[rule based autotagging] Add Create Rule API Logic #17792

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 20 commits into from
Jun 5, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- [Rule based auto-tagging] Add get rule API ([#17336](https://github.com/opensearch-project/OpenSearch/pull/17336))
- [Rule based auto-tagging] Add Delete Rule API ([#18184](https://github.com/opensearch-project/OpenSearch/pull/18184))
- Add paginated wlm/stats API ([#17638](https://github.com/opensearch-project/OpenSearch/pull/17638))
- [Rule based auto-tagging] Add Create rule API ([#17792](https://github.com/opensearch-project/OpenSearch/pull/17792))
- Implement parallel shard refresh behind cluster settings ([#17782](https://github.com/opensearch-project/OpenSearch/pull/17782))
- Bump OpenSearch Core main branch to 3.0.0 ([#18039](https://github.com/opensearch-project/OpenSearch/pull/18039))
- [Rule based Auto-tagging] Add wlm `ActionFilter` ([#17791](https://github.com/opensearch-project/OpenSearch/pull/17791))
Expand Down
1 change: 0 additions & 1 deletion modules/autotagging-commons/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* compatible open source license.
*/


opensearchplugin {
name = "rule-framework"
description = 'OpenSearch Rule Framework plugin'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.action.ActionRequest;
import org.opensearch.action.ActionRequestValidationException;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.rule.autotagging.Rule;

import java.io.IOException;

/**
* A request for create Rule
* Example request:
* curl -X PUT "localhost:9200/_rules/{featureType}/" -H 'Content-Type: application/json' -d '
* {
* "description": "description1",
* "attribute_name": ["log*", "event*"],
* "feature_type": "poOiU851RwyLYvV5lbvv5w"
* }'
* @opensearch.experimental
*/
public class CreateRuleRequest extends ActionRequest {
private final Rule rule;

/**
* constructor for CreateRuleRequest
* @param rule the rule to create
*/
public CreateRuleRequest(Rule rule) {
this.rule = rule;
}

/**
* Constructs a CreateRuleRequest from a StreamInput for deserialization
* @param in - The {@link StreamInput} instance to read from.
*/
public CreateRuleRequest(StreamInput in) throws IOException {
super(in);
rule = new Rule(in);
}

@Override
public ActionRequestValidationException validate() {
try {
rule.getFeatureType().getFeatureValueValidator().validate(rule.getFeatureValue());
return null;
} catch (Exception e) {
ActionRequestValidationException validationException = new ActionRequestValidationException();
validationException.addValidationError("Validation failed: " + e.getMessage());
return validationException;

Check warning on line 58 in modules/autotagging-commons/common/src/main/java/org/opensearch/rule/CreateRuleRequest.java

View check run for this annotation

Codecov / codecov/patch

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/CreateRuleRequest.java#L53-L58

Added lines #L53 - L58 were not covered by tests
}
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
rule.writeTo(out);
}

/**
* rule getter
*/
public Rule getRule() {
return rule;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.core.action.ActionResponse;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.rule.autotagging.Rule;

import java.io.IOException;
import java.util.Map;

import static org.opensearch.rule.autotagging.Rule._ID_STRING;

/**
* Response for the create API for Rule
* Example response:
* {
* "_id":"wi6VApYBoX5wstmtU_8l",
* "description":"description1",
* "index_pattern":["log*", "uvent*"],
* "workload_group":"poOiU851RwyLYvV5lbvv5w",
* "updated_at":"2025-04-04T20:54:22.406Z"
* }
* @opensearch.experimental
*/
public class CreateRuleResponse extends ActionResponse implements ToXContent, ToXContentObject {
private final String _id;
private final Rule rule;

/**
* contructor for CreateRuleResponse
* @param id - the id for the rule created
* @param rule - the rule created
*/
public CreateRuleResponse(String id, final Rule rule) {
this._id = id;
this.rule = rule;
}

/**
* Constructs a CreateRuleResponse from a StreamInput for deserialization
* @param in - The {@link StreamInput} instance to read from.
*/
public CreateRuleResponse(StreamInput in) throws IOException {
_id = in.readString();
rule = new Rule(in);
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(_id);
rule.writeTo(out);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return rule.toXContent(builder, new MapParams(Map.of(_ID_STRING, _id)));
}

/**
* rule getter
*/
public Rule getRule() {
return rule;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@
/**
* A request for get Rule
* Example Request:
* The endpoint "localhost:9200/_wlm/rule" is specific to the Workload Management feature to manage rules
* curl -X GET "localhost:9200/_wlm/rule" - get all rules
* curl -X GET "localhost:9200/_wlm/rule/{_id}" - get single rule by id
* curl -X GET "localhost:9200/_wlm/rule?index_pattern=a,b" - get all rules containing attribute index_pattern as a or b
* curl -X GET "localhost:9200/_rules/{featureType}/" - get all rules for {featureType}
* curl -X GET "localhost:9200/_rules/{featureType}/{_id}" - get single rule by id
* curl -X GET "localhost:9200/_rules/{featureType}?index_pattern=a,b" - get all rules containing attribute index_pattern as a or b for {featureType}
* @opensearch.experimental
*/
@ExperimentalApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
* "rules": [
* {
* "_id": "z1MJApUB0zgMcDmz-UQq",
* "description": "Rule for tagging query_group_id to index123"
* "description": "Rule for tagging workload_group_id to index123"
* "index_pattern": ["index123"],
* "query_group": "query_group_id",
* "workload_group": "workload_group_id",
* "updated_at": "2025-02-14T01:19:22.589Z"
* },
* ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
*/
public interface RulePersistenceService {

/**
* Create rules based on the provided request.
* @param request The request containing the details for creating the rule.
* @param listener The listener that will handle the response or failure.
*/
void createRule(CreateRuleRequest request, ActionListener<CreateRuleResponse> listener);

/**
* Get rules based on the provided request.
* @param request The request containing the details for retrieving the rule.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.core.action.ActionListener;

/**
* Interface that handles rule routing logic
* @opensearch.experimental
*/
public interface RuleRoutingService {

/**
* Handles a create rule request by routing it to the appropriate node.
* @param request the create rule request
* @param listener listener to handle the final response
*/
void handleCreateRuleRequest(CreateRuleRequest request, ActionListener<CreateRuleResponse> listener);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.common.annotation.ExperimentalApi;
import org.opensearch.rule.autotagging.Attribute;
import org.opensearch.rule.autotagging.Rule;

import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
* Utility class for operations related to {@link Rule} objects.
* @opensearch.experimental
*/
@ExperimentalApi
public class RuleUtils {

/**
* constructor for RuleUtils
*/
public RuleUtils() {}

Check warning on line 30 in modules/autotagging-commons/common/src/main/java/org/opensearch/rule/RuleUtils.java

View check run for this annotation

Codecov / codecov/patch

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/RuleUtils.java#L30

Added line #L30 was not covered by tests

/**
* Checks if a duplicate rule exists and returns its id.
* Two rules are considered to be duplicate when meeting all the criteria below
* 1. They have the same feature type
* 2. They have the exact same attributes
* 3. For each attribute, the sets of values must intersect — i.e., at least one common value must exist
* between the current rule and the one being checked.
*
* @param rule The rule to be validated against ruleMap.
* @param ruleMap This map contains existing rules to be checked
*/
public static Optional<String> getDuplicateRuleId(Rule rule, Map<String, Rule> ruleMap) {
Map<Attribute, Set<String>> targetAttributeMap = rule.getAttributeMap();
for (Map.Entry<String, Rule> entry : ruleMap.entrySet()) {
Rule currRule = entry.getValue();
Map<Attribute, Set<String>> existingAttributeMap = currRule.getAttributeMap();

if (rule.getFeatureType() != currRule.getFeatureType() || targetAttributeMap.size() != existingAttributeMap.size()) {
continue;
}
boolean allAttributesIntersect = true;
for (Attribute attribute : targetAttributeMap.keySet()) {
Set<String> targetAttributeValues = targetAttributeMap.get(attribute);
Set<String> existingAttributeValues = existingAttributeMap.get(attribute);
if (existingAttributeValues == null || Collections.disjoint(targetAttributeValues, existingAttributeValues)) {
allAttributesIntersect = false;
break;
}
}
if (allAttributesIntersect) {
return Optional.of(entry.getKey());
}
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ private static void validateFeatureType(FeatureType featureType) {
"Feature type name " + name + " should not be null, empty or have more than " + MAX_FEATURE_TYPE_NAME_LENGTH + "characters"
);
}
if (featureType.getFeatureValueValidator() == null) {
throw new IllegalStateException("FeatureValueValidator is not defined for feature type " + name);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@

/**
* Represents a feature type within the auto-tagging feature. Feature types define different categories of
* characteristics that can be used for tagging and classification. Implementations of this interface are
* responsible for registering feature types in {@link AutoTaggingRegistry}. Implementations must ensure that
* characteristics that can be used for tagging and classification. Implementations must ensure that
* feature types are uniquely identifiable by their class and name.
*
* Implementers should follow these guidelines:
* Feature types should be singletons and managed centrally to avoid duplicates.
* {@link #registerFeatureType()} must be called during initialization to ensure the feature type is available.
*
* @opensearch.experimental
*/
Expand All @@ -49,6 +47,16 @@
*/
Map<String, Attribute> getAllowedAttributesRegistry();

/**
* returns the validator for feature value
*/
default FeatureValueValidator getFeatureValueValidator() {
return new FeatureValueValidator() {
@Override
public void validate(String featureValue) {}

Check warning on line 56 in modules/autotagging-commons/common/src/main/java/org/opensearch/rule/autotagging/FeatureType.java

View check run for this annotation

Codecov / codecov/patch

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/autotagging/FeatureType.java#L56

Added line #L56 was not covered by tests
};
}

/**
* returns max attribute values
* @return
Expand All @@ -65,11 +73,6 @@
return DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH;
}

/**
* makes the feature type usable and available to framework plugin
*/
void registerFeatureType();

/**
* checks the validity of the input attribute
* @param attribute
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule.autotagging;

/**
* Interface for validating a feature value against pre-defined values (such as
* values from the index, cluster state, etc.) for a specific feature type.
* @opensearch.experimental
*/
public interface FeatureValueValidator {
/**
* Validates the given feature value.
* @param featureValue the value to validate
*/
void validate(String featureValue);
}
Loading
Loading