enhancement #I61D1N 解析增加 enable 逻辑,完成 apollo 改造

This commit is contained in:
gaibu
2024-01-14 22:56:03 +08:00
parent d15eeb9855
commit 443b1b51bc
4 changed files with 199 additions and 106 deletions

View File

@@ -59,6 +59,10 @@ public class NodeConvertHelper {
nodeSimpleVO.setLanguage(matchItemList.get(3)); nodeSimpleVO.setLanguage(matchItemList.get(3));
} }
if (matchItemList.size() > 4) {
nodeSimpleVO.setEnable(Boolean.TRUE.toString().equalsIgnoreCase(matchItemList.get(4)));
}
return nodeSimpleVO; return nodeSimpleVO;
} }
@@ -73,6 +77,10 @@ public class NodeConvertHelper {
private String language; private String language;
private Boolean enable = Boolean.TRUE;
private String script;
public String getNodeId() { public String getNodeId() {
return nodeId; return nodeId;
} }
@@ -104,5 +112,21 @@ public class NodeConvertHelper {
public void setLanguage(String language) { public void setLanguage(String language) {
this.language = language; this.language = language;
} }
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
} }
} }

View File

@@ -0,0 +1,108 @@
package com.yomahub.liteflow.util;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.parser.helper.NodeConvertHelper;
/**
* 插件通用工具类
*
* @author gaibu
*/
public class RuleParsePluginUtil {
private static final String CHAIN_XML_PATTERN = "<chain id=\"{}\" enable=\"{}\">{}</chain>";
private static final String NODE_ITEM_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\" enable=\"{}\"><![CDATA[{}]]></node>";
private static final String NODE_ITEM_WITH_LANGUAGE_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\" language=\"{}\" enable=\"{}\"><![CDATA[{}]]></node>";
public static ChainDto parseChainKey(String chainKey) {
String[] chainProp = chainKey.split(StrPool.COLON);
if (chainProp.length == 2) {
return new ChainDto(chainProp[0], chainProp[1]);
}
return new ChainDto(chainKey);
}
public static String toScriptXml(NodeConvertHelper.NodeSimpleVO simpleVO) {
if (StrUtil.isNotBlank(simpleVO.getLanguage())) {
return StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN,
simpleVO.getNodeId(),
simpleVO.getName(),
simpleVO.getType(),
simpleVO.getLanguage(),
simpleVO.getEnable(),
simpleVO.getScript()
);
} else {
return StrUtil.format(NODE_ITEM_XML_PATTERN,
simpleVO.getNodeId(),
simpleVO.getName(),
simpleVO.getType(),
simpleVO.getEnable(),
simpleVO.getScript()
);
}
}
public static Pair<Boolean/*启停*/, String/*id*/> parseIdKey(String idKey) {
String[] idProp = idKey.split(StrPool.COLON);
if (idProp.length == 2) {
String id = idProp[0];
String enableStr = idProp[1];
return new Pair<>(Boolean.TRUE.toString().equalsIgnoreCase(enableStr), id);
}
return new Pair<>(Boolean.TRUE, idKey);
}
public static class ChainDto {
/**
* chain id
*/
private String id;
/**
* chain 启用状态,默认启用
*/
private String enable = Boolean.TRUE.toString();
public boolean isDisable() {
return !isEnable();
}
public boolean isEnable() {
return Boolean.TRUE.toString().equalsIgnoreCase(enable);
}
public ChainDto(String chainId) {
ChainDto chainDto = new ChainDto(chainId, null);
this.enable = chainDto.getEnable();
this.id = chainDto.getId();
}
public ChainDto(String chainId, String enable) {
this.id = chainId;
if (StrUtil.isBlank(enable)) {
this.enable = Boolean.TRUE.toString();
return;
}
if (Boolean.TRUE.toString().equalsIgnoreCase(enable)) {
this.enable = Boolean.TRUE.toString();
return;
}
this.enable = Boolean.FALSE.toString();
}
public String getId() {
return id;
}
public String getEnable() {
return enable;
}
public String toElXml(String elContent) {
return StrUtil.format(CHAIN_XML_PATTERN, id, enable, elContent);
}
}
}

View File

