From 389804a39a4dd894da7ee138558b1c4b4ceda70b Mon Sep 17 00:00:00 2001 From: gezuao <673843192@qq.com> Date: Mon, 16 Oct 2023 21:03:48 +0800 Subject: [PATCH 01/21] =?UTF-8?q?EL=E8=A1=A8=E8=BE=BE=E5=BC=8F=E7=BB=84?= =?UTF-8?q?=E8=A3=85=E5=8D=95=E6=B5=8B=E4=BD=BF=E7=94=A8validate=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=EF=BC=8C=E5=B9=B6=E4=BF=AE=E5=A4=8D=E5=88=86=E5=8F=B7?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E3=80=81data=E8=B0=83=E7=94=A8=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/builder/el/AndELWrapper.java | 31 +--- .../liteflow/builder/el/CatchELWrapper.java | 28 +--- .../liteflow/builder/el/ELWrapper.java | 39 +++-- .../liteflow/builder/el/FinallyELWrapper.java | 32 +--- .../liteflow/builder/el/ForELWrapper.java | 26 --- .../liteflow/builder/el/IfELWrapper.java | 28 +--- .../builder/el/IteratorELWrapper.java | 26 --- .../liteflow/builder/el/LoopELWrapper.java | 28 +--- .../liteflow/builder/el/NodeELWrapper.java | 17 +- .../liteflow/builder/el/NotELWrapper.java | 28 +--- .../liteflow/builder/el/OrELWrapper.java | 28 +--- .../liteflow/builder/el/PreELWrapper.java | 28 +--- .../liteflow/builder/el/SwitchELWrapper.java | 28 +--- .../liteflow/builder/el/ThenELWrapper.java | 35 +--- .../liteflow/builder/el/WhenELWrapper.java | 28 +--- .../liteflow/builder/el/WhileELWrapper.java | 26 --- .../test/builder/CatchELBuilderTest.java | 45 ++++-- .../test/builder/ComplexELBuilderTest.java | 76 ++++++++- .../test/builder/IfELBuilderTest.java | 121 +++++++++----- .../test/builder/LogicELBuilderTest.java | 93 +++++++---- .../test/builder/LoopELBuilderTest.java | 151 +++++++++++------- .../test/builder/NodeELBuilderTest.java | 35 ++-- .../test/builder/SwitchELBuilderTest.java | 53 +++--- .../liteflow/test/builder/TestContext.java | 25 +++ .../test/builder/ThenELBuilderTest.java | 103 +++++++----- .../test/builder/WhenELBuilderTest.java | 79 ++++++--- .../liteflow/test/builder/cmp/ACmp.java | 21 +++ .../liteflow/test/builder/cmp/BCmp.java | 25 +++ .../liteflow/test/builder/cmp/CCmp.java | 24 +++ .../CustomThreadExecutor1.java | 29 ++++ .../liteflow/test/builder/vo/User.java | 43 +++++ 31 files changed, 738 insertions(+), 641 deletions(-) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/TestContext.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/CCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/customTreadExecutor/CustomThreadExecutor1.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/vo/User.java diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/AndELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/AndELWrapper.java index 2a6695983..ab2d9107a 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/AndELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/AndELWrapper.java @@ -39,40 +39,15 @@ public class AndELWrapper extends ELWrapper { } @Override - public AndELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public AndELWrapper data(String dataName, String jsonString) { - // 校验字符串符合Json格式 - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public AndELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - - @Override - protected AndELWrapper maxWaitSeconds(Integer maxWaitSeconds){ + public AndELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); return this; } @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + // 根据depth是否为null,决定输出是否格式化 Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/CatchELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/CatchELWrapper.java index 93fe18adb..3ecaa5471 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/CatchELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/CatchELWrapper.java @@ -39,32 +39,6 @@ public class CatchELWrapper extends ELWrapper { return this; } - @Override - public CatchELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public CatchELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public CatchELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public CatchELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -73,6 +47,8 @@ public class CatchELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java index e251a407c..0bc4acd83 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.builder.el; import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.util.JsonUtil; import java.util.ArrayList; import java.util.Arrays; @@ -111,7 +112,11 @@ public abstract class ELWrapper { * @param object JavaBean * @return {@link ELWrapper} */ - protected abstract ELWrapper data(String dataName, Object object); + protected ELWrapper data(String dataName, Object object){ + setData("'" + JsonUtil.toJsonString(object) + "'"); + setDataName(dataName); + return this; + } /** * 设置表达式data属性 @@ -120,7 +125,11 @@ public abstract class ELWrapper { * @param jsonString JSON格式字符串 * @return {@link ELWrapper} */ - protected abstract ELWrapper data(String dataName, String jsonString); + protected ELWrapper data(String dataName, String jsonString){ + setData(jsonString); + setDataName(dataName); + return this; + } /** * 设置data属性 @@ -129,7 +138,12 @@ public abstract class ELWrapper { * @param jsonMap 键值映射 * @return {@link ELWrapper} */ - protected abstract ELWrapper data(String dataName, Map jsonMap); + protected ELWrapper data(String dataName, Map jsonMap){ + setData("'" + JsonUtil.toJsonString(jsonMap) + "'"); + setDataName(dataName); + return this; + } + protected ELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -144,7 +158,7 @@ public abstract class ELWrapper { public String toEL(){ StringBuilder paramContext = new StringBuilder(); String elContext = toEL(null, paramContext); - return paramContext.append(elContext).toString(); + return paramContext.append(elContext).append(";").toString(); } /** @@ -161,7 +175,7 @@ public abstract class ELWrapper { } else { elContext = toEL(0, paramContext); } - return paramContext.append(elContext).toString(); + return paramContext.append(elContext).append(";").toString(); } /** @@ -186,10 +200,6 @@ public abstract class ELWrapper { if(this.getTag() != null){ elContext.append(StrUtil.format(".tag(\"{}\")", this.getTag())); } - if(this.getData() != null){ - elContext.append(StrUtil.format(".data({})", this.getDataName())); - paramContext.append(StrUtil.format("{} = '{}';\n", this.getDataName(), this.getData())); - } if(this.getMaxWaitSeconds() != null){ elContext.append(StrUtil.format(".maxWaitSeconds({})", String.valueOf(this.getMaxWaitSeconds()))); } @@ -218,4 +228,15 @@ public abstract class ELWrapper { elContext.append(StrUtil.repeat(ELBus.TAB, depth)); } } + + /** + * 检查子表达式是否有最长等待秒数定义 + */ + protected void checkMaxWaitSeconds(){ + for(ELWrapper sonElWrapper : this.getElWrapperList()){ + if(sonElWrapper != null && sonElWrapper.getMaxWaitSeconds() != null){ + throw new IllegalArgumentException("maxWaitSeconds必须定义在完整的语义之后!"); + } + } + } } diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/FinallyELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/FinallyELWrapper.java index 5dc7e2afd..210582e28 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/FinallyELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/FinallyELWrapper.java @@ -31,32 +31,6 @@ public class FinallyELWrapper extends ELWrapper { return this; } - @Override - public FinallyELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public FinallyELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public FinallyELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - /** * 后置组件无法设置maxWaitSeconds属性,重载用protected修饰 * @@ -71,6 +45,8 @@ public class FinallyELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); @@ -108,9 +84,5 @@ public class FinallyELWrapper extends ELWrapper { if(this.getTag() != null){ elContext.append(StrUtil.format(".tag(\"{}\")", this.getTag())); } - if(this.getData() != null){ - elContext.append(StrUtil.format(".data({})", this.getDataName())); - paramContext.append(StrUtil.format("{} = '{}'\n", this.getDataName(), this.getData())); - } } } diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ForELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ForELWrapper.java index c57029cb7..27edafd97 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ForELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ForELWrapper.java @@ -55,32 +55,6 @@ public class ForELWrapper extends LoopELWrapper { return this; } - @Override - public ForELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public ForELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public ForELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public ForELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IfELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IfELWrapper.java index 34100a303..e6d79e882 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IfELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IfELWrapper.java @@ -213,32 +213,6 @@ public class IfELWrapper extends ELWrapper { return this; } - @Override - public IfELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public IfELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public IfELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public IfELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -247,6 +221,8 @@ public class IfELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IteratorELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IteratorELWrapper.java index f488cbbc4..5c4f1dd31 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IteratorELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IteratorELWrapper.java @@ -43,32 +43,6 @@ public class IteratorELWrapper extends LoopELWrapper { return this; } - @Override - public IteratorELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public IteratorELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public IteratorELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public IteratorELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/LoopELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/LoopELWrapper.java index a235cc72e..e7113c78d 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/LoopELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/LoopELWrapper.java @@ -54,32 +54,6 @@ public abstract class LoopELWrapper extends ELWrapper { return this; } - @Override - public LoopELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public LoopELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public LoopELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public LoopELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -88,6 +62,8 @@ public abstract class LoopELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java index 8fe1f73d0..f3cf92dd1 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java @@ -60,18 +60,18 @@ public class NodeELWrapper extends ELWrapper { @Override public NodeELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); + setData("'" + JsonUtil.toJsonString(object) + "'"); setDataName(dataName); return this; } @Override public NodeELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } +// try { +// JsonUtil.parseObject(jsonString); +// } catch (Exception e){ +// throw new RuntimeException("字符串不符合Json格式!"); +// } setData(jsonString); setDataName(dataName); return this; @@ -79,7 +79,7 @@ public class NodeELWrapper extends ELWrapper { @Override public NodeELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); + setData("'" + JsonUtil.toJsonString(jsonMap) + "'"); setDataName(dataName); return this; } @@ -114,7 +114,8 @@ public class NodeELWrapper extends ELWrapper { } if(this.getData() != null){ elContext.append(StrUtil.format(".data({})", this.getDataName())); - paramContext.append(StrUtil.format("{} = '{}'\n", this.getDataName(), this.getData())); +// paramContext.append(StrUtil.format("{} = '{}'\n", this.getDataName(), this.getData())); + paramContext.append(StrUtil.format("{} = {}", this.getDataName(), this.getData())).append(";\n"); } if(this.getMaxWaitSeconds() != null){ elContext.append(StrUtil.format(".maxWaitSeconds({})", String.valueOf(this.getMaxWaitSeconds()))); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NotELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NotELWrapper.java index 8dfbde83b..9782b952d 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NotELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NotELWrapper.java @@ -30,32 +30,6 @@ public class NotELWrapper extends ELWrapper { return this; } - @Override - public NotELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public NotELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public NotELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public NotELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -64,6 +38,8 @@ public class NotELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/OrELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/OrELWrapper.java index fe4bae56a..cdf599cc1 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/OrELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/OrELWrapper.java @@ -38,32 +38,6 @@ public class OrELWrapper extends ELWrapper { return this; } - @Override - public OrELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public OrELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public OrELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public OrELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -72,6 +46,8 @@ public class OrELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/PreELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/PreELWrapper.java index d993229a7..b80f83b91 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/PreELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/PreELWrapper.java @@ -30,32 +30,6 @@ public class PreELWrapper extends ELWrapper { return this; } - @Override - public PreELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public PreELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public PreELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public PreELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -64,6 +38,8 @@ public class PreELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SwitchELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SwitchELWrapper.java index 90198de14..97bf45d74 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SwitchELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SwitchELWrapper.java @@ -50,32 +50,6 @@ public class SwitchELWrapper extends ELWrapper { return this; } - @Override - public SwitchELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public SwitchELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public SwitchELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public SwitchELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -84,6 +58,8 @@ public class SwitchELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ThenELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ThenELWrapper.java index 5d9c17cdf..63a32aba5 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ThenELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ThenELWrapper.java @@ -76,39 +76,6 @@ public class ThenELWrapper extends ELWrapper { return this; } - /** - * data关键字的约束:允许以Bean、jsonString、map类型输入数据,必须包含dataName参数。 - * - * @param dataName data名称 - * @param javaBean JavaBean - * @return {@link ThenELWrapper} - */ - @Override - public ThenELWrapper data(String dataName, Object javaBean) { - setData(JsonUtil.toJsonString(javaBean)); - setDataName(dataName); - return this; - } - - @Override - public ThenELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public ThenELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public ThenELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -117,6 +84,8 @@ public class ThenELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhenELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhenELWrapper.java index 9008b9e71..35c97bd2d 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhenELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhenELWrapper.java @@ -69,32 +69,6 @@ public class WhenELWrapper extends ELWrapper { return this; } - @Override - public WhenELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public WhenELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public WhenELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public WhenELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); @@ -103,6 +77,8 @@ public class WhenELWrapper extends ELWrapper { @Override protected String toEL(Integer depth, StringBuilder paramContext) { + checkMaxWaitSeconds(); + Integer sonDepth = depth == null ? null : depth + 1; StringBuilder sb = new StringBuilder(); diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhileELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhileELWrapper.java index 3aff09f02..90f91a6c6 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhileELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhileELWrapper.java @@ -50,32 +50,6 @@ public class WhileELWrapper extends LoopELWrapper { return this; } - @Override - public WhileELWrapper data(String dataName, Object object) { - setData(JsonUtil.toJsonString(object)); - setDataName(dataName); - return this; - } - - @Override - public WhileELWrapper data(String dataName, String jsonString) { - try { - JsonUtil.parseObject(jsonString); - } catch (Exception e){ - throw new RuntimeException("字符串不符合Json格式!"); - } - setData(jsonString); - setDataName(dataName); - return this; - } - - @Override - public WhileELWrapper data(String dataName, Map jsonMap) { - setData(JsonUtil.toJsonString(jsonMap)); - setDataName(dataName); - return this; - } - @Override public WhileELWrapper maxWaitSeconds(Integer maxWaitSeconds){ setMaxWaitSeconds(maxWaitSeconds); diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/CatchELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/CatchELBuilderTest.java index 4be667cba..0dabcf5c7 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/CatchELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/CatchELBuilderTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.builder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -22,68 +23,76 @@ public class CatchELBuilderTest extends BaseTest { // catch捕获异常调用测试 @Test public void testCatch1(){ - String expectedStr = "CATCH(THEN(node(\"a\"),node(\"b\"))).DO(node(\"c\"))"; + String expectedStr = "CATCH(THEN(node(\"a\"),node(\"b\"))).DO(node(\"c\"));"; Assertions.assertEquals(expectedStr, ELBus.catchException(ELBus.then("a", "b")).doOpt("c").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException(ELBus.then("a", "b")).doOpt("c").toEL())); } @Test public void testCatch2(){ - String expectedStr = "CATCH(\n\tTHEN(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n)"; + String expectedStr = "CATCH(\n\tTHEN(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n);"; Assertions.assertEquals(expectedStr, ELBus.catchException(ELBus.then("a", "b")).doOpt("c").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException(ELBus.then("a", "b")).doOpt("c").toEL(true))); } // 属性设置测试 @Test public void testCatch3(){ - String expectedStr = "CATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3)"; + String expectedStr = "CATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, ELBus.catchException("a").doOpt(ELBus.then("b", "c")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL())); } @Test public void testCatch4(){ - String expectedStr = "CATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3)"; + String expectedStr = "CATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, ELBus.catchException("a").doOpt(ELBus.then("b", "c")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL(true))); } // data 设置 jsonStr @Test public void testCatch5(){ - String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(catchData)"; + String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(catchData)));"; Assertions.assertEquals(expectedStr, - ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", "{\"name\":\"zhangsan\",\"age\":18}").toEL()); + ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL())); } @Test public void testCatch6(){ - String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(catchData)"; + String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(catchData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true)); + ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL(true)); System.out.println(expectedStr); - } + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL(true))); + } // data 设置 map @Test public void testCatch7(){ Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(catchData)"; + String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(catchData)));"; Assertions.assertEquals(expectedStr, - ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL()); + ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL())); } @Test public void testCatch8(){ Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(catchData)"; + String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(catchData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL(true)); + ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL(true))); } private static class ParamClass{ private String name; @@ -101,19 +110,21 @@ public class CatchELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(catchData)"; + String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(catchData)));"; Assertions.assertEquals(expectedStr, - ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL()); + ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL())); } @Test public void testCatch10(){ ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(catchData)"; + String expectedStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(catchData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL(true)); + ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.catchException("a").doOpt(ELBus.then("b", ELBus.node("c").data("catchData", name2Value))).toEL(true))); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ComplexELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ComplexELBuilderTest.java index 135437fe8..0da5dfd05 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ComplexELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ComplexELBuilderTest.java @@ -1,12 +1,28 @@ package com.yomahub.liteflow.test.builder; +import cn.hutool.core.date.DateUtil; +import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.ELWrapper; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.builder.el.ThenELWrapper; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; +import com.yomahub.liteflow.test.builder.cmp.ACmp; +import com.yomahub.liteflow.test.builder.cmp.BCmp; +import com.yomahub.liteflow.test.builder.vo.User; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; + +import javax.annotation.Resource; +import java.util.Date; /** * 复杂编排例子测试 @@ -16,7 +32,12 @@ import org.springframework.boot.test.context.SpringBootTest; */ @SpringBootTest(classes = ComplexELBuilderTest.class) @EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.builder.ComplexELBuilderTest" }) public class ComplexELBuilderTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + /* 复杂编排例子1 THEN( @@ -52,12 +73,12 @@ public class ComplexELBuilderTest extends BaseTest { ), "Z" ); - String expectedStr = "THEN(node(\"A\"),WHEN(THEN(node(\"B\"),node(\"C\")),THEN(node(\"D\"),node(\"E\"),node(\"F\")),THEN(SWITCH(node(\"G\")).TO(THEN(node(\"H\"),node(\"I\"),WHEN(node(\"J\"),node(\"K\"))).id(\"t1\"),THEN(node(\"L\"),node(\"M\")).id(\"t2\")),node(\"N\"))),node(\"Z\"))"; + String expectedStr = "THEN(node(\"A\"),WHEN(THEN(node(\"B\"),node(\"C\")),THEN(node(\"D\"),node(\"E\"),node(\"F\")),THEN(SWITCH(node(\"G\")).TO(THEN(node(\"H\"),node(\"I\"),WHEN(node(\"J\"),node(\"K\"))).id(\"t1\"),THEN(node(\"L\"),node(\"M\")).id(\"t2\")),node(\"N\"))),node(\"Z\"));"; Assertions.assertEquals(expectedStr, complexEl1.toEL()); System.out.println(expectedStr); - expectedStr = "THEN(\n\tnode(\"A\"),\n\tWHEN(\n\t\tTHEN(\n\t\t\tnode(\"B\"),\n\t\t\tnode(\"C\")\n\t\t),\n\t\tTHEN(\n\t\t\tnode(\"D\"),\n\t\t\tnode(\"E\"),\n\t\t\tnode(\"F\")\n\t\t),\n\t\tTHEN(\n\t\t\tSWITCH(node(\"G\")).TO(\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"H\"),\n\t\t\t\t\tnode(\"I\"),\n\t\t\t\t\tWHEN(\n\t\t\t\t\t\tnode(\"J\"),\n\t\t\t\t\t\tnode(\"K\")\n\t\t\t\t\t)\n\t\t\t\t).id(\"t1\"),\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"L\"),\n\t\t\t\t\tnode(\"M\")\n\t\t\t\t).id(\"t2\")\n\t\t\t),\n\t\t\tnode(\"N\")\n\t\t)\n\t),\n\tnode(\"Z\")\n)"; + expectedStr = "THEN(\n\tnode(\"A\"),\n\tWHEN(\n\t\tTHEN(\n\t\t\tnode(\"B\"),\n\t\t\tnode(\"C\")\n\t\t),\n\t\tTHEN(\n\t\t\tnode(\"D\"),\n\t\t\tnode(\"E\"),\n\t\t\tnode(\"F\")\n\t\t),\n\t\tTHEN(\n\t\t\tSWITCH(node(\"G\")).TO(\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"H\"),\n\t\t\t\t\tnode(\"I\"),\n\t\t\t\t\tWHEN(\n\t\t\t\t\t\tnode(\"J\"),\n\t\t\t\t\t\tnode(\"K\")\n\t\t\t\t\t)\n\t\t\t\t).id(\"t1\"),\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"L\"),\n\t\t\t\t\tnode(\"M\")\n\t\t\t\t).id(\"t2\")\n\t\t\t),\n\t\t\tnode(\"N\")\n\t\t)\n\t),\n\tnode(\"Z\")\n);"; Assertions.assertEquals(expectedStr, complexEl1.toEL(true)); System.out.println(expectedStr); @@ -106,15 +127,62 @@ public class ComplexELBuilderTest extends BaseTest { ), ELBus.node("Z") ); - String expectedStr = "THEN(node(\"A\"),SWITCH(node(\"B\")).TO(THEN(node(\"D\"),node(\"E\"),node(\"F\")).id(\"t1\"),THEN(node(\"C\"),WHEN(THEN(SWITCH(node(\"G\")).TO(THEN(node(\"H\"),node(\"I\")).id(\"t2\"),node(\"J\")),node(\"K\")),THEN(node(\"L\"),node(\"M\")))).id(\"t3\")),node(\"Z\"))"; + String expectedStr = "THEN(node(\"A\"),SWITCH(node(\"B\")).TO(THEN(node(\"D\"),node(\"E\"),node(\"F\")).id(\"t1\"),THEN(node(\"C\"),WHEN(THEN(SWITCH(node(\"G\")).TO(THEN(node(\"H\"),node(\"I\")).id(\"t2\"),node(\"J\")),node(\"K\")),THEN(node(\"L\"),node(\"M\")))).id(\"t3\")),node(\"Z\"));"; Assertions.assertEquals(expectedStr, complexEl2.toEL()); System.out.println(expectedStr); - expectedStr = "THEN(\n\tnode(\"A\"),\n\tSWITCH(node(\"B\")).TO(\n\t\tTHEN(\n\t\t\tnode(\"D\"),\n\t\t\tnode(\"E\"),\n\t\t\tnode(\"F\")\n\t\t).id(\"t1\"),\n\t\tTHEN(\n\t\t\tnode(\"C\"),\n\t\t\tWHEN(\n\t\t\t\tTHEN(\n\t\t\t\t\tSWITCH(node(\"G\")).TO(\n\t\t\t\t\t\tTHEN(\n\t\t\t\t\t\t\tnode(\"H\"),\n\t\t\t\t\t\t\tnode(\"I\")\n\t\t\t\t\t\t).id(\"t2\"),\n\t\t\t\t\t\tnode(\"J\")\n\t\t\t\t\t),\n\t\t\t\t\tnode(\"K\")\n\t\t\t\t),\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"L\"),\n\t\t\t\t\tnode(\"M\")\n\t\t\t\t)\n\t\t\t)\n\t\t).id(\"t3\")\n\t),\n\tnode(\"Z\")\n)"; + expectedStr = "THEN(\n\tnode(\"A\"),\n\tSWITCH(node(\"B\")).TO(\n\t\tTHEN(\n\t\t\tnode(\"D\"),\n\t\t\tnode(\"E\"),\n\t\t\tnode(\"F\")\n\t\t).id(\"t1\"),\n\t\tTHEN(\n\t\t\tnode(\"C\"),\n\t\t\tWHEN(\n\t\t\t\tTHEN(\n\t\t\t\t\tSWITCH(node(\"G\")).TO(\n\t\t\t\t\t\tTHEN(\n\t\t\t\t\t\t\tnode(\"H\"),\n\t\t\t\t\t\t\tnode(\"I\")\n\t\t\t\t\t\t).id(\"t2\"),\n\t\t\t\t\t\tnode(\"J\")\n\t\t\t\t\t),\n\t\t\t\t\tnode(\"K\")\n\t\t\t\t),\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"L\"),\n\t\t\t\t\tnode(\"M\")\n\t\t\t\t)\n\t\t\t)\n\t\t).id(\"t3\")\n\t),\n\tnode(\"Z\")\n);"; Assertions.assertEquals(expectedStr, complexEl2.toEL(true)); System.out.println(expectedStr); } + /** + * 创建Node,创建EL表达式,创建Chain + * 执行Chain,校验Data参数 + */ + @Test + public void test3(){ + LiteFlowNodeBuilder.createNode() + .setId("a") + .setName("组件A") + .setType(NodeTypeEnum.COMMON) + .setClazz(ACmp.class) + .build(); + LiteFlowNodeBuilder.createNode() + .setId("b") + .setName("组件B") + .setType(NodeTypeEnum.COMMON) + .setClazz(BCmp.class) + .build(); + + ELWrapper el = ELBus.then(ELBus.node("a").data("sql", "\"select * from member t where t.id=10001\""), + ELBus.node("b").data("cmpData", "'{\"name\":\"jack\",\"age\":27,\"birth\":\"1995-10-01\"}'")); + + String expectStr = "sql = \"select * from member t\n" + + " where t.id=10001\";\n" + + " cmpData = '{\"name\":\"jack\",\"age\":27,\"birth\":\"1995-10-01\"}';\n" + + "\n" + + " THEN(\n" + + " node(\"a\").data(sql),\n" + + " node(\"b\").data(cmpData)\n" + + " );"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(expectStr)); + + Assertions.assertTrue(LiteFlowChainELBuilder.validate(el.toEL())); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(el.toEL(true))); + LiteFlowChainELBuilder.createChain().setChainId("chain1").setEL( + el.toEL(true) + ).build(); + + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertTrue(response.isSuccess()); + + DefaultContext context = response.getFirstContextBean(); + User user = context.getData("user"); + Assertions.assertEquals(27, user.getAge()); + Assertions.assertEquals("jack", user.getName()); + Assertions.assertEquals(0, user.getBirth().compareTo(DateUtil.parseDate("1995-10-01").toJdkDate())); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/IfELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/IfELBuilderTest.java index 9ba7e6807..11670b8f9 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/IfELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/IfELBuilderTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.builder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -22,194 +23,224 @@ public class IfELBuilderTest extends BaseTest { // if三元函数测试 @Test public void testIf1(){ - String expectedStr = "IF(node(\"a\"),THEN(node(\"c\"),node(\"d\")),WHEN(node(\"e\"),node(\"f\")))"; + String expectedStr = "IF(node(\"a\"),THEN(node(\"c\"),node(\"d\")),WHEN(node(\"e\"),node(\"f\")));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", ELBus.then("c", "d"), ELBus.when("e", "f")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", ELBus.then("c", "d"), ELBus.when("e", "f")).toEL())); } // 格式化输出测试 @Test public void testIf2(){ - String expectedStr = "IF(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t),\n\tWHEN(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n)"; + String expectedStr = "IF(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t),\n\tWHEN(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.node("a"), ELBus.then("c", "d"), ELBus.when("e", "f")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.node("a"), ELBus.then("c", "d"), ELBus.when("e", "f")).toEL(true))); } // If二元函数测试 @Test public void testIf3(){ - String expectedStr = "IF(node(\"a\"),THEN(node(\"b\"),node(\"c\"))).ELSE(WHEN(node(\"c\"),node(\"d\")))"; + String expectedStr = "IF(node(\"a\"),THEN(node(\"b\"),node(\"c\"))).ELSE(WHEN(node(\"c\"),node(\"d\")));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.node("a"), ELBus.then("b", "c")).elseOpt(ELBus.when("c", "d")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.node("a"), ELBus.then("b", "c")).elseOpt(ELBus.when("c", "d")).toEL())); } // 格式化输出测试 @Test public void testIf4(){ - String expectedStr = "IF(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).ELSE(\n\tWHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n)"; + String expectedStr = "IF(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).ELSE(\n\tWHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", ELBus.then("b", "c")).elseOpt(ELBus.when("c", "d")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", ELBus.then("b", "c")).elseOpt(ELBus.when("c", "d")).toEL(true))); } // ELIF调用测试 @Test public void testIf5(){ - String expectedStr = "IF(node(\"a\"),node(\"b\")).ELIF(node(\"f1\"),node(\"c\")).ELIF(node(\"f2\"),node(\"d\")).ELSE(node(\"e\"))"; + String expectedStr = "IF(node(\"a\"),node(\"b\")).ELIF(node(\"f1\"),node(\"c\")).ELIF(node(\"f2\"),node(\"d\")).ELSE(node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b").elIfOpt("f1", "c").elIfOpt("f2","d").elseOpt("e").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b").elIfOpt("f1", "c").elIfOpt("f2","d").elseOpt("e").toEL())); } // 格式化输出测试 @Test public void testIf6(){ - String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\")\n).ELIF(\n\tnode(\"f1\"),\n\tnode(\"c\")\n).ELIF(\n\tnode(\"f2\"),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n)"; + String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\")\n).ELIF(\n\tnode(\"f1\"),\n\tnode(\"c\")\n).ELIF(\n\tnode(\"f2\"),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b").elIfOpt("f1", "c").elIfOpt("f2","d").elseOpt("e").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b").elIfOpt("f1", "c").elIfOpt("f2","d").elseOpt("e").toEL(true))); } // IF嵌套调用测试 @Test public void testIf7(){ - String expectedStr = "IF(node(\"a\"),node(\"b\"),IF(node(\"c\"),node(\"d\")).ELSE(node(\"e\")))"; + String expectedStr = "IF(node(\"a\"),node(\"b\"),IF(node(\"c\"),node(\"d\")).ELSE(node(\"e\")));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b", ELBus.ifOpt("c", "d").elseOpt("e")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.ifOpt("c", "d").elseOpt("e")).toEL())); } // 格式化输出测试 @Test public void testIf8(){ - String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tIF(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t).ELSE(\n\t\tnode(\"e\")\n\t)\n)"; + String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tIF(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t).ELSE(\n\t\tnode(\"e\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b", ELBus.ifOpt("c", "d").elseOpt("e")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.ifOpt("c", "d").elseOpt("e")).toEL(true))); } // IF嵌套调用测试 @Test public void testIf9(){ - String expectedStr = "IF(node(\"a\"),node(\"b\")).ELSE(IF(node(\"c\"),node(\"d\"),node(\"e\")))"; + String expectedStr = "IF(node(\"a\"),node(\"b\")).ELSE(IF(node(\"c\"),node(\"d\"),node(\"e\")));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b").elseOpt(ELBus.ifOpt("c", "d", "e")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b").elseOpt(ELBus.ifOpt("c", "d", "e")).toEL())); } // 格式化输出测试 @Test public void testIf10(){ - String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\")\n).ELSE(\n\tIF(\n\t\tnode(\"c\"),\n\t\tnode(\"d\"),\n\t\tnode(\"e\")\n\t)\n)"; + String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\")\n).ELSE(\n\tIF(\n\t\tnode(\"c\"),\n\t\tnode(\"d\"),\n\t\tnode(\"e\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b").elseOpt(ELBus.ifOpt("c", "d", "e")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b").elseOpt(ELBus.ifOpt("c", "d", "e")).toEL(true))); } // 与表达式输出测试 @Test public void testIf11(){ - String expectedStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\"),node(\"e\"))"; + String expectedStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\"),node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.and("a", "b", "c"), "d", "e").toEL()); System.out.println(expectedStr); - expectedStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELSE(node(\"e\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d", "e").toEL())); + expectedStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELSE(node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elseOpt("e").toEL()); System.out.println(expectedStr); - expectedStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELIF(AND(node(\"f1\"),node(\"f2\")),node(\"e\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elseOpt("e").toEL())); + expectedStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELIF(AND(node(\"f1\"),node(\"f2\")),node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elIfOpt(ELBus.and("f1", "f2"), "e").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elIfOpt(ELBus.and("f1", "f2"), "e").toEL())); } // 格式化输出测试 @Test public void testIf12(){ - String expectedStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tnode(\"e\")\n)"; + String expectedStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.and("a", "b", "c"), "d", "e").toEL(true)); System.out.println(expectedStr); - expectedStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d", "e").toEL(true))); + expectedStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elseOpt("e").toEL(true)); System.out.println(expectedStr); - expectedStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELIF(\n\tAND(\n\t\tnode(\"f1\"),\n\t\tnode(\"f2\")\n\t),\n\tnode(\"e\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elseOpt("e").toEL(true))); + expectedStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELIF(\n\tAND(\n\t\tnode(\"f1\"),\n\t\tnode(\"f2\")\n\t),\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elIfOpt(ELBus.and("f1", "f2"), "e").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elIfOpt(ELBus.and("f1", "f2"), "e").toEL(true))); } // 或表达式测试 @Test public void testIf13(){ - String expectedStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\"),node(\"e\"))"; + String expectedStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\"),node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.or("a", "b", "c"), "d", "e").toEL()); System.out.println(expectedStr); - expectedStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELSE(node(\"e\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d", "e").toEL())); + expectedStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELSE(node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elseOpt("e").toEL()); System.out.println(expectedStr); - expectedStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELIF(OR(node(\"f1\"),node(\"f2\")),node(\"e\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elseOpt("e").toEL())); + expectedStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELIF(OR(node(\"f1\"),node(\"f2\")),node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elIfOpt(ELBus.or("f1", "f2"), "e").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elIfOpt(ELBus.or("f1", "f2"), "e").toEL())); } // 格式化输出测试 @Test public void testIf14(){ - String expectedStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tnode(\"e\")\n)"; + String expectedStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.or("a", "b", "c"), "d", "e").toEL(true)); System.out.println(expectedStr); - expectedStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d", "e").toEL(true))); + expectedStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elseOpt("e").toEL(true)); System.out.println(expectedStr); - expectedStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELIF(\n\tOR(\n\t\tnode(\"f1\"),\n\t\tnode(\"f2\")\n\t),\n\tnode(\"e\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elseOpt("e").toEL(true))); + expectedStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELIF(\n\tOR(\n\t\tnode(\"f1\"),\n\t\tnode(\"f2\")\n\t),\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elIfOpt(ELBus.or("f1", "f2"), "e").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elIfOpt(ELBus.or("f1", "f2"), "e").toEL(true))); } // 非表达式测试 @Test public void testIf15(){ - String expectedStr = "IF(NOT(node(\"a\")),node(\"b\"),node(\"c\"))"; + String expectedStr = "IF(NOT(node(\"a\")),node(\"b\"),node(\"c\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.not("a"), "b", "c").toEL()); System.out.println(expectedStr); - expectedStr = "IF(NOT(node(\"a\")),node(\"b\")).ELSE(node(\"c\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.not("a"), "b", "c").toEL())); + expectedStr = "IF(NOT(node(\"a\")),node(\"b\")).ELSE(node(\"c\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.not("a"), "b").elseOpt("c").toEL()); System.out.println(expectedStr); - expectedStr = "IF(NOT(node(\"a\")),node(\"b\")).ELIF(NOT(node(\"f\")),node(\"c\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.not("a"), "b").elseOpt("c").toEL())); + expectedStr = "IF(NOT(node(\"a\")),node(\"b\")).ELIF(NOT(node(\"f\")),node(\"c\"));"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.not("a"), "b").elIfOpt(ELBus.not("f"), "c").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.not("a"), "b").elIfOpt(ELBus.not("f"), "c").toEL())); } // 格式化输出测试 @Test public void testIf16(){ - String expectedStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\"),\n\tnode(\"c\")\n)"; + String expectedStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\"),\n\tnode(\"c\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.not("a"), "b", "c").toEL(true)); System.out.println(expectedStr); - expectedStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\")\n).ELSE(\n\tnode(\"c\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.not("a"), "b", "c").toEL(true))); + expectedStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\")\n).ELSE(\n\tnode(\"c\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.not("a"), "b").elseOpt("c").toEL(true)); System.out.println(expectedStr); - expectedStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\")\n).ELIF(\n\tNOT(\n\t\tnode(\"f\")\n\t),\n\tnode(\"c\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.not("a"), "b").elseOpt("c").toEL(true))); + expectedStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\")\n).ELIF(\n\tNOT(\n\t\tnode(\"f\")\n\t),\n\tnode(\"c\")\n);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt(ELBus.not("a"), "b").elIfOpt(ELBus.not("f"), "c").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt(ELBus.not("a"), "b").elIfOpt(ELBus.not("f"), "c").toEL(true))); } // 属性测试 @Test public void testIf17(){ - String expectedStr = "IF(node(\"a\"),node(\"b\"),node(\"c\")).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(6)"; + String expectedStr = "IF(node(\"a\"),node(\"b\"),node(\"c\")).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(6);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b", "c").id("this is a id").tag("this is a tag").maxWaitSeconds(6).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", "c").id("this is a id").tag("this is a tag").maxWaitSeconds(6).toEL())); } // 格式化输出 @Test public void testIf18(){ - String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(6)"; + String expectedStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(6);"; Assertions.assertEquals(expectedStr, ELBus.ifOpt("a", "b", "c").id("this is a id").tag("this is a tag").maxWaitSeconds(6).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", "c").id("this is a id").tag("this is a tag").maxWaitSeconds(6).toEL(true))); } // data map 测试 @Test @@ -217,10 +248,11 @@ public class IfELBuilderTest extends BaseTest { Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\")).data(ifData)"; + String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\").data(ifData));"; Assertions.assertEquals(expectedStr, - ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL()); + ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL())); } // 格式化输出 @Test @@ -228,26 +260,29 @@ public class IfELBuilderTest extends BaseTest { Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).data(ifData)"; + String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\").data(ifData)\n);"; Assertions.assertEquals(expectedStr, - ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL(true)); + ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL(true))); } // data JsonStr 测试 @Test public void testIf21(){ - String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\")).data(ifData)"; + String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\").data(ifData));"; Assertions.assertEquals(expectedStr, - ELBus.ifOpt("a", "b", "c").data("ifData", "{\"name\":\"zhangsan\",\"age\":18}").toEL()); + ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", "'{\"name\":\"zhangsan\",\"age\":18}'")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", "'{\"name\":\"zhangsan\",\"age\":18}'")).toEL())); } // 格式化输出 @Test public void testIf22(){ - String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).data(ifData)"; + String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\").data(ifData)\n);"; Assertions.assertEquals(expectedStr, - ELBus.ifOpt("a", "b", "c").data("ifData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true)); + ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", "'{\"name\":\"zhangsan\",\"age\":18}'")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", "'{\"name\":\"zhangsan\",\"age\":18}'")).toEL(true))); } private static class ParamClass{ private String name; @@ -265,10 +300,11 @@ public class IfELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\")).data(ifData)"; + String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\").data(ifData));"; Assertions.assertEquals(expectedStr, - ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL()); + ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL())); } // 格式化输出 @Test @@ -276,10 +312,11 @@ public class IfELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).data(ifData)"; + String expectedStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\").data(ifData)\n);"; Assertions.assertEquals(expectedStr, - ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL(true)); + ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ifOpt("a", "b", ELBus.node("c").data("ifData", name2Value)).toEL(true))); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LogicELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LogicELBuilderTest.java index f65887506..199dd63ce 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LogicELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LogicELBuilderTest.java @@ -1,6 +1,6 @@ package com.yomahub.liteflow.test.builder; -import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.*; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -22,82 +22,92 @@ public class LogicELBuilderTest extends BaseTest { // 与或非表达式调用 测试 @Test public void testlogic1(){ - String expectedStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")))"; + String expectedStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")));"; Assertions.assertEquals(expectedStr, ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).toEL())); } @Test public void testlogic2(){ - String expectedStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n)"; + String expectedStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).toEL(true))); } @Test public void testlogic3(){ - String expectedStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")))"; + String expectedStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")));"; Assertions.assertEquals(expectedStr, ELBus.and("a").and(ELBus.or("b").or("c")).and(ELBus.not("d")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a").and(ELBus.or("b").or("c")).and(ELBus.not("d")).toEL())); } @Test public void testlogic4(){ - String expectedStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n)"; + String expectedStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.and("a").and(ELBus.or("b").or("c")).and(ELBus.not("d")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a").and(ELBus.or("b").or("c")).and(ELBus.not("d")).toEL(true))); } // 属性设置 @Test public void testlogic5(){ - String expectedStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")).id(\"this is a id\").maxWaitSeconds(4),NOT(node(\"d\")).tag(\"this is a tag\"))"; + String expectedStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")).id(\"this is a id\"),NOT(node(\"d\")).tag(\"this is a tag\")).maxWaitSeconds(4);"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c").id("this is a id").maxWaitSeconds(4), ELBus.not("d").tag("this is a tag")).toEL()); + ELBus.and("a", ELBus.or("b", "c").id("this is a id"), ELBus.not("d").tag("this is a tag")).maxWaitSeconds(4).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c").id("this is a id"), ELBus.not("d").tag("this is a tag")).maxWaitSeconds(4).toEL())); } @Test public void testlogic6(){ - String expectedStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").maxWaitSeconds(4),\n\tNOT(\n\t\tnode(\"d\")\n\t).tag(\"this is a tag\")\n)"; + String expectedStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tNOT(\n\t\tnode(\"d\")\n\t).tag(\"this is a tag\")\n).maxWaitSeconds(4);"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c").id("this is a id").maxWaitSeconds(4), ELBus.not("d").tag("this is a tag")).toEL(true)); + ELBus.and("a", ELBus.or("b", "c").id("this is a id"), ELBus.not("d").tag("this is a tag")).maxWaitSeconds(4).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c").id("this is a id"), ELBus.not("d").tag("this is a tag")).maxWaitSeconds(4).toEL(true))); } @Test public void testlogic7(){ - String expectedStr = "andData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\"))).data(andData)"; + String expectedStr = "andData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\").data(andData)));"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("andData", "{\"name\":\"zhangsan\",\"age\":18}").toEL()); + ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("andData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("andData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL())); } @Test public void testlogic8(){ - String expectedStr = "andData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n).data(andData)"; + String expectedStr = "andData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\").data(andData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("andData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true)); + ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("andData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("andData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).toEL(true))); } @Test public void testlogic9(){ Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "orData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\"))).data(orData)"; + String expectedStr = "orData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\").data(orData)));"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("orData", name2Value).toEL()); + ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("orData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("orData", name2Value))).toEL())); } @Test public void testlogic10(){ Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "orData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n).data(orData)"; + String expectedStr = "orData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\").data(orData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("orData", name2Value).toEL(true)); + ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("orData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("orData", name2Value))).toEL(true))); } private static class ParamClass{ private String name; @@ -114,58 +124,83 @@ public class LogicELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "notData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")).data(notData))"; + String expectedStr = "notData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\").data(notData)));"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d").data("notData", name2Value)).toEL()); + ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("notData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("notData", name2Value))).toEL())); } @Test public void testlogic12(){ ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "notData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t).data(notData)\n)"; + String expectedStr = "notData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\").data(notData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d").data("notData", name2Value)).toEL(true)); + ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("notData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.and("a", ELBus.or("b", "c"), ELBus.not(ELBus.node("d").data("notData", name2Value))).toEL(true))); } // NOT调用方法补充测试 @Test public void testLogic13(){ - String expectedStr = "NOT(node(\"a\"))"; + String expectedStr = "NOT(node(\"a\"));"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.node("a")).toEL()); System.out.println(expectedStr); - expectedStr = "NOT(AND(node(\"a\"),node(\"b\"),node(\"c\")))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.node("a")).toEL())); + expectedStr = "NOT(AND(node(\"a\"),node(\"b\"),node(\"c\")));"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.and("a", "b", "c")).toEL()); System.out.println(expectedStr); - expectedStr = "NOT(OR(node(\"a\"),node(\"b\"),node(\"c\")))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.and("a", "b", "c")).toEL())); + expectedStr = "NOT(OR(node(\"a\"),node(\"b\"),node(\"c\")));"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.or("a", "b", "c")).toEL()); System.out.println(expectedStr); - expectedStr = "NOT(NOT(node(\"a\")))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.or("a", "b", "c")).toEL())); + expectedStr = "NOT(NOT(node(\"a\")));"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.not(ELBus.node("a"))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.not(ELBus.node("a"))).toEL())); } @Test public void testLogic14(){ - String expectedStr = "NOT(\n\tnode(\"a\")\n)"; + String expectedStr = "NOT(\n\tnode(\"a\")\n);"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.node("a")).toEL(true)); System.out.println(expectedStr); - expectedStr = "NOT(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.node("a")).toEL(true))); + expectedStr = "NOT(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.and("a", "b", "c")).toEL(true)); System.out.println(expectedStr); - expectedStr = "NOT(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.and("a", "b", "c")).toEL(true))); + expectedStr = "NOT(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.or("a", "b", "c")).toEL(true)); System.out.println(expectedStr); - expectedStr = "NOT(\n\tNOT(\n\t\tnode(\"a\")\n\t)\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.or("a", "b", "c")).toEL(true))); + expectedStr = "NOT(\n\tNOT(\n\t\tnode(\"a\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.not(ELBus.not(ELBus.node("a"))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.not(ELBus.not(ELBus.node("a"))).toEL(true))); + } + + @Test + public void testLogic(){ + AndELWrapper andEl = ELBus.and("a", "b").id("this is a id").tag("this is a tag").maxWaitSeconds(5); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(andEl.toEL())); + OrELWrapper orEl = ELBus.or("a", "b").maxWaitSeconds(3); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(orEl.toEL())); + NotELWrapper notEl = ELBus.not("a").maxWaitSeconds(2); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(notEl.toEL())); + +// Assertions.assertTrue(LiteFlowChainELBuilder.validate("AND(node(\"a\"),OR(node(\"b\"),node(\"c\")).id(\"this is a id\"),NOT(node(\"d\")).tag(\"this is a tag\").maxWaitSeconds(3))")); + +// ThenELWrapper thenELWrapper = ELBus.then(ELBus.when("a", "b", ELBus.when("c", "d").maxWaitSeconds(3))); +// Assertions.assertTrue(LiteFlowChainELBuilder.validate(thenELWrapper.toEL())); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LoopELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LoopELBuilderTest.java index b5c6182ce..f3d1ea60b 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LoopELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LoopELBuilderTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.builder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -22,159 +23,189 @@ public class LoopELBuilderTest extends BaseTest { // for 限定次数循环 @Test public void testLoop1(){ - String expectedStr = "FOR(3).DO(THEN(node(\"a\"),node(\"b\"),node(\"c\"))).BREAK(node(\"d\"))"; + String expectedStr = "FOR(3).DO(THEN(node(\"a\"),node(\"b\"),node(\"c\"))).BREAK(node(\"d\"));"; Assertions.assertEquals(expectedStr, ELBus.forOpt(3).doOpt(ELBus.then("a", "b", "c")).breakOpt("d").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt(3).doOpt(ELBus.then("a", "b", "c")).breakOpt("d").toEL())); } // 格式化输出 @Test public void testLoop2(){ - String expectedStr = "FOR(3).DO(\n\tTHEN(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"d\")\n)"; + String expectedStr = "FOR(3).DO(\n\tTHEN(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"d\")\n);"; Assertions.assertEquals(expectedStr, ELBus.forOpt(3).doOpt(ELBus.then("a", "b", "c")).breakOpt("d").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt(3).doOpt(ELBus.then("a", "b", "c")).breakOpt("d").toEL(true))); } // for 单节点循环测试 @Test public void testLoop3(){ - String expectedStr = "FOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(AND(node(\"e\"),node(\"f\")))"; + String expectedStr = "FOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(AND(node(\"e\"),node(\"f\")));"; Assertions.assertEquals(expectedStr, ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.and("e", "f")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.and("e", "f")).toEL())); } @Test public void testLoop4(){ - String expectedStr = "FOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tAND(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n)"; + String expectedStr = "FOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tAND(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.forOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.and("e", "f")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.and("e", "f")).toEL(true))); } // parallel语句测试 @Test public void testLoop5(){ - String expectedStr = "FOR(node(\"a\")).parallel(true).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(node(\"e\"))"; + String expectedStr = "FOR(node(\"a\")).parallel(true).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(node(\"e\"));"; Assertions.assertEquals(expectedStr, ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").parallel(true).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").parallel(true).toEL())); } @Test public void testLoop6(){ - String expectedStr = "FOR(\n\tnode(\"a\")\n).parallel(true).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tnode(\"e\")\n)"; + String expectedStr = "FOR(\n\tnode(\"a\")\n).parallel(true).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tnode(\"e\")\n);"; Assertions.assertEquals(expectedStr, ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").parallel(true).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").parallel(true).toEL(true))); } // 属性测试 @Test public void testLoop7(){ - String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\nFOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(node(\"e\")).id(\"this is a id\").tag(\"this is a tag\").data(forData).maxWaitSeconds(3)"; + String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\nFOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(node(\"e\").data(forData)).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, - ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").id("this is a id").tag("this is a tag").maxWaitSeconds(3).data("forData", "{\"name\":\"zhangsan\",\"age\":18}").toEL()); + ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.node("e").data("forData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.node("e").data("forData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL())); } @Test public void testLoop8(){ - String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\nFOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tnode(\"e\")\n).id(\"this is a id\").tag(\"this is a tag\").data(forData).maxWaitSeconds(3)"; + String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\nFOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tnode(\"e\").data(forData)\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, - ELBus.forOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c", "d")).breakOpt("e").id("this is a id").tag("this is a tag").maxWaitSeconds(3).data("forData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true)); + ELBus.forOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.node("e").data("forData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.node("e").data("forData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL(true))); } // while调用测试 @Test public void testLoop9(){ - String expectedStr = "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).BREAK(node(\"f\"))"; + String expectedStr = "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).BREAK(node(\"f\"));"; Assertions.assertEquals(expectedStr, ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("f").toEL()); Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.node("a")).doOpt(ELBus.then("b", "c")).breakOpt("f").toEL()); System.out.println(expectedStr); - expectedStr = "WHILE(AND(node(\"a\"),node(\"b\"))).DO(node(\"c\")).BREAK(node(\"d\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("f").toEL())); + + expectedStr = "WHILE(AND(node(\"a\"),node(\"b\"))).DO(node(\"c\")).BREAK(node(\"d\"));"; Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.and("a", "b")).doOpt("c").breakOpt("d").toEL()); System.out.println(expectedStr); - expectedStr = "WHILE(OR(node(\"a\"),node(\"b\"))).DO(node(\"c\")).BREAK(node(\"d\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt(ELBus.and("a", "b")).doOpt("c").breakOpt("d").toEL())); + + expectedStr = "WHILE(OR(node(\"a\"),node(\"b\"))).DO(node(\"c\")).BREAK(node(\"d\"));"; Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.or("a", "b")).doOpt("c").breakOpt("d").toEL()); System.out.println(expectedStr); - expectedStr = "WHILE(NOT(node(\"a\"))).DO(node(\"c\")).BREAK(node(\"d\"))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt(ELBus.or("a", "b")).doOpt("c").breakOpt("d").toEL())); + + expectedStr = "WHILE(NOT(node(\"a\"))).DO(node(\"c\")).BREAK(node(\"d\"));"; Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.not("a")).doOpt("c").breakOpt("d").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt(ELBus.not("a")).doOpt("c").breakOpt("d").toEL())); } @Test public void testLoop10(){ - String expectedStr = "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"f\")\n)"; + String expectedStr = "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"f\")\n);"; Assertions.assertEquals(expectedStr, ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("f").toEL(true)); Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.node("a")).doOpt(ELBus.then("b", "c")).breakOpt("f").toEL(true)); System.out.println(expectedStr); - expectedStr = "WHILE(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("f").toEL(true))); + + expectedStr = "WHILE(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n);"; Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.and("a", "b")).doOpt("c").breakOpt("d").toEL(true)); System.out.println(expectedStr); - expectedStr = "WHILE(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt(ELBus.and("a", "b")).doOpt("c").breakOpt("d").toEL(true))); + + expectedStr = "WHILE(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n);"; Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.or("a", "b")).doOpt("c").breakOpt("d").toEL(true)); System.out.println(expectedStr); - expectedStr = "WHILE(\n\tNOT(\n\t\tnode(\"a\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt(ELBus.or("a", "b")).doOpt("c").breakOpt("d").toEL(true))); + + expectedStr = "WHILE(\n\tNOT(\n\t\tnode(\"a\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n);"; Assertions.assertEquals(expectedStr, ELBus.whileOpt(ELBus.not("a")).doOpt("c").breakOpt("d").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt(ELBus.not("a")).doOpt("c").breakOpt("d").toEL(true))); } // while属性调用测试 @Test public void testLoop11(){ - String expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHILE(node(\"a\")).parallel(true).DO(THEN(node(\"b\"),node(\"c\"))).BREAK(node(\"d\")).id(\"this is a ig\").tag(\"this is a tag\").data(whileData).maxWaitSeconds(3)"; + String expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHILE(node(\"a\")).parallel(true).DO(THEN(node(\"b\"),node(\"c\"))).BREAK(node(\"d\").data(whileData)).id(\"this is a ig\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, - ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("d").id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("whileData", "{\"name\":\"zhangsan\",\"age\":18}").toEL()); + ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt(ELBus.node("d").data("whileData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt(ELBus.node("d").data("whileData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL())); } @Test public void testLoop12(){ - String expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHILE(\n\tnode(\"a\")\n).parallel(true).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"d\")\n).id(\"this is a ig\").tag(\"this is a tag\").data(whileData).maxWaitSeconds(3)"; + String expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHILE(\n\tnode(\"a\")\n).parallel(true).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"d\").data(whileData)\n).id(\"this is a ig\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, - ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("d").id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("whileData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true)); + ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt(ELBus.node("d").data("whileData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt(ELBus.node("d").data("whileData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL(true))); } // Iterator 调用测试 @Test public void testLoop13(){ - String expectedStr = "ITERATOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\")))"; + String expectedStr = "ITERATOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\")));"; Assertions.assertEquals(expectedStr, ELBus.iteratorOpt("a").doOpt(ELBus.when("b", "c")).toEL()); System.out.println(expectedStr); - expectedStr = "ITERATOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\")))"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.when("b", "c")).toEL())); + expectedStr = "ITERATOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\")));"; Assertions.assertEquals(expectedStr, ELBus.iteratorOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c")).toEL())); } @Test public void testLoop14(){ - String expectedStr = "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)"; + String expectedStr = "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.iteratorOpt("a").doOpt(ELBus.when("b", "c")).toEL(true)); System.out.println(expectedStr); - expectedStr = "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)"; + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.when("b", "c")).toEL(true))); + expectedStr = "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.iteratorOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c")).toEL(true))); } // iterator 属性测试 @Test public void testLoop15(){ - String expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\nITERATOR(node(\"a\")).parallel(true).DO(THEN(node(\"b\"),node(\"c\"))).id(\"this is a ig\").tag(\"this is a tag\").data(iteratorData).maxWaitSeconds(3)"; + String expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\nITERATOR(node(\"a\")).parallel(true).DO(THEN(node(\"b\"),node(\"c\").data(iteratorData))).id(\"this is a ig\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, - ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("iteratorData", "{\"name\":\"zhangsan\",\"age\":18}").toEL()); + ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL())); } @Test public void testLoop16(){ - String expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\nITERATOR(\n\tnode(\"a\")\n).parallel(true).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).id(\"this is a ig\").tag(\"this is a tag\").data(iteratorData).maxWaitSeconds(3)"; + String expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\nITERATOR(\n\tnode(\"a\")\n).parallel(true).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(iteratorData)\n\t)\n).id(\"this is a ig\").tag(\"this is a tag\").maxWaitSeconds(3);"; Assertions.assertEquals(expectedStr, - ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("iteratorData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true)); + ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", "'{\"name\":\"zhangsan\",\"age\":18}'"))).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).toEL(true))); } // data Map 参数 测试 @Test @@ -183,20 +214,23 @@ public class LoopELBuilderTest extends BaseTest { name2Value.put("name", "zhangsan"); name2Value.put("age", 18); String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "FOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(forData)"; + "FOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(forData)));"; Assertions.assertEquals(expectedStr, - ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL()); + ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL())); expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(whileData)"; + "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(whileData)));"; Assertions.assertEquals(expectedStr, - ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL()); + ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL())); expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "ITERATOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(iteratorData)"; + "ITERATOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(iteratorData)));"; Assertions.assertEquals(expectedStr, - ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL()); + ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL())); } @Test @@ -205,20 +239,23 @@ public class LoopELBuilderTest extends BaseTest { name2Value.put("name", "zhangsan"); name2Value.put("age", 18); String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "FOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(forData)"; + "FOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(forData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL(true)); + ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL(true))); expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(whileData)"; + "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whileData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL(true)); + ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL(true))); expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(iteratorData)"; + "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(iteratorData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL(true)); + ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL(true))); } private static class ParamClass{ private String name; @@ -237,20 +274,23 @@ public class LoopELBuilderTest extends BaseTest { name2Value.age = 18; name2Value.name = "zhangsan"; String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "FOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(forData)"; + "FOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(forData)));"; Assertions.assertEquals(expectedStr, - ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL()); + ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL())); expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(whileData)"; + "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(whileData)));"; Assertions.assertEquals(expectedStr, - ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL()); + ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL())); expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "ITERATOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(iteratorData)"; + "ITERATOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\").data(iteratorData)));"; Assertions.assertEquals(expectedStr, - ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL()); + ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL())); } @Test @@ -259,20 +299,23 @@ public class LoopELBuilderTest extends BaseTest { name2Value.age = 18; name2Value.name = "zhangsan"; String expectedStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "FOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(forData)"; + "FOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(forData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL(true)); + ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.forOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("forData", name2Value))).toEL(true))); expectedStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(whileData)"; + "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whileData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL(true)); + ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.whileOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("whileData", name2Value))).toEL(true))); expectedStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + - "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(iteratorData)"; + "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(iteratorData)\n\t)\n);"; Assertions.assertEquals(expectedStr, - ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL(true)); + ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", ELBus.node("c").data("iteratorData", name2Value))).toEL(true))); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/NodeELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/NodeELBuilderTest.java index abbd7e004..b4446a503 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/NodeELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/NodeELBuilderTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.builder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.builder.el.NodeELWrapper; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; @@ -22,47 +23,51 @@ import java.util.Map; public class NodeELBuilderTest extends BaseTest { @Test public void testNodeEL1(){ - String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}"; - String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" + - "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)"; + String jsonStr = "'{\"name\":\"zhangsan\",\"age\":18}'"; + String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + + "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4);"; NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", jsonStr); Assertions.assertEquals(expectedStr, node.toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(node.toEL())); } @Test public void testNodeEL2(){ - String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}"; - String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" + - "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)"; + String jsonStr = "'{\"name\":\"zhangsan\",\"age\":18}'"; + String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + + "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4);"; NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", jsonStr); Assertions.assertEquals(expectedStr, node.toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(node.toEL(true))); } @Test public void testNodeEL3(){ Map name2Value = new HashMap<>(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" + - "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)"; + String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + + "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4);"; NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value); Assertions.assertEquals(expectedStr, node.toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(node.toEL())); } @Test public void testNodeEL4(){ Map name2Value = new HashMap<>(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" + - "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)"; + String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + + "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4);"; NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value); Assertions.assertEquals(expectedStr, node.toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(node.toEL(true))); } private static class ParamClass{ private String name; @@ -79,23 +84,25 @@ public class NodeELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.age = 18; name2Value.name = "zhangsan"; - String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" + - "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)"; + String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + + "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4);"; NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value); Assertions.assertEquals(expectedStr, node.toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(node.toEL())); } @Test public void testNodeEL6(){ ParamClass name2Value = new ParamClass(); name2Value.age = 18; name2Value.name = "zhangsan"; - String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" + - "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)"; + String expectedStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}';\n" + + "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4);"; NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value); Assertions.assertEquals(expectedStr, node.toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(node.toEL(true))); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SwitchELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SwitchELBuilderTest.java index 82c85c92c..6a6525516 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SwitchELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SwitchELBuilderTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.builder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -23,55 +24,61 @@ public class SwitchELBuilderTest extends BaseTest { // Switch调用方法测试 @Test public void testSwitch1(){ - String expectedStr = "SWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).DEFAULT(node(\"f\"))"; + String expectedStr = "SWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).DEFAULT(node(\"f\"));"; Assertions.assertEquals(expectedStr, ELBus.switchOpt("a").to("b", "c", "d").defaultOpt("f").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", "d").defaultOpt("f").toEL())); } // 格式化输出测试 @Test public void testSwitch2(){ - String expectedStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).DEFAULT(\n\tnode(\"f\")\n)"; + String expectedStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).DEFAULT(\n\tnode(\"f\")\n);"; Assertions.assertEquals(expectedStr, ELBus.switchOpt("a").to("b", "c", "d").defaultOpt("f").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", "d").defaultOpt("f").toEL(true))); } // switch和THEN when嵌套调用测试 @Test public void testSwitch3(){ - String expectedStr = "SWITCH(node(\"a\")).TO(node(\"b\"),THEN(node(\"c\"),node(\"d\")),WHEN(node(\"e\"),node(\"f\"))).DEFAULT(THEN(node(\"g\"),node(\"h\")))"; + String expectedStr = "SWITCH(node(\"a\")).TO(node(\"b\"),THEN(node(\"c\"),node(\"d\")),WHEN(node(\"e\"),node(\"f\"))).DEFAULT(THEN(node(\"g\"),node(\"h\")));"; Assertions.assertEquals(expectedStr, ELBus.switchOpt("a").to("b", ELBus.then("c", "d"), ELBus.when("e", "f")).defaultOpt(ELBus.then("g", "h")).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", ELBus.then("c", "d"), ELBus.when("e", "f")).defaultOpt(ELBus.then("g", "h")).toEL())); } // 格式化输出测试 @Test public void testSwitch4(){ - String expectedStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tTHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t),\n\tWHEN(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n).DEFAULT(\n\tTHEN(\n\t\tnode(\"g\"),\n\t\tnode(\"h\")\n\t)\n)"; + String expectedStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tTHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t),\n\tWHEN(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n).DEFAULT(\n\tTHEN(\n\t\tnode(\"g\"),\n\t\tnode(\"h\")\n\t)\n);"; Assertions.assertEquals(expectedStr, ELBus.switchOpt("a").to("b", ELBus.then("c", "d"), ELBus.when("e", "f")).defaultOpt(ELBus.then("g", "h")).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", ELBus.then("c", "d"), ELBus.when("e", "f")).defaultOpt(ELBus.then("g", "h")).toEL(true))); } // 属性设置测试 @Test public void testSwitch5(){ - String expectedStr = "SWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(5)"; + String expectedStr = "SWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(5);"; Assertions.assertEquals(expectedStr, ELBus.switchOpt("a").to("b", "c", "d").id("this is a id").tag("this is a tag").maxWaitSeconds(5).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", "d").id("this is a id").tag("this is a tag").maxWaitSeconds(5).toEL())); } // 格式化输出测试 @Test public void testSwitch6(){ - String expectedStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(5)"; + String expectedStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(5);"; Assertions.assertEquals(expectedStr, ELBus.switchOpt("a").to("b", "c", "d").id("this is a id").tag("this is a tag").maxWaitSeconds(5).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", "d").id("this is a id").tag("this is a tag").maxWaitSeconds(5).toEL(true))); } // data属性测试 @@ -80,10 +87,11 @@ public class SwitchELBuilderTest extends BaseTest { Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).data(switchData)"; + String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\").data(switchData));"; Assertions.assertEquals(expectedStr, - ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL()); + ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", name2Value)).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c",ELBus.node("d").data("switchData", name2Value)).toEL())); } @Test @@ -91,28 +99,31 @@ public class SwitchELBuilderTest extends BaseTest { Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).data(switchData)"; + String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\").data(switchData)\n);"; Assertions.assertEquals(expectedStr, - ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL(true)); + ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", name2Value)).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", name2Value)).toEL(true))); } @Test public void testSwitch9(){ - String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}"; - String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).data(switchData)"; + String jsonStr = "'{\"name\":\"zhangsan\",\"age\":18}'"; + String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\").data(switchData));"; Assertions.assertEquals(expectedStr, - ELBus.switchOpt("a").to("b", "c", "d").data("switchData", jsonStr).toEL()); + ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", jsonStr)).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", jsonStr)).toEL())); } @Test public void testSwitch10(){ - String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}"; - String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).data(switchData)"; + String jsonStr = "'{\"name\":\"zhangsan\",\"age\":18}'"; + String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\").data(switchData)\n);"; Assertions.assertEquals(expectedStr, - ELBus.switchOpt("a").to("b", "c", "d").data("switchData", jsonStr).toEL(true)); + ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", jsonStr)).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", jsonStr)).toEL(true))); } private static class ParamClass{ @@ -131,10 +142,11 @@ public class SwitchELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).data(switchData)"; + String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\").data(switchData));"; Assertions.assertEquals(expectedStr, - ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL()); + ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", name2Value)).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", name2Value)).toEL())); } @Test @@ -142,10 +154,11 @@ public class SwitchELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).data(switchData)"; + String expectedStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\").data(switchData)\n);"; Assertions.assertEquals(expectedStr, - ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL(true)); + ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", name2Value)).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.switchOpt("a").to("b", "c", ELBus.node("d").data("switchData", name2Value)).toEL(true))); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/TestContext.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/TestContext.java new file mode 100644 index 000000000..90a7c4fb8 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/TestContext.java @@ -0,0 +1,25 @@ +package com.yomahub.liteflow.test.builder; + +import cn.hutool.core.collection.ConcurrentHashSet; + +import java.util.Set; + +/** + * EL表达式装配并执行测试 + * + * @author gezuao + * @since 2.11.1 + */ +public class TestContext { + + private Set set = new ConcurrentHashSet<>(); + + public void add2Set(String str) { + set.add(str); + } + + public Set getSet() { + return set; + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ThenELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ThenELBuilderTest.java index 8c347bf18..89cd92dfb 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ThenELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ThenELBuilderTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.builder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.test.BaseTest; import com.yomahub.liteflow.util.JsonUtil; import org.junit.jupiter.api.Assertions; @@ -24,87 +25,99 @@ public class ThenELBuilderTest extends BaseTest { // then组件测试 @Test public void testThen1(){ - Assertions.assertEquals("THEN(node(\"a\"),node(\"b\"))", + Assertions.assertEquals("THEN(node(\"a\"),node(\"b\"));", ELBus.then("a", "b").toEL()); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", "b").toEL())); } // 格式化输出测试 @Test public void testThen2(){ Assertions.assertEquals("THEN(\n\tnode(\"a\")," + - "\n\tnode(\"b\")\n)", + "\n\tnode(\"b\")\n);", ELBus.then("a", "b").toEL(true)); System.out.println("THEN(\n\tnode(\"a\")," + - "\n\tnode(\"b\")\n)"); + "\n\tnode(\"b\")\n);"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", "b").toEL(true))); } // then组件then方法调用测试 @Test public void testThen3(){ - Assertions.assertEquals("THEN(node(\"a\"),node(\"b\"),node(\"c\"))", + Assertions.assertEquals("THEN(node(\"a\"),node(\"b\"),node(\"c\"));", ELBus.then("a", "b").then("c").toEL()); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", "b").then("c").toEL())); } // 格式化输出测试 @Test public void testThen4(){ Assertions.assertEquals("THEN(\n\tnode(\"a\"),\n\tnode(\"b\")," + - "\n\tnode(\"c\")\n)", + "\n\tnode(\"c\")\n);", ELBus.then("a", "b").then("c").toEL(true)); System.out.println("THEN(\n\tnode(\"a\"),\n\tnode(\"b\")," + - "\n\tnode(\"c\")\n)"); + "\n\tnode(\"c\")\n);"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", "b").then("c").toEL(true))); } // then组件嵌套调用测试 @Test public void testThen5(){ - Assertions.assertEquals("THEN(node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"))", + Assertions.assertEquals("THEN(node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"));", ELBus.then("a", ELBus.then("b").then("c")).then("d").toEL()); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c")).then("d").toEL())); } // 格式化输出测试 @Test public void testThen6(){ - Assertions.assertEquals("THEN(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)", + Assertions.assertEquals("THEN(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);", ELBus.then("a", ELBus.then("b").then("c")).then("d").toEL(true)); - System.out.println("THEN(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)"); + System.out.println("THEN(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c")).then("d").toEL(true))); } // pre组件测试 @Test public void testThen7(){ - Assertions.assertEquals("THEN(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"))", + Assertions.assertEquals("THEN(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"));", ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").pre("pp").toEL()); - System.out.println("THEN(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"))"); + System.out.println("THEN(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"));"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").pre("pp").toEL())); } // 格式化输出测试 @Test public void testThen8(){ - Assertions.assertEquals("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)", + Assertions.assertEquals("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);", ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").pre("pp").toEL(true)); - System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)"); + System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").pre("pp").toEL(true))); } // pre finally 格式测试 @Test public void testThen9(){ - Assertions.assertEquals("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")))", + Assertions.assertEquals("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")));", ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").finallyOpt("f").toEL()); - System.out.println("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")))"); + System.out.println("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")));"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").finallyOpt("f").toEL())); } // 格式化输出测试 @Test public void testThen10(){ - Assertions.assertEquals("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n)", + Assertions.assertEquals("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n);", ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").finallyOpt("f").toEL(true)); - System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n)"); + System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n);"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").finallyOpt("f").toEL(true))); } // 属性设置测试 @Test public void testThen11(){ - Assertions.assertEquals("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\")", + Assertions.assertEquals("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\");", ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").pre("p").finallyOpt("f").toEL()); - System.out.println("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\")"); + System.out.println("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").pre("p").finallyOpt("f").toEL())); } // 格式化输出测试 @Test public void testThen12(){ - Assertions.assertEquals("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\")", + Assertions.assertEquals("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");", ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").pre("p").finallyOpt("f").toEL(true)); - System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\")"); + System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").pre("p").finallyOpt("f").toEL(true))); } // data属性测试 @Test @@ -113,9 +126,10 @@ public class ThenELBuilderTest extends BaseTest { name2Value.put("name", "zhangsan"); name2Value.put("age", 18); System.out.println(JsonUtil.toJsonString(name2Value)); - Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)", - ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL()); - System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)"); + Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");", + ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL()); + System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL())); } // 格式化输出测试 @Test @@ -123,23 +137,26 @@ public class ThenELBuilderTest extends BaseTest { Map name2Value = new HashMap(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\").data(thenData)", - ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL(true)); - System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\").data(thenData)"); + Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");", + ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true)); + System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true))); } // data属性测试 Json字符串赋值data @Test public void testThen15(){ - Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)", - ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}").pre("p").finallyOpt("f").toEL()); - System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)"); + Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");", + ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).pre("p").finallyOpt("f").toEL()); + System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).pre("p").finallyOpt("f").toEL())); } // 格式化输出测试 Json字符串赋值data @Test public void testThen16(){ - Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\").data(thenData)", - ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}").pre("p").finallyOpt("f").toEL(true)); - System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\").data(thenData)"); + Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");", + ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).pre("p").finallyOpt("f").toEL(true)); + System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).pre("p").finallyOpt("f").toEL(true))); } private static class ParamClass{ private String name; @@ -157,9 +174,10 @@ public class ThenELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)", - ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL()); - System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)"); + Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");", + ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL()); + System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL())); } // 格式化输出测试 @Test @@ -167,24 +185,27 @@ public class ThenELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\").data(thenData)", - ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL(true)); - System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\").data(thenData)"); + Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");", + ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true)); + System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true))); } // maxWaitSecond测试 @Test public void testThen19(){ - String expectedStr = "THEN(node(\"a\"),node(\"b\")).maxWaitSeconds(5)"; + String expectedStr = "THEN(node(\"a\"),node(\"b\")).maxWaitSeconds(5);"; Assertions.assertEquals(expectedStr, ELBus.then("a").then("b").maxWaitSeconds(5).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a").then("b").maxWaitSeconds(5).toEL())); } // 格式化输出测试 @Test public void testThen20(){ - String expectedStr = "THEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5)"; + String expectedStr = "THEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5);"; Assertions.assertEquals(expectedStr, ELBus.then("a").then("b").maxWaitSeconds(5).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.then("a").then("b").maxWaitSeconds(5).toEL(true))); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/WhenELBuilderTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/WhenELBuilderTest.java index 127fceaa4..2208be038 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/WhenELBuilderTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/WhenELBuilderTest.java @@ -1,6 +1,8 @@ package com.yomahub.liteflow.test.builder; import com.yomahub.liteflow.builder.el.ELBus; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; +import com.yomahub.liteflow.builder.el.WhenELWrapper; import com.yomahub.liteflow.test.BaseTest; import com.yomahub.liteflow.util.JsonUtil; import org.junit.jupiter.api.Assertions; @@ -8,6 +10,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import javax.annotation.meta.When; import java.util.HashMap; import java.util.Map; @@ -23,95 +26,107 @@ public class WhenELBuilderTest extends BaseTest { // then组件测试 @Test public void testWhen1(){ - String expectedStr = "WHEN(node(\"a\"),node(\"b\"))"; + String expectedStr = "WHEN(node(\"a\"),node(\"b\"));"; Assertions.assertEquals(expectedStr, ELBus.when("a", "b").toEL()); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b").toEL())); } // 格式化输出测试 @Test public void testWhen2(){ - String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n)"; + String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n);"; Assertions.assertEquals(expectedStr, ELBus.when("a", "b").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b").toEL(true))); } // then组件then方法调用测试 @Test public void testWhen3(){ - String expectedStr = "WHEN(node(\"a\"),node(\"b\"),node(\"c\"))"; + String expectedStr = "WHEN(node(\"a\"),node(\"b\"),node(\"c\"));"; Assertions.assertEquals(expectedStr, ELBus.when("a", "b").when("c").toEL()); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b").when("c").toEL())); } // 格式化输出测试 @Test public void testWhen4(){ - String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n)"; + String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n);"; Assertions.assertEquals(expectedStr, ELBus.when("a", "b").when("c").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b").when("c").toEL(true))); } // then组件嵌套调用测试 @Test public void testWhen5(){ - String expectedStr = "WHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")),node(\"d\"))"; + String expectedStr = "WHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")),node(\"d\"));"; Assertions.assertEquals(expectedStr, ELBus.when("a", ELBus.when("b").when("c")).when("d").toEL()); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when("c")).when("d").toEL())); } // 格式化输出测试 @Test public void testWhen6(){ - String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)"; + String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);"; Assertions.assertEquals(expectedStr, ELBus.when("a", ELBus.when("b").when("c")).when("d").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when("c")).when("d").toEL(true))); } // WHEN特有属性测试 any ignoreError customThreadExecutor must @Test public void testWhen7(){ - String expectedStr = "WHEN(node(\"a\"),node(\"b\"),WHEN(node(\"c\"),node(\"d\")).any(true).threadPool(\"WhenELBuilderTest.customThreadPool\").id(\"node1\")).ignoreError(true).must(\"a\", \"task1\", \"node1\")"; + String expectedStr = "WHEN(node(\"a\"),node(\"b\"),WHEN(node(\"c\"),node(\"d\")).any(true).threadPool(\"com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1\").id(\"node1\")).ignoreError(true).must(\"a\", \"task1\", \"node1\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", "b", ELBus.when("c").when("d").customThreadExecutor("WhenELBuilderTest.customThreadPool").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL()); + ELBus.when("a", "b", ELBus.when("c").when("d").id("node1").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b", ELBus.when("c").when("d").id("node1").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL())); } // 格式化输出测试 @Test public void testWhen8(){ - String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tWHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t).any(true).threadPool(\"WhenELBuilderTest.customThreadPool\").id(\"node1\")\n).ignoreError(true).must(\"a\", \"task1\", \"node1\")"; + String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tWHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t).any(true).threadPool(\"com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1\").id(\"node1\")\n).ignoreError(true).must(\"a\", \"task1\", \"node1\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", "b", ELBus.when("c").when("d").customThreadExecutor("WhenELBuilderTest.customThreadPool").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL(true)); + ELBus.when("a", "b", ELBus.when("c").when("d").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b", ELBus.when("c").when("d").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL(true))); } // maxWaitSeconds 属性测试 @Test public void testWhen9(){ - String expectedStr = "WHEN(node(\"a\"),node(\"b\")).maxWaitSeconds(5)"; + String expectedStr = "WHEN(node(\"a\"),node(\"b\")).maxWaitSeconds(5);"; Assertions.assertEquals(expectedStr, ELBus.when("a", "b").maxWaitSeconds(5).toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b").maxWaitSeconds(5).toEL())); } // 格式化输出测试 @Test public void testWhen10(){ - String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5)"; + String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5);"; Assertions.assertEquals(expectedStr, ELBus.when("a", "b").maxWaitSeconds(5).toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", "b").maxWaitSeconds(5).toEL(true))); } // 属性设置测试 @Test public void testWhen11(){ - String expectedStr = "WHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\")"; + String expectedStr = "WHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, ELBus.when("a", ELBus.when("b").when("c").id("this is a id")).when("d").tag("this is a tag").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when("c").id("this is a id")).when("d").tag("this is a tag").toEL())); } // 格式化输出测试 @Test public void testWhen12(){ - String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\")"; + String expectedStr = "WHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, ELBus.when("a", ELBus.when("b").when("c").id("this is a id")).when("d").tag("this is a tag").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when("c").id("this is a id")).when("d").tag("this is a tag").toEL(true))); } // data属性测试 @Test @@ -120,10 +135,11 @@ public class WhenELBuilderTest extends BaseTest { name2Value.put("name", "zhangsan"); name2Value.put("age", 18); System.out.println(JsonUtil.toJsonString(name2Value)); - String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\").data(whenData),node(\"d\")).tag(\"this is a tag\")"; + String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\").data(whenData)).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL(false)); + ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL(false)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL(false))); } // 格式化输出测试 @Test @@ -131,26 +147,29 @@ public class WhenELBuilderTest extends BaseTest { Map name2Value = new HashMap<>(); name2Value.put("name", "zhangsan"); name2Value.put("age", 18); - String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").data(whenData),\n\tnode(\"d\")\n).tag(\"this is a tag\")"; + String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whenData)\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL(true)); + ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL(true))); } // data属性测试 Json字符串赋值data @Test public void testWhen15(){ - String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\").data(whenData),node(\"d\")).tag(\"this is a tag\")"; + String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\").data(whenData)).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", ELBus.when("b").when("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}").id("this is a id")).when("d").tag("this is a tag").toEL()); + ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id")).when("d").tag("this is a tag").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id")).when("d").tag("this is a tag").toEL())); } // 格式化输出测试 Json字符串赋值data @Test public void testWhen16(){ - String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").data(whenData),\n\tnode(\"d\")\n).tag(\"this is a tag\")"; + String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whenData)\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", ELBus.when("b").when("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}").id("this is a id")).when("d").tag("this is a tag").toEL(true)); + ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id")).when("d").tag("this is a tag").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", "'{\"name\":\"zhangsan\",\"age\":18}'")).id("this is a id")).when("d").tag("this is a tag").toEL(true))); } private static class ParamClass{ private String name; @@ -168,10 +187,11 @@ public class WhenELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\").data(whenData),node(\"d\")).tag(\"this is a tag\")"; + String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\").data(whenData)).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL()); + ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL()); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL())); } // 格式化输出测试 @Test @@ -179,9 +199,16 @@ public class WhenELBuilderTest extends BaseTest { ParamClass name2Value = new ParamClass(); name2Value.name = "zhangsan"; name2Value.age = 18; - String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").data(whenData),\n\tnode(\"d\")\n).tag(\"this is a tag\")"; + String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whenData)\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");"; Assertions.assertEquals(expectedStr, - ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL(true)); + ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL(true)); System.out.println(expectedStr); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.when("a", ELBus.when("b").when(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).when("d").tag("this is a tag").toEL(true))); + } + + @Test + public void testWHEN(){ + WhenELWrapper el = ELBus.when("a", "b", "c").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1"); + Assertions.assertTrue(LiteFlowChainELBuilder.validate(el.toEL())); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/ACmp.java new file mode 100644 index 000000000..239f7150a --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/ACmp.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.test.builder.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +/** + * EL表达式装配并执行测试 + * + * @author gezuao + * @since 2.11.1 + */ +@Component("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println(this.getCmpData(String.class)); + System.out.println("ACmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/BCmp.java new file mode 100644 index 000000000..39be023de --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/BCmp.java @@ -0,0 +1,25 @@ +package com.yomahub.liteflow.test.builder.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; +import com.yomahub.liteflow.test.builder.vo.User; +import org.springframework.stereotype.Component; + +/** + * EL表达式装配并执行测试 + * + * @author gezuao + * @since 2.11.1 + */ +@Component("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + User user = this.getCmpData(User.class); + DefaultContext context = this.getFirstContextBean(); + context.setData("user", user); + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/CCmp.java new file mode 100644 index 000000000..5f2434285 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/cmp/CCmp.java @@ -0,0 +1,24 @@ +package com.yomahub.liteflow.test.builder.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.test.builder.TestContext; +import org.springframework.stereotype.Component; + +/** + * EL表达式装配并执行测试 + * + * @author gezuao + * @since 2.11.1 + */ +@Component("c") +public class CCmp extends NodeComponent { + + @Override + public void process() { + String data = this.getCmpData(String.class); + TestContext context = this.getFirstContextBean(); + context.add2Set(data); + System.out.println("CCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/customTreadExecutor/CustomThreadExecutor1.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/customTreadExecutor/CustomThreadExecutor1.java new file mode 100644 index 000000000..4a4d95409 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/customTreadExecutor/CustomThreadExecutor1.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.test.builder.customTreadExecutor; + +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.thread.ExecutorBuilder; + +import java.util.concurrent.ExecutorService; + +/** + * EL表达式装配并执行测试 + * + * @author gezuao + * @since 2.11.1 + */ +public class CustomThreadExecutor1 implements ExecutorBuilder { + + @Override + public ExecutorService buildExecutor() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + // 只有在非spring的场景下liteflowConfig才会为null + if (ObjectUtil.isNull(liteflowConfig)) { + liteflowConfig = new LiteflowConfig(); + } + return buildDefaultExecutor(liteflowConfig.getWhenMaxWorkers(), liteflowConfig.getWhenMaxWorkers(), + liteflowConfig.getWhenQueueLimit(), "customer-when-1-thead-"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/vo/User.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/vo/User.java new file mode 100644 index 000000000..c75b056ee --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/vo/User.java @@ -0,0 +1,43 @@ +package com.yomahub.liteflow.test.builder.vo; + +import java.util.Date; + +/** + * EL表达式装配并执行测试 + * + * @author gezuao + * @since 2.11.1 + */ +public class User { + + private String name; + + private int age; + + private Date birth; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Date getBirth() { + return birth; + } + + public void setBirth(Date birth) { + this.birth = birth; + } + +} From 1535c7f6d6b414fb1f6b0143e0b58ed0857ebfe6 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 17 Oct 2023 13:15:24 +0800 Subject: [PATCH 02/21] =?UTF-8?q?=E4=BC=98=E5=8C=96ELBuilder=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E7=9A=84=E4=B8=80=E4=BA=9Bapi=E7=9A=84=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E4=BF=AE=E9=A5=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/yomahub/liteflow/builder/el/ELWrapper.java | 10 ++++++++-- .../com/yomahub/liteflow/builder/el/NodeELWrapper.java | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java index 0bc4acd83..096bf9b4a 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java @@ -95,7 +95,10 @@ public abstract class ELWrapper { * @param tag 标记内容 * @return {@link ELWrapper} */ - protected abstract ELWrapper tag(String tag); + public ELWrapper tag(String tag){ + this.setTag(tag); + return this; + } /** * 设置组件的id @@ -103,7 +106,10 @@ public abstract class ELWrapper { * @param id 编号 * @return {@link ELWrapper} */ - protected abstract ELWrapper id(String id); + public ELWrapper id(String id){ + this.setId(id); + return this; + } /** * 设置表达式data属性 diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java index f3cf92dd1..3a837b992 100644 --- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java +++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java @@ -53,7 +53,7 @@ public class NodeELWrapper extends ELWrapper { * @return {@link NodeELWrapper} */ @Override - protected NodeELWrapper id(String id) { + public NodeELWrapper id(String id) { this.setId(id); return this; } From f76b78299c4225da5f101b32902c49bf343506d2 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 17 Oct 2023 21:51:43 +0800 Subject: [PATCH 03/21] =?UTF-8?q?bug=20#I88U0Q=20SQL=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=BD=93eldata=E5=AD=97=E6=AE=B5=E4=B8=BA=E7=A9=BA=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E5=90=AF=E5=8A=A8=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/yomahub/liteflow/flow/FlowBus.java | 9 ++++++++- .../parser/apollo/util/ApolloParseHelper.java | 4 ---- .../liteflow/parser/etcd/util/EtcdParserHelper.java | 4 ---- .../redis/mode/polling/RedisParserPollingMode.java | 5 ++--- .../mode/subscribe/RedisParserSubscribeMode.java | 4 ---- .../liteflow/parser/sql/read/AbstractSqlRead.java | 10 +++++++--- .../liteflow/parser/sql/read/impl/ChainRead.java | 8 +++----- .../liteflow/parser/sql/read/impl/ScriptRead.java | 10 +++++----- .../yomahub/liteflow/parser/sql/util/JDBCHelper.java | 12 +++++++++--- .../liteflow/parser/zk/util/ZkParserHelper.java | 7 +++---- .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../test/etcd/EtcdWithXmlELSpringbootTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 2 +- .../test/nacos/NacosWithXmlELSpringbootTest.java | 9 +++++---- .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + .../java/com/yomahub/liteflow/test/BaseTest.java | 1 + 34 files changed, 65 insertions(+), 41 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java index db3c0e654..90e15cc5a 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; /** @@ -58,6 +59,8 @@ public class FlowBus { private static final Map fallbackNodeMap = new CopyOnWriteHashMap<>(); + private static AtomicBoolean initStat = new AtomicBoolean(false); + private FlowBus() { } @@ -82,7 +85,7 @@ public class FlowBus { } public static boolean needInit() { - return MapUtil.isEmpty(chainMap); + return initStat.compareAndSet(false, true); } public static boolean containNode(String nodeId) { @@ -297,4 +300,8 @@ public class FlowBus { fallbackNodeMap.put(nodeType, node); } + public static void clearStat(){ + initStat.set(false); + } + } diff --git a/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java b/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java index 01dfc4acc..cfb981469 100644 --- a/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java @@ -80,10 +80,6 @@ public class ApolloParseHelper { try { // 1. handle chain Set propertyNames = chainConfig.getPropertyNames(); - if (CollectionUtil.isEmpty(propertyNames)) { - throw new ApolloException(StrUtil.format("There are no chains in namespace : {}", - apolloParserConfigVO.getChainNamespace())); - } List chainItemContentList = propertyNames.stream() .map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY))) .collect(Collectors.toList()); diff --git a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java index 1b234e034..04504f17e 100644 --- a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java @@ -77,10 +77,6 @@ public class EtcdParserHelper { try { // 检查chainPath路径下有没有子节点 List chainNameList = client.getChildrenKeys(etcdParserVO.getChainPath(), SEPARATOR); - if (CollectionUtil.isEmpty(chainNameList)) { - throw new EtcdException( - StrUtil.format("There are no chains in path [{}]", etcdParserVO.getChainPath())); - } // 获取chainPath路径下的所有子节点内容List List chainItemContentList = new ArrayList<>(); diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java index 352bf1f9e..52370099f 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java @@ -121,9 +121,6 @@ public class RedisParserPollingMode implements RedisParserHelper { // 检查chainKey下有没有子节点 String chainKey = redisParserVO.getChainKey(); Set chainNameSet = chainClient.hkeys(chainKey); - if (CollectionUtil.isEmpty(chainNameSet)) { - throw new RedisException(StrUtil.format("There are no chains in key [{}]", chainKey)); - } chainNum = chainNameSet.size(); // 获取chainKey下的所有子节点内容List List chainItemContentList = new ArrayList<>(); @@ -131,6 +128,8 @@ public class RedisParserPollingMode implements RedisParserHelper { String chainData = chainClient.hget(chainKey, chainName); if (StrUtil.isNotBlank(chainData)) { chainItemContentList.add(StrUtil.format(CHAIN_XML_PATTERN, chainName, chainData)); + }else{ + continue; } //计算该chainData的SHA值 diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java index aff895cd9..f21116d4d 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java @@ -86,10 +86,6 @@ public class RedisParserSubscribeMode implements RedisParserHelper { try { // 检查chainKey下有没有子节点 Map chainMap = chainClient.getMap(redisParserVO.getChainKey()); - if (CollectionUtil.isEmpty(chainMap)) { - throw new RedisException(StrUtil.format("There are no chains in key [{}]", - redisParserVO.getChainKey())); - } // 获取chainKey下的所有子节点内容List List chainItemContentList = new ArrayList<>(); for (Map.Entry entry : chainMap.entrySet()) { diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java index 4c144aef6..0b7d9437d 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java @@ -87,10 +87,14 @@ public abstract class AbstractSqlRead implements SqlRead { } - public String getStringFromResultSet(ResultSet rs, String field) throws SQLException { - String data = rs.getString(field); + public String getStringFromRs(ResultSet rs, String field) throws SQLException { + return rs.getString(field); + } + + public String getStringFromRsWithCheck(ResultSet rs, String field) throws SQLException { + String data = getStringFromRs(rs, field); if (StrUtil.isBlank(data)) { - throw new ELSQLException(StrUtil.format("exist {} field value is empty", field)); + throw new ELSQLException(StrUtil.format("field[{}] value is empty", field)); } return data; } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java index 69eadd692..0c13497f6 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java @@ -39,24 +39,22 @@ public class ChainRead extends AbstractSqlRead { throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property"); } - String sqlCmd = StrUtil.format(SqlReadConstant.SQL_PATTERN, chainNameField, elDataField, chainTableName, + return StrUtil.format(SqlReadConstant.SQL_PATTERN, chainNameField, elDataField, chainTableName, chainApplicationNameField); - - return sqlCmd; } @Override public String buildXmlElement(ResultSet rs) throws SQLException { String elDataField = super.config.getElDataField(); - return getStringFromResultSet(rs, elDataField); + return getStringFromRs(rs, elDataField); } @Override public String buildXmlElementUniqueKey(ResultSet rs) throws SQLException { String chainNameField = super.config.getChainNameField(); - return getStringFromResultSet(rs, chainNameField); + return getStringFromRsWithCheck(rs, chainNameField); } @Override diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java index a0b054b0d..0ecbb2a7c 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java @@ -82,7 +82,7 @@ public class ScriptRead extends AbstractSqlRead { public String buildXmlElement(ResultSet rs) throws SQLException { String scriptDataField = super.config.getScriptDataField(); - return getStringFromResultSet(rs, scriptDataField); + return getStringFromRs(rs, scriptDataField); } @@ -93,10 +93,10 @@ public class ScriptRead extends AbstractSqlRead { String scriptTypeField = super.config.getScriptTypeField(); String scriptLanguageField = super.config.getScriptLanguageField(); - String id = getStringFromResultSet(rs, scriptIdField); - String name = getStringFromResultSet(rs, scriptNameField); - String type = getStringFromResultSet(rs, scriptTypeField); - String language = withLanguage() ? getStringFromResultSet(rs, scriptLanguageField) : null; + String id = getStringFromRsWithCheck(rs, scriptIdField); + String name = getStringFromRsWithCheck(rs, scriptNameField); + String type = getStringFromRsWithCheck(rs, scriptTypeField); + String language = withLanguage() ? getStringFromRs(rs, scriptLanguageField) : null; NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); if (Objects.isNull(nodeTypeEnum)) { diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java index 7b9b0efda..4c5b91d72 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java @@ -22,6 +22,8 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Predicate; import static com.yomahub.liteflow.parser.constant.SqlReadConstant.*; @@ -91,9 +93,13 @@ public class JDBCHelper { // 获取 chain 数据 Map chainMap = chainRead.read(); List chainList = new ArrayList<>(); - chainMap.forEach((chainName, elData) -> { - chainList.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(chainName), elData)); - }); + + chainMap.entrySet().stream() + .filter(entry -> StrUtil.isNotBlank(entry.getValue())) + .forEach( + entry -> chainList.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(entry.getKey()), entry.getValue())) + ); + String chainsContent = CollUtil.join(chainList, StrUtil.EMPTY); // 获取脚本数据 diff --git a/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java b/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java index 28f81702e..a9811533c 100644 --- a/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java @@ -66,15 +66,14 @@ public class ZkParserHelper { // 检查chainPath路径下有没有子节点 List chainNameList = client.getChildren().forPath(zkParserVO.getChainPath()); - if (CollectionUtil.isEmpty(chainNameList)) { - throw new ZkException(StrUtil.format("There are no chains in path [{}]", zkParserVO.getChainPath())); - } - // 获取chainPath路径下的所有子节点内容List List chainItemContentList = new ArrayList<>(); for (String chainName : chainNameList) { String chainData = new String( client.getData().forPath(StrUtil.format("{}/{}", zkParserVO.getChainPath(), chainName))); + if (StrUtil.isBlank(chainData)){ + continue; + } chainItemContentList.add(StrUtil.format(CHAIN_XML_PATTERN, chainName, chainData)); } // 合并成所有chain的xml内容 diff --git a/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index c83a47308..f4f462939 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -23,6 +23,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/BaseTest.java index 6f5819b99..2d425d2cd 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java index 64f6dfdcb..4c6ec29e8 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java @@ -50,6 +50,7 @@ public class EtcdWithXmlELSpringbootTest extends BaseTest { @AfterEach public void after() { FlowBus.cleanCache(); + FlowBus.clearStat(); } @Test diff --git a/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..b5ba97770 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,6 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } - } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/nacos/NacosWithXmlELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/nacos/NacosWithXmlELSpringbootTest.java index 1e33174eb..837888949 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/nacos/NacosWithXmlELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nacos-springboot/src/test/java/com/yomahub/liteflow/test/nacos/NacosWithXmlELSpringbootTest.java @@ -42,6 +42,7 @@ public class NacosWithXmlELSpringbootTest extends BaseTest { @AfterEach public void after() { FlowBus.cleanCache(); + FlowBus.clearStat(); } @Test @@ -55,17 +56,17 @@ public class NacosWithXmlELSpringbootTest extends BaseTest { @Test public void testNacosWithXml2() throws Exception { - String flowXml = "THEN(a, b, c);"; - String changedFlowXml = "THEN(a, c);"; + String flowXml = "THEN(a, b, c);"; + String changedFlowXml = "THEN(a, c);"; when(nacosConfigService.getConfig(anyString(), anyString(), anyLong())).thenReturn(flowXml) .thenReturn(changedFlowXml); - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); Assertions.assertEquals("a==>b==>c", response.getExecuteStepStrWithoutTime()); FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_XML, changedFlowXml); - response = flowExecutor.execute2Resp("chain1", "arg"); + response = flowExecutor.execute2Resp("chain2", "arg"); Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime()); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/BaseTest.java index a9494a2aa..507be0173 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { LiteflowConfigGetter.clean(); FlowExecutorHolder.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index e282c0fd0..191dbc898 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,5 +18,6 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/BaseTest.java index 814da97c7..ca79784d0 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -16,6 +16,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot-dynamic/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot-dynamic/src/test/java/com/yomahub/liteflow/test/BaseTest.java index eb7287357..048888b78 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot-dynamic/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot-dynamic/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -22,6 +22,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index eb7287357..048888b78 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -22,6 +22,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java index df1e70c4c..251c9aab1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -18,6 +18,7 @@ public class BaseTest { SpiFactoryCleaner.clean(); LiteflowConfigGetter.clean(); FlowInitHook.cleanHook(); + FlowBus.clearStat(); } } From 60859f4a908828502baba1115287cefe0e7b4782 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 17 Oct 2023 21:58:32 +0800 Subject: [PATCH 04/21] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=88=902.11.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c736ec756..d4dee3d18 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ - 2.11.1 + 2.11.2 UTF-8 UTF-8 8 From 84a9edbc58429398208bcd25180973e2f7fced74 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 24 Oct 2023 12:51:35 +0800 Subject: [PATCH 05/21] =?UTF-8?q?bug=20#I8AF1O=20=E4=BF=AE=E5=A4=8Dredis?= =?UTF-8?q?=E7=9A=84=E8=AE=A2=E9=98=85=E6=A8=A1=E5=BC=8Fmode=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/parser/redis/RedisXmlELParser.java | 14 +++++++++----- .../liteflow/parser/redis/mode/RClient.java | 10 +++++----- .../RedisWithXmlELSubscribeSpringbootTest.java | 9 +++++---- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java index 547b291c5..756c8e729 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java @@ -40,13 +40,17 @@ public class RedisXmlELParser extends ClassXmlFlowELParser { try { RedisParserVO redisParserVO = null; + String configJson; if (MapUtil.isNotEmpty((liteflowConfig.getRuleSourceExtDataMap()))) { - redisParserVO = BeanUtil.toBean(liteflowConfig.getRuleSourceExtDataMap(), - RedisParserVO.class, CopyOptions.create()); - } - else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())) { - redisParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), RedisParserVO.class); + configJson = JsonUtil.toJsonString(liteflowConfig.getRuleSourceExtDataMap()); + }else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())) { + configJson = liteflowConfig.getRuleSourceExtData(); + }else{ + throw new RedisException(ERROR_COMMON_MSG); } + + redisParserVO = JsonUtil.parseObject(configJson, RedisParserVO.class); + if (Objects.isNull(redisParserVO)) { throw new RedisException(ERROR_COMMON_MSG); } diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RClient.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RClient.java index cfa4a1bfa..535dcd648 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RClient.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RClient.java @@ -37,7 +37,7 @@ public class RClient { * @return hashmap */ public Map getMap(String key) { - RMapCache mapCache = redissonClient.getMapCache(key); + RMapCache mapCache = redissonClient.getMapCache(key, StringCodec.INSTANCE); Set mapFieldSet = mapCache.keySet(); if (CollectionUtil.isEmpty(mapFieldSet)) { return map; @@ -69,7 +69,7 @@ public class RClient { * @return keySet */ public Set hkeys(String key) { - RMap map = redissonClient.getMap(key, new StringCodec()); + RMap map = redissonClient.getMap(key, StringCodec.INSTANCE); return map.readAllKeySet(); } @@ -81,7 +81,7 @@ public class RClient { * @return hash value */ public String hget(String key, String field) { - RMap map = redissonClient.getMap(key, new StringCodec()); + RMap map = redissonClient.getMap(key, StringCodec.INSTANCE); return map.get(field); } @@ -91,7 +91,7 @@ public class RClient { * @return shaDigest */ public String scriptLoad(String luaScript) { - RScript script = redissonClient.getScript(new StringCodec()); + RScript script = redissonClient.getScript(StringCodec.INSTANCE); return script.scriptLoad(luaScript); } @@ -102,7 +102,7 @@ public class RClient { * @return string */ public String evalSha(String shaDigest, String... args){ - RScript script = redissonClient.getScript(new StringCodec()); + RScript script = redissonClient.getScript(StringCodec.INSTANCE); return script.evalSha(RScript.Mode.READ_ONLY, shaDigest, RScript.ReturnType.VALUE, Arrays.asList(args)).toString(); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELSubscribeSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELSubscribeSpringbootTest.java index 523d13d0d..a5388e8dc 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELSubscribeSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELSubscribeSpringbootTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.redisson.Redisson; import org.redisson.api.RMapCache; import org.redisson.api.RedissonClient; +import org.redisson.client.codec.StringCodec; import org.redisson.config.Config; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -60,8 +61,8 @@ public class RedisWithXmlELSubscribeSpringbootTest extends BaseTest { Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(1); redissonClient = Redisson.create(config); - RMapCache chainKey = redissonClient.getMapCache("testChainKey"); - RMapCache scriptKey = redissonClient.getMapCache("testScriptKey"); + RMapCache chainKey = redissonClient.getMapCache("testChainKey", StringCodec.INSTANCE); + RMapCache scriptKey = redissonClient.getMapCache("testScriptKey", StringCodec.INSTANCE); scriptKey.put("s1:script:脚本s1:groovy", "defaultContext.setData(\"test1\",\"hello s1\");"); scriptKey.put("s2:script:脚本s2:js", "defaultContext.setData(\"test2\",\"hello s2\");"); scriptKey.put("s3:script:脚本s3", "defaultContext.setData(\"test3\",\"hello s3\");"); @@ -183,8 +184,8 @@ public class RedisWithXmlELSubscribeSpringbootTest extends BaseTest { //redis内规则数据数据清空 public static void testCleanData() { if (ObjectUtil.isNotNull(redissonClient)) { - RMapCache chainKey = redissonClient.getMapCache("testChainKey"); - RMapCache scriptKey = redissonClient.getMapCache("testScriptKey"); + RMapCache chainKey = redissonClient.getMapCache("testChainKey", StringCodec.INSTANCE); + RMapCache scriptKey = redissonClient.getMapCache("testScriptKey", StringCodec.INSTANCE); for (String key : chainKey.keySet()) { chainKey.remove(key); } From bc4dfc21cb472f0f2c0a541ce2c7e1bcdab5d8f7 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 24 Oct 2023 19:16:35 +0800 Subject: [PATCH 06/21] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=88=B0v2.11.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d4dee3d18..064dafbe2 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ - 2.11.2 + 2.11.3 UTF-8 UTF-8 8 From 5da0e7bf4a5deb45ed35d51dcd73ef942feeb1be Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 24 Oct 2023 19:17:16 +0800 Subject: [PATCH 07/21] =?UTF-8?q?WhenFutureObj=E5=AF=B9=E8=B1=A1=E4=B8=AD?= =?UTF-8?q?=E7=9A=84executorName=E6=94=B9=E6=88=90executorId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/flow/parallel/WhenFutureObj.java | 25 +++++++++---------- .../strategy/ParallelStrategyExecutor.java | 4 +-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/WhenFutureObj.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/WhenFutureObj.java index c0953b891..33bb8b963 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/WhenFutureObj.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/WhenFutureObj.java @@ -2,7 +2,6 @@ package com.yomahub.liteflow.flow.parallel; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.exception.WhenTimeoutException; -import com.yomahub.liteflow.property.LiteflowConfigGetter; /** * 并行异步CompletableFuture里的值对象 @@ -16,34 +15,34 @@ public class WhenFutureObj { private boolean timeout; - private String executorName; + private String executorId; private Exception ex; - public static WhenFutureObj success(String executorName) { + public static WhenFutureObj success(String executorId) { WhenFutureObj result = new WhenFutureObj(); result.setSuccess(true); result.setTimeout(false); - result.setExecutorName(executorName); + result.setExecutorId(executorId); return result; } - public static WhenFutureObj fail(String executorName, Exception ex) { + public static WhenFutureObj fail(String executorId, Exception ex) { WhenFutureObj result = new WhenFutureObj(); result.setSuccess(false); result.setTimeout(false); - result.setExecutorName(executorName); + result.setExecutorId(executorId); result.setEx(ex); return result; } - public static WhenFutureObj timeOut(String executorName) { + public static WhenFutureObj timeOut(String executorId) { WhenFutureObj result = new WhenFutureObj(); result.setSuccess(false); result.setTimeout(true); - result.setExecutorName(executorName); + result.setExecutorId(executorId); result.setEx(new WhenTimeoutException( - StrUtil.format("Timed out when executing the component[{}]",executorName))); + StrUtil.format("Timed out when executing the component[{}]",executorId))); return result; } @@ -55,12 +54,12 @@ public class WhenFutureObj { this.success = success; } - public String getExecutorName() { - return executorName; + public String getExecutorId() { + return executorId; } - public void setExecutorName(String executorName) { - this.executorName = executorName; + public void setExecutorId(String executorId) { + this.executorId = executorId; } public Exception getEx() { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java index cd390352a..47b6c4ca1 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java @@ -207,7 +207,7 @@ public abstract class ParallelStrategyExecutor { // 输出超时信息 timeOutWhenFutureObjList.forEach(whenFutureObj -> LOG.warn( - "executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", whenFutureObj.getExecutorName())); + "executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", whenFutureObj.getExecutorId())); // 当配置了 ignoreError = false,出现 interrupted 或者 !f.get() 的情况,将抛出 WhenExecuteException if (!whenCondition.isIgnoreError()) { @@ -219,7 +219,7 @@ public abstract class ParallelStrategyExecutor { // 循环判断CompletableFuture的返回值,如果异步执行失败,则抛出相应的业务异常 for (WhenFutureObj whenFutureObj : allCompletableWhenFutureObjList) { if (!whenFutureObj.isSuccess()) { - LOG.info(StrUtil.format("when-executor[{}] execute failed. errorResume [false].", whenFutureObj.getExecutorName())); + LOG.info(StrUtil.format("when-executor[{}] execute failed. errorResume [false].", whenFutureObj.getExecutorId())); throw whenFutureObj.getEx(); } } From a3a601d510a5ecb26b3ae4c1623569f17863847d Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 24 Oct 2023 19:17:54 +0800 Subject: [PATCH 08/21] =?UTF-8?q?=E7=BB=99=E6=89=80=E6=9C=89=E7=9A=84Condi?= =?UTF-8?q?tion=E8=AE=BE=E7=BD=AE=E9=BB=98=E8=AE=A4Id=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8DCondition=E6=B2=A1Id=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/yomahub/liteflow/flow/element/Condition.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java index 3bfbe3203..0d4eec6a7 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java @@ -10,6 +10,7 @@ package com.yomahub.liteflow.flow.element; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.enums.ConditionTypeEnum; import com.yomahub.liteflow.enums.ExecuteTypeEnum; import com.yomahub.liteflow.exception.ChainEndException; @@ -127,7 +128,11 @@ public abstract class Condition implements Executable{ @Override public String getId() { - return id; + if (StrUtil.isBlank(this.id)){ + return StrUtil.format("condition-{}",this.getConditionType().getName()); + }else{ + return id; + } } @Override From ddc24e2707f9a44414e417a427589b35e83aab6e Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 25 Oct 2023 12:16:08 +0800 Subject: [PATCH 09/21] =?UTF-8?q?=E7=BB=99=E6=89=80=E6=9C=89=E7=9A=84Condi?= =?UTF-8?q?tion=E8=AE=BE=E7=BD=AE=E9=BB=98=E8=AE=A4Id=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8DCondition=E6=B2=A1Id=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.zh-CN.md | 6 ++++++ static/img/yuncheng-banner.png | Bin 0 -> 41929 bytes 2 files changed, 6 insertions(+) create mode 100644 static/img/yuncheng-banner.png diff --git a/README.zh-CN.md b/README.zh-CN.md index c2e8f278d..e62852fb3 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -57,6 +57,12 @@ LiteFlow拥有极其详细易懂的文档体系,能帮助你解决在使用框 LiteFlow期待你的了解! +**赞助商** + +云程企业级低代码平台 + + + **微信公众号** 社区群需要邀请入群。关注公众号后点击`个人微信`加我,我可以拉你入群 diff --git a/static/img/yuncheng-banner.png b/static/img/yuncheng-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..5e057ddc7c7d17a4b1e2bdb3c5a1b25c1ca64047 GIT binary patch literal 41929 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 Date: Wed, 25 Oct 2023 12:18:14 +0800 Subject: [PATCH 10/21] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.zh-CN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index e62852fb3..fef607250 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -57,9 +57,9 @@ LiteFlow拥有极其详细易懂的文档体系,能帮助你解决在使用框 LiteFlow期待你的了解! -**赞助商** +## 🦾赞助商 -云程企业级低代码平台 +**云程企业级低代码平台** From 3d897e49583eb97af4f458b27dedbf50256954ab Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 25 Oct 2023 12:48:37 +0800 Subject: [PATCH 11/21] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/img/yuncheng-banner.png | Bin 41929 -> 78451 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/static/img/yuncheng-banner.png b/static/img/yuncheng-banner.png index 5e057ddc7c7d17a4b1e2bdb3c5a1b25c1ca64047..34db09fe46cd3d54b60249df631dc3f9b93e90d8 100644 GIT binary patch literal 78451 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�hfNT-mR#}ZI_iqFa6e@1R+j{;}5RF1MD1WxJny^I5> z%m9&qMA&&zX2bI-ozIsOc4+36?O$`0(?~#|`CwN)*4K{6hOSu?N70{yq=Ha537*$$ zvD?J3iY@+;9b`o!|99C?pyAiib8qqW?tT`4sZFKej$Y$~8B8ycN35?ifE8yK^uX@_ zRw{^uOf83IW73-M)0pE%oCEv-mhTa_HaT^`dKA7Bh$8p8&O_MS~mJ zTPTyAjiCA?vwy42>u*cT^hyC^u3wW{|H_o}I4#l2$NpG=-3CUkvw%j%-*$U_j~`PE z(=xSCwlLMsl*=FD(6*5}T>Y*~uhYzXvk)4#|0ll#xM13Yf9rJebu_gmd!O%_`A#;l zRwqR(V1aFT!NpTF^)+YOMHX%2hre2p_tW`U+TwAEEtJlh>HjD{FLFk@`=$#L%&n#C z5`pmGq@a>RY!JFY@!O(aT&NC!CeEcM^o$?W2fPtiy6&&*EnA;i$DSp4z(FUJz{Up9 zJPTv7>pzBCj@D)*Cey`~Dd%b6tC{z1Fv^wSg=|C3Yh> zW!1G4cmXUNi^Z8g6@+IY;iUsD79&YxwlSH^V%iig03#71lJIr~EtUD_KJ&xP#)4s* z13jL_lb4GMtL57_-{4zQG40GYe+ii>EN>GShCxYWW4z@n`qG~W3BRlVL~AZo*uk}| zwESHw<3{~=M7}59lEzh9^L?F%sM{AtkF>L9I~FhrFyKqQqB<99#Y>@;QQ2E*vL6;B zUXxPQ^T=rp;;Z$3%9kx-B%hb+lF_yth7eL1hU z>R0<+Ay!PIWQ`M^q+r;F=$yDmL_*@flKC>5N!M>j7~{-zX#i=UlsL{cBcRuyTeN2T z^AURL{u`EMt`Ht9luFr)2FA`y&yyN~OivM9QLMqMePd@-#{1ZnH+_#NZ z(A;%F2avYD{!~~L(T=9018NW8#2$M0yIs}=D0|BRa7ZK9zX)g~0sbreJb7tlXFn>z zd~raXrP)xvY!GPjS-D$G2Vjz45?ZT?7&rRc;Ny&s#nU)z-mN>x;|RQiPS$S`M5Vgb1&Ir=pME39trmW4L}jR1*)^ha+yKQvgN{`^B&VS0MA7E1ZG|Wzn3Q# zC08!Yq9*3|(&d&KW#$kgkqLa`9dWeDM6acwu8`#HP*tmu3V-JPIg6h+s3~nIRrMGd z|7i4M-89|9Ex8)Mx*DyE+(9l>c8PJnr0;?b{`5BNLVH57Wu0-wL+o{!!6bl;vvx?9ZD z+#!nWhpN_Jh}5R-y`AOteV(Tvp+mfvmO$A4mxODBEu}$Mjwz$ZMA3YOC}Xkz1pmE- z$_<4svP^}jGVI@qZerYq>km8#Pb3HjF6$K7w-LZq&T1#E@e@ z=R`-?{c!y_CCL#^^f$ON7tM2_469h*(~YLfPOE$e}M&7WL|`Eug!_jQqLas1_C z;+8|Nz%KoNON`bJ8*iKjYOEn*&C#dOhq-bgP_?N*r)zQo`qXiTt&2PYhiux}Q|@-A

cFfq8uJ7_8UZOO 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 Date: Thu, 26 Oct 2023 11:51:44 +0800 Subject: [PATCH 12/21] =?UTF-8?q?bug=20#I8AWHT=20LiteFlowChainELBuilder.va?= =?UTF-8?q?lidate()=E5=AD=98=E5=9C=A8bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/builder/el/LiteFlowChainELBuilder.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java index cb6fd7748..23f2b7c03 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java @@ -3,6 +3,7 @@ package com.yomahub.liteflow.builder.el; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -145,17 +146,21 @@ public class LiteFlowChainELBuilder { // 这里无论多复杂的,外面必定有一个最外层的Condition,所以这里只有一个,内部可以嵌套很多层,这点和以前的不太一样 Condition condition = (Condition) EXPRESS_RUNNER.execute(elStr, context, errorList, true, true); + if (Objects.isNull(condition)){ + throw new QLException(StrUtil.format("parse el fail,el:[{}]", elStr)); + } + // 把主要的condition加入 this.conditionList.add(condition); return this; } catch (QLException e) { // EL 底层会包装异常,这里是曲线处理 - if (Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) { + if (ObjectUtil.isNotNull(e.getCause()) && Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) { // 构建错误信息 String msg = buildDataNotFoundExceptionMsg(elStr); throw new ELParseException(msg); } - throw new ELParseException(e.getCause().getMessage()); + throw new ELParseException(e.getMessage()); } catch (Exception e) { String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId()); throw new ELParseException(errMsg + e.getMessage()); @@ -172,7 +177,7 @@ public class LiteFlowChainELBuilder { LiteFlowChainELBuilder.createChain().setEL(elStr); return Boolean.TRUE; } catch (Exception e) { - LOG.error(e.getMessage()); + LOG.error("validate error",e); } return Boolean.FALSE; } From 915e23597ae155fbd5e1c2198312cfde5d3d7877 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Thu, 26 Oct 2023 18:22:27 +0800 Subject: [PATCH 13/21] =?UTF-8?q?=E5=8E=BB=E9=99=A4rollback=E4=B8=AD?= =?UTF-8?q?=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84monitor=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/yomahub/liteflow/core/NodeComponent.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 2ecca94ca..55fe2e528 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -178,11 +178,6 @@ public abstract class NodeComponent { // 往CmpStep中放入时间消耗信息 cmpStep.setRollbackTimeSpent(timeSpent); - // 性能统计 - if (ObjectUtil.isNotNull(monitorBus)) { - CompStatistics statistics = new CompStatistics(this.getClass().getSimpleName(), timeSpent); - monitorBus.addStatistics(statistics); - } } } From 91a15af3c0941c61d0e835cf2204beae8d116bc1 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Fri, 27 Oct 2023 15:25:22 +0800 Subject: [PATCH 14/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dspringboot=E4=B8=8B?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=B3=A8=E5=85=A5=E6=B2=A1=E5=8A=A0=E9=A6=96?= =?UTF-8?q?=E6=AC=A1=E5=88=A4=E6=96=AD=E8=80=8C=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E4=BA=8C=E6=AC=A1=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/yomahub/liteflow/springboot/LiteflowExecutorInit.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowExecutorInit.java b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowExecutorInit.java index 2113178be..34ff78300 100644 --- a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowExecutorInit.java +++ b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowExecutorInit.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.springboot; import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.FlowBus; import org.springframework.beans.factory.InitializingBean; /** @@ -19,6 +20,7 @@ public class LiteflowExecutorInit implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { flowExecutor.init(true); + FlowBus.needInit(); } } From 6dbae72238b53c98b2c1bc6c631b3a81cc2ce97e Mon Sep 17 00:00:00 2001 From: rain <672378783@qq.com> Date: Sat, 28 Oct 2023 16:36:55 +0800 Subject: [PATCH 15/21] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E4=BC=9A=E5=A4=9A=E6=AC=A1rollback=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/core/NodeComponent.java | 9 +++++- .../RollbackELDeclMultiSpringbootTest.java | 8 +++++ .../liteflow/test/rollback/cmp/CmpConfig.java | 29 +++++++++++++++++++ .../src/test/resources/rollback/flow.el.xml | 4 +++ .../RollbackELDeclSpringbootTest.java | 8 +++++ .../src/test/resources/rollback/flow.el.xml | 4 +++ .../liteflow/test/rollback/RollbackTest.java | 8 +++++ .../src/test/resources/rollback/flow.el.xml | 6 ++++ .../test/rollback/RollbackSpringbootTest.java | 8 +++++ .../src/test/resources/rollback/flow.el.xml | 4 +++ .../test/rollback/RollbackSpringbootTest.java | 10 +++++++ .../liteflow/test/rollback/cmp/MCmp.java | 25 ++++++++++++++++ .../liteflow/test/rollback/cmp/NCmp.java | 20 +++++++++++++ .../src/test/resources/rollback/flow.el.xml | 4 +++ .../test/rollback/RollbackSpringTest.java | 8 +++++ .../liteflow/test/rollback/cmp/MCmp.java | 25 ++++++++++++++++ .../liteflow/test/rollback/cmp/NCmp.java | 20 +++++++++++++ .../src/test/resources/rollback/flow.el.xml | 4 +++ 18 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 55fe2e528..bd67c1831 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -7,6 +7,7 @@ */ package com.yomahub.liteflow.core; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -32,6 +33,7 @@ import com.yomahub.liteflow.monitor.CompStatistics; import com.yomahub.liteflow.monitor.MonitorBus; import java.lang.reflect.Method; +import java.util.Deque; import java.util.Map; /** @@ -156,10 +158,15 @@ public abstract class NodeComponent { public void doRollback() throws Exception { Slot slot = this.getSlot(); - + Deque rollbackSteps = slot.getRollbackSteps(); + if(!CollUtil.isEmpty(rollbackSteps)) { + Node refNode = rollbackSteps.peekLast().getRefNode(); + if(refNode == this.getRefNode()) return; + } CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE); cmpStep.setTag(this.getTag()); cmpStep.setInstance(this); + cmpStep.setRefNode(this.getRefNode()); slot.addRollbackStep(cmpStep); StopWatch stopWatch = new StopWatch(); diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java index 022dc9aec..8fcc1c34b 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java @@ -100,4 +100,12 @@ public class RollbackELDeclMultiSpringbootTest extends BaseTest { Assertions.assertEquals("321", context.getData("test")); } + @Test + // 对重试的测试 + public void testRetry() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("n==>m", response.getRollbackStepStr()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java index 1c1e43ad8..b88545014 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java @@ -3,6 +3,7 @@ package com.yomahub.liteflow.test.rollback.cmp; import cn.hutool.core.collection.ListUtil; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.annotation.LiteflowRetry; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.enums.LiteFlowMethodEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; @@ -145,4 +146,32 @@ public class CmpConfig { System.out.println("XCmp rollback!"); } + private int flag = 0; + @LiteflowRetry(5) + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "m") + public void processM(NodeComponent bindCmp) { + if(flag < 2) { + flag ++; + throw new RuntimeException(); + } + System.out.println("MCmp executed!"); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "m") + public void rollbackM() throws Exception { + System.out.println("MCmp rollback!"); + } + + @LiteflowRetry(3) + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "n") + public void processN(NodeComponent bindCmp) { + System.out.println("NCmp executed!"); + throw new RuntimeException(); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "n") + public void rollbackN() throws Exception { + System.out.println("NCmp rollback!"); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml index 887403167..a6db91b48 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml @@ -35,4 +35,8 @@ THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + + + THEN( m, n ); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java index c89f3c72c..952c8567e 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java @@ -102,4 +102,12 @@ public class RollbackELDeclSpringbootTest extends BaseTest { Assertions.assertEquals("321", context.getData("test")); } + @Test + // 对重试的测试 + public void testRetry() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("n==>m", response.getRollbackStepStr()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml index 2e46ba7af..8ab1a1403 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml @@ -35,4 +35,8 @@ THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + + + THEN( m, n ); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java index f8d58918e..97498e070 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java @@ -99,4 +99,12 @@ public class RollbackTest extends BaseTest { Assertions.assertEquals("321", context.getData("test")); } + @Test + // 对重试的测试 + public void testRetry() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("n==>m", response.getRollbackStepStr()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml index 7072cf2b9..da327932d 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml @@ -12,6 +12,8 @@ + + THEN( a, b, WHEN(c, d).ignoreError(true), CATCH(e) ); @@ -48,4 +50,8 @@ THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + + + THEN( m, n ); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java index 07d7486f6..fdaa63d62 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java @@ -94,4 +94,12 @@ public class RollbackSpringbootTest extends BaseTest { Assertions.assertEquals("321", context.getData("test")); } + @Test + // 对重试的测试 + public void testRetry() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("n==>m", response.getRollbackStepStr()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml index 2e46ba7af..8ab1a1403 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml @@ -35,4 +35,8 @@ THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + + + THEN( m, n ); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java index 8e230f7fa..133806e92 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java @@ -2,6 +2,8 @@ package com.yomahub.liteflow.test.rollback; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; @@ -97,4 +99,12 @@ public class RollbackSpringbootTest extends BaseTest { Assertions.assertFalse(response.isSuccess()); Assertions.assertEquals("321", context.getData("test")); } + + @Test + // 对重试的测试 + public void testRetry() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("n==>m", response.getRollbackStepStr()); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java new file mode 100644 index 000000000..975de743c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java @@ -0,0 +1,25 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("m") +@LiteflowRetry(5) +public class MCmp extends NodeComponent { + + private int flag = 0; + @Override + public void process() { + if(flag < 2) { + flag ++; + throw new RuntimeException(); + } + System.out.println("MCmp executed!"); + } + + @Override + public void rollback() throws Exception { + System.out.println("MCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java new file mode 100644 index 000000000..65d509968 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("n") +@LiteflowRetry(3) +public class NCmp extends NodeComponent { + @Override + public void process() { + System.out.println("NCmp executed!"); + throw new RuntimeException(); + } + + @Override + public void rollback() throws Exception { + System.out.println("NCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml index 2e46ba7af..8ab1a1403 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml @@ -35,4 +35,8 @@ THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + + + THEN( m, n ); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java index e4b08ccc9..59a4e6160 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java @@ -95,5 +95,13 @@ public class RollbackSpringTest extends BaseTest { Assertions.assertEquals("321", context.getData("test")); } + @Test + // 对重试的测试 + public void testRetry() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("n==>m", response.getRollbackStepStr()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java new file mode 100644 index 000000000..975de743c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java @@ -0,0 +1,25 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("m") +@LiteflowRetry(5) +public class MCmp extends NodeComponent { + + private int flag = 0; + @Override + public void process() { + if(flag < 2) { + flag ++; + throw new RuntimeException(); + } + System.out.println("MCmp executed!"); + } + + @Override + public void rollback() throws Exception { + System.out.println("MCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java new file mode 100644 index 000000000..65d509968 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("n") +@LiteflowRetry(3) +public class NCmp extends NodeComponent { + @Override + public void process() { + System.out.println("NCmp executed!"); + throw new RuntimeException(); + } + + @Override + public void rollback() throws Exception { + System.out.println("NCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml index 2e46ba7af..8ab1a1403 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml @@ -35,4 +35,8 @@ THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + + + THEN( m, n ); + \ No newline at end of file From 6c65aa2ad5b8e525854a0512da602af21bebd4bf Mon Sep 17 00:00:00 2001 From: houxinyu Date: Sat, 28 Oct 2023 21:04:10 +0800 Subject: [PATCH 16/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dredis=E5=93=A8=E5=85=B5?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/parser/redis/RedisXmlELParser.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java index 756c8e729..1de16453d 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/RedisXmlELParser.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.parser.redis; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.StrFormatter; import cn.hutool.core.util.ObjectUtil; @@ -9,6 +10,7 @@ import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.FlowInitHook; import com.yomahub.liteflow.parser.el.ClassXmlFlowELParser; import com.yomahub.liteflow.parser.redis.exception.RedisException; +import com.yomahub.liteflow.parser.redis.mode.RedisMode; import com.yomahub.liteflow.parser.redis.mode.polling.RedisParserPollingMode; import com.yomahub.liteflow.parser.redis.mode.subscribe.RedisParserSubscribeMode; import com.yomahub.liteflow.parser.redis.mode.RedisParserHelper; @@ -97,12 +99,18 @@ public class RedisXmlELParser extends ClassXmlFlowELParser { } private void checkParserVO(RedisParserVO redisParserVO) { - if (StrUtil.isBlank(redisParserVO.getHost())) { + if (redisParserVO.getRedisMode().equals(RedisMode.SINGLE) && StrUtil.isBlank(redisParserVO.getHost())) { throw new RedisException(StrFormatter.format(ERROR_MSG_PATTERN, "host")); } - if (ObjectUtil.isNull(redisParserVO.getPort())) { + if (redisParserVO.getRedisMode().equals(RedisMode.SINGLE) && ObjectUtil.isNull(redisParserVO.getPort())) { throw new RedisException(StrFormatter.format(ERROR_MSG_PATTERN, "port")); } + if (redisParserVO.getRedisMode().equals(RedisMode.SENTINEL) && StrUtil.isBlank(redisParserVO.getMasterName())) { + throw new RedisException(StrFormatter.format(ERROR_MSG_PATTERN, "master name")); + } + if (redisParserVO.getRedisMode().equals(RedisMode.SENTINEL) && CollectionUtil.isEmpty(redisParserVO.getSentinelAddress())) { + throw new RedisException(StrFormatter.format(ERROR_MSG_PATTERN, "sentinel address list")); + } if (ObjectUtil.isNull(redisParserVO.getChainDataBase())) { throw new RedisException(StrFormatter.format(ERROR_MSG_PATTERN, "chainDataBase")); } From f8e064f9daaf2c73ac6b47c7127c6148f028c429 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Sat, 28 Oct 2023 22:48:45 +0800 Subject: [PATCH 17/21] =?UTF-8?q?enhancement=20#I8BPM9=20SQL=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=A2=9E=E5=8A=A0enable=E5=AD=97=E6=AE=B5=E7=9A=84?= =?UTF-8?q?=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/constant/SqlReadConstant.java | 3 +++ .../parser/sql/read/AbstractSqlRead.java | 10 ++++--- .../parser/sql/read/impl/ChainRead.java | 9 ++++++- .../parser/sql/read/impl/ScriptRead.java | 4 +++ .../liteflow/parser/sql/vo/SQLParserVO.java | 27 +++++++++++++++++++ 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/SqlReadConstant.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/SqlReadConstant.java index 68d8e5ee8..f816aec98 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/SqlReadConstant.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/SqlReadConstant.java @@ -5,12 +5,15 @@ package com.yomahub.liteflow.parser.constant; * * @author tangkc * @author houxinyu + * @author Bryan.Zhang * @since 2.11.1 */ public class SqlReadConstant { public static final String SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}=?"; + public static final String SQL_ENABLE_PATTERN = "AND {}=?"; + public static final String SCRIPT_SQL_CHECK_PATTERN = "SELECT 1 FROM {} "; public static final String SCRIPT_SQL_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?"; diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java index 0b7d9437d..9b15600a3 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java @@ -8,10 +8,7 @@ import com.yomahub.liteflow.parser.sql.exception.ELSQLException; import com.yomahub.liteflow.parser.sql.util.LiteFlowJdbcUtil; import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; +import java.sql.*; import java.util.HashMap; import java.util.Map; @@ -20,6 +17,7 @@ import java.util.Map; * * @author tangkc * @author houxinyu + * @author Bryan.Zhang * @since 2.11.1 */ public abstract class AbstractSqlRead implements SqlRead { @@ -52,6 +50,10 @@ public abstract class AbstractSqlRead implements SqlRead { // 设置游标拉取数量 stmt.setFetchSize(SqlReadConstant.FETCH_SIZE_MAX); stmt.setString(1, config.getApplicationName()); + ParameterMetaData parameterMetaData = stmt.getParameterMetaData(); + if (parameterMetaData.getParameterCount() == 2){ + stmt.setBoolean(2, true); + } rs = stmt.executeQuery(); while (rs.next()) { diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java index 0c13497f6..d2b9defc0 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java @@ -30,6 +30,7 @@ public class ChainRead extends AbstractSqlRead { String chainNameField = super.config.getChainNameField(); String chainApplicationNameField = super.config.getChainApplicationNameField(); String applicationName = super.config.getApplicationName(); + String chainEnableField = super.config.getChainEnableField(); if (StrUtil.isBlank(chainTableName)) { throw new ELSQLException("You did not define the chainTableName property"); @@ -39,8 +40,14 @@ public class ChainRead extends AbstractSqlRead { throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property"); } - return StrUtil.format(SqlReadConstant.SQL_PATTERN, chainNameField, elDataField, chainTableName, + String sqlCmd = StrUtil.format(SqlReadConstant.SQL_PATTERN, chainNameField, elDataField, chainTableName, chainApplicationNameField); + + if (StrUtil.isNotBlank(chainEnableField)){ + sqlCmd = StrUtil.format("{} {}", sqlCmd, StrUtil.format(SqlReadConstant.SQL_ENABLE_PATTERN, chainEnableField)); + } + + return sqlCmd; } @Override diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java index 0ecbb2a7c..2703a5298 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java @@ -40,6 +40,7 @@ public class ScriptRead extends AbstractSqlRead { String scriptTypeField = super.config.getScriptTypeField(); String scriptApplicationNameField = super.config.getScriptApplicationNameField(); String applicationName = super.config.getApplicationName(); + String scriptEnableField = super.config.getScriptEnableField(); if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); @@ -74,6 +75,9 @@ public class ScriptRead extends AbstractSqlRead { ); } + if (StrUtil.isNotBlank(scriptEnableField)){ + sqlCmd = StrUtil.format("{} {}", sqlCmd, StrUtil.format(SqlReadConstant.SQL_ENABLE_PATTERN, scriptEnableField)); + } return sqlCmd; } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java index 8824328dd..97fa91840 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java @@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil; * 用于解析 RuleSourceExtData 的 VO 类,用于 sql 模式中 * * @author tangkc + * @author Bryan.Zhang * @since 2.9.0 */ public class SQLParserVO { @@ -55,6 +56,11 @@ public class SQLParserVO { */ private String elDataField = "el_data"; + /** + * 是否启动某一条chain + */ + private String chainEnableField; + /** * 脚本 node 表名 */ @@ -90,6 +96,11 @@ public class SQLParserVO { */ private String scriptLanguageField; + /** + * 是否启动这一条脚本 + */ + private String scriptEnableField; + /** * 轮询机制是否开启 默认不开启 */ @@ -276,4 +287,20 @@ public class SQLParserVO { public void setSqlLogEnabled(Boolean sqlLogEnabled) { this.sqlLogEnabled = sqlLogEnabled; } + + public String getChainEnableField() { + return chainEnableField; + } + + public void setChainEnableField(String chainEnableField) { + this.chainEnableField = chainEnableField; + } + + public String getScriptEnableField() { + return scriptEnableField; + } + + public void setScriptEnableField(String scriptEnableField) { + this.scriptEnableField = scriptEnableField; + } } From 470cb3c5b9ee1bac1f0b9144a5421834c3b4e1cd Mon Sep 17 00:00:00 2001 From: rain <672378783@qq.com> Date: Mon, 30 Oct 2023 14:28:03 +0800 Subject: [PATCH 18/21] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E4=BC=9A=E5=A4=9A=E6=AC=A1rollback=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/yomahub/liteflow/core/NodeComponent.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index bd67c1831..7fcdd1d79 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -160,8 +160,10 @@ public abstract class NodeComponent { Slot slot = this.getSlot(); Deque rollbackSteps = slot.getRollbackSteps(); if(!CollUtil.isEmpty(rollbackSteps)) { - Node refNode = rollbackSteps.peekLast().getRefNode(); - if(refNode == this.getRefNode()) return; + for (CmpStep rollbackStep : rollbackSteps) { + Node refNode = rollbackStep.getRefNode(); + if(refNode == this.getRefNode()) return; + } } CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE); cmpStep.setTag(this.getTag()); From 95cdb9ca6814d133b35ea99238706fa5d9a9e73b Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 31 Oct 2023 00:11:54 +0800 Subject: [PATCH 19/21] =?UTF-8?q?bug=20#I8AR0L=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E4=BA=86=E9=87=8D=E8=AF=95=E5=92=8C=E5=9B=9E?= =?UTF-8?q?=E6=BB=9A=EF=BC=8C=E5=9B=9E=E6=BB=9A=E4=B9=9F=E4=BC=9A=E8=A2=AB?= =?UTF-8?q?=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/core/NodeComponent.java | 12 ++++----- .../liteflow/test/rollback/cmp/MCmp.java | 25 +++++++++++++++++++ .../liteflow/test/rollback/cmp/NCmp.java | 20 +++++++++++++++ .../liteflow/test/rollback/cmp/MCmp.java | 23 +++++++++++++++++ .../liteflow/test/rollback/cmp/NCmp.java | 18 +++++++++++++ 5 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 7fcdd1d79..5d8c81c70 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -35,6 +35,7 @@ import com.yomahub.liteflow.monitor.MonitorBus; import java.lang.reflect.Method; import java.util.Deque; import java.util.Map; +import java.util.function.Predicate; /** * 普通组件抽象类 @@ -158,13 +159,12 @@ public abstract class NodeComponent { public void doRollback() throws Exception { Slot slot = this.getSlot(); - Deque rollbackSteps = slot.getRollbackSteps(); - if(!CollUtil.isEmpty(rollbackSteps)) { - for (CmpStep rollbackStep : rollbackSteps) { - Node refNode = rollbackStep.getRefNode(); - if(refNode == this.getRefNode()) return; - } + + boolean alreadyRollback = slot.getRollbackSteps().stream().anyMatch(cmpStep -> cmpStep.getRefNode().equals(getRefNode())); + if (alreadyRollback){ + return; } + CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE); cmpStep.setTag(this.getTag()); cmpStep.setInstance(this); diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java new file mode 100644 index 000000000..975de743c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java @@ -0,0 +1,25 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("m") +@LiteflowRetry(5) +public class MCmp extends NodeComponent { + + private int flag = 0; + @Override + public void process() { + if(flag < 2) { + flag ++; + throw new RuntimeException(); + } + System.out.println("MCmp executed!"); + } + + @Override + public void rollback() throws Exception { + System.out.println("MCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java new file mode 100644 index 000000000..65d509968 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("n") +@LiteflowRetry(3) +public class NCmp extends NodeComponent { + @Override + public void process() { + System.out.println("NCmp executed!"); + throw new RuntimeException(); + } + + @Override + public void rollback() throws Exception { + System.out.println("NCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java new file mode 100644 index 000000000..da538e236 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowRetry(5) +public class MCmp extends NodeComponent { + + private int flag = 0; + @Override + public void process() { + if(flag < 2) { + flag ++; + throw new RuntimeException(); + } + System.out.println("MCmp executed!"); + } + + @Override + public void rollback() throws Exception { + System.out.println("MCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java new file mode 100644 index 000000000..66d51ef08 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowRetry(3) +public class NCmp extends NodeComponent { + @Override + public void process() { + System.out.println("NCmp executed!"); + throw new RuntimeException(); + } + + @Override + public void rollback() throws Exception { + System.out.println("NCmp rollback!"); + } +} From d30020d4ffb1671ece39cc31a53b19190df64c48 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 31 Oct 2023 12:45:35 +0800 Subject: [PATCH 20/21] =?UTF-8?q?bug=20#I8AR0L=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E4=BA=86=E9=87=8D=E8=AF=95=E5=92=8C=E5=9B=9E?= =?UTF-8?q?=E6=BB=9A=EF=BC=8C=E5=9B=9E=E6=BB=9A=E4=B9=9F=E4=BC=9A=E8=A2=AB?= =?UTF-8?q?=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/test/rollback/cmp/MCmp.java | 25 +++++++++++++++++++ .../liteflow/test/rollback/cmp/NCmp.java | 20 +++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java new file mode 100644 index 000000000..5e7b10944 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/MCmp.java @@ -0,0 +1,25 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.noear.solon.annotation.Component; + +@Component("m") +@LiteflowRetry(5) +public class MCmp extends NodeComponent { + + private int flag = 0; + @Override + public void process() { + if(flag < 2) { + flag ++; + throw new RuntimeException(); + } + System.out.println("MCmp executed!"); + } + + @Override + public void rollback() throws Exception { + System.out.println("MCmp rollback!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java new file mode 100644 index 000000000..34abed913 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/NCmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.rollback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; +import org.noear.solon.annotation.Component; + +@Component("n") +@LiteflowRetry(3) +public class NCmp extends NodeComponent { + @Override + public void process() { + System.out.println("NCmp executed!"); + throw new RuntimeException(); + } + + @Override + public void rollback() throws Exception { + System.out.println("NCmp rollback!"); + } +} From 34018069a2abf177a04ae0c37bddf511b3c40510 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 31 Oct 2023 15:44:58 +0800 Subject: [PATCH 21/21] =?UTF-8?q?=E4=BC=98=E5=8C=96EL=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E7=9A=84=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java index 23f2b7c03..c2d22fc92 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java @@ -159,8 +159,11 @@ public class LiteFlowChainELBuilder { // 构建错误信息 String msg = buildDataNotFoundExceptionMsg(elStr); throw new ELParseException(msg); + }else if (ObjectUtil.isNotNull(e.getCause())){ + throw new ELParseException(e.getCause().getMessage()); + }else{ + throw new ELParseException(e.getMessage()); } - throw new ELParseException(e.getMessage()); } catch (Exception e) { String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId()); throw new ELParseException(errMsg + e.getMessage());