github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/static/js/alert.js (about)

     1  /*
     2  Copyright 2023.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  'use strict';
    18  
    19  let alertData = {queryParams :{}};
    20  let alertEditFlag = 0;
    21  let alertID;
    22  let alertRule_name = "alertRule_name";
    23  let query_string = "query_string";
    24  let condition = "condition";
    25  let notification_channel_type = "notification_channel_type";
    26  let messageTemplateInfo =
    27    '<i class="fa fa-info-circle position-absolute info-icon sendMsg" rel="tooltip" id="info-icon-msg" style="display: block;" title = "You can use following template variables:' +
    28  '\n' + inDoubleBrackets("alert_rule_name") +
    29  '\n' + inDoubleBrackets("query_string") +
    30  '\n' + inDoubleBrackets('condition') +
    31  '\n' + inDoubleBrackets('queryLanguage') + '"></i>';
    32  let messageInputBox = document.getElementById("message-info");
    33  if(messageInputBox)
    34      messageInputBox.innerHTML += messageTemplateInfo;
    35  
    36  // If there's double brackets next to each other, the templating system will
    37  // try to replace what's inside the brackets with a value. We don't want that
    38  // in this case.
    39  function inDoubleBrackets(str) {
    40      return "{" + "{" + str + "}" + "}";
    41  }
    42  
    43  let mapConditionTypeToIndex =new Map([
    44      ["Is above",0],
    45      ["Is below",1],
    46      ["Equal to",2],
    47      ["Not equal to",3]
    48  ]);
    49  
    50  let mapIndexToConditionType =new Map([
    51      [0,"Is above"],
    52      [1,"Is below"],
    53      [2,"Equal to"],
    54      [3,"Not equal to"]
    55  ]);
    56  
    57  let mapIndexToAlertState=new Map([
    58      [0,"Normal"],
    59      [1,"Pending"],
    60      [2,"Firing"],
    61  ]);
    62  
    63  const alertForm =$('#alert-form');
    64  
    65  $(document).ready(function () {
    66      if (Cookies.get('theme')) {
    67          theme = Cookies.get('theme');
    68          $('body').attr('data-theme', theme);
    69          
    70      }
    71      $('.theme-btn').on('click', themePickerHandler);
    72      $("#logs-language-btn").show();
    73      let startTime = "now-30m";
    74      let endTime = "now";
    75      datePickerHandler(startTime, endTime, startTime);
    76      setupEventHandlers();
    77  
    78      $('.alert-condition-options li').on('click', setAlertConditionHandler);
    79      $('#contact-points-dropdown').on('click', contactPointsDropdownHandler);
    80      $('#logs-language-options li').on('click', setLogsLangHandler);
    81      $('#data-source-options li').on('click', setDataSourceHandler);
    82      $('#cancel-alert-btn').on('click',function(){
    83          window.location.href='../all-alerts.html';
    84          resetAddAlertForm();
    85      });
    86      
    87      alertForm.on('submit',(e)=>submitAddAlertForm(e));
    88    
    89      const tooltipIds = ["info-icon-spl", "info-icon-msg", "info-evaluate-every", "info-evaluate-for"];
    90  
    91      tooltipIds.forEach(id => {
    92          $(`#${id}`).tooltip({
    93              delay: { show: 0, hide: 300 },
    94              trigger: "click"
    95          }).on("click", function () {
    96              $(`#${id}`).tooltip("show");
    97          });
    98      });
    99  
   100      $(document).mouseup(function (e) {
   101          if ($(e.target).closest(".tooltip-inner").length === 0) {
   102              tooltipIds.forEach(id => $(`#${id}`).tooltip("hide"));
   103          }
   104      });
   105      getAlertId();
   106      if(window.location.href.includes("alert-details.html")){
   107          alertDetailsFunctions();
   108      }
   109  });
   110  
   111  function getAlertId() {
   112      const urlParams = new URLSearchParams(window.location.search);
   113  
   114      if (urlParams.has('id')) {
   115          const id = urlParams.get('id');
   116          editAlert(id);
   117          alertID = id;
   118      } else if (urlParams.has('queryLanguage')) {
   119          const queryLanguage = urlParams.get('queryLanguage');
   120          const searchText = urlParams.get('searchText');
   121          const startEpoch = urlParams.get('startEpoch');
   122          const endEpoch = urlParams.get('endEpoch');
   123      
   124          createAlertFromLogs(queryLanguage, searchText, startEpoch, endEpoch);
   125      }
   126  }
   127  
   128  function editAlert(alertId){
   129      $.ajax({
   130          method: "get",
   131          url: "api/alerts/" + alertId,
   132          headers: {
   133              'Content-Type': 'application/json; charset=utf-8',
   134              'Accept': '*/*'
   135          },
   136          dataType: 'json',
   137          crossDomain: true,
   138      }).then(function (res) {
   139          if(window.location.href.includes("alert-details.html")){
   140              displayAlertProperties(res.alert)
   141          }else{
   142              alertEditFlag = 1;
   143              displayAlert(res.alert);
   144          }
   145      })
   146  }
   147  
   148  function setAlertConditionHandler(e) {
   149      $('.alert-condition-option').removeClass('active');
   150      $('#alert-condition span').html($(this).html());
   151      $(this).addClass('active');
   152      let optionId = $(this).attr('id');  
   153  }
   154  
   155  function contactPointsDropdownHandler() {
   156      //get all contact points 
   157      $.ajax({
   158          method: "get",
   159          url: "api/alerts/allContacts",
   160          headers: {
   161              'Content-Type': 'application/json; charset=utf-8',
   162              'Accept': '*/*'
   163          },
   164          dataType: 'json',
   165          crossDomain: true,
   166      }).then(function (res) {
   167          if (res.contacts) {
   168          let dropdown = $('.contact-points-options');
   169          
   170          res.contacts.forEach((cp) => {
   171              if (!$(`.contact-points-option:contains(${cp.contact_name})`).length) {
   172                  dropdown.append(`<li class="contact-points-option" id="${cp.contact_id}">${cp.contact_name}</li>`);
   173              }
   174          });
   175      }
   176  })}
   177  
   178  
   179  $('.contact-points-options').on('click', 'li', function () {
   180      $('.contact-points-option').removeClass('active');
   181      $('#contact-points-dropdown span').html($(this).html());
   182      $('#contact-points-dropdown span').attr('id', $(this).attr('id'));
   183      $(this).addClass('active');
   184  
   185      if ($(this).html() === 'Add New') {
   186          $('.popupOverlay, .popupContent').addClass('active');
   187          $('#contact-form-container').css("display", "block");
   188      }
   189  });
   190  
   191  $(document).keyup(function(e) {
   192      if (e.key === "Escape" || e.key === "Esc") {
   193          $('.popupOverlay, .popupContent').removeClass('active');
   194      }
   195  });
   196  
   197  
   198  
   199  //create new alert rule
   200  function submitAddAlertForm(e){
   201      e.preventDefault();
   202      setAlertRule();
   203      alertEditFlag ? updateAlertRule(alertData) : createNewAlertRule(alertData);
   204  }
   205  
   206  function setAlertRule(){
   207      let dataSource = $('#alert-data-source span').text();
   208      alertData.alert_name = $('#alert-rule-name').val(),
   209      alertData.queryParams.data_source = dataSource;
   210      alertData.queryParams.queryLanguage = $('#logs-language-btn span').text();
   211      alertData.queryParams.queryText= $('#query').val(),
   212      alertData.queryParams.startTime= filterStartDate,
   213      alertData.queryParams.endTime= filterEndDate,
   214      alertData.condition= mapConditionTypeToIndex.get($('#alert-condition span').text()),
   215      alertData.eval_interval= parseInt($('#evaluate-every').val()),
   216      alertData.eval_for= parseInt($('#evaluate-for').val()),
   217      alertData.contact_name= $('#contact-points-dropdown span').text(),
   218      alertData.contact_id= $('#contact-points-dropdown span').attr('id'),
   219      alertData.message= $('.message').val()
   220      alertData.value = parseFloat($('#threshold-value').val());
   221      alertData.message = $(".message").val();
   222      alertData.labels =[]
   223      
   224      $('.label-container').each(function() {
   225        let labelName = $(this).find('#label-key').val();
   226        let labelVal = $(this).find('#label-value').val();
   227          if (labelName && labelVal) {
   228              let labelEntry  = {
   229                  label_name: labelName,
   230                  label_value: labelVal
   231                };
   232                alertData.labels.push(labelEntry);
   233          }
   234      })
   235  }
   236  
   237  function createNewAlertRule(alertData){
   238      $.ajax({
   239          method: "post",
   240          url: "api/alerts/create",
   241          headers: {
   242              'Content-Type': 'application/json; charset=utf-8',
   243              'Accept': '*/*'
   244          },
   245          data: JSON.stringify(alertData),
   246          dataType: 'json',
   247          crossDomain: true,
   248      }).then((res)=>{
   249          resetAddAlertForm();
   250          window.location.href='../all-alerts.html';
   251      }).catch((err)=>{
   252          showToast(err.responseJSON.error)
   253      });
   254  }
   255  
   256  // update alert rule
   257  function updateAlertRule(alertData){
   258          $.ajax({
   259          method: "post",
   260          url: "api/alerts/update",
   261          headers: {
   262              'Content-Type': 'application/json; charset=utf-8',
   263              'Accept': '*/*'
   264          },
   265          data: JSON.stringify(alertData),
   266          dataType: 'json',
   267          crossDomain: true,
   268      }).then((res)=>{
   269          resetAddAlertForm();
   270          window.location.href='../all-alerts.html';
   271      }).catch((err)=>{
   272          showToast(err.responseJSON.error)
   273      });
   274  }
   275  
   276  //reset alert form
   277  function resetAddAlertForm(){
   278      alertForm[0].reset();
   279  }
   280  
   281  function displayAlert(res){
   282      $('#alert-rule-name').val(res.alert_name);
   283      $('#alert-data-source span').html(res.queryParams.data_source);
   284      const queryLanguage = res.queryParams.queryLanguage;
   285      $('#logs-language-btn span').text(queryLanguage);
   286      $('.logs-language-option').removeClass('active');
   287      $(`.logs-language-option:contains(${queryLanguage})`).addClass('active');
   288      displayQueryToolTip(queryLanguage);
   289      $('#query').val(res.queryParams.queryText);
   290      $(`.ranges .inner-range #${res.queryParams.startTime}`).addClass('active');
   291      datePickerHandler(res.queryParams.startTime, res.queryParams.endTime, res.queryParams.startTime)
   292      let conditionType = mapIndexToConditionType.get(res.condition)
   293      $('.alert-condition-option').removeClass('active');
   294      $(`.alert-condition-options #option-${res.condition}`).addClass('active');
   295      $('#alert-condition span').text(conditionType);
   296      $('#threshold-value').val(res.value);
   297      $('#evaluate-every').val(res.eval_interval);
   298      $('#evaluate-for').val(res.eval_for);
   299      $('.message').val(res.message);
   300      if(alertEditFlag){
   301          alertData.alert_id = res.alert_id;
   302      }
   303      $('#contact-points-dropdown span').html(res.contact_name);
   304      $('#contact-points-dropdown span').attr('id', res.contact_id);
   305      
   306      let isFirst = true;
   307      (res.labels).forEach(function(label){
   308          let labelContainer;
   309          if (isFirst) {
   310              labelContainer = $('.label-container');
   311              isFirst = false;
   312          } else {
   313              labelContainer = $('.label-container').first().clone();
   314              labelContainer.append('<button class="btn-simple delete-icon" type="button" id="delete-alert-label"></button>');
   315          }
   316          labelContainer.find("#label-key").val(label.label_name);
   317          labelContainer.find("#label-value").val(label.label_value);
   318          labelContainer.appendTo('.label-main-container');
   319      })
   320  
   321  }
   322  
   323  function showToast(msg) {
   324      let toast =
   325          `<div class="div-toast" id="save-db-modal"> 
   326          ${msg}
   327          <button type="button" aria-label="Close" class="toast-close">✖</button>
   328      <div>`
   329      $('body').prepend(toast);
   330      $('.toast-close').on('click', removeToast)
   331      setTimeout(removeToast, 2000);
   332  }
   333  
   334  function removeToast() {
   335      $('.div-toast').remove();
   336  }
   337  
   338  function setLogsLangHandler(e) {
   339      $('.logs-language-option').removeClass('active');
   340      $('#logs-language-btn span').html($(this).html());
   341      $(this).addClass('active');
   342      displayQueryToolTip($(this).html());
   343  }
   344  
   345  function setDataSourceHandler(e) {
   346      $('.data-source-option').removeClass('active');
   347      $('#alert-data-source span').html($(this).html());
   348      $(this).addClass('active');
   349  }
   350  
   351  function displayQueryToolTip(selectedQueryLang) {
   352      $('#info-icon-pipeQL, #info-icon-spl').hide();
   353      if (selectedQueryLang === "Pipe QL") {
   354          $('#info-icon-pipeQL').show();
   355      } else if (selectedQueryLang === "Splunk QL") {
   356          $('#info-icon-spl').show();
   357      }
   358  }
   359  
   360  // Display Alert Details
   361  function displayAlertProperties(res) {
   362      const queryParams = res.queryParams;
   363      $('.alert-name').text(res.alert_name);
   364      $('.alert-status').text(mapIndexToAlertState.get(res.state));
   365      $('.alert-query').val(queryParams.queryText);
   366      $('.alert-type').text(queryParams.data_source);
   367      $('.alert-query-language').text(queryParams.queryLanguage);
   368      $('.alert-condition').text(mapIndexToConditionType.get(res.condition));
   369      $('.alert-value').text(res.value);
   370      $('.alert-every').text(res.eval_interval);
   371      $('.alert-for').text(res.eval_for);
   372      $('.alert-contact-point').text(res.contact_name);
   373      const labelContainer = $('.alert-labels-container');
   374      const labels = res.labels;
   375      labels.forEach(label => {
   376          const labelElement = $('<div>').addClass('label-element').text(`${label.label_name}=${label.label_value}`);
   377          labelContainer.append(labelElement);
   378      })
   379  }
   380  
   381  // Add Label
   382  $(".add-label-container").on("click", function () {
   383      var labelContainer = $(".label-container").first().clone();
   384      labelContainer.find("#label-key").val("");
   385      labelContainer.find("#label-value").val("");
   386      labelContainer.append('<button class="btn-simple delete-icon" type="button" id="delete-alert-label"></button>');
   387      labelContainer.appendTo(".label-main-container");
   388  });
   389  
   390  // Delete Label
   391  $(".label-main-container").on("click", ".delete-icon", function () {
   392      $(this).closest(".label-container").remove();
   393  });
   394  
   395  //On Alert Details Page 
   396  function alertDetailsFunctions(){
   397      function editAlert(event){        
   398          var queryString = "?id=" + alertID;
   399          window.location.href = "../alert.html" + queryString;
   400          event.stopPropagation();
   401      }
   402  
   403      function deleteAlert() {
   404          $.ajax({
   405              method: 'delete',
   406              url: 'api/alerts/delete',
   407              headers: {
   408                  'Content-Type': 'application/json; charset=utf-8',
   409                  Accept: '*/*',
   410              },
   411              data: JSON.stringify({
   412                  alert_id: alertID
   413              }),
   414              crossDomain: true,
   415          }).then(function (res) {
   416              showToast(res.message)
   417              window.location.href='../all-alerts.html';
   418          });
   419      }
   420  
   421      function showPrompt(event) {
   422          event.stopPropagation();
   423          $('.popupOverlay, .popupContent').addClass('active');
   424  
   425          $('#cancel-btn, .popupOverlay, #delete-btn').click(function () {
   426              $('.popupOverlay, .popupContent').removeClass('active');
   427          });
   428          $('#delete-btn').click(deleteAlert)
   429      }
   430  
   431      $('#edit-alert-btn').on('click',editAlert)
   432      $('#delete-alert').on('click',showPrompt)
   433      $('#cancel-alert-details').on('click',function(){
   434          window.location.href='../all-alerts.html';
   435      })
   436  }
   437  
   438  //Create alert from logs
   439  function createAlertFromLogs(queryLanguage, query, startEpoch, endEpoch){
   440      $('#alert-rule-name').focus();
   441      $('#query').val(query);
   442      $(`.ranges .inner-range #${startEpoch}`).addClass('active');
   443      datePickerHandler(startEpoch, endEpoch , startEpoch)
   444  }