feat: 支持工作流插件复用与试运行
- 新增工作流插件类型、发布快照同步、实时可用性与下线影响检查 - 收口绑定候选、分类权限、间接环路校验与运行态优雅降级 - 补齐管理端工作流插件配置、详情与试运行界面及定向测试
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
package tech.easyflow.ai.plugin.workflow.dependency;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import tech.easyflow.ai.entity.Plugin;
|
||||
import tech.easyflow.ai.entity.PluginItem;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.enums.PluginType;
|
||||
import tech.easyflow.ai.mapper.PluginItemMapper;
|
||||
import tech.easyflow.ai.mapper.PluginMapper;
|
||||
import tech.easyflow.ai.service.WorkflowService;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class WorkflowPluginDependencyServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void testPublishedCheckShouldIgnoreUnpublishedDraftCycle() {
|
||||
WorkflowPluginDependencyServiceImpl service = newService(
|
||||
workflows(
|
||||
workflowVariant(rootWorkflowContent("2"), rootWorkflowContent("2")),
|
||||
workflowVariant(pluginWorkflowContent("700"), terminalWorkflowContent())
|
||||
),
|
||||
plugins(),
|
||||
pluginItems()
|
||||
);
|
||||
|
||||
Assert.assertTrue(service.containsPluginReferenceTransitively(BigInteger.ONE, BigInteger.valueOf(900)));
|
||||
Assert.assertFalse(service.containsPluginReferenceTransitivelyInPublishedSnapshot(BigInteger.ONE, BigInteger.valueOf(900)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPublishedCheckShouldBlockPublishedCycleEvenWhenDraftClean() {
|
||||
WorkflowPluginDependencyServiceImpl service = newService(
|
||||
workflows(
|
||||
workflowVariant(rootWorkflowContent("2"), rootWorkflowContent("2")),
|
||||
workflowVariant(terminalWorkflowContent(), pluginWorkflowContent("700"))
|
||||
),
|
||||
plugins(),
|
||||
pluginItems()
|
||||
);
|
||||
|
||||
Assert.assertFalse(service.containsPluginReferenceTransitively(BigInteger.ONE, BigInteger.valueOf(900)));
|
||||
Assert.assertTrue(service.containsPluginReferenceTransitivelyInPublishedSnapshot(BigInteger.ONE, BigInteger.valueOf(900)));
|
||||
}
|
||||
|
||||
private static WorkflowPluginDependencyServiceImpl newService(Map<String, WorkflowVariant> workflowStore,
|
||||
Map<String, Plugin> pluginStore,
|
||||
Map<String, PluginItem> pluginItemStore) {
|
||||
return new WorkflowPluginDependencyServiceImpl(
|
||||
mockPluginMapper(pluginStore),
|
||||
mockPluginItemMapper(pluginItemStore),
|
||||
mockWorkflowService(workflowStore)
|
||||
);
|
||||
}
|
||||
|
||||
private static Map<String, WorkflowVariant> workflows(WorkflowVariant root, WorkflowVariant child) {
|
||||
Map<String, WorkflowVariant> workflows = new HashMap<>();
|
||||
workflows.put("1", root);
|
||||
workflows.put("2", child);
|
||||
return workflows;
|
||||
}
|
||||
|
||||
private static Map<String, Plugin> plugins() {
|
||||
Map<String, Plugin> plugins = new HashMap<>();
|
||||
Plugin plugin = new Plugin();
|
||||
plugin.setId(BigInteger.valueOf(900));
|
||||
plugin.setType(PluginType.WORKFLOW.getCode());
|
||||
plugin.setWorkflowId(BigInteger.valueOf(30));
|
||||
plugins.put("900", plugin);
|
||||
return plugins;
|
||||
}
|
||||
|
||||
private static Map<String, PluginItem> pluginItems() {
|
||||
Map<String, PluginItem> pluginItems = new HashMap<>();
|
||||
PluginItem pluginItem = new PluginItem();
|
||||
pluginItem.setId(BigInteger.valueOf(700));
|
||||
pluginItem.setPluginId(BigInteger.valueOf(900));
|
||||
pluginItems.put("700", pluginItem);
|
||||
return pluginItems;
|
||||
}
|
||||
|
||||
private static WorkflowService mockWorkflowService(Map<String, WorkflowVariant> workflowStore) {
|
||||
return (WorkflowService) Proxy.newProxyInstance(
|
||||
WorkflowService.class.getClassLoader(),
|
||||
new Class[]{WorkflowService.class},
|
||||
(proxy, method, args) -> {
|
||||
String methodName = method.getName();
|
||||
if ("getById".equals(methodName)) {
|
||||
return buildWorkflow(workflowStore, args == null ? null : args[0], false);
|
||||
}
|
||||
if ("getPublishedById".equals(methodName)) {
|
||||
return buildWorkflow(workflowStore, args == null ? null : args[0], true);
|
||||
}
|
||||
if ("equals".equals(methodName)) {
|
||||
return proxy == args[0];
|
||||
}
|
||||
if ("hashCode".equals(methodName)) {
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
if (method.getReturnType() == boolean.class) {
|
||||
return false;
|
||||
}
|
||||
if (method.getReturnType() == int.class) {
|
||||
return 0;
|
||||
}
|
||||
if (method.getReturnType() == long.class) {
|
||||
return 0L;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static PluginMapper mockPluginMapper(Map<String, Plugin> pluginStore) {
|
||||
return (PluginMapper) Proxy.newProxyInstance(
|
||||
PluginMapper.class.getClassLoader(),
|
||||
new Class[]{PluginMapper.class},
|
||||
(proxy, method, args) -> {
|
||||
if ("selectOneById".equals(method.getName())) {
|
||||
return pluginStore.get(String.valueOf(args[0]));
|
||||
}
|
||||
if ("equals".equals(method.getName())) {
|
||||
return proxy == args[0];
|
||||
}
|
||||
if ("hashCode".equals(method.getName())) {
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
if (method.getReturnType() == boolean.class) {
|
||||
return false;
|
||||
}
|
||||
if (method.getReturnType() == int.class) {
|
||||
return 0;
|
||||
}
|
||||
if (method.getReturnType() == long.class) {
|
||||
return 0L;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static PluginItemMapper mockPluginItemMapper(Map<String, PluginItem> pluginItemStore) {
|
||||
return (PluginItemMapper) Proxy.newProxyInstance(
|
||||
PluginItemMapper.class.getClassLoader(),
|
||||
new Class[]{PluginItemMapper.class},
|
||||
(proxy, method, args) -> {
|
||||
if ("selectOneById".equals(method.getName())) {
|
||||
return pluginItemStore.get(String.valueOf(args[0]));
|
||||
}
|
||||
if ("equals".equals(method.getName())) {
|
||||
return proxy == args[0];
|
||||
}
|
||||
if ("hashCode".equals(method.getName())) {
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
if (method.getReturnType() == boolean.class) {
|
||||
return false;
|
||||
}
|
||||
if (method.getReturnType() == int.class) {
|
||||
return 0;
|
||||
}
|
||||
if (method.getReturnType() == long.class) {
|
||||
return 0L;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static Workflow buildWorkflow(Map<String, WorkflowVariant> workflowStore, Object idValue, boolean published) {
|
||||
if (idValue == null) {
|
||||
return null;
|
||||
}
|
||||
WorkflowVariant variant = workflowStore.get(String.valueOf(idValue));
|
||||
if (variant == null) {
|
||||
return null;
|
||||
}
|
||||
Workflow workflow = new Workflow();
|
||||
try {
|
||||
workflow.setId(new BigInteger(String.valueOf(idValue)));
|
||||
} catch (Exception ignored) {
|
||||
workflow.setId(null);
|
||||
}
|
||||
workflow.setContent(published ? variant.publishedContent : variant.draftContent);
|
||||
return workflow;
|
||||
}
|
||||
|
||||
private static WorkflowVariant workflowVariant(String draftContent, String publishedContent) {
|
||||
WorkflowVariant variant = new WorkflowVariant();
|
||||
variant.draftContent = draftContent;
|
||||
variant.publishedContent = publishedContent;
|
||||
return variant;
|
||||
}
|
||||
|
||||
private static String rootWorkflowContent(String childWorkflowId) {
|
||||
return workflowJson(array(workflowNode("wf-1", childWorkflowId)));
|
||||
}
|
||||
|
||||
private static String pluginWorkflowContent(String pluginItemId) {
|
||||
return workflowJson(array(pluginNode("plugin-1", pluginItemId)));
|
||||
}
|
||||
|
||||
private static String terminalWorkflowContent() {
|
||||
return workflowJson(new JSONArray());
|
||||
}
|
||||
|
||||
private static String workflowJson(JSONArray nodes) {
|
||||
JSONObject root = new JSONObject();
|
||||
root.put("nodes", nodes);
|
||||
root.put("edges", new JSONArray());
|
||||
return root.toJSONString();
|
||||
}
|
||||
|
||||
private static JSONArray array(JSONObject... objects) {
|
||||
JSONArray array = new JSONArray();
|
||||
for (JSONObject object : objects) {
|
||||
array.add(object);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private static JSONObject workflowNode(String id, String workflowId) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("workflowId", workflowId);
|
||||
return node(id, "workflow-node", data);
|
||||
}
|
||||
|
||||
private static JSONObject pluginNode(String id, String pluginItemId) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("pluginId", pluginItemId);
|
||||
return node(id, "plugin-node", data);
|
||||
}
|
||||
|
||||
private static JSONObject node(String id, String type, JSONObject data) {
|
||||
JSONObject node = new JSONObject();
|
||||
node.put("id", id);
|
||||
node.put("type", type);
|
||||
node.put("data", data);
|
||||
return node;
|
||||
}
|
||||
|
||||
private static class WorkflowVariant {
|
||||
private String draftContent;
|
||||
private String publishedContent;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user