Browse Source

[Feature#4310][Alert-SPI] Plug-ins containing UI components provide display pages (#4311)

* [Feature#4310][Alert-SPI] Plug-ins containing UI components provide display pages

*Some plugins (such as alert plugin) need to provide UI interfaces to users.
*We use from-creat to dynamically generate UI interfaces. Related parameters are mainly provided by pluginParams.
*From-create can generate dynamic ui based on this parameter.

this closes #4310

* add license head

* rename

* add ut

* add license

* add query plugin detail interface

* fix error
Kirs 4 years ago
parent
commit
17d44216a4

+ 2 - 1
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManager.java

@@ -22,6 +22,7 @@ import static java.util.Objects.requireNonNull;
 
 import static com.google.common.base.Preconditions.checkState;
 
+import org.apache.dolphinscheduler.common.enums.PluginType;
 import org.apache.dolphinscheduler.dao.entity.PluginDefine;
 import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
 import org.apache.dolphinscheduler.spi.alert.AlertChannel;
@@ -91,7 +92,7 @@ public class AlertPluginManager extends AbstractDolphinPluginManager {
             String nameEn = alertChannelFactory.getName();
             String paramsJson = PluginParamsTransfer.transferParamsToJson(params);
 
-            PluginDefine pluginDefine = new PluginDefine(nameEn, "alert", paramsJson);
+            PluginDefine pluginDefine = new PluginDefine(nameEn, PluginType.ALERT.getDesc(), paramsJson);
             pluginDao.addOrUpdatePluginDefine(pluginDefine);
         }
     }

+ 93 - 0
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/UiPluginController.java

@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dolphinscheduler.api.controller;
+
+import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PLUGINS_ERROR;
+
+import org.apache.dolphinscheduler.api.exceptions.ApiException;
+import org.apache.dolphinscheduler.api.service.UiPluginService;
+import org.apache.dolphinscheduler.api.utils.Result;
+import org.apache.dolphinscheduler.common.Constants;
+import org.apache.dolphinscheduler.common.enums.PluginType;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import springfox.documentation.annotations.ApiIgnore;
+
+/**
+ * UiPluginController
+ * Some plugins (such as alert plugin) need to provide UI interfaces to users.
+ * We use from-creat to dynamically generate UI interfaces. Related parameters are mainly provided by pluginParams.
+ * From-create can generate dynamic ui based on this parameter.
+ */
+@Api(tags = "UI_PLUGINS", position = 1)
+@RestController
+@RequestMapping("ui-plugins")
+public class UiPluginController extends BaseController {
+
+    private static final Logger logger = LoggerFactory.getLogger(UiPluginController.class);
+
+    @Autowired
+    UiPluginService uiPluginService;
+
+    @ApiOperation(value = "queryUiPluginsByType", notes = "QUERY_UI_PLUGINS_BY_TYPE")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "pluginType", value = "pluginType", required = true, dataType = "PluginType"),
+    })
+    @PostMapping(value = "/queryUiPluginsByType")
+    @ResponseStatus(HttpStatus.CREATED)
+    @ApiException(QUERY_PLUGINS_ERROR)
+    public Result queryUiPluginsByType(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                       @RequestParam(value = "pluginType") PluginType pluginType) {
+
+        logger.info("query plugins by type , pluginType: {}", pluginType);
+        Map<String, Object> result = uiPluginService.queryUiPluginsByType(pluginType);
+        return returnDataList(result);
+    }
+
+    @ApiOperation(value = "queryUiPluginDetailById", notes = "QUERY_UI_PLUGIN_DETAIL_BY_ID")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "id", value = "id", required = true, dataType = "PluginType"),
+    })
+    @PostMapping(value = "/queryUiPluginsByID")
+    @ResponseStatus(HttpStatus.CREATED)
+    @ApiException(QUERY_PLUGINS_ERROR)
+    public Result queryUiPluginDetailById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                          @RequestParam("pluginId") Integer pluginId) {
+
+        logger.info("query plugin detail by id , pluginId: {}", pluginId);
+        Map<String, Object> result = uiPluginService.queryUiPluginDetailById(pluginId);
+        return returnDataList(result);
+    }
+}

