$()

in server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/ec.js [30:358]


$(function () {
  if (sessionStorage.ecDetailsJSON === undefined) {
    sessionStorage.ecDetailsJSON = JSON.stringify([]);
  }

  // display datatables errors in the console instead of in alerts
  $.fn.dataTable.ext.errMode = 'throw';

  compactorsTable = $('#compactorsTable').DataTable({
    "ajax": {
      "url": '/rest/ec/compactors',
      "dataSrc": "compactors"
    },
    "stateSave": true,
    "dom": 't<"align-left"l>p',
    "columnDefs": [{
        "targets": "duration",
        "render": function (data, type, row) {
          if (type === 'display') data = timeDuration(data);
          return data;
        }
      },
      {
        "targets": "date",
        "render": function (data, type, row) {
          if (type === 'display') data = dateFormat(data);
          return data;
        }
      }
    ],
    "columns": [{
        "data": "server"
      },
      {
        "data": "groupName"
      },
      {
        "data": "lastContact"
      }
    ]
  });

  const hostnameColumnName = 'hostname';
  const queueNameColumnName = 'queueName';
  const tableIdColumnName = 'tableId';
  const durationColumnName = 'duration';

  // Create a table for running compactors
  runningTable = $('#runningTable').DataTable({
    "ajax": {
      "url": '/rest/ec/running',
      "dataSrc": "running"
    },
    "stateSave": true,
    "dom": 't<"align-left"l>p',
    "columnDefs": [{
        "targets": "duration",
        "render": function (data, type, row) {
          if (type === 'display') data = timeDuration(data);
          return data;
        }
      },
      {
        "targets": "date",
        "render": function (data, type, row) {
          if (type === 'display') data = dateFormat(data);
          return data;
        }
      }
    ],
    "columns": [{
        "data": "server",
        "name": hostnameColumnName
      },
      {
        "data": "kind"
      },
      {
        "data": "status"
      },
      {
        "data": "queueName",
        "name": queueNameColumnName
      },
      {
        "data": "tableId",
        "name": tableIdColumnName
      },
      {
        "data": "numFiles"
      },
      {
        "data": "progress",
        "type": "html",
        "render": function (data, type, row, meta) {
          if (type === 'display') {
            if (row.progress < 0) {
              data = '--';
            } else {
              var p = Math.round(Number(row.progress));
              console.log("Compaction progress = %" + p);
              data = '<div class="progress"><div class="progress-bar" role="progressbar" style="min-width: 2em; width:' +
                p + '%;">' + p + '%</div></div>';
            }
          }
          return data;
        }
      },
      {
        "data": "lastUpdate"
      },
      {
        "data": "duration",
        "name": durationColumnName
      },
      { // more column settings
        "class": "details-control",
        "orderable": false,
        "data": null,
        "defaultContent": ""
      }
    ]
  });

  function handleFilterKeyup(input, feedbackElement, columnName) {
    if (isValidRegex(input) || input === '') { // if valid, apply the filter
      feedbackElement.hide();
      $(this).removeClass('is-invalid');
      const isRegex = true;
      const smartEnabled = false;
      runningTable
        .column(`${columnName}:name`)
        .search(input, isRegex, smartEnabled)
        .draw();
    } else { // if invalid, show the warning
      feedbackElement.show();
      $(this).addClass('is-invalid');
    }
  }

  $('#hostname-filter').on('keyup', function () {
    handleFilterKeyup.call(this, this.value, $('#hostname-feedback'), hostnameColumnName);
  });

  $('#queue-filter').on('keyup', function () {
    handleFilterKeyup.call(this, this.value, $('#queue-feedback'), queueNameColumnName);
  });

  $('#tableid-filter').on('keyup', function () {
    handleFilterKeyup.call(this, this.value, $('#tableid-feedback'), tableIdColumnName);
  });

  $('#duration-filter').on('keyup', function () {
    runningTable.draw();
  });

  // Clear Filters button handler
  $('#clear-filters').on('click', function () {
    $(this).prop('disabled', true); // disable the clear button

    // set the filter inputs to empty and trigger the keyup event to clear the filters
    $('#hostname-filter').val('').trigger('keyup');
    $('#queue-filter').val('').trigger('keyup');
    $('#tableid-filter').val('').trigger('keyup');
    $('#duration-filter').val('').trigger('keyup');

    $(this).prop('disabled', false); // re-enable the clear
  });

  // Custom filter function for duration
  $.fn.dataTable.ext.search.push(function (settings, data, dataIndex) {
    if (settings.nTable.id !== 'runningTable') {
      return true;
    }

    const durationColIndex = runningTable.column(`${durationColumnName}:name`).index();
    const durationStr = data[durationColIndex];
    const durationSeconds = parseDuration(durationStr);

    const input = $('#duration-filter').val().trim();
    if (input === '') {
      $('#duration-feedback').hide();
      return true;
    }

    const match = validateDurationInput(input);
    if (!match) {
      $('#duration-feedback').show();
      return false;
    }

    $('#duration-feedback').hide();
    const operator = match[1];
    const value = parseInt(match[2]);
    const unit = match[3];
    const filterSeconds = convertToSeconds(value, unit);

    switch (operator) {
    case '>':
      return durationSeconds > filterSeconds;
    case '>=':
      return durationSeconds >= filterSeconds;
    case '<':
      return durationSeconds < filterSeconds;
    case '<=':
      return durationSeconds <= filterSeconds;
    default:
      console.error(`Unexpected operator "${operator}" encountered in duration filter.`);
      return true;
    }
  });

  // Helper function to convert duration strings to seconds
  function convertToSeconds(value, unit) {
    switch (unit.toLowerCase()) {
    case 's':
      return value;
    case 'm':
      return value * 60;
    case 'h':
      return value * 3600;
    case 'd':
      return value * 86400;
    default:
      console.error(`Unexpected unit "${unit}" encountered in duration filter. Defaulting to seconds.`);
      return value;
    }
  }

  // Helper function to validate duration input. Makes sure that the input is in the format of '<operator> <value> <unit>'
  function validateDurationInput(input) {
    return input.match(/^([<>]=?)\s*(\d+)([smhd])$/i);
  }

  /**
   * @param {number} durationStr duration in milliseconds
   * @returns duration in seconds
   */
  function parseDuration(durationStr) {
    // Assuming durationStr is in milliseconds
    const milliseconds = parseInt(durationStr, 10);
    const seconds = milliseconds / 1000;
    return seconds;
  }

  // Create a table for compaction coordinator
  coordinatorTable = $('#coordinatorTable').DataTable({
    "ajax": {
      "url": '/rest/ec',
      "dataSrc": function (data) {
        // the data needs to be in an array to work with DataTables
        var arr = [];
        if (data === undefined) {
          console.warn('the value of "data" is undefined');
        } else {
          arr = [data];
        }

        return arr;
      }
    },
    "stateSave": true,
    "searching": false,
    "paging": false,
    "info": false,
    "columnDefs": [{
      "targets": "duration",
      "render": function (data, type, row) {
        if (type === 'display') data = timeDuration(data);
        return data;
      }
    }],
    "columns": [{
        "data": "server"
      },
      {
        "data": "numQueues"
      },
      {
        "data": "numCompactors"
      },
      {
        "data": "lastContact"
      }
    ]
  });

  // Array to track the ids of the details displayed rows
  var detailRows = [];
  $("#runningTable tbody").on('click', 'tr td.details-control', function () {
    var tr = $(this).closest('tr');
    var row = runningTable.row(tr);
    var idx = $.inArray(tr.attr('id'), detailRows);

    if (row.child.isShown()) {
      tr.removeClass('details');
      row.child.hide();

      // Remove from the 'open' array
      detailRows.splice(idx, 1);
    } else {
      var rci = row.data();
      var ecid = rci.ecid;
      var idSuffix = ecid.substring(ecid.length - 5, ecid.length);
      tr.addClass('details');
      // put all the information into html for a single row
      var htmlRow = "<table class='table table-bordered table-striped table-condensed' id='table" + idSuffix + "'>"
      htmlRow += "<thead><tr><th>#</th><th>Input Files</th><th>Size</th><th>Entries</th></tr></thead>";
      htmlRow += "<tbody></tbody></table>";
      htmlRow += "Output File: <span id='outputFile" + idSuffix + "'></span><br>";
      htmlRow += ecid;
      row.child(htmlRow).show();
      // show the row then populate the table
      var ecDetails = getDetailsFromStorage(idSuffix);
      if (ecDetails.length === 0) {
        getRunningDetails(ecid, idSuffix);
      } else {
        console.log("Got cached details for " + idSuffix);
        populateDetails(ecDetails, idSuffix);
      }

      // Add to the 'open' array
      if (idx === -1) {
        detailRows.push(tr.attr('id'));
      }
    }
  });
  refreshECTables();
});