@@ -2,8 +2,8 @@ package com.yomahub.liteflow.parser.apollo.util;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService; import com.ctrip.framework.apollo.ConfigService;
@@ -17,6 +17,7 @@ import com.yomahub.liteflow.parser.apollo.exception.ApolloException;
import com.yomahub.liteflow.parser.apollo.vo.ApolloParserConfigVO; import com.yomahub.liteflow.parser.apollo.vo.ApolloParserConfigVO;
import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.helper.NodeConvertHelper;
import com.yomahub.liteflow.spi.holder.ContextAwareHolder; import com.yomahub.liteflow.spi.holder.ContextAwareHolder;
import com.yomahub.liteflow.util.RuleParsePluginUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -35,12 +36,8 @@ public class ApolloParseHelper {
private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.class); private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.class);
private final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
private final String NODE_XML_PATTERN = "<nodes>{}</nodes>"; private final String NODE_XML_PATTERN = "<nodes>{}</nodes>";
private final String NODE_ITEM_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\"><![CDATA[{}]]></node>";
private final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}{}</flow>"; private final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}{}</flow>";
private final ApolloParserConfigVO apolloParserConfigVO; private final ApolloParserConfigVO apolloParserConfigVO;
@@ -81,7 +78,7 @@ public class ApolloParseHelper {
// 1. handle chain // 1. handle chain
Set<String> propertyNames = chainConfig.getPropertyNames(); Set<String> propertyNames = chainConfig.getPropertyNames();
List<String> chainItemContentList = propertyNames.stream() List<String> chainItemContentList = propertyNames.stream()
.map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY))) .map(item -> RuleParsePluginUtil.parseChainKey(item).toElXml(chainConfig.getProperty(item, StrUtil.EMPTY)))
.collect(Collectors.toList()); .collect(Collectors.toList());
// merge all chain content // merge all chain content
String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY);
@@ -95,8 +92,7 @@ public class ApolloParseHelper {
List<String> scriptItemContentList = scriptNamespaces.stream() List<String> scriptItemContentList = scriptNamespaces.stream()
.map(item -> convert(item, scriptConfig.getProperty(item, StrUtil.EMPTY))) .map(item -> convert(item, scriptConfig.getProperty(item, StrUtil.EMPTY)))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(item -> StrUtil.format(NODE_ITEM_XML_PATTERN, item.getNodeId(), item.getName(), item.getType(), .map(RuleParsePluginUtil::toScriptXml)
item.getScript()))
.collect(Collectors.toList()); .collect(Collectors.toList());
scriptAllContent = StrUtil.format(NODE_XML_PATTERN, scriptAllContent = StrUtil.format(NODE_XML_PATTERN,
@@ -118,16 +114,25 @@ public class ApolloParseHelper {
ConfigChange configChange = changeEvent.getChange(changeKey); ConfigChange configChange = changeEvent.getChange(changeKey);
String newValue = configChange.getNewValue(); String newValue = configChange.getNewValue();
PropertyChangeType changeType = configChange.getChangeType(); PropertyChangeType changeType = configChange.getChangeType();
Pair<Boolean/*启停*/, String/*id*/> pair = RuleParsePluginUtil.parseIdKey(changeKey);
String id = pair.getValue();
switch (changeType) { switch (changeType) {
case ADDED: case ADDED:
case MODIFIED: case MODIFIED:
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey,
newValue); newValue);
LiteFlowChainELBuilder.createChain().setChainId(changeKey).setEL(newValue).build(); // 如果是启用,就正常更新
if (pair.getKey()) {
LiteFlowChainELBuilder.createChain().setChainId(id).setEL(newValue).build();
}
// 如果是禁用,就删除
else {
FlowBus.removeChain(id);
}
break; break;
case DELETED: case DELETED:
LOG.info("starting reload flow config... delete key={}", changeKey); LOG.info("starting reload flow config... delete key={}", changeKey);
FlowBus.removeChain(changeKey); FlowBus.removeChain(id);
break; break;
default: default:
} }
@@ -142,7 +147,7 @@ public class ApolloParseHelper {
if (DELETED.equals(changeType)) { if (DELETED.equals(changeType)) {
newValue = null; newValue = null;
} }
NodeSimpleVO nodeSimpleVO = convert(changeKey, newValue); NodeConvertHelper.NodeSimpleVO nodeSimpleVO = convert(changeKey, newValue);
if (Objects.isNull(nodeSimpleVO)) { if (Objects.isNull(nodeSimpleVO)) {
// key不符合规范的时候直接忽略 // key不符合规范的时候直接忽略
LOG.error("key={} is not a valid node config, ignore it", changeKey); LOG.error("key={} is not a valid node config, ignore it", changeKey);
@@ -154,12 +159,19 @@ public class ApolloParseHelper {
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey,
newValue); newValue);
LiteFlowNodeBuilder.createScriptNode() // 启用就正常更新
.setId(nodeSimpleVO.getNodeId()) if (nodeSimpleVO.getEnable()) {
.setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) LiteFlowNodeBuilder.createScriptNode()
.setName(nodeSimpleVO.getName()) .setId(nodeSimpleVO.getNodeId())
.setScript(nodeSimpleVO.getScript()) .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType()))
.build(); .setName(nodeSimpleVO.getName())
.setScript(nodeSimpleVO.getScript())
.build();
}
// 禁用就删除
else {
FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId());
}
break; break;
case DELETED: case DELETED:
LOG.info("starting reload flow config... delete key={}", changeKey); LOG.info("starting reload flow config... delete key={}", changeKey);
@@ -171,72 +183,13 @@ public class ApolloParseHelper {
} }
} }
private NodeSimpleVO convert(String key, String value) { private NodeConvertHelper.NodeSimpleVO convert(String key, String value) {
// 不需要去理解这串正则,就是一个匹配冒号的 NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(key);
// 一定得是a:b或是a:b:c...这种完整类型的字符串的
List<String> matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", key);
if (CollUtil.isEmpty(matchItemList)) {
return null;
}
NodeSimpleVO nodeSimpleVO = new NodeSimpleVO();
if (matchItemList.size() > 1) {
nodeSimpleVO.setNodeId(matchItemList.get(0));
nodeSimpleVO.setType(matchItemList.get(1));
}
if (matchItemList.size() > 2) {
nodeSimpleVO.setName(matchItemList.get(2));
}
// set script // set script
nodeSimpleVO.setScript(value); nodeSimpleVO.setScript(value);
return nodeSimpleVO; return nodeSimpleVO;
} }
private static class NodeSimpleVO {
private String nodeId;
private String type;
private String name = StrUtil.EMPTY;
private String script;
public String getNodeId() {
return nodeId;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
}
} }