+ 10 - 4
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java

@@ -180,12 +180,12 @@ public enum Status {
     MOVE_PROCESS_DEFINITION_ERROR(10150, "move process definition from {0} to {1} error : {2}", "从{0}移动工作流到{1}错误 : {2}"),
     SWITCH_PROCESS_DEFINITION_VERSION_ERROR(10151, "Switch process definition version error", "切换工作流版本出错"),
     SWITCH_PROCESS_DEFINITION_VERSION_NOT_EXIST_PROCESS_DEFINITION_ERROR(10152
-            , "Switch process definition version error: not exists process definition, [process definition id {0}]", "切换工作流版本出错:工作流不存在,[工作流id {0}]"),
+        , "Switch process definition version error: not exists process definition, [process definition id {0}]", "切换工作流版本出错:工作流不存在,[工作流id {0}]"),
     SWITCH_PROCESS_DEFINITION_VERSION_NOT_EXIST_PROCESS_DEFINITION_VERSION_ERROR(10153
-            , "Switch process definition version error: not exists process definition version, [process definition id {0}] [version number {1}]", "切换工作流版本出错:工作流版本信息不存在,[工作流id {0}] [版本号 {1}]"),
+        , "Switch process definition version error: not exists process definition version, [process definition id {0}] [version number {1}]", "切换工作流版本出错:工作流版本信息不存在,[工作流id {0}] [版本号 {1}]"),
     QUERY_PROCESS_DEFINITION_VERSIONS_ERROR(10154, "query process definition versions error", "查询工作流历史版本信息出错"),
     QUERY_PROCESS_DEFINITION_VERSIONS_PAGE_NO_OR_PAGE_SIZE_LESS_THAN_1_ERROR(10155
-            , "query process definition versions error: [page number:{0}] < 1 or [page size:{1}] < 1", "查询工作流历史版本出错:[pageNo:{0}] < 1 或 [pageSize:{1}] < 1"),
+        , "query process definition versions error: [page number:{0}] < 1 or [page size:{1}] < 1", "查询工作流历史版本出错:[pageNo:{0}] < 1 或 [pageSize:{1}] < 1"),
     DELETE_PROCESS_DEFINITION_VERSION_ERROR(10156, "delete process definition version error", "删除工作流历史版本出错"),
 
     QUERY_USER_CREATED_PROJECT_ERROR(10157, "query user created project error error", "查询用户创建的项目错误"),
