diff --git a/docs/docs/en/guide/alert/alert_plugin_user_guide.md b/docs/docs/en/guide/alert/alert_plugin_user_guide.md index 330a7e3f7f..0445b32c5a 100644 --- a/docs/docs/en/guide/alert/alert_plugin_user_guide.md +++ b/docs/docs/en/guide/alert/alert_plugin_user_guide.md @@ -13,6 +13,8 @@ Steps to be used are as follows: - Select the corresponding alarm plug-in and fill in the relevant alarm parameters. - Select `Alarm Group Management`, create an alarm group, and choose the corresponding alarm instance. +> You can click `Test Send` button to test whether the alarm instance is configured correctly. + ![alert-instance01](../../../../img/new_ui/dev/alert/alert_instance01.png) ![alert-instance02](../../../../img/new_ui/dev/alert/alert_instance02.png) diff --git a/docs/docs/zh/guide/alert/alert_plugin_user_guide.md b/docs/docs/zh/guide/alert/alert_plugin_user_guide.md index dbea97b961..5af6b17508 100644 --- a/docs/docs/zh/guide/alert/alert_plugin_user_guide.md +++ b/docs/docs/zh/guide/alert/alert_plugin_user_guide.md @@ -10,6 +10,8 @@ 然后选择告警组管理,创建告警组,选择相应的告警实例即可。 +> 可以使用`测试发送`功能来验证配置的告警实例是否正确。 + ![alert-instance01](../../../../img/new_ui/dev/alert/alert_instance01.png) ![alert-instance02](../../../../img/new_ui/dev/alert/alert_instance02.png) ![alert-instance03](../../../../img/new_ui/dev/alert/alert_instance03.png) diff --git a/docs/img/new_ui/dev/alert/alert_instance01.png b/docs/img/new_ui/dev/alert/alert_instance01.png index c5a515254f..3ebe89d010 100644 Binary files a/docs/img/new_ui/dev/alert/alert_instance01.png and b/docs/img/new_ui/dev/alert/alert_instance01.png differ diff --git a/docs/img/new_ui/dev/alert/alert_instance02.png b/docs/img/new_ui/dev/alert/alert_instance02.png index 37be6dd859..6ee6867146 100644 Binary files a/docs/img/new_ui/dev/alert/alert_instance02.png and b/docs/img/new_ui/dev/alert/alert_instance02.png differ diff --git a/docs/img/new_ui/dev/alert/alert_instance03.png b/docs/img/new_ui/dev/alert/alert_instance03.png index 094c38b65f..8260fdcc3d 100644 Binary files a/docs/img/new_ui/dev/alert/alert_instance03.png and b/docs/img/new_ui/dev/alert/alert_instance03.png differ diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-api/src/main/java/org/apache/dolphinscheduler/alert/api/AlertConstants.java b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-api/src/main/java/org/apache/dolphinscheduler/alert/api/AlertConstants.java index a94a31a8a9..cf6f071dfb 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-api/src/main/java/org/apache/dolphinscheduler/alert/api/AlertConstants.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-api/src/main/java/org/apache/dolphinscheduler/alert/api/AlertConstants.java @@ -31,6 +31,8 @@ public final class AlertConstants { public static final String WARNING_TYPE = "warningType"; public static final String NAME_WARNING_TYPE = "WarningType"; + public static final String TEST_TITLE = "DolphinScheduler test alert"; + public static final String TEST_CONTENT = "[{\"message\":\" This is a test alert message form DolphinScheduler\"}]"; private AlertConstants() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/rpc/AlertOperatorImpl.java b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/rpc/AlertOperatorImpl.java index 36c0a5556d..9f11fa6c2e 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/rpc/AlertOperatorImpl.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/rpc/AlertOperatorImpl.java @@ -20,6 +20,7 @@ import org.apache.dolphinscheduler.alert.service.AlertBootstrapService; import org.apache.dolphinscheduler.extract.alert.IAlertOperator; import org.apache.dolphinscheduler.extract.alert.request.AlertSendRequest; import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse; +import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest; import lombok.extern.slf4j.Slf4j; @@ -44,4 +45,14 @@ public class AlertOperatorImpl implements IAlertOperator { log.info("Handle AlertSendRequest finish: {}", alertSendResponse); return alertSendResponse; } + + @Override + public AlertSendResponse sendTestAlert(AlertTestSendRequest alertSendRequest) { + log.info("Received AlertTestSendRequest : {}", alertSendRequest); + AlertSendResponse alertSendResponse = alertBootstrapService.syncTestSend( + alertSendRequest.getPluginDefineId(), + alertSendRequest.getPluginInstanceParams()); + log.info("Handle AlertTestSendRequest finish: {}", alertSendResponse); + return alertSendResponse; + } } diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/AlertBootstrapService.java b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/AlertBootstrapService.java index 57ef75d6d6..77e62a65a0 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/AlertBootstrapService.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/AlertBootstrapService.java @@ -38,6 +38,7 @@ import org.apache.dolphinscheduler.dao.entity.Alert; import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance; import org.apache.dolphinscheduler.dao.entity.AlertSendStatus; import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse; +import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; @@ -192,7 +193,7 @@ public final class AlertBootstrapService extends BaseDaemonThread implements Aut if (alertResult != null) { AlertSendResponse.AlertSendResponseResult alertSendResponseResult = new AlertSendResponse.AlertSendResponseResult( - Boolean.parseBoolean(String.valueOf(alertResult.getStatus())), + Boolean.parseBoolean(alertResult.getStatus()), alertResult.getMessage()); sendResponseStatus = sendResponseStatus && alertSendResponseResult.isSuccess(); sendResponseResults.add(alertSendResponseResult); @@ -302,6 +303,61 @@ public final class AlertBootstrapService extends BaseDaemonThread implements Aut } } + public AlertSendResponse syncTestSend(int pluginDefineId, String pluginInstanceParams) { + + boolean sendResponseStatus = true; + List sendResponseResults = new ArrayList<>(); + + Optional alertChannelOptional = alertPluginManager.getAlertChannel(pluginDefineId); + if (!alertChannelOptional.isPresent()) { + String message = String.format("Test send alert error: the channel doesn't exist, pluginDefineId: %s", + pluginDefineId); + AlertSendResponse.AlertSendResponseResult alertSendResponseResult = + new AlertSendResponse.AlertSendResponseResult(); + alertSendResponseResult.setSuccess(false); + alertSendResponseResult.setMessage(message); + sendResponseResults.add(alertSendResponseResult); + log.error("Test send alert error : not found plugin {}", pluginDefineId); + return new AlertSendResponse(false, sendResponseResults); + } + AlertChannel alertChannel = alertChannelOptional.get(); + + Map paramsMap = PluginParamsTransfer.getPluginParamsMap(pluginInstanceParams); + + AlertData alertData = AlertData.builder() + .title(AlertConstants.TEST_TITLE) + .content(AlertConstants.TEST_CONTENT) + .warnType(WarningType.ALL.getCode()) + .build(); + + AlertInfo alertInfo = AlertInfo.builder() + .alertData(alertData) + .alertParams(paramsMap) + .build(); + + try { + AlertResult alertResult = alertChannel.process(alertInfo); + if (alertResult != null) { + AlertSendResponse.AlertSendResponseResult alertSendResponseResult = + new AlertSendResponse.AlertSendResponseResult( + Boolean.parseBoolean(alertResult.getStatus()), + alertResult.getMessage()); + sendResponseStatus = alertSendResponseResult.isSuccess(); + sendResponseResults.add(alertSendResponseResult); + } + } catch (Exception e) { + log.error("Test send alert error", e); + AlertSendResponse.AlertSendResponseResult alertSendResponseResult = + new AlertSendResponse.AlertSendResponseResult(); + alertSendResponseResult.setSuccess(false); + alertSendResponseResult.setMessage(e.getMessage()); + sendResponseResults.add(alertSendResponseResult); + return new AlertSendResponse(false, sendResponseResults); + } + + return new AlertSendResponse(sendResponseStatus, sendResponseResults); + } + @Override public void close() { log.info("Closed AlertBootstrapService..."); diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/ListenerEventPostService.java b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/ListenerEventPostService.java index e3a1bdefee..b57562c711 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/ListenerEventPostService.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/ListenerEventPostService.java @@ -259,5 +259,4 @@ public final class ListenerEventPostService extends BaseDaemonThread implements public void close() { log.info("Closed ListenerEventPostService..."); } - } diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertBootstrapServiceTest.java b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertBootstrapServiceTest.java index 3819200690..eafba16585 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertBootstrapServiceTest.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertBootstrapServiceTest.java @@ -26,15 +26,19 @@ import org.apache.dolphinscheduler.alert.config.AlertConfig; import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager; import org.apache.dolphinscheduler.alert.service.AlertBootstrapService; import org.apache.dolphinscheduler.common.enums.WarningType; +import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.AlertDao; import org.apache.dolphinscheduler.dao.PluginDao; import org.apache.dolphinscheduler.dao.entity.Alert; import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance; +import org.apache.dolphinscheduler.dao.entity.ListenerEvent; import org.apache.dolphinscheduler.dao.entity.PluginDefine; import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse; +import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.Assertions; @@ -42,6 +46,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.slf4j.Logger; @@ -63,6 +68,18 @@ public class AlertBootstrapServiceTest { @InjectMocks private AlertBootstrapService alertBootstrapService; + private static final String PLUGIN_INSTANCE_PARAMS = + "{\"User\":\"xx\",\"receivers\":\"xx\",\"sender\":\"xx\",\"smtpSslTrust\":\"*\",\"enableSmtpAuth\":\"true\",\"receiverCcs\":null,\"showType\":\"table\",\"starttlsEnable\":\"false\",\"serverPort\":\"25\",\"serverHost\":\"xx\",\"Password\":\"xx\",\"sslEnable\":\"false\"}"; + + private static final String PLUGIN_INSTANCE_NAME = "alert-instance-mail"; + private static final String TITLE = "alert mail test TITLE"; + private static final String CONTENT = "alert mail test CONTENT"; + private static final List EVENTS = new ArrayList<>(); + + private static final int PLUGIN_DEFINE_ID = 1; + + private static final int ALERT_GROUP_ID = 1; + @BeforeEach public void before() { MockitoAnnotations.initMocks(this); @@ -70,17 +87,12 @@ public class AlertBootstrapServiceTest { @Test public void testSyncHandler() { - - int alertGroupId = 1; - String title = "alert mail test title"; - String content = "alert mail test content"; - // 1.alert instance does not exist - when(alertDao.listInstanceByAlertGroupId(alertGroupId)).thenReturn(null); + when(alertDao.listInstanceByAlertGroupId(ALERT_GROUP_ID)).thenReturn(null); when(alertConfig.getWaitTimeout()).thenReturn(0); AlertSendResponse alertSendResponse = - alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode()); + alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode()); Assertions.assertFalse(alertSendResponse.isSuccess()); alertSendResponse.getResResults().forEach(result -> logger .info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage())); @@ -101,7 +113,7 @@ public class AlertBootstrapServiceTest { when(pluginDao.getPluginDefineById(pluginDefineId)).thenReturn(pluginDefine); alertSendResponse = - alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode()); + alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode()); Assertions.assertFalse(alertSendResponse.isSuccess()); alertSendResponse.getResResults().forEach(result -> logger .info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage())); @@ -113,7 +125,7 @@ public class AlertBootstrapServiceTest { when(alertConfig.getWaitTimeout()).thenReturn(0); alertSendResponse = - alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode()); + alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode()); Assertions.assertFalse(alertSendResponse.isSuccess()); alertSendResponse.getResResults().forEach(result -> logger .info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage())); @@ -126,7 +138,7 @@ public class AlertBootstrapServiceTest { when(alertPluginManager.getAlertChannel(1)).thenReturn(Optional.of(alertChannelMock)); alertSendResponse = - alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode()); + alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode()); Assertions.assertFalse(alertSendResponse.isSuccess()); alertSendResponse.getResResults().forEach(result -> logger .info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage())); @@ -140,7 +152,7 @@ public class AlertBootstrapServiceTest { when(alertConfig.getWaitTimeout()).thenReturn(5000); alertSendResponse = - alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode()); + alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode()); Assertions.assertTrue(alertSendResponse.isSuccess()); alertSendResponse.getResResults().forEach(result -> logger .info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage())); @@ -149,15 +161,12 @@ public class AlertBootstrapServiceTest { @Test public void testRun() { - int alertGroupId = 1; - String title = "alert mail test title"; - String content = "alert mail test content"; List alertList = new ArrayList<>(); Alert alert = new Alert(); alert.setId(1); - alert.setAlertGroupId(alertGroupId); - alert.setTitle(title); - alert.setContent(content); + alert.setAlertGroupId(ALERT_GROUP_ID); + alert.setTitle(TITLE); + alert.setContent(CONTENT); alert.setWarningType(WarningType.FAILURE); alertList.add(alert); @@ -170,7 +179,7 @@ public class AlertBootstrapServiceTest { AlertPluginInstance alertPluginInstance = new AlertPluginInstance( pluginDefineId, pluginInstanceParams, pluginInstanceName); alertInstanceList.add(alertPluginInstance); - when(alertDao.listInstanceByAlertGroupId(alertGroupId)).thenReturn(alertInstanceList); + when(alertDao.listInstanceByAlertGroupId(ALERT_GROUP_ID)).thenReturn(alertInstanceList); String pluginName = "alert-plugin-mail"; PluginDefine pluginDefine = new PluginDefine(pluginName, "1", null); @@ -186,4 +195,20 @@ public class AlertBootstrapServiceTest { when(alertDao.listInstanceByAlertGroupId(1)).thenReturn(new ArrayList<>()); alertBootstrapService.send(alertList); } + + @Test + public void testSendAlert() { + AlertResult sendResult = new AlertResult(); + sendResult.setStatus(String.valueOf(true)); + sendResult.setMessage(String.format("Alert Plugin %s send success", PLUGIN_INSTANCE_NAME)); + AlertChannel alertChannelMock = mock(AlertChannel.class); + when(alertChannelMock.process(Mockito.any())).thenReturn(sendResult); + when(alertPluginManager.getAlertChannel(1)).thenReturn(Optional.of(alertChannelMock)); + Map paramsMap = JSONUtils.toMap(PLUGIN_INSTANCE_PARAMS); + MockedStatic pluginParamsTransferMockedStatic = + Mockito.mockStatic(PluginParamsTransfer.class); + pluginParamsTransferMockedStatic.when(() -> PluginParamsTransfer.getPluginParamsMap(PLUGIN_INSTANCE_PARAMS)) + .thenReturn(paramsMap); + alertBootstrapService.syncTestSend(PLUGIN_DEFINE_ID, PLUGIN_INSTANCE_PARAMS); + } } diff --git a/dolphinscheduler-api/pom.xml b/dolphinscheduler-api/pom.xml index 40fe41c190..681a85318a 100644 --- a/dolphinscheduler-api/pom.xml +++ b/dolphinscheduler-api/pom.xml @@ -228,6 +228,10 @@ com.azure.resourcemanager azure-resourcemanager-datafactory + + org.apache.dolphinscheduler + dolphinscheduler-extract-alert + diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java index 688f2c594c..a676bf283f 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java @@ -22,6 +22,7 @@ import static org.apache.dolphinscheduler.api.enums.Status.DELETE_ALERT_PLUGIN_I import static org.apache.dolphinscheduler.api.enums.Status.GET_ALERT_PLUGIN_INSTANCE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.LIST_PAGING_ALERT_PLUGIN_INSTANCE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_ALL_ALERT_PLUGIN_INSTANCE_ERROR; +import static org.apache.dolphinscheduler.api.enums.Status.SEND_TEST_ALERT_PLUGIN_INSTANCE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_ALERT_PLUGIN_INSTANCE_ERROR; import org.apache.dolphinscheduler.api.enums.Status; @@ -99,6 +100,19 @@ public class AlertPluginInstanceController extends BaseController { return returnDataList(result); } + @Operation(summary = "testSendAlertPluginInstance", description = "TEST_SEND_ALERT_PLUGIN_INSTANCE") + @Parameters({ + @Parameter(name = "pluginDefineId", description = "ALERT_PLUGIN_DEFINE_ID", required = true, schema = @Schema(implementation = int.class, example = "100")), + @Parameter(name = "pluginInstanceParams", description = "ALERT_PLUGIN_INSTANCE_PARAMS", required = true, schema = @Schema(implementation = String.class, example = "ALERT_PLUGIN_INSTANCE_PARAMS")) + }) + @PostMapping(value = "/test-send") + @ResponseStatus(HttpStatus.OK) + @ApiException(SEND_TEST_ALERT_PLUGIN_INSTANCE_ERROR) + public Result testSendAlertPluginInstance(@RequestParam(value = "pluginDefineId") int pluginDefineId, + @RequestParam(value = "pluginInstanceParams") String pluginInstanceParams) { + return alertPluginInstanceService.testSend(pluginDefineId, pluginInstanceParams); + } + /** * updateAlertPluginInstance * diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java index e37eca0f3f..44f2de8604 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java @@ -472,7 +472,10 @@ public enum Status { "failed to delete the alert instance, there is an alarm group associated with this alert instance", "删除告警实例失败,存在与此告警实例关联的警报组"), PROCESS_DEFINITION_VERSION_IS_USED(110013, "this process definition version is used", "此工作流定义版本被使用"), - + ALERT_TEST_SENDING_FAILED(110014, "Alert test sending failed, [{0}]", "alert测试发送失败,[{0}]"), + ALERT_CHANNEL_NOT_EXIST(110015, "Alert channel not exist", "alert channel不存在"), + SEND_TEST_ALERT_PLUGIN_INSTANCE_ERROR(110016, "send test alert plugin instance error", "发送测试告警错误"), + ALERT_SERVER_NOT_EXIST(110017, "Alert server does not exist", "Alert server不存在"), CREATE_ENVIRONMENT_ERROR(120001, "create environment error", "创建环境失败"), ENVIRONMENT_NAME_EXISTS(120002, "this environment name [{0}] already exists", "环境名称[{0}]已经存在"), ENVIRONMENT_NAME_IS_NULL(120003, "this environment name shouldn't be empty.", "环境名称不能为空"), diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceService.java index 156531f160..e8679c27af 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceService.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceService.java @@ -94,4 +94,6 @@ public interface AlertPluginInstanceService { * @return plugins */ Result listPaging(User loginUser, String searchVal, int pageNo, int pageSize); + + Result testSend(int pluginDefineId, String pluginInstanceParams); } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AlertPluginInstanceServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AlertPluginInstanceServiceImpl.java index fffb95fbcf..65ba0c0334 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AlertPluginInstanceServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AlertPluginInstanceServiceImpl.java @@ -31,6 +31,7 @@ import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.AlertPluginInstanceType; import org.apache.dolphinscheduler.common.enums.AuthorizationType; import org.apache.dolphinscheduler.common.enums.WarningType; +import org.apache.dolphinscheduler.common.model.Server; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.AlertGroup; import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance; @@ -39,6 +40,13 @@ import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.mapper.AlertGroupMapper; import org.apache.dolphinscheduler.dao.mapper.AlertPluginInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.PluginDefineMapper; +import org.apache.dolphinscheduler.extract.alert.IAlertOperator; +import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse; +import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest; +import org.apache.dolphinscheduler.extract.base.client.SingletonJdkDynamicRpcClientProxyFactory; +import org.apache.dolphinscheduler.extract.base.utils.Host; +import org.apache.dolphinscheduler.registry.api.RegistryClient; +import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer; import org.apache.commons.collections4.CollectionUtils; @@ -82,6 +90,9 @@ public class AlertPluginInstanceServiceImpl extends BaseServiceImpl implements A private final Integer GLOBAL_ALERT_GROUP_ID = 2; + @Autowired + private RegistryClient registryClient; + /** * creat alert plugin instance * @@ -352,4 +363,49 @@ public class AlertPluginInstanceServiceImpl extends BaseServiceImpl implements A return first.isPresent(); } + public Optional getAlertServerAddress() { + List serverList = registryClient.getServerList(RegistryNodeType.ALERT_SERVER); + if (CollectionUtils.isEmpty(serverList)) { + return Optional.empty(); + } + Server server = serverList.get(0); + return Optional.of(new Host(server.getHost(), server.getPort())); + } + + @Override + public Result testSend(int pluginDefineId, String pluginInstanceParams) { + Result result = new Result<>(); + Optional alertServerAddressOptional = getAlertServerAddress(); + if (!alertServerAddressOptional.isPresent()) { + log.error("Cannot get alert server address, please check the alert server is running"); + putMsg(result, Status.ALERT_SERVER_NOT_EXIST); + return result; + } + + Host alertServerAddress = alertServerAddressOptional.get(); + AlertTestSendRequest alertTestSendRequest = new AlertTestSendRequest( + pluginDefineId, + pluginInstanceParams); + + AlertSendResponse alertSendResponse; + + try { + IAlertOperator alertOperator = SingletonJdkDynamicRpcClientProxyFactory + .getProxyClient(alertServerAddress.getAddress(), IAlertOperator.class); + alertSendResponse = alertOperator.sendTestAlert(alertTestSendRequest); + log.info("Send alert to: {} successfully, response: {}", alertServerAddress, alertSendResponse); + } catch (Exception e) { + log.error("Send alert: {} to: {} failed", alertTestSendRequest, alertServerAddress, e); + putMsg(result, Status.ALERT_TEST_SENDING_FAILED, e.getMessage()); + return result; + } + + if (alertSendResponse.isSuccess()) { + putMsg(result, Status.SUCCESS); + } else { + putMsg(result, Status.ALERT_TEST_SENDING_FAILED, alertSendResponse.getResResults().get(0).getMessage()); + } + + return result; + } } diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceControllerTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceControllerTest.java index 870fb1f82b..b2ffbe2b2f 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceControllerTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceControllerTest.java @@ -92,6 +92,34 @@ public class AlertPluginInstanceControllerTest extends AbstractControllerTest { assertThat(actualResponseContent.toString()).isEqualTo(expectResponseContent.toString()); } + @Test + public void testSendAlertPluginInstance() throws Exception { + // Given + Result result = JSONUtils.parseObject( + "{\"code\":0,\"msg\":\"success\",\"data\":\"Test Data\",\"success\":true,\"failed\":false}", + Result.class); + + final MultiValueMap paramsMap = new LinkedMultiValueMap<>(); + paramsMap.add("pluginDefineId", String.valueOf(pluginDefineId)); + paramsMap.add("pluginInstanceParams", pluginInstanceParams); + + when(alertPluginInstanceService.testSend(eq(pluginDefineId), eq(pluginInstanceParams))) + .thenReturn(result); + + // When + final MvcResult mvcResult = mockMvc.perform(post("/alert-plugin-instances/test-send") + .header(SESSION_ID, sessionId) + .params(paramsMap)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andReturn(); + + // Then + final Result actualResponseContent = + JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); + assertThat(actualResponseContent.toString()).isEqualTo(expectResponseContent.toString()); + } + @Test public void testUpdateAlertPluginInstance() throws Exception { // Given diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceServiceTest.java index a91d2da37f..8072c72e3b 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceServiceTest.java @@ -25,11 +25,13 @@ import org.apache.dolphinscheduler.api.enums.Status; import org.apache.dolphinscheduler.api.permission.ResourcePermissionCheckService; import org.apache.dolphinscheduler.api.service.impl.AlertPluginInstanceServiceImpl; import org.apache.dolphinscheduler.api.service.impl.BaseServiceImpl; +import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.AlertPluginInstanceType; import org.apache.dolphinscheduler.common.enums.AuthorizationType; import org.apache.dolphinscheduler.common.enums.UserType; import org.apache.dolphinscheduler.common.enums.WarningType; +import org.apache.dolphinscheduler.common.model.Server; import org.apache.dolphinscheduler.dao.entity.AlertGroup; import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance; import org.apache.dolphinscheduler.dao.entity.PluginDefine; @@ -37,6 +39,10 @@ import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.mapper.AlertGroupMapper; import org.apache.dolphinscheduler.dao.mapper.AlertPluginInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.PluginDefineMapper; +import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse; +import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest; +import org.apache.dolphinscheduler.registry.api.RegistryClient; +import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; import java.util.ArrayList; import java.util.Arrays; @@ -78,6 +84,9 @@ public class AlertPluginInstanceServiceTest { @Mock private AlertGroupMapper alertGroupMapper; + @Mock + private RegistryClient registryClient; + private List alertPluginInstances; private User user; @@ -190,6 +199,26 @@ public class AlertPluginInstanceServiceTest { Assertions.assertNotNull(result.get(Constants.DATA_LIST)); } + @Test + public void testSendAlert() { + Result result; + Mockito.when(registryClient.getServerList(RegistryNodeType.ALERT_SERVER)).thenReturn(new ArrayList<>()); + result = alertPluginInstanceService.testSend(1, uiParams); + Assertions.assertEquals(Status.ALERT_SERVER_NOT_EXIST.getCode(), result.getCode()); + AlertSendResponse.AlertSendResponseResult alertResult = new AlertSendResponse.AlertSendResponseResult(); + alertResult.setSuccess(true); + AlertTestSendRequest alertTestSendRequest = new AlertTestSendRequest( + 1, + uiParams); + Server server = new Server(); + server.setPort(50052); + server.setHost("127.0.0.1"); + Mockito.when(registryClient.getServerList(RegistryNodeType.ALERT_SERVER)) + .thenReturn(Collections.singletonList(server)); + result = alertPluginInstanceService.testSend(1, uiParams); + Assertions.assertEquals(Status.ALERT_TEST_SENDING_FAILED.getCode(), result.getCode()); + } + @Test public void testDelete() { List ids = Arrays.asList("11,2,3", "5,96", null, "98,1"); diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/IAlertOperator.java b/dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/IAlertOperator.java index 295bd3bd62..44823c34a1 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/IAlertOperator.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/IAlertOperator.java @@ -19,6 +19,7 @@ package org.apache.dolphinscheduler.extract.alert; import org.apache.dolphinscheduler.extract.alert.request.AlertSendRequest; import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse; +import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest; import org.apache.dolphinscheduler.extract.base.RpcMethod; import org.apache.dolphinscheduler.extract.base.RpcService; @@ -28,4 +29,7 @@ public interface IAlertOperator { @RpcMethod AlertSendResponse sendAlert(AlertSendRequest alertSendRequest); + @RpcMethod + AlertSendResponse sendTestAlert(AlertTestSendRequest alertSendRequest); + } diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/request/AlertTestSendRequest.java b/dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/request/AlertTestSendRequest.java new file mode 100644 index 0000000000..887bc2b48f --- /dev/null +++ b/dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/request/AlertTestSendRequest.java @@ -0,0 +1,32 @@ +/* + * 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.extract.alert.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AlertTestSendRequest { + + private int pluginDefineId; + + private String pluginInstanceParams; +} diff --git a/dolphinscheduler-registry/dolphinscheduler-registry-api/pom.xml b/dolphinscheduler-registry/dolphinscheduler-registry-api/pom.xml index 8ee7b8f6f8..5e51f68b49 100644 --- a/dolphinscheduler-registry/dolphinscheduler-registry-api/pom.xml +++ b/dolphinscheduler-registry/dolphinscheduler-registry-api/pom.xml @@ -33,5 +33,9 @@ org.apache.dolphinscheduler dolphinscheduler-common + + org.apache.dolphinscheduler + dolphinscheduler-extract-base + diff --git a/dolphinscheduler-registry/dolphinscheduler-registry-api/src/main/java/org/apache/dolphinscheduler/registry/api/RegistryClient.java b/dolphinscheduler-registry/dolphinscheduler-registry-api/src/main/java/org/apache/dolphinscheduler/registry/api/RegistryClient.java index f690b6800f..3193614d0d 100644 --- a/dolphinscheduler-registry/dolphinscheduler-registry-api/src/main/java/org/apache/dolphinscheduler/registry/api/RegistryClient.java +++ b/dolphinscheduler-registry/dolphinscheduler-registry-api/src/main/java/org/apache/dolphinscheduler/registry/api/RegistryClient.java @@ -238,5 +238,4 @@ public class RegistryClient { private Collection getServerNodes(RegistryNodeType nodeType) { return getChildrenKeys(nodeType.getRegistryPath()); } - } diff --git a/dolphinscheduler-ui/src/locales/en_US/security.ts b/dolphinscheduler-ui/src/locales/en_US/security.ts index 578047fd14..b9bb18966c 100644 --- a/dolphinscheduler-ui/src/locales/en_US/security.ts +++ b/dolphinscheduler-ui/src/locales/en_US/security.ts @@ -168,7 +168,8 @@ export default { user_password: 'Password', user_password_tips: 'Please enter a password containing letters and numbers with a length between 6 and 20', - confirm_password_tips: 'The both of password and confirm password are not same.', + confirm_password_tips: + 'The both of password and confirm password are not same.', user_type: 'User Type', ordinary_user: 'Ordinary users', administrator: 'Administrator', @@ -216,6 +217,7 @@ export default { confirm: 'Confirm', cancel: 'Cancel', submit: 'Submit', + test_send: 'Test Send', create_alarm_instance: 'Create Alarm Instance', select_plugin: 'Select plugin', select_plugin_tips: 'Select Alarm plugin', diff --git a/dolphinscheduler-ui/src/locales/zh_CN/security.ts b/dolphinscheduler-ui/src/locales/zh_CN/security.ts index 30f25e5953..b16624885b 100644 --- a/dolphinscheduler-ui/src/locales/zh_CN/security.ts +++ b/dolphinscheduler-ui/src/locales/zh_CN/security.ts @@ -213,6 +213,7 @@ export default { confirm: '确定', cancel: '取消', submit: '提交', + test_send: '测试发送', create_alarm_instance: '创建告警实例', select_plugin: '选择插件', select_plugin_tips: '请选择告警插件', diff --git a/dolphinscheduler-ui/src/service/modules/alert-plugin/index.ts b/dolphinscheduler-ui/src/service/modules/alert-plugin/index.ts index 7081438a18..4f92f7d9d6 100644 --- a/dolphinscheduler-ui/src/service/modules/alert-plugin/index.ts +++ b/dolphinscheduler-ui/src/service/modules/alert-plugin/index.ts @@ -21,7 +21,8 @@ import { PluginInstanceReq, InstanceNameReq, IdReq, - UpdatePluginInstanceReq + UpdatePluginInstanceReq, + TestPluginInstanceReq } from './types' export function queryAlertPluginInstanceListPaging(params: ListReq): any { @@ -40,6 +41,14 @@ export function createAlertPluginInstance(data: PluginInstanceReq): any { }) } +export function testAlertPluginInstance(data: TestPluginInstanceReq): any { + return axios({ + url: '/alert-plugin-instances/test-send', + method: 'post', + data + }) +} + export function verifyAlertInstanceName(params: InstanceNameReq): any { return axios({ url: '/alert-plugin-instances/verify-name', diff --git a/dolphinscheduler-ui/src/service/modules/alert-plugin/types.ts b/dolphinscheduler-ui/src/service/modules/alert-plugin/types.ts index ff977af5ab..8551c027de 100644 --- a/dolphinscheduler-ui/src/service/modules/alert-plugin/types.ts +++ b/dolphinscheduler-ui/src/service/modules/alert-plugin/types.ts @@ -29,6 +29,11 @@ interface PluginInstanceReq { pluginInstanceParams: string } +interface TestPluginInstanceReq { + pluginDefineId: number + pluginInstanceParams: string +} + interface InstanceNameReq { alertInstanceName: string } @@ -58,5 +63,6 @@ export { InstanceNameReq, IdReq, UpdatePluginInstanceReq, - AlertPluginItem + AlertPluginItem, + TestPluginInstanceReq } diff --git a/dolphinscheduler-ui/src/views/security/alarm-instance-manage/detail.tsx b/dolphinscheduler-ui/src/views/security/alarm-instance-manage/detail.tsx index 86e39a27f2..893b443cf9 100644 --- a/dolphinscheduler-ui/src/views/security/alarm-instance-manage/detail.tsx +++ b/dolphinscheduler-ui/src/views/security/alarm-instance-manage/detail.tsx @@ -23,7 +23,15 @@ import { ref, getCurrentInstance } from 'vue' -import { NSelect, NInput, NSwitch, NRadioGroup, NSpace, NRadio } from 'naive-ui' +import { + NSelect, + NInput, + NSwitch, + NRadioGroup, + NSpace, + NRadio, + NButton +} from 'naive-ui' import { isFunction } from 'lodash' import { useI18n } from 'vue-i18n' import { useForm } from './use-form' @@ -69,7 +77,7 @@ const DetailModal = defineComponent({ changePlugin } = useForm() - const { status, createOrUpdate } = useDetail(getFormValues) + const { status, createOrUpdate, testSend } = useDetail(getFormValues) const onCancel = () => { resetForm() @@ -86,6 +94,11 @@ const DetailModal = defineComponent({ ctx.emit('update') } } + const onTest = async () => { + await state.detailFormRef.validate() + testSend(state.json) + } + const onChangePlugin = changePlugin const trim = getCurrentInstance()?.appContext.config.globalProperties.trim @@ -98,7 +111,9 @@ const DetailModal = defineComponent({ ) watch( () => state.detailForm.instanceType, - () => warningTypeSpan.value = state.detailForm.instanceType === 'GLOBAL' ? 0 : 24 + () => + (warningTypeSpan.value = + state.detailForm.instanceType === 'GLOBAL' ? 0 : 24) ) watch( () => state.json, @@ -131,6 +146,7 @@ const DetailModal = defineComponent({ elements, onChangePlugin, onSubmit, + onTest, onCancel, trim } @@ -150,7 +166,9 @@ const DetailModal = defineComponent({ saving, onChangePlugin, onCancel, - onSubmit + onSubmit, + onTest, + testing } = this const { currentRecord } = props return ( @@ -195,11 +213,11 @@ const DetailModal = defineComponent({ label: t('security.alarm_instance.is_global_instance'), widget: ( + checkedValue={'GLOBAL'} + uncheckedValue={'NORMAL'} + disabled={!!currentRecord?.id} + v-model:value={detailForm.instanceType} + /> ) }, { @@ -208,18 +226,12 @@ const DetailModal = defineComponent({ span: warningTypeSpan, widget: ( - - - {"success"} - - - {"failure"} - - - {"all"} - - - + + {'success'} + {'failure'} + {'all'} + + ) }, { @@ -244,6 +256,18 @@ const DetailModal = defineComponent({ cols: 24 }} /> + ), + + 'btn-middle': () => ( + + {t('security.alarm_instance.test_send')} + ) }} diff --git a/dolphinscheduler-ui/src/views/security/alarm-instance-manage/use-detail.ts b/dolphinscheduler-ui/src/views/security/alarm-instance-manage/use-detail.ts index c6ac01a528..eb208e0e96 100644 --- a/dolphinscheduler-ui/src/views/security/alarm-instance-manage/use-detail.ts +++ b/dolphinscheduler-ui/src/views/security/alarm-instance-manage/use-detail.ts @@ -19,14 +19,19 @@ import { reactive } from 'vue' import { isFunction } from 'lodash' import { createAlertPluginInstance, + testAlertPluginInstance, updateAlertPluginInstance, verifyAlertInstanceName } from '@/service/modules/alert-plugin' import type { IJsonItem, IRecord } from './types' +import { useI18n } from 'vue-i18n' export function useDetail(getFormValues: Function) { + const { t } = useI18n() + const status = reactive({ saving: false, + testing: false, loading: false }) @@ -41,6 +46,26 @@ export function useDetail(getFormValues: Function) { return JSON.stringify(json) } + const testSend = async (json?: IJsonItem[]) => { + const values = getFormValues() + + if (status.testing) return + status.testing = true + try { + const res = await testAlertPluginInstance({ + pluginDefineId: values.pluginDefineId, + pluginInstanceParams: formatParams(json, values) + }) + window.$message.success( + res + ? res.msg + : `${t('security.alarm_instance.test_send')} ${t('home.success')}` + ) + } finally { + status.testing = false + } + } + const createOrUpdate = async (currentRecord: IRecord, json?: IJsonItem[]) => { const values = getFormValues() if (status.saving) return false @@ -79,5 +104,5 @@ export function useDetail(getFormValues: Function) { } } - return { status, createOrUpdate } + return { status, createOrUpdate, testSend } }