This is an automated email from the ASF dual-hosted git repository.

akshayrai09 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 8ec8499  [TE] added a couple of validations to check if detection is 
subscribed & valid; cleaned up other validations (#5113)
8ec8499 is described below

commit 8ec849968f6025ca471929b6861c0ae1b5f993d0
Author: Akshay Rai <[email protected]>
AuthorDate: Fri Mar 6 09:46:48 2020 -0800

    [TE] added a couple of validations to check if detection is subscribed & 
valid; cleaned up other validations (#5113)
    
    Validations added:
    * Make sure subscription group subscribes to the detectionName while 
creating an alert
    * Make sure all the subscribed detections are valid
    * recipients should be configured under the EMAIL scheme params and not at 
the root level
    
    Validations removed:
    * type is not compulsory in subscription group
    * type is not compulsory for sub-entity alerts.
    
    Added unit tests for newly added validations
---
 .../alert/scheme/DetectionEmailAlerter.java        |  2 +-
 .../validators/DetectionConfigValidator.java       | 39 ++++++++--------
 .../validators/SubscriptionConfigValidator.java    | 52 +++++++++++++++-------
 .../thirdeye/detection/yaml/YamlResource.java      | 32 ++++++++++++-
 .../yaml/translator/DetectionConfigTranslator.java |  4 +-
 .../translator/SubscriptionConfigTranslator.java   |  3 +-
 .../validators/DetectionConfigValidatorTest.java   |  9 ----
 .../thirdeye/detection/yaml/YamlResourceTest.java  | 48 +++++++++++++++++---
 .../yaml/detection/detection-config-1.yaml         |  2 +-
 .../yaml/detection/detection-config-2.yaml         |  2 +-
 .../yaml/subscription/subscription-config-1.yaml   | 15 ++++---
 .../yaml/subscription/subscription-config-2.yaml   | 14 +++---
 .../yaml/subscription/subscription-config-3.yaml   | 14 +++---
 .../yaml/subscription/subscription-config-4.yaml   | 14 +++---
 .../yaml/subscription/subscription-config-5.yaml   | 13 +++---
 .../src/test/resources/sample-alert-config.yml     | 14 +++---
 16 files changed, 171 insertions(+), 106 deletions(-)

diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/alert/scheme/DetectionEmailAlerter.java
 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/alert/scheme/DetectionEmailAlerter.java
index 24177b2..d429125 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/alert/scheme/DetectionEmailAlerter.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/alert/scheme/DetectionEmailAlerter.java
@@ -63,7 +63,7 @@ import static 
org.apache.pinot.thirdeye.notification.commons.SmtpConfiguration.S
 public class DetectionEmailAlerter extends DetectionAlertScheme {
   private static final Logger LOG = 
LoggerFactory.getLogger(DetectionEmailAlerter.class);
 
-  private static final String PROP_RECIPIENTS = "recipients";
+  public static final String PROP_RECIPIENTS = "recipients";
   private static final String PROP_TO = "to";
   private static final String PROP_CC = "cc";
   private static final String PROP_BCC = "bcc";
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidator.java
 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidator.java
index 8af3462..fbefa5f 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidator.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidator.java
@@ -21,8 +21,6 @@ package org.apache.pinot.thirdeye.detection.validators;
 
 import com.google.common.base.Preconditions;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -38,6 +36,7 @@ import 
org.apache.pinot.thirdeye.detection.DetectionPipelineLoader;
 import org.quartz.CronExpression;
 
 import static org.apache.pinot.thirdeye.detection.ConfigUtils.*;
+import static 
org.apache.pinot.thirdeye.detection.alert.scheme.DetectionEmailAlerter.*;
 
 
 public class DetectionConfigValidator implements 
ConfigValidator<DetectionConfigDTO> {
@@ -58,6 +57,7 @@ public class DetectionConfigValidator implements 
ConfigValidator<DetectionConfig
   private static final String PROP_DETECTION_NAME = "detectionName";
   private static final String PROP_MAX_DURATION = "maxDuration";
   private static final String PROP_CLASS_NAME = "className";
+  private static final String PROP_CRON = "cron";
 
   private static final String LEGACY_METRIC_ALERT = "COMPOSITE";
   private static final String METRIC_ALERT = "METRIC_ALERT";
@@ -129,12 +129,11 @@ public class DetectionConfigValidator implements 
ConfigValidator<DetectionConfig
         "Missing property ( " + PROP_NAME + " ) in one of the sub-alerts under 
" + parentAlertName);
     String alertName = MapUtils.getString(detectionYaml, PROP_NAME);
 
-    Preconditions.checkArgument(detectionYaml.containsKey(PROP_TYPE),
-        "Missing property ( " + PROP_TYPE + " ) in sub-alert " + alertName);
     String alertType = MapUtils.getString(detectionYaml, PROP_TYPE);
-
-    Preconditions.checkArgument(SUPPORTED_ALERT_TYPES.contains(alertType),
-        "Unsupported type (" + alertType + ") in sub-alert " + alertName);
+    if (alertType != null) {
+      Preconditions.checkArgument(SUPPORTED_ALERT_TYPES.contains(alertType),
+          "Unsupported type (" + alertType + ") in sub-alert " + alertName);
+    }
   }
 
   private void validateMetricAlertConfig(Map<String, Object> detectionYaml, 
String parentAlertName)
@@ -227,33 +226,31 @@ public class DetectionConfigValidator implements 
ConfigValidator<DetectionConfig
   }
 
   /**
-   * Validate the the detection yaml configuration.
+   * Make sure the config adheres to detection config syntax
    *
    * @param detectionYaml the detection yaml configuration to be validated
    */
   @Override
   public void validateYaml(Map<String, Object> detectionYaml) throws 
IllegalArgumentException {
-    // Validate detectionName
-    
Preconditions.checkArgument(detectionYaml.containsKey(PROP_DETECTION_NAME), 
"Property missing: " + PROP_DETECTION_NAME);
-    String alertName = MapUtils.getString(detectionYaml, PROP_DETECTION_NAME);
+    // detectionName is a required field
+    Preconditions.checkArgument(detectionYaml.containsKey(PROP_DETECTION_NAME),
+        "Property missing: " + PROP_DETECTION_NAME);
 
     // Hack to support 'detectionName' attribute at root level and 'name' 
attribute elsewhere
     // We consistently use 'name' as a convention to define the sub-alerts. 
However, at the root
     // level, as a convention, we will use 'detectionName' which defines the 
name of the complete alert.
+    String alertName = MapUtils.getString(detectionYaml, PROP_DETECTION_NAME);
     detectionYaml.put(PROP_NAME, alertName);
 
-    // By default if 'type' is not specified, we assume it as a METRIC_ALERT
-    if (!detectionYaml.containsKey(PROP_TYPE)) {
-      detectionYaml.put(PROP_TYPE, METRIC_ALERT);
-    }
-
     // Validate config depending on the type (METRIC_ALERT OR COMPOSITE_ALERT)
-    if (detectionYaml.get(PROP_TYPE).equals(COMPOSITE_ALERT)) {
-      validateCompositeAlertConfig(detectionYaml, alertName);
-    } else {
-      // The legacy type 'COMPOSITE' will be treated as a metric alert along 
with the new convention METRIC_ALERT.
-      // This is applicable only at the root level to maintain backward 
compatibility.
+    if (detectionYaml.get(PROP_TYPE) == null || 
detectionYaml.get(PROP_TYPE).equals(METRIC_ALERT)) {
+      // By default, we treat every alert as a METRIC_ALERT unless explicitly 
specified.
+      // Even the legacy type termed 'COMPOSITE' will be treated as a metric 
alert along
+      // with the new convention METRIC_ALERT. This is applicable only at the 
root level
+      // to maintain backward compatibility.
       validateMetricAlertConfig(detectionYaml, alertName);
+    } else {
+      validateCompositeAlertConfig(detectionYaml, alertName);
     }
   }
 
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/SubscriptionConfigValidator.java
 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/SubscriptionConfigValidator.java
index e953613..d21e5f5 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/SubscriptionConfigValidator.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/validators/SubscriptionConfigValidator.java
@@ -24,10 +24,12 @@ import java.util.List;
 import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.pinot.thirdeye.datalayer.dto.DetectionAlertConfigDTO;
+import org.apache.pinot.thirdeye.datalayer.util.Predicate;
 import org.apache.pinot.thirdeye.datasource.DAORegistry;
 import org.apache.pinot.thirdeye.detection.ConfigUtils;
 import org.quartz.CronExpression;
 
+import static 
org.apache.pinot.thirdeye.detection.alert.scheme.DetectionEmailAlerter.PROP_RECIPIENTS;
 import static 
org.apache.pinot.thirdeye.detection.yaml.translator.SubscriptionConfigTranslator.*;
 
 
@@ -36,21 +38,17 @@ public class SubscriptionConfigValidator implements 
ConfigValidator<DetectionAle
   private static final String PROP_CLASS_NAME = "className";
 
   /**
-   * Perform validation on the alert config like verifying if all the required 
fields are set
+   * Perform validation on the parsed & constructed subscription config
    */
   @Override
   public void validateConfig(DetectionAlertConfigDTO alertConfig) throws 
IllegalArgumentException {
     Preconditions.checkNotNull(alertConfig);
 
     // Check for all the required fields in the alert
-    Preconditions.checkArgument(!StringUtils.isEmpty(alertConfig.getName()), 
"Subscription group name field cannot be left empty.");
-    
Preconditions.checkArgument(!StringUtils.isEmpty(alertConfig.getApplication()), 
"Application field cannot be left empty");
-
-    // Empty subscription properties
-    Preconditions.checkArgument((alertConfig.getProperties() != null
-            && alertConfig.getProperties().get(PROP_CLASS_NAME) != null
-            && 
StringUtils.isNotEmpty((alertConfig.getProperties().get(PROP_CLASS_NAME).toString()))),
-        "'Type' field cannot be left empty.");
+    Preconditions.checkArgument(!StringUtils.isEmpty(alertConfig.getName()),
+        "Subscription group name field cannot be left empty.");
+    
Preconditions.checkArgument(!StringUtils.isEmpty(alertConfig.getApplication()),
+        "Application field cannot be left empty");
 
     // Application name should be valid
     
Preconditions.checkArgument(!DAORegistry.getInstance().getApplicationDAO().findByName(alertConfig.getApplication()).isEmpty(),
@@ -61,7 +59,13 @@ public class SubscriptionConfigValidator implements 
ConfigValidator<DetectionAle
     // Cron Validator
     
Preconditions.checkArgument(CronExpression.isValidExpression(alertConfig.getCronExpression()),
         "The subscription cron specified is incorrect. Please verify your cron 
expression using online cron"
-            + " makers.");
+        + " makers.");
+
+    // Empty subscription properties
+    Preconditions.checkArgument((alertConfig.getProperties() != null
+            && alertConfig.getProperties().get(PROP_CLASS_NAME) != null
+            && 
StringUtils.isNotEmpty((alertConfig.getProperties().get(PROP_CLASS_NAME).toString()))),
+        "'Type' field cannot be left empty.");
 
     // At least one alertScheme is required
     Preconditions.checkArgument((alertConfig.getAlertSchemes() != null && 
!alertConfig.getAlertSchemes().isEmpty()),
@@ -74,17 +78,35 @@ public class SubscriptionConfigValidator implements 
ConfigValidator<DetectionAle
 
     // detectionConfigIds cannot be empty
     List<Long> detectionIds = 
ConfigUtils.getLongs(alertConfig.getProperties().get(PROP_DETECTION_CONFIG_IDS));
-    Preconditions.checkArgument((detectionIds != null && 
!detectionIds.isEmpty()),
-        "A notification group should subscribe to at least one alert. If you 
wish to unsubscribe, set active"
-            + " to false.");
+    Preconditions.checkArgument(!detectionIds.isEmpty(),
+        "A notification group should subscribe to at least one alert. If you 
wish to unsubscribe, set"
+            + " active to false.");
 
     // TODO add more checks like email validity, alert type check, scheme type 
check etc.
   }
 
-  // Validate the raw subscription yaml configuration
+  /**
+   * Checks to ensure the fields adhere to the syntax
+   */
   @Override
   public void validateYaml(Map<String, Object> config) throws 
IllegalArgumentException {
-    // Add validations
+    // Make sure recipients are configured at the right place
+    Preconditions.checkArgument(!config.containsKey(PROP_RECIPIENTS),
+        "Recipients should be configured under the EMAIL alertScheme within 
params");
+
+    // Subscription group must subscribe to at least one alert
+    List<String> detectionNames = 
ConfigUtils.getList(config.get(PROP_DETECTION_NAMES));
+    Preconditions.checkArgument(!detectionNames.isEmpty(),
+        "A subscription group should subscribe to at least one alert. If you 
wish to unsubscribe, set"
+            + " active to false in the subscription config.");
+
+    // Make sure the subscribed detections are valid
+    for (String detectionName : detectionNames) {
+      
Preconditions.checkArgument(!DAORegistry.getInstance().getDetectionConfigManager()
+              .findByPredicate(Predicate.EQ("name", detectionName)).isEmpty(),
+          "Cannot find detection " + detectionName + " - Please ensure the 
detections listed under "
+              + PROP_DETECTION_NAMES + " exist and are correctly configured.");
+    }
   }
 
   /**
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/YamlResource.java
 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/YamlResource.java
index cd870a7..6d7ae6d 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/YamlResource.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/YamlResource.java
@@ -26,6 +26,7 @@ import io.dropwizard.auth.Auth;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
@@ -261,6 +262,23 @@ public class YamlResource {
     return 
Response.status(Response.Status.UNAUTHORIZED).entity(responseMessage).build();
   }
 
+  /**
+   * Perform some basic validations on the create alert payload
+   */
+  public void validateCreateAlertYaml(Map<String, String> config) throws 
IOException {
+    Preconditions.checkArgument(config.containsKey(PROP_DETECTION), "Detection 
pipeline yaml is missing");
+    Preconditions.checkArgument(config.containsKey(PROP_SUBSCRIPTION), 
"Subscription group yaml is missing.");
+
+    // check if subscription group has subscribed to the detection
+    Map<String, Object> detectionConfigMap = new 
HashMap<>(ConfigUtils.getMap(this.yaml.load(config.get(PROP_DETECTION))));
+    String detectionName = MapUtils.getString(detectionConfigMap, 
PROP_DETECTION_NAME);
+    Map<String, Object> subscriptionConfigMap = new 
HashMap<>(ConfigUtils.getMap(this.yaml.load(config.get(PROP_SUBSCRIPTION))));
+    List<String> detectionNames = 
ConfigUtils.getList(subscriptionConfigMap.get(PROP_DETECTION_NAMES));
+    Preconditions.checkArgument(detectionNames.contains(detectionName),
+        "You have not subscribed to the alert. Please copy-paste the 
detectionName under the "
+            + PROP_DETECTION_NAMES + " field in your subscription group.");
+  }
+
   @POST
   @Path("/create-alert")
   @Produces(MediaType.APPLICATION_JSON)
@@ -272,9 +290,19 @@ public class YamlResource {
       @ApiParam(value =  "a json contains both subscription and detection yaml 
as string")  String payload,
       @ApiParam("tuning window start time for tunable components") 
@QueryParam("startTime") long startTime,
       @ApiParam("tuning window end time for tunable components") 
@QueryParam("endTime") long endTime) throws Exception {
+    // validate the payload
     Map<String, String> yamls;
+    try {
+      Preconditions.checkArgument(StringUtils.isNotBlank(payload), "The Yaml 
Payload in the request is empty.");
+      yamls = OBJECT_MAPPER.readValue(payload, Map.class);
+      validateCreateAlertYaml(yamls);
+    } catch (IllegalArgumentException e) {
+      return processBadRequestResponse(PROP_DETECTION, 
YamlOperations.CREATING.name(), payload, e);
+    } catch (Exception e) {
+      return processServerErrorResponse(PROP_DETECTION, 
YamlOperations.CREATING.name(), payload, e);
+    }
 
-    // Detection
+    // Parse and save the detection config
     long detectionConfigId;
     try {
       validatePayload(payload);
@@ -289,7 +317,7 @@ public class YamlResource {
       return processServerErrorResponse(PROP_DETECTION, 
YamlOperations.CREATING.name(), payload, e);
     }
 
-    // Notification
+    // Parse and save the subscription config
     long detectionAlertConfigId;
     try {
       String subscriptionYaml = yamls.get(PROP_SUBSCRIPTION);
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/DetectionConfigTranslator.java
 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/DetectionConfigTranslator.java
index 91d2fd9..4481c41 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/DetectionConfigTranslator.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/DetectionConfigTranslator.java
@@ -288,9 +288,7 @@ public class DetectionConfigTranslator extends 
ConfigTranslator<DetectionConfigD
     yamlConfigMap.put(PROP_NAME, alertName);
 
     // By default if 'type' is not specified, we assume it as a METRIC_ALERT
-    if (!yamlConfigMap.containsKey(PROP_TYPE)) {
-      yamlConfigMap.put(PROP_TYPE, METRIC_ALERT);
-    }
+    yamlConfigMap.putIfAbsent(PROP_TYPE, METRIC_ALERT);
 
     // Translate config depending on the type (METRIC_ALERT OR COMPOSITE_ALERT)
     Map<String, Object> properties;
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/SubscriptionConfigTranslator.java
 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/SubscriptionConfigTranslator.java
index 6eabccc..ca0e635 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/SubscriptionConfigTranslator.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/org/apache/pinot/thirdeye/detection/yaml/translator/SubscriptionConfigTranslator.java
@@ -58,6 +58,7 @@ public class SubscriptionConfigTranslator extends 
ConfigTranslator<DetectionAler
   public static final String PROP_TYPE = "type";
   public static final String PROP_CLASS_NAME = "className";
   public static final String PROP_PARAM = "params";
+  public static final String DEFAULT_ALERTER_PIPELINE = 
"DEFAULT_ALERTER_PIPELINE";
 
   static final String PROP_ALERT_SUPPRESSORS = "alertSuppressors";
   static final String PROP_REFERENCE_LINKS = "referenceLinks";
@@ -92,7 +93,7 @@ public class SubscriptionConfigTranslator extends 
ConfigTranslator<DetectionAler
     Map<String, Object> properties = new HashMap<>();
 
     // Default subscription type is "DEFAULT_ALERTER_PIPELINE"
-    alertYamlConfigs.putIfAbsent(PROP_TYPE, "DEFAULT_ALERTER_PIPELINE");
+    alertYamlConfigs.putIfAbsent(PROP_TYPE, DEFAULT_ALERTER_PIPELINE);
 
     for (Map.Entry<String, Object> entry : alertYamlConfigs.entrySet()) {
       if (entry.getKey().equals(PROP_TYPE)) {
diff --git 
a/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidatorTest.java
 
b/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidatorTest.java
index d6084ac..9539342 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidatorTest.java
+++ 
b/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/validators/DetectionConfigValidatorTest.java
@@ -199,13 +199,4 @@ public class DetectionConfigValidatorTest {
     DetectionConfigValidator detectionConfigValidator = new 
DetectionConfigValidator(provider);
     detectionConfigValidator.validateYaml(this.yamlConfig2);
   }
-
-  // type is a compulsory field at sub-alert levels
-  @Test(expectedExceptions = IllegalArgumentException.class)
-  public void testNoTypeUnderAlertsValidation() {
-    ((Map<String, Object>) 
ConfigUtils.getList(this.yamlConfig2.get("alerts")).get(0)).remove("type");
-
-    DetectionConfigValidator detectionConfigValidator = new 
DetectionConfigValidator(provider);
-    detectionConfigValidator.validateYaml(this.yamlConfig2);
-  }
 }
diff --git 
a/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/yaml/YamlResourceTest.java
 
b/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/yaml/YamlResourceTest.java
index 0feb1fc..884110f 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/yaml/YamlResourceTest.java
+++ 
b/thirdeye/thirdeye-pinot/src/test/java/org/apache/pinot/thirdeye/detection/yaml/YamlResourceTest.java
@@ -1,5 +1,7 @@
 package org.apache.pinot.thirdeye.detection.yaml;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import org.apache.pinot.thirdeye.auth.ThirdEyePrincipal;
 import org.apache.pinot.thirdeye.dashboard.DetectionPreviewConfiguration;
@@ -67,6 +69,36 @@ public class YamlResourceTest {
   }
 
   @Test
+  public void testValidateCreateAlertYaml() throws IOException {
+    // No validation error should be thrown for valid yaml payload
+    String detectionPayload = 
IOUtils.toString(this.getClass().getResourceAsStream("detection/detection-config-2.yaml"));
+    String subscriptionPayload = 
IOUtils.toString(this.getClass().getResourceAsStream("subscription/subscription-config-5.yaml"));
+    Map<String, String> config = new HashMap<>();
+    config.put("detection", detectionPayload);
+    config.put("subscription", subscriptionPayload);
+    try {
+      this.yamlResource.validateCreateAlertYaml(config);
+    } catch (Exception e) {
+      Assert.fail("No exception should be thrown for valid payload");
+      return;
+    }
+
+    // Throw error if the subscription group is not subscribed to the detection
+    detectionPayload = 
IOUtils.toString(this.getClass().getResourceAsStream("detection/detection-config-2.yaml"));
+    subscriptionPayload = 
IOUtils.toString(this.getClass().getResourceAsStream("subscription/subscription-config-2.yaml"));
+    config.put("detection", detectionPayload);
+    config.put("subscription", subscriptionPayload);
+    try {
+      this.yamlResource.validateCreateAlertYaml(config);
+    } catch (IllegalArgumentException e) {
+      Assert.assertEquals(e.getMessage(), "You have not subscribed to the 
alert. Please copy-paste the"
+          + " detectionName under the subscribedDetections field in your 
subscription group.");
+      return;
+    }
+    Assert.fail("Since the subscription group has not subscribed to the alert, 
an error should have been thrown.");
+  }
+
+  @Test
   public void testCreateOrUpdateDetectionConfig() throws IOException {
     String blankYaml = "";
     try {
@@ -95,7 +127,7 @@ public class YamlResourceTest {
       long id = this.yamlResource.createOrUpdateDetectionConfig(user, 
validYaml);
       DetectionConfigDTO detection = 
daoRegistry.getDetectionConfigManager().findById(id);
       Assert.assertNotNull(detection);
-      Assert.assertEquals(detection.getName(), "testPipeline");
+      Assert.assertEquals(detection.getName(), "test_detection_1");
     } catch (Exception e) {
       Assert.fail("Exception should not be thrown for valid yaml. Message: " + 
e + " Cause: " + e.getCause(), e);
     }
@@ -106,7 +138,7 @@ public class YamlResourceTest {
       long id = this.yamlResource.createOrUpdateDetectionConfig(user, 
updatedYaml);
       DetectionConfigDTO detection = 
daoRegistry.getDetectionConfigManager().findById(id);
       Assert.assertNotNull(detection);
-      Assert.assertEquals(detection.getName(), "testPipeline");
+      Assert.assertEquals(detection.getName(), "test_detection_2");
       Assert.assertEquals(detection.getDescription(), "My modified pipeline");
     } catch (Exception e) {
       Assert.fail("Exception should not be thrown if detection already exists. 
Message: " + e + " Cause: " + e.getCause());
@@ -146,7 +178,8 @@ public class YamlResourceTest {
       this.yamlResource.createSubscriptionConfig(blankYaml);
       Assert.fail("Exception not thrown on empty yaml");
     } catch (Exception e) {
-      Assert.assertEquals(e.getMessage(), "Subscription group name field 
cannot be left empty.");
+      Assert.assertEquals(e.getMessage(), "A subscription group should 
subscribe to at least one alert."
+          + " If you wish to unsubscribe, set active to false in the 
subscription config.");
     }
 
     String inValidYaml = "application:test:application";
@@ -157,7 +190,8 @@ public class YamlResourceTest {
       Assert.assertEquals(e.getMessage(), "Could not parse as map: 
application:test:application");
     }
 
-    String noSubscriptGroupYaml = "application: test_application";
+    // Error should be thrown if subscriptionGroupName is not defined
+    String noSubscriptGroupYaml = "application: 
test_application\nsubscribedDetections:\n- test_detection_1\n";
     try {
       this.yamlResource.createSubscriptionConfig(noSubscriptGroupYaml);
       Assert.fail("Exception not thrown on empty yaml");
@@ -240,7 +274,8 @@ public class YamlResourceTest {
       this.yamlResource.updateSubscriptionGroup(user, oldId, blankYaml);
       Assert.fail("Exception not thrown on empty yaml");
     } catch (Exception e) {
-      Assert.assertEquals(e.getMessage(), "Subscription group name field 
cannot be left empty.");
+      Assert.assertEquals(e.getMessage(), "A subscription group should 
subscribe to at least one alert."
+          + " If you wish to unsubscribe, set active to false in the 
subscription config.");
     }
 
     String inValidYaml = "application:test:application";
@@ -251,7 +286,8 @@ public class YamlResourceTest {
       Assert.assertEquals(e.getMessage(), "Could not parse as map: 
application:test:application");
     }
 
-    String noSubscriptGroupYaml = "application: test_app";
+    // Error should be thrown if no subscriptionGroupName is specified
+    String noSubscriptGroupYaml = "application: 
test_app\nsubscribedDetections:\n- test_detection_1";
     try {
       this.yamlResource.updateSubscriptionGroup(user, oldId, 
noSubscriptGroupYaml);
       Assert.fail("Exception not thrown on empty yaml");
diff --git 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-1.yaml
 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-1.yaml
index 9c19a76..3242f95 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-1.yaml
+++ 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-1.yaml
@@ -1,4 +1,4 @@
-detectionName: testPipeline
+detectionName: test_detection_1
 description: My test pipeline
 type: METRIC_ALERT
 metric: test_metric
diff --git 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-2.yaml
 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-2.yaml
index 1330155..ff899a3 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-2.yaml
+++ 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/detection/detection-config-2.yaml
@@ -1,4 +1,4 @@
-detectionName: testPipeline
+detectionName: test_detection_2
 description: My modified pipeline
 type: METRIC_ALERT
 metric: test_metric
diff --git 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-1.yaml
 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-1.yaml
index 6a16719..bb7de8a 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-1.yaml
+++ 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-1.yaml
@@ -10,16 +10,17 @@ dimensionRecipients:
   - "[email protected]"
 dimension: app_name
 
-fromAddress: [email protected]
-
-recipients:
- to:
-  - "[email protected]"
- cc:
-  - "[email protected]"
+subscribedDetections:
+- test_detection_1
 
 alertSchemes:
 - type: EMAIL
+  params:
+    recipients:
+      to:
+        - "[email protected]"
+      cc:
+        - "[email protected]"
 - type: IRIS
   params:
     plan: thirdye_test_plan
diff --git 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-2.yaml
 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-2.yaml
index ef47ca5..4da06f7 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-2.yaml
+++ 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-2.yaml
@@ -12,16 +12,14 @@ dimensionRecipients:
   - "[email protected]"
 dimension: app_name
 
-fromAddress: [email protected]
-
-recipients:
- to:
-  - "[email protected]"
- cc:
-  - "[email protected]"
-
 alertSchemes:
 - type: EMAIL
+  params:
+    recipients:
+      to:
+        - "[email protected]"
+      cc:
+        - "[email protected]"
 - type: IRIS
   params:
     plan: thirdye_test_plan
diff --git 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-3.yaml
 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-3.yaml
index ca0c621..a479a22 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-3.yaml
+++ 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-3.yaml
@@ -13,16 +13,14 @@ dimensionRecipients:
   - "[email protected]"
 dimension: app_name
 
-fromAddress: [email protected]
-
-recipients:
- to:
-  - "[email protected]"
- cc:
-  - "[email protected]"
-
 alertSchemes:
 - type: EMAIL
+  params:
+    recipients:
+      to:
+        - "[email protected]"
+      cc:
+        - "[email protected]"
 - type: IRIS
   params:
     plan: thirdye_test_plan
diff --git 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-4.yaml
 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-4.yaml
index 139fe9b..8b0f9bc 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-4.yaml
+++ 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-4.yaml
@@ -13,16 +13,14 @@ dimensionRecipients:
   - "[email protected]"
 dimension: app_name
 
-fromAddress: [email protected]
-
-recipients:
- to:
-  - "[email protected]"
- cc:
-  - "[email protected]"
-
 alertSchemes:
 - type: EMAIL
+  params:
+    recipients:
+      to:
+        - "[email protected]"
+      cc:
+        - "[email protected]"
 - type: IRIS
   params:
     plan: thirdye_test_plan
diff --git 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-5.yaml
 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-5.yaml
index 9f04929..2863725 100644
--- 
a/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-5.yaml
+++ 
b/thirdeye/thirdeye-pinot/src/test/resources/org/apache/pinot/thirdeye/detection/yaml/subscription/subscription-config-5.yaml
@@ -13,19 +13,16 @@ dimensionRecipients:
   - "[email protected]"
 dimension: app_name
 
-fromAddress: [email protected]
-
-recipients:
- to:
-  - "[email protected]"
- cc:
-  - "[email protected]"
-
 alertSchemes:
 - type: EMAIL
   params:
     template: ENTITY_GROUPBY_REPORT
     subject: METRICS
+    recipients:
+      to:
+        - "[email protected]"
+      cc:
+        - "[email protected]"
 - type: IRIS
   params:
     plan: thirdye_test_plan
diff --git a/thirdeye/thirdeye-pinot/src/test/resources/sample-alert-config.yml 
b/thirdeye/thirdeye-pinot/src/test/resources/sample-alert-config.yml
index 9f00a88..3e5d3fe 100644
--- a/thirdeye/thirdeye-pinot/src/test/resources/sample-alert-config.yml
+++ b/thirdeye/thirdeye-pinot/src/test/resources/sample-alert-config.yml
@@ -14,13 +14,13 @@ subscribedDetections:
 # or for advanced critical use-cases setup Iris alert by referring to the 
documentation
 alertSchemes:
   - type: EMAIL
-recipients:
-  to:
-    - "[email protected]"          # Specify alert recipient email address here
-    - "[email protected]"
-  cc:
-    - "[email protected]"
-fromAddress: [email protected]
+    params:
+      recipients:
+        to:
+          - "[email protected]"          # Specify alert recipient email 
address here
+          - "[email protected]"
+        cc:
+          - "[email protected]"
 
 # The below links will appear in the email alerts. This will help alert 
recipients to quickly refer and act on.
 referenceLinks:


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to