@@ -278,13 +278,19 @@ public enum Status {
     QUEUE_COUNT_ERROR(90001, "queue count error", "查询队列数据错误"),
 
     KERBEROS_STARTUP_STATE(100001, "get kerberos startup state error", "获取kerberos启动状态错误"),
+
+    //plugin
+    PLUGIN_NOT_A_UI_COMPONENT(110001, "query plugin error, this plugin has no UI component", "查询插件错误,此插件无UI组件"),
+    QUERY_PLUGINS_RESULT_IS_NULL(110002, "query plugins result is null", "查询插件为空"),
+    QUERY_PLUGINS_ERROR(110003, "query plugins error", "查询插件错误"),
+    QUERY_PLUGIN_DETAIL_RESULT_IS_NULL(110004, "query plugin detail result is null", "查询插件详情结果为空"),
     ;
 
     private final int code;
     private final String enMsg;
     private final String zhMsg;
 
-    private Status(int code, String enMsg, String zhMsg) {
+    Status(int code, String enMsg, String zhMsg) {
         this.code = code;
         this.enMsg = enMsg;
         this.zhMsg = zhMsg;

+ 33 - 0
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UiPluginService.java

@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dolphinscheduler.api.service;
+
+import org.apache.dolphinscheduler.common.enums.PluginType;
+
+import java.util.Map;
+
+/**
+ * UiPluginService
+ */
+public interface UiPluginService {
+
+    Map<String, Object> queryUiPluginsByType(PluginType pluginType);
+
+    Map<String, Object> queryUiPluginDetailById(int id);
+
+}

+ 74 - 0
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UiPluginServiceImpl.java

@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dolphinscheduler.api.service.impl;
+
+import org.apache.dolphinscheduler.api.enums.Status;
+import org.apache.dolphinscheduler.api.service.BaseService;
+import org.apache.dolphinscheduler.api.service.UiPluginService;
+import org.apache.dolphinscheduler.common.Constants;
+import org.apache.dolphinscheduler.common.enums.PluginType;
+import org.apache.dolphinscheduler.common.utils.CollectionUtils;
+import org.apache.dolphinscheduler.dao.entity.PluginDefine;
+import org.apache.dolphinscheduler.dao.mapper.PluginDefineMapper;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * UiPluginServiceImpl
+ */
+@Service
+public class UiPluginServiceImpl extends BaseService implements UiPluginService {
+
+    @Autowired
+    PluginDefineMapper pluginDefineMapper;
+
+    @Override
+    public Map<String, Object> queryUiPluginsByType(PluginType pluginType) {
+        Map<String, Object> result = new HashMap<>();
+        if (!pluginType.getHasUi()) {
+            putMsg(result, Status.PLUGIN_NOT_A_UI_COMPONENT);
+            return result;
+        }
+        List<PluginDefine> pluginDefines = pluginDefineMapper.queryByPluginType(pluginType.getDesc());
+        if (CollectionUtils.isEmpty(pluginDefines)) {
+            putMsg(result, Status.QUERY_PLUGINS_RESULT_IS_NULL);
+            return result;
+        }
+        putMsg(result, Status.SUCCESS);
+        result.put(Constants.DATA_LIST, pluginDefines);
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> queryUiPluginDetailById(int id) {
+        Map<String, Object> result = new HashMap<>();
+        PluginDefine pluginDefine = pluginDefineMapper.queryDetailById(id);
+        if (null == pluginDefine) {
+            putMsg(result, Status.QUERY_PLUGIN_DETAIL_RESULT_IS_NULL);
+            return result;
+        }
+        putMsg(result, Status.SUCCESS);
+        result.put(Constants.DATA_LIST, pluginDefine);
+        return result;
+    }
+}

+ 86 - 0
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UiPluginServiceTest.java

@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dolphinscheduler.api.service;
+
+import org.apache.dolphinscheduler.api.enums.Status;
+import org.apache.dolphinscheduler.api.service.impl.UiPluginServiceImpl;
+import org.apache.dolphinscheduler.common.enums.PluginType;
+import org.apache.dolphinscheduler.dao.entity.PluginDefine;
+import org.apache.dolphinscheduler.dao.mapper.PluginDefineMapper;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * UiPluginServiceTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class UiPluginServiceTest {
+
+    @InjectMocks
+    UiPluginServiceImpl uiPluginService;
+
+    @Mock
+    PluginDefineMapper pluginDefineMapper;
+
+    private PluginDefine pluginDefine;
+
+    @Before
+    public void before() {
+        String pluginParams = "[{\"field\":\"receivers\",\"props\":null,\"type\"}]";
+        pluginDefine = new PluginDefine("email-alert", "alert", pluginParams);
+    }
+
+    @Test
+    public void testQueryPlugins1() {
+        Map<String, Object> result = uiPluginService.queryUiPluginsByType(PluginType.REGISTER);
+        Assert.assertEquals(Status.PLUGIN_NOT_A_UI_COMPONENT, result.get("status"));
+    }
+
+    @Test
+    public void testQueryPlugins2() {
+        Map<String, Object> result = uiPluginService.queryUiPluginsByType(PluginType.ALERT);
+        Mockito.when(pluginDefineMapper.queryByPluginType(PluginType.ALERT.getDesc())).thenReturn(null);
+        Assert.assertEquals(Status.QUERY_PLUGINS_RESULT_IS_NULL, result.get("status"));
+
+        Mockito.when(pluginDefineMapper.queryByPluginType(PluginType.ALERT.getDesc())).thenReturn(Collections.singletonList(pluginDefine));
+        result = uiPluginService.queryUiPluginsByType(PluginType.ALERT);
+        Assert.assertEquals(Status.SUCCESS, result.get("status"));
+    }
+
+    @Test
+    public void testQueryPluginDetailById() {
+        Mockito.when(pluginDefineMapper.queryDetailById(1)).thenReturn(null);
+        Map<String, Object> result = uiPluginService.queryUiPluginDetailById(1);
+        Assert.assertEquals(Status.QUERY_PLUGIN_DETAIL_RESULT_IS_NULL, result.get("status"));
+
+        Mockito.when(pluginDefineMapper.queryDetailById(1)).thenReturn(pluginDefine);
+        result = uiPluginService.queryUiPluginDetailById(1);
+        Assert.assertEquals(Status.SUCCESS, result.get("status"));
+    }
+
+}

+ 70 - 0
dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/PluginType.java

@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dolphinscheduler.common.enums;
+
+import java.util.HashMap;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+
+/**
+ * PluginType
+ */
+public enum PluginType {
+
+    ALERT(1, "alert", true),
+    REGISTER(2, "register", false);
+
+    PluginType(int code, String desc, boolean hasUi) {
+        this.code = code;
+        this.desc = desc;
+        this.hasUi = hasUi;
+    }
+
+    @EnumValue
+    private final int code;
+    private final String desc;
+    private final boolean hasUi;
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public boolean getHasUi() {
+        return hasUi;
+    }
+
+
+    private static HashMap<Integer, PluginType> PLUGIN_TYPE_MAP = new HashMap<>();
+
+    static {
+        for (PluginType pluginType : PluginType.values()) {
+            PLUGIN_TYPE_MAP.put(pluginType.getCode(), pluginType);
+        }
+    }
+
+    public static PluginType of(int type) {
+        if (PLUGIN_TYPE_MAP.containsKey(type)) {
+            return PLUGIN_TYPE_MAP.get(type);
+        }
+        throw new IllegalArgumentException("invalid type : " + type);
+    }
+}

+ 8 - 0
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/PluginDefineMapper.java

@@ -42,6 +42,14 @@ public interface PluginDefineMapper extends BaseMapper<PluginDefine> {
      */
     List<PluginDefine> queryByPluginType(@Param("pluginType") String pluginType);
 
+    /**
+     * query detail by id
+     *
+     * @param id id
+     * @return PluginDefineDetail
+     */
+    PluginDefine queryDetailById(@Param("id") int id);
+
     /**
      * query by name and type
      *

+ 8 - 1
dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/PluginDefineMapper.xml

@@ -25,7 +25,7 @@
     </select>
 
     <select id="queryByPluginType" resultType="org.apache.dolphinscheduler.dao.entity.PluginDefine">
-        select *
+        select id,plugin_name,plugin_type,create_time,update_time
         from t_ds_plugin_define
         where plugin_type = #{pluginType}
     </select>
@@ -35,4 +35,11 @@
         from t_ds_plugin_define
         where plugin_name = #{pluginName} and plugin_type = #{pluginType}
     </select>
+
+    <select id="queryDetailById" resultType="org.apache.dolphinscheduler.dao.entity.PluginDefine">
+        select id,plugin_name,plugin_type,plugin_params,create_time,update_time
+        from t_ds_plugin_define
+        where id = #{id}
+    </select>
+
 </mapper>

+ 1 - 0
pom.xml

@@ -801,6 +801,7 @@
                         <include>**/api/service/TaskInstanceServiceTest.java</include>
                         <include>**/api/service/TenantServiceTest.java</include>
                         <include>**/api/service/UdfFuncServiceTest.java</include>
+                        <include>**/api/service/UiPluginServiceTest.java</include>
                         <include>**/api/service/UserAlertGroupServiceTest.java</include>
                         <include>**/api/service/UsersServiceTest.java</include>
                         <include>**/api/service/WorkerGroupServiceTest.java</include>