View File

@@ -3,10 +3,10 @@ package com.yomahub.liteflow.test.apollo;
import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.Config;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.exception.ChainNotFoundException;
import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.flow.LiteflowResponse;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@@ -16,13 +16,12 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.event.annotation.AfterTestClass;
import org.springframework.test.context.event.annotation.BeforeTestClass;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Set; import java.util.Set;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.when;
/** /**
* @Description: * @Description:
@@ -33,35 +32,44 @@ import static org.mockito.Mockito.*;
@TestPropertySource(value = "classpath:/apollo/application-xml.properties") @TestPropertySource(value = "classpath:/apollo/application-xml.properties")
@SpringBootTest(classes = ApolloWithXmlELSpringbootTest.class) @SpringBootTest(classes = ApolloWithXmlELSpringbootTest.class)
@EnableAutoConfiguration @EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.apollo.cmp" }) @ComponentScan({"com.yomahub.liteflow.test.apollo.cmp"})
public class ApolloWithXmlELSpringbootTest { public class ApolloWithXmlELSpringbootTest {
@MockBean(name = "chainConfig") @MockBean(name = "chainConfig")
private Config chainConfig; private Config chainConfig;
@MockBean(name = "scriptConfig") @MockBean(name = "scriptConfig")
private Config scriptConfig; private Config scriptConfig;
@Resource @Resource
private FlowExecutor flowExecutor; private FlowExecutor flowExecutor;
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
} }
@Test
public void testApolloWithXml1() {
Set<String> chainNameList = Sets.newHashSet("chain1");
Set<String> scriptNodeValueList = Sets.newHashSet("s1:script:脚本s1");
when(chainConfig.getPropertyNames()).thenReturn(chainNameList);
when(scriptConfig.getPropertyNames()).thenReturn(scriptNodeValueList);
String chain1Data = "THEN(a, b, c, s1);"; @Test
String scriptNodeValue = "defaultContext.setData(\"test\",\"hello\");"; public void testApolloWithXml1() {
when(chainConfig.getProperty(anyString(), anyString())).thenReturn(chain1Data); Set<String> chainNameList = Sets.newHashSet("chain1", "chain2:false");
when(scriptConfig.getProperty(anyString(), anyString())).thenReturn(scriptNodeValue); Set<String> scriptNodeValueList = Sets.newHashSet("s1:script:脚本s1", "s2:script:脚本s1:groovy:false");
when(chainConfig.getPropertyNames()).thenReturn(chainNameList);
when(scriptConfig.getPropertyNames()).thenReturn(scriptNodeValueList);
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); when(chainConfig.getProperty("chain1", "")).thenReturn("THEN(a, b, c, s1);");
Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStrWithoutTime()); when(chainConfig.getProperty("chain2:false", "")).thenReturn("THEN(a, b, c, s1);");
} when(scriptConfig.getProperty("s1:script:脚本s1", "")).thenReturn("defaultContext.setData(\"test\",\"hello\");");
when(scriptConfig.getProperty("s2:script:脚本s1:groovy:false", "")).thenReturn("defaultContext.setData(\"test\",\"hello\");");
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStrWithoutTime());
// 测试 chain 停用
Assertions.assertThrows(ChainNotFoundException.class, () -> {
throw flowExecutor.execute2Resp("chain2", "arg").getCause();
});
// 测试 script 停用
Assertions.assertTrue(!FlowBus.getNodeMap().containsKey("s2"));
}
} }