blob: c6ab62116437ecee4f907c6ab417e566d2dc40b0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.core.workflow;
import java.util.Arrays;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.google.common.base.Suppliers;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport;
import org.apache.brooklyn.core.workflow.steps.variables.TransformVariableWorkflowStep;
import org.apache.brooklyn.entity.stock.BasicApplication;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.text.Strings;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class WorkflowTransformTest extends BrooklynMgmtUnitTestSupport {
protected void loadTypes() {
WorkflowBasicTest.addWorkflowStepTypes(mgmt);
}
BasicApplication app;
@BeforeMethod(alwaysRun = true)
@Override
public void setUp() throws Exception {
super.setUp();
loadTypes();
app = mgmt.getEntityManager().createEntity(EntitySpec.create(BasicApplication.class));
}
@AfterMethod(alwaysRun = true, timeOut = Asserts.THIRTY_SECONDS_TIMEOUT_MS)
@Override
public void tearDown() throws Exception {
if (app!=null) Entities.unmanage(app);
super.tearDown();
}
static Object runWorkflowSteps(Entity entity, String ...steps) {
return WorkflowBasicTest.runWorkflow(entity, Arrays.stream(steps).map(s -> "- "+Strings.indent(2, s).trim()).collect(Collectors.joining("\n")), "test").getTask(false).get().getUnchecked();
}
Object runWorkflowSteps(String ...steps) {
return runWorkflowSteps(app, steps);
}
Object transform(String args) {
return runWorkflowSteps(app, "transform "+args);
}
@Test
public void testTransformShorthand() {
Function<String,Map<String,Object>> parse = shorthand -> {
TransformVariableWorkflowStep s = new TransformVariableWorkflowStep();
s.populateFromShorthand(shorthand);
return s.input;
};
Asserts.assertEquals(parse.apply("variable x"), MutableMap.of(
"variable", MutableMap.of("name", "x")));
Asserts.assertEquals(parse.apply("value ${x}"), MutableMap.of(
"value", "${x}"));
Asserts.assertEquals(parse.apply("x"), MutableMap.of(
"vv_auto", "x"));
Asserts.assertEquals(parse.apply("x = 1 | foo"), MutableMap.of(
"vv_auto", "x",
"value", "1",
"transform", "foo"));
Asserts.assertEquals(parse.apply("integer x = 1 | foo"), MutableMap.of(
"variable", MutableMap.of("type", "integer"),
"vv_auto", "x",
"value", "1",
"transform", "foo"));
Asserts.assertEquals(parse.apply("integer variable x | foo"), MutableMap.of(
"variable", MutableMap.of("name", "x", "type", "integer"),
"transform", "foo"));
Asserts.assertEquals(parse.apply("integer value ${x} | foo"), MutableMap.of(
"variable", MutableMap.of("type", "integer"),
"value", "${x}",
"transform", "foo"));
Asserts.assertFailsWith(() -> parse.apply("integer variable x = 99 | foo"),
e -> Asserts.expectedFailureContainsIgnoreCase(e, "Invalid shorthand expression for transform"));
Asserts.assertFailsWith(() -> parse.apply("integer value x = 99 | foo"),
e -> Asserts.expectedFailureContainsIgnoreCase(e, "Invalid shorthand expression for transform"));
}
@Test
public void testTransformTrim() {
String untrimmed = "Hello, World! ";
String trimmed = untrimmed.trim();
Asserts.assertEquals(
runWorkflowSteps(
"let mystring = '"+untrimmed+"'",
"transform mytrimmedstring = ${mystring} | trim",
"return ${mytrimmedstring}"),
trimmed);
}
@Test
public void testTransformRegex() {
Asserts.assertEquals(transform("value 'silly world' | replace regex l. k"), "siky world");
Asserts.assertEquals(transform("value 'silly world' | replace all regex l. k"), "siky work");
// with slash
Asserts.assertEquals(transform("value 'abc/def/ghi' | replace regex 'c/d' 000"), "ab000ef/ghi");
// with space
Asserts.assertEquals(transform("value 'abc def ghi' | replace regex 'c d' 000"), "ab000ef ghi");
// greedy
Asserts.assertEquals(transform("value 'abc def ghi c2d' | replace regex 'c.*d' 000"), "ab000");
Asserts.assertEquals(transform("value 'abc def ghi c2d' | replace all regex 'c.*d' 000"), "ab000");
// non-greedy qualifier
Asserts.assertEquals(transform("value 'abc def ghi c2d' | replace regex 'c.*?d' 000"), "ab000ef ghi c2d");
Asserts.assertEquals(transform("value 'abc def ghi c2d' | replace all regex 'c.*?d' 000"), "ab000ef ghi 000");
Asserts.assertEquals(transform("value 'abc def ghi' | replace regex 'c d' ''"), "abef ghi");
}
@Test
public void testTransformLiteral() {
Asserts.assertEquals(transform("value 'abc def ghi' | replace literal c.*d 000"), "abc def ghi");
Asserts.assertEquals(transform("value 'abc.*def ghi c.*d' | replace literal c.*d 000"), "ab000ef ghi c.*d");
Asserts.assertEquals(transform("value 'abc.*def ghi c.*d' | replace all literal c.*d 000"), "ab000ef ghi 000");
}
@Test
public void testTransformGlob() {
Asserts.assertEquals(transform("value 'abc def ghi' | replace glob c*e 000"), "ab000f ghi");
// glob is greedy, unless all is specified where it is not
Asserts.assertEquals(transform("value 'abc def ghi c2e' | replace glob c*e 000"), "ab000");
Asserts.assertEquals(transform("value 'abc def ghi c2e' | replace all glob c*e 000"), "ab000f ghi 000");
}
@Test
public void testMapDirect() {
Asserts.assertEquals(runWorkflowSteps(
"let map myMap = {a: 1}",
"let myMap.a = ${myMap.a} + 2",
"return ${myMap.a}"),
3);
}
@Test
public void testReturnTransformWithMapYaml() {
Asserts.assertEquals(WorkflowBasicTest.runWorkflow(app, Strings.lines(
"- step: let s",
" value: |",
" bogus",
" - not valid: yaml: here",
" that's: okay",
" ---",
" key: value",
"- transform ${s} | yaml | return",
"- return should not come here",
""), "test").getTask(false).get().getUnchecked(),
MutableMap.of("key", "value"));
}
@Test
public void testSetVarTransform() {
Asserts.assertEquals(WorkflowBasicTest.runWorkflow(app, Strings.lines(
"- step: let s",
" value: \"key: Value\"",
"- transform ${s} | yaml | set y",
"- transform y.key2 = ${output.key} | to_upper_case",
"- transform ${output.key} | to_lower_case", // output should still be the yaml map transformed from ${s}
"- transform ${output} | set y.key3", // output passed in here will be 'value' from previous step
"- transform true | set y.key4a",
"- transform value true | set y.key4b",
"- transform boolean value true | set y.key4c",
"- return ${y}",
""), "test").getTask(true).get().getUnchecked(),
MutableMap.of("key", "Value", "key2", "VALUE", "key3", "value", "key4a", "true", "key4b", "true", "key4c", true));
}
@Test
public void testValueTransform() {
Asserts.assertEquals(WorkflowBasicTest.runWorkflow(app, Strings.lines(
"- let map m = { a: x }",
"- transform value ${m} | size"
), "test").getTask(true).get().getUnchecked(), 1);
// "- let x = hello",
// "- transform value ${x} | append world"), "test").getTask(true).get().getUnchecked(), "helloworld");
// "- let apply_result = hello",
// "- transform value ${apply_result} | to_upper_case"
// , "- return ${apply_result}"
// ), "test").getTask(true).get().getUnchecked(), "HELLO");
}
@Test
public void testResolveTransform() {
Asserts.assertEquals(runWorkflowSteps(
"let a = b",
"let b = c",
"let x = \"${\" ${a} \"}\"",
"transform variable x | resolve_expression",
"return ${x}"
), "c");
Asserts.assertEquals(runWorkflowSteps(
"let a = b",
"let b = c",
"transform value {${a}} | prepend $ | resolve_expression | return"
), "c");
}
@Test
public void testSliceRemoveAndAppendTransform() {
// slice list
Asserts.assertEquals(transform("value ['a','bb','ccc'] | type list | slice 1"), MutableList.of("bb", "ccc"));
Asserts.assertEquals(transform("value ['a','bb','ccc'] | type list | slice 1 -1"), MutableList.of("bb"));
Asserts.assertEquals(transform("value ['a','bb','ccc'] | type list | slice -1"), MutableList.of("ccc"));
// slice string
Asserts.assertEquals(transform("value abc | slice 1"), "bc");
// append and prepend list
Asserts.assertEquals(transform("value ['a','bb'] | type list | append ccc"), MutableList.of("a", "bb", "ccc"));
Asserts.assertEquals(runWorkflowSteps(
"transform value ['ccc'] | type list | set c",
"transform value ['a','bb'] | type list | append ${c}"
), MutableList.of("a", "bb", MutableList.of("ccc")));
Asserts.assertEquals(transform("value ['a','bb'] | type list | prepend ccc"), MutableList.of("ccc", "a", "bb"));
// append and prepend string
Asserts.assertEquals(transform("value hello | append _world"), "hello_world");
Asserts.assertEquals(transform("value hello | append \" world\""), "hello world");
// remove list
Asserts.assertEquals(transform("value ['a','bb','ccc'] | type list | remove 1"), MutableList.of("a", "ccc"));
// remove map
Asserts.assertEquals(
runWorkflowSteps(
"step: transform\n" +
"transform: type map | remove b\n" +
"value:\n" +
" a: 1\n" +
" b: 2"),
MutableMap.of("a", 1));
}
@Test
public void testTransformJoin() {
Asserts.assertEquals( runWorkflowSteps(
"let list words = [ \"hello\" , 1, \"world\" ]",
"transform ${words} | join \" \" | return"),
"hello 1 world");
}
@Test
public void testTransformSplit() {
BiFunction<String,String,Object> split = (input, command) -> runWorkflowSteps(
"let words = "+input,
"transform ${words} | split "+command);
Asserts.assertEquals( split.apply("\"hello 1 world\"", "\" \""), MutableList.of("hello", "1", "world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "\" \""), MutableList.of("hello", "1 world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "literal \" \""), MutableList.of("hello", "1 world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "regex \" +\""), MutableList.of("hello", "1", "world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "\" +\""), MutableList.of("hello 1 world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "keep_delimiters regex \" +\""), MutableList.of("hello", " ", "1", " ", "world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "limit 4 keep_delimiters regex \" +\""), MutableList.of("hello", " ", "1", " "));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "keep_delimiters \" \""), MutableList.of("hello", " ", "", " ", "1", " ", "world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "keep_delimiters regex \" \""), MutableList.of("hello", " ", "", " ", "1", " ", "world"));
Asserts.assertEquals( split.apply("\"hello 1 world\"", "\" \""), MutableList.of("hello", "", "1", "world"));
Asserts.assertEquals( split.apply("\" hello 1 world \"", "\" \" | trim"), MutableList.of("hello", "1", "world"));
// leading and trailing matches generate an empty string in the output list
Asserts.assertEquals( split.apply("\" h ey \"", "\" \""), MutableList.of("", "h", "ey", ""));
Asserts.assertEquals( split.apply("\" h ey \"", "regex \" \""), MutableList.of("", "h", "ey", ""));
// empty string matches every break, including start and end, once
Asserts.assertEquals( split.apply("\"hey\"", "regex \"\""), MutableList.of("", "h", "e", "y", ""));
Asserts.assertEquals( split.apply("\"hey\"", "\"\""), MutableList.of("", "h", "e", "y", ""));
Asserts.assertEquals( split.apply("\"\"", "regex \"\""), MutableList.of(""));
Asserts.assertEquals( split.apply("\"\"", "\"\""), MutableList.of(""));
}
@Test
public void testTransformSum() {
Asserts.assertEquals( runWorkflowSteps(
"let list nums = [ 1, 2, 3]",
"transform ${nums} | sum | return"),
6);
Asserts.assertFailsWith( () -> runWorkflowSteps(
"let list nums = [ 1, 2, 3]",
"transform ${nums} | sum 4 | return"),
e -> Asserts.expectedFailureContainsIgnoreCase(e, "sum", "does not accept args", "4"));
}
// done in camp project, WorkflowExpressionsYamlTest
//public void testTransformGet() {
}