[transform] added a new chain transform

Enables chaining multiple transforms

Original commit: elastic/x-pack-elasticsearch@312b7330df
This commit is contained in:
uboness 2015-02-25 17:33:23 +02:00
parent 46f6572756
commit df491d036f
3 changed files with 249 additions and 0 deletions

View File

@ -7,6 +7,7 @@ package org.elasticsearch.alerts.support.init;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.alerts.transform.ChainTransform;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.Multibinder;
@ -24,6 +25,7 @@ public class InitializingModule extends AbstractModule {
Multibinder<InitializingService.Initializable> mbinder = Multibinder.newSetBinder(binder(), InitializingService.Initializable.class);
mbinder.addBinding().to(ClientProxy.class);
mbinder.addBinding().to(ScriptServiceProxy.class);
mbinder.addBinding().to(ChainTransform.Parser.class);
bind(InitializingService.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.alerts.transform;
import org.elasticsearch.alerts.AlertsSettingsException;
import org.elasticsearch.alerts.ExecutionContext;
import org.elasticsearch.alerts.Payload;
import org.elasticsearch.alerts.support.init.InitializingService;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
/**
*
*/
public class ChainTransform extends Transform {
public static final String TYPE = "chain";
private final ImmutableList<Transform> transforms;
public ChainTransform(ImmutableList<Transform> transforms) {
this.transforms = transforms;
}
@Override
public String type() {
return TYPE;
}
ImmutableList<Transform> transforms() {
return transforms;
}
@Override
public Result apply(ExecutionContext ctx, Payload payload) throws IOException {
for (Transform transform : transforms) {
payload = transform.apply(ctx, payload).payload();
}
return new Result(TYPE, payload);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startArray();
for (Transform transform : transforms) {
builder.startObject()
.field(transform.type(), transform)
.endObject();
}
return builder.endArray();
}
public static class Parser implements Transform.Parser<ChainTransform>, InitializingService.Initializable {
private TransformRegistry registry;
// used by guice
public Parser() {
}
// used for tests
Parser(TransformRegistry registry) {
this.registry = registry;
}
@Override
public void init(Injector injector) {
this.registry = injector.getInstance(TransformRegistry.class);
}
@Override
public String type() {
return TYPE;
}
@Override
public ChainTransform parse(XContentParser parser) throws IOException {
XContentParser.Token token = parser.currentToken();
if (token != XContentParser.Token.START_ARRAY) {
throw new AlertsSettingsException("could not parse [chain] transform. expected an array of objects, but found [" + token + '}');
}
ImmutableList.Builder<Transform> builder = ImmutableList.builder();
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token != XContentParser.Token.START_OBJECT) {
throw new AlertsSettingsException("could not parse [chain] transform. expected a transform object, but found [" + token + "]");
}
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
builder.add(registry.parse(currentFieldName, parser));
} else {
throw new AlertsSettingsException("could not parse [chain] transform. expected a transform object, but found [" + token + "]");
}
}
}
return new ChainTransform(builder.build());
}
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.alerts.transform;
import org.elasticsearch.alerts.ExecutionContext;
import org.elasticsearch.alerts.Payload;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock;
/**
*
*/
public class ChainTransformTests extends ElasticsearchTestCase {
@Test
public void testApply() throws Exception {
ChainTransform transform = new ChainTransform(ImmutableList.<Transform>of(
new NamedTransform("name1"),
new NamedTransform("name2"),
new NamedTransform("name3")));
ExecutionContext ctx = mock(ExecutionContext.class);
Payload payload = new Payload.Simple(new HashMap<String, Object>());
Transform.Result result = transform.apply(ctx, payload);
Map<String, Object> data = result.payload().data();
assertThat(data, notNullValue());
assertThat(data, hasKey("names"));
assertThat(data.get("names"), instanceOf(List.class));
List<String> names = (List<String>) data.get("names");
assertThat(names, hasSize(3));
assertThat(names, contains("name1", "name2", "name3"));
}
@Test
public void testParser() throws Exception {
Map<String, Transform.Parser> parsers = ImmutableMap.<String, Transform.Parser>builder()
.put("named", new NamedTransform.Parser())
.build();
TransformRegistry registry = new TransformRegistry(parsers);
ChainTransform.Parser transformParser = new ChainTransform.Parser(registry);
XContentBuilder builder = jsonBuilder().startArray()
.startObject().startObject("named").field("name", "name1").endObject().endObject()
.startObject().startObject("named").field("name", "name2").endObject().endObject()
.startObject().startObject("named").field("name", "name3").endObject().endObject()
.endArray();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
ChainTransform transform = transformParser.parse(parser);
assertThat(transform, notNullValue());
assertThat(transform.transforms(), notNullValue());
assertThat(transform.transforms(), hasSize(3));
for (int i = 0; i < transform.transforms().size(); i++) {
assertThat(transform.transforms().get(i), instanceOf(NamedTransform.class));
assertThat(((NamedTransform) transform.transforms().get(i)).name, is("name" + (i + 1)));
}
}
private static class NamedTransform extends Transform {
private final String name;
public NamedTransform(String name) {
this.name = name;
}
@Override
public String type() {
return "noop";
}
@Override
public Result apply(ExecutionContext ctx, Payload payload) throws IOException {
Map<String, Object> data = new HashMap<>(payload.data());
List<String> names = (List<String>) data.get("names");
if (names == null) {
names = new ArrayList<>();
data.put("names", names);
}
names.add(name);
return new Result("named", new Payload.Simple(data));
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().field("name", name).endObject();
}
public static class Parser implements Transform.Parser<NamedTransform> {
@Override
public String type() {
return "named";
}
@Override
public NamedTransform parse(XContentParser parser) throws IOException {
assert parser.currentToken() == XContentParser.Token.START_OBJECT;
XContentParser.Token token = parser.nextToken();
assert token == XContentParser.Token.FIELD_NAME; // the "name" field
token = parser.nextToken();
assert token == XContentParser.Token.VALUE_STRING;
String name = parser.text();
token = parser.nextToken();
assert token == XContentParser.Token.END_OBJECT;
return new NamedTransform(name);
}
}
}
}