code: /*
   * 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.skywalking.oap.server.core.alarm.provider;
  
  import java.io.InputStream;
  import java.io.Reader;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.stream.Collectors;
  
  import org.apache.skywalking.oap.server.core.alarm.provider.discord.DiscordSettings;
  import org.apache.skywalking.oap.server.core.alarm.provider.pagerduty.PagerDutySettings;
  import org.apache.skywalking.oap.server.library.util.StringUtil;
  import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
  import org.apache.skywalking.oap.server.core.alarm.provider.feishu.FeishuSettings;
  import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
  import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
  import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
  import org.apache.skywalking.oap.server.core.alarm.provider.welink.WeLinkSettings;
  import org.apache.skywalking.oap.server.library.util.CollectionUtils;
  import org.yaml.snakeyaml.LoaderOptions;
  import org.yaml.snakeyaml.Yaml;
  import org.yaml.snakeyaml.constructor.SafeConstructor;
  
  /**
   * Rule Reader parses the given `alarm-settings.yml` config file, to the target {@link Rules}.
   */
  public class RulesReader {
      private Map yamlData;
  
      public RulesReader(InputStream inputStream) {
          Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
          yamlData = yaml.load(inputStream);
      }
  
      public RulesReader(Reader io) {
          Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
          yamlData = yaml.load(io);
      }
  
      /**
       * Read rule config file to {@link Rules}
       */
      public Rules readRules() {
          Rules rules = new Rules();
  
          if (Objects.nonNull(yamlData)) {
              readRulesConfig(rules);
              readWebHookConfig(rules);
              readGrpcConfig(rules);
              readSlackConfig(rules);
              readWechatConfig(rules);
              readCompositeRuleConfig(rules);
              readDingtalkConfig(rules);
              readFeishuConfig(rules);
              readWeLinkConfig(rules);
              readPagerDutyConfig(rules);
              readDiscordConfig(rules);
          }
          return rules;
      }
  
      /**
       * Read rule config into {@link AlarmRule}
       */
      private void readRulesConfig(Rules rules) {
          Map rulesData = (Map) yamlData.get("rules");
          if (rulesData == null) {
              return;
          }
          rules.setRules(new ArrayList<>());
          rulesData.forEach((k, v) -> {
              if (((String) k).endsWith("_rule")) {
                  AlarmRule alarmRule = new AlarmRule();
                  alarmRule.setAlarmRuleName((String) k);
                  Map settings = (Map) v;
                  Object metricsName = settings.get("metrics-name");
                  if (metricsName == null) {
                      throw new IllegalArgumentException("metrics-name can't be null");
                  }
  
                  alarmRule.setMetricsName((String) metricsName);
                  alarmRule.setIncludeNames((ArrayList) settings.getOrDefault("include-names", new ArrayList(0)));
                  alarmRule.setExcludeNames((ArrayList) settings.getOrDefault("exclude-names", new ArrayList(0)));
                  alarmRule.setIncludeNamesRegex((String) settings.getOrDefault("include-names-regex", ""));
                  alarmRule.setExcludeNamesRegex((String) settings.getOrDefault("exclude-names-regex", ""));
                  alarmRule.setIncludeLabels(
                          (ArrayList) settings.getOrDefault("include-labels", new ArrayList(0)));
                  alarmRule.setExcludeLabels(
                          (ArrayList) settings.getOrDefault("exclude-labels", new ArrayList(0)));
                  alarmRule.setIncludeLabelsRegex((String) settings.getOrDefault("include-labels-regex", ""));
                  alarmRule.setExcludeLabelsRegex((String) settings.getOrDefault("exclude-labels-regex", ""));
                  alarmRule.setThreshold(settings.get("threshold").toString());
                  alarmRule.setOp((String) settings.get("op"));
                  alarmRule.setPeriod((Integer) settings.getOrDefault("period", 1));
                  alarmRule.setCount((Integer) settings.getOrDefault("count", 1));
                  // How many times of checks, the alarm keeps silence after alarm triggered, default as same as period.
                  alarmRule.setSilencePeriod((Integer) settings.getOrDefault("silence-period", alarmRule.getPeriod()));
                  alarmRule.setOnlyAsCondition((Boolean) settings.getOrDefault("only-as-condition", false));
                  alarmRule.setMessage(
                          (String) settings.getOrDefault("message", "Alarm caused by Rule " + alarmRule
                                  .getAlarmRuleName()));
                  alarmRule.setTags((Map) settings.getOrDefault("tags", new HashMap<String, String>()));
                  rules.getRules().add(alarmRule);
              }
          });
      }
  
      /**
       * Read web hook config
       */
      private void readWebHookConfig(Rules rules) {
          List webhooks = (List) yamlData.get("webhooks");
          if (webhooks != null) {
              rules.setWebhooks(new ArrayList<>());
              webhooks.forEach(url -> {
                  rules.getWebhooks().add((String) url);
              });
          }
      }
  
      /**
       * Read grpc hook config into {@link GRPCAlarmSetting}
       */
      private void readGrpcConfig(Rules rules) {
          Map grpchooks = (Map) yamlData.get("gRPCHook");
          if (grpchooks != null) {
              GRPCAlarmSetting grpcAlarmSetting = new GRPCAlarmSetting();
              Object targetHost = grpchooks.get("target_host");
              if (targetHost != null) {
                  grpcAlarmSetting.setTargetHost((String) targetHost);
              }
  
              Object targetPort = grpchooks.get("target_port");
              if (targetPort != null) {
                  grpcAlarmSetting.setTargetPort((Integer) targetPort);
              }
  
              rules.setGrpchookSetting(grpcAlarmSetting);
          }
      }
  
      /**
       * Read slack hook config into {@link SlackSettings}
       */
      private void readSlackConfig(Rules rules) {
          Map slacks = (Map) yamlData.get("slackHooks");
          if (slacks != null) {
              SlackSettings slackSettings = new SlackSettings();
              Object textTemplate = slacks.getOrDefault("textTemplate", "");
              slackSettings.setTextTemplate((String) textTemplate);
  
              List<String> slackWebhooks = (List<String>) slacks.get("webhooks");
              if (slackWebhooks != null) {
                  slackSettings.getWebhooks().addAll(slackWebhooks);
              }
              rules.setSlacks(slackSettings);
          }
      }
  
      /**
       * Read wechat hook config into {@link WechatSettings}
       */
      private void readWechatConfig(Rules rules) {
          Map wechatConfig = (Map) yamlData.get("wechatHooks");
          if (wechatConfig != null) {
              WechatSettings wechatSettings = new WechatSettings();
              Object textTemplate = wechatConfig.getOrDefault("textTemplate", "");
              wechatSettings.setTextTemplate((String) textTemplate);
              List<String> wechatWebhooks = (List<String>) wechatConfig.get("webhooks");
              if (wechatWebhooks != null) {
                  wechatSettings.getWebhooks().addAll(wechatWebhooks);
              }
              rules.setWecchats(wechatSettings);
          }
      }
  
      /**
       * Read composite rule config into {@link CompositeAlarmRule}
       */
      private void readCompositeRuleConfig(Rules rules) {
          Map compositeRulesData = (Map) yamlData.get("composite-rules");
          if (compositeRulesData == null) {
              return;
          }
          compositeRulesData.forEach((k, v) -> {
              String ruleName = (String) k;
              if (ruleName.endsWith("_rule")) {
                  Map settings = (Map) v;
                  CompositeAlarmRule compositeAlarmRule = new CompositeAlarmRule();
                  compositeAlarmRule.setAlarmRuleName(ruleName);
                  String expression = (String) settings.get("expression");
                  if (expression == null) {
                      throw new IllegalArgumentException("expression can't be null");
                  }
                  compositeAlarmRule.setExpression(expression);
                  compositeAlarmRule.setMessage(
                          (String) settings.getOrDefault("message", "Alarm caused by Rule " + ruleName));
                  compositeAlarmRule.setTags((Map) settings.getOrDefault("tags", new HashMap<String, String>(0)));
                  rules.getCompositeRules().add(compositeAlarmRule);
              }
          });
      }
  
      /**
       * Read dingtalk hook config into {@link DingtalkSettings}
       */
      private void readDingtalkConfig(Rules rules) {
          Map dingtalkConfig = (Map) yamlData.get("dingtalkHooks");
          if (dingtalkConfig != null) {
              DingtalkSettings dingtalkSettings = new DingtalkSettings();
              Object textTemplate = dingtalkConfig.getOrDefault("textTemplate", "");
              dingtalkSettings.setTextTemplate((String) textTemplate);
              List<Map<String, Object>> wechatWebhooks = (List<Map<String, Object>>) dingtalkConfig.get("webhooks");
              if (wechatWebhooks != null) {
                  wechatWebhooks.forEach(wechatWebhook -> {
                      Object secret = wechatWebhook.getOrDefault("secret", "");
                      Object url = wechatWebhook.getOrDefault("url", "");
                      dingtalkSettings.getWebhooks().add(new DingtalkSettings.WebHookUrl((String) secret, (String) url));
                  });
              }
              rules.setDingtalks(dingtalkSettings);
          }
      }
  
      /**
       * Read feishu hook config into {@link FeishuSettings}
       */
      private void readFeishuConfig(Rules rules) {
          Map feishuConfig = (Map) yamlData.get("feishuHooks");
          if (feishuConfig != null) {
              FeishuSettings feishuSettings = new FeishuSettings();
              Object textTemplate = feishuConfig.getOrDefault("textTemplate", "");
              feishuSettings.setTextTemplate((String) textTemplate);
              List<Map<String, Object>> wechatWebhooks = (List<Map<String, Object>>) feishuConfig.get("webhooks");
              if (wechatWebhooks != null) {
                  wechatWebhooks.forEach(wechatWebhook -> {
                      Object secret = wechatWebhook.getOrDefault("secret", "");
                      Object url = wechatWebhook.getOrDefault("url", "");
                      feishuSettings.getWebhooks().add(new FeishuSettings.WebHookUrl((String) secret, (String) url));
                  });
              }
              rules.setFeishus(feishuSettings);
          }
      }
  
      /**
       * Read WeLink hook config into {@link WeLinkSettings}
       */
      @SuppressWarnings("unchecked")
      private void readWeLinkConfig(Rules rules) {
          Map<String, Object> welinkConfig = (Map<String, Object>) yamlData.getOrDefault(
              "welinkHooks",
              Collections.EMPTY_MAP
          );
          String textTemplate = (String) welinkConfig.get("textTemplate");
          List<Map<String, String>> welinkWebHooks = (List<Map<String, String>>) welinkConfig.get("webhooks");
          if (StringUtil.isBlank(textTemplate) || CollectionUtils.isEmpty(welinkWebHooks)) {
              return;
          }
          List<WeLinkSettings.WebHookUrl> webHookUrls = welinkWebHooks.stream().map(
              WeLinkSettings.WebHookUrl::generateFromMap
          ).collect(Collectors.toList());
  
          WeLinkSettings welinkSettings = new WeLinkSettings();
          welinkSettings.setTextTemplate(textTemplate);
          welinkSettings.setWebhooks(webHookUrls);
          rules.setWelinks(welinkSettings);
      }
  
      /**
       * Read PagerDuty hook config into {@link PagerDutySettings}
       */
      private void readPagerDutyConfig(Rules rules) {
          Map<String, Object> pagerDutyConfig = (Map<String, Object>) yamlData.get("pagerDutyHooks");
          if (pagerDutyConfig != null) {
              PagerDutySettings pagerDutySettings = new PagerDutySettings();
              String textTemplate = (String) pagerDutyConfig.getOrDefault("textTemplate", "");
              pagerDutySettings.setTextTemplate(textTemplate);
  
              List<String> integrationKeys = (List<String>) pagerDutyConfig.get("integrationKeys");
              if (integrationKeys != null) {
                  pagerDutySettings.getIntegrationKeys().addAll(integrationKeys);
              }
  
              rules.setPagerDutySettings(pagerDutySettings);
          }
      }
  
      /**
       * Read Discord hook config into {@link DiscordSettings}
       */
      @SuppressWarnings("unchecked")
      private void readDiscordConfig(Rules rules) {
          Map<String, Object> discordConfig = (Map<String, Object>) yamlData.getOrDefault(
                  "discordHooks",
                  Collections.EMPTY_MAP
          );
          String textTemplate = (String) discordConfig.get("textTemplate");
          List<Map<String, String>> discordWebHooks = (List<Map<String, String>>) discordConfig.get("webhooks");
          if (StringUtil.isBlank(textTemplate) || CollectionUtils.isEmpty(discordWebHooks)) {
              return;
          }
          List<DiscordSettings.WebHookUrl> webHookUrls = discordWebHooks.stream().map(
                  DiscordSettings.WebHookUrl::generateFromMap
          ).collect(Collectors.toList());
  
          DiscordSettings discordSettings = new DiscordSettings();
          discordSettings.setTextTemplate(textTemplate);
          discordSettings.setWebhooks(webHookUrls);
          rules.setDiscordSettings(discordSettings);
      }
  }
  