Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright OpenSearch Contributors
* 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.dataprepper.model.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks a method as a pipeline transformation function that can be invoked
* dynamically from template YAML files via the {@code FUNCTION_NAME} placeholder.
* <p>
* Annotated methods must be {@code public static} and accept a single {@code String}
* parameter, returning a {@code String} result.
* <p>
* The enclosing class must implement
* {@link org.opensearch.dataprepper.model.plugin.PipelineTransformFunctionProvider}.
*
* @since 2.12
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TransformationFunction {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright OpenSearch Contributors
* 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.dataprepper.model.plugin;

/**
* Marker interface for classes that provide pipeline transformation functions.
* Classes implementing this interface can be referenced in rule YAML files via
* the {@code function_providers} field and have their methods invoked dynamically
* during pipeline template transformation.
* <p>
* Methods intended to be callable from templates must also be annotated with
* {@link org.opensearch.dataprepper.model.annotations.TransformationFunction}.
*
* @since 2.12
*/
public interface PipelineTransformFunctionProvider {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright OpenSearch Contributors
* 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.dataprepper.model.annotations;

import org.junit.jupiter.api.Test;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class TransformationFunctionTest {

@Test
void annotation_is_retained_at_runtime() {
Retention retention = TransformationFunction.class.getAnnotation(Retention.class);
assertNotNull(retention);
assertThat(retention.value(), equalTo(RetentionPolicy.RUNTIME));
}

@Test
void annotation_targets_methods() {
Target target = TransformationFunction.class.getAnnotation(Target.class);
assertNotNull(target);
assertThat(target.value().length, equalTo(1));
assertThat(target.value()[0], equalTo(ElementType.METHOD));
}

@Test
void annotation_is_documented() {
Documented documented = TransformationFunction.class.getAnnotation(Documented.class);
assertNotNull(documented);
}

@Test
void annotation_is_present_on_annotated_method() throws NoSuchMethodException {
assertTrue(AnnotatedClass.class.getMethod("annotatedMethod", String.class)
.isAnnotationPresent(TransformationFunction.class));
}

@Test
void annotation_is_not_present_on_unannotated_method() throws NoSuchMethodException {
assertTrue(!AnnotatedClass.class.getMethod("unannotatedMethod", String.class)
.isAnnotationPresent(TransformationFunction.class));
}

static class AnnotatedClass {
@TransformationFunction
public static String annotatedMethod(String input) {
return input;
}

public static String unannotatedMethod(String input) {
return input;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright OpenSearch Contributors
* 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.dataprepper.model.plugin;

import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class PipelineTransformFunctionProviderTest {

@Test
void implementing_class_is_assignable_from_interface() {
PipelineTransformFunctionProvider provider = new TestFunctionProvider();
assertNotNull(provider);
assertTrue(provider instanceof PipelineTransformFunctionProvider);
}

@Test
void interface_is_assignable_from_implementing_class() {
assertThat(PipelineTransformFunctionProvider.class.isAssignableFrom(TestFunctionProvider.class),
equalTo(true));
}

@Test
void interface_is_not_assignable_from_non_implementing_class() {
assertThat(PipelineTransformFunctionProvider.class.isAssignableFrom(NonProvider.class),
equalTo(false));
}

@Test
void interface_has_no_declared_methods() {
assertThat(PipelineTransformFunctionProvider.class.getDeclaredMethods().length, equalTo(0));
}

@Test
void interface_is_public() {
assertTrue(java.lang.reflect.Modifier.isPublic(PipelineTransformFunctionProvider.class.getModifiers()));
}

@Test
void interface_is_an_interface() {
assertTrue(PipelineTransformFunctionProvider.class.isInterface());
}

static class TestFunctionProvider implements PipelineTransformFunctionProvider {
public static String sampleFunction(String input) {
return input;
}
}

static class NonProvider {
public static String someMethod(String input) {
return input;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/*
* Copyright OpenSearch Contributors
* 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.dataprepper.pipeline.parser.rule;

import com.fasterxml.jackson.core.JsonProcessingException;
Expand Down Expand Up @@ -62,6 +67,7 @@ public RuleEvaluatorResult isTransformationNeeded(PipelinesDataFlowModel pipelin
return RuleEvaluatorResult.builder()
.withEvaluatedResult(true)
.withPipelineTemplateModel(templateModel)
.withFunctionProviders(ruleFileEvaluation.getFunctionProviders())
.withPipelineName(entry.getKey())
.build();
}
Expand All @@ -76,6 +82,7 @@ public RuleEvaluatorResult isTransformationNeeded(PipelinesDataFlowModel pipelin
return RuleEvaluatorResult.builder()
.withEvaluatedResult(false)
.withPipelineName(null)
.withFunctionProviders(null)
.withPipelineTemplateModel(null)
.build();
}
Expand Down Expand Up @@ -131,6 +138,7 @@ private RuleFileEvaluation evaluate(String pipelinesJson) {
return RuleFileEvaluation.builder()
.withPluginName(pluginName)
.withRuleFileName(parsedRule.fileName)
.withFunctionProviders(rulesModel.getFunctionProviders())
.withResult(true)
.build();
}
Expand Down Expand Up @@ -163,4 +171,4 @@ private static class ParsedRule {
this.fileName = fileName;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
/*
* Copyright OpenSearch Contributors
* 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.dataprepper.pipeline.parser.rule;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.opensearch.dataprepper.pipeline.parser.transformer.PipelineTemplateModel;

import java.util.List;

@Builder(setterPrefix = "with")
@Getter
@AllArgsConstructor
Expand All @@ -20,6 +27,8 @@ public class RuleEvaluatorResult {

private PipelineTemplateModel pipelineTemplateModel;

private List<String> functionProviders;

public RuleEvaluatorResult() {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
import lombok.Builder;
import lombok.Data;

import java.util.List;

@Builder(setterPrefix = "with")
@AllArgsConstructor
@Data
public class RuleFileEvaluation {
private Boolean result;
private String ruleFileName;
private String pluginName;
private List<String> functionProviders;

public RuleFileEvaluation() {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/*
* Copyright OpenSearch Contributors
* 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.dataprepper.pipeline.parser.rule;

import com.fasterxml.jackson.annotation.JsonInclude;
Expand All @@ -22,13 +27,17 @@ public class RuleTransformerModel {
@JsonProperty("plugin_name")
private String pluginName;

@JsonProperty("function_providers")
private List<String> functionProviders;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should have a special requirement on the package name as additional protection. For example, the package name must contain dataprepper_transformer. This would place a requirement for packages like:

  • org.opensearch.dataprepper.documentdb.dataprepper_transformer.MyTransformer
  • com.mycompany.myplugin.dataprepper_transformer.MyTransformer


public RuleTransformerModel() {
}

@Override
public String toString() {
return "RuleConfiguration{" +
"applyWhen=" + applyWhen +
"\npluginName="+ pluginName +'}';
"\nfunctionProviders=" + functionProviders +
"\npluginName=" + pluginName + '}';
}
}
}
Loading
Loading