[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 erroralert_plugin_design
parent
0f31152aee
commit
17d44216a4
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
pom.xml
1
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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue