349 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			349 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
<script>
 | 
						|
import {
 | 
						|
  GlButton,
 | 
						|
  GlDropdown,
 | 
						|
  GlDropdownItem,
 | 
						|
  GlModal,
 | 
						|
  GlModalDirective,
 | 
						|
  GlLink,
 | 
						|
} from '@gitlab/ui';
 | 
						|
import _ from 'underscore';
 | 
						|
import { mapActions, mapState } from 'vuex';
 | 
						|
import { s__ } from '~/locale';
 | 
						|
import Icon from '~/vue_shared/components/icon.vue';
 | 
						|
import '~/vue_shared/mixins/is_ee';
 | 
						|
import { getParameterValues } from '~/lib/utils/url_utility';
 | 
						|
import MonitorAreaChart from './charts/area.vue';
 | 
						|
import GraphGroup from './graph_group.vue';
 | 
						|
import EmptyState from './empty_state.vue';
 | 
						|
import { timeWindows, timeWindowsKeyNames } from '../constants';
 | 
						|
import { getTimeDiff } from '../utils';
 | 
						|
 | 
						|
const sidebarAnimationDuration = 150;
 | 
						|
let sidebarMutationObserver;
 | 
						|
 | 
						|
export default {
 | 
						|
  components: {
 | 
						|
    MonitorAreaChart,
 | 
						|
    GraphGroup,
 | 
						|
    EmptyState,
 | 
						|
    Icon,
 | 
						|
    GlButton,
 | 
						|
    GlDropdown,
 | 
						|
    GlDropdownItem,
 | 
						|
    GlLink,
 | 
						|
    GlModal,
 | 
						|
  },
 | 
						|
  directives: {
 | 
						|
    GlModalDirective,
 | 
						|
  },
 | 
						|
  props: {
 | 
						|
    externalDashboardPath: {
 | 
						|
      type: String,
 | 
						|
      required: false,
 | 
						|
      default: '',
 | 
						|
    },
 | 
						|
    hasMetrics: {
 | 
						|
      type: Boolean,
 | 
						|
      required: false,
 | 
						|
      default: true,
 | 
						|
    },
 | 
						|
    showPanels: {
 | 
						|
      type: Boolean,
 | 
						|
      required: false,
 | 
						|
      default: true,
 | 
						|
    },
 | 
						|
    documentationPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    settingsPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    clustersPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    tagsPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    projectPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    metricsEndpoint: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    deploymentEndpoint: {
 | 
						|
      type: String,
 | 
						|
      required: false,
 | 
						|
      default: null,
 | 
						|
    },
 | 
						|
    emptyGettingStartedSvgPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    emptyLoadingSvgPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    emptyNoDataSvgPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    emptyUnableToConnectSvgPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    environmentsEndpoint: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    currentEnvironmentName: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    showTimeWindowDropdown: {
 | 
						|
      type: Boolean,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    customMetricsAvailable: {
 | 
						|
      type: Boolean,
 | 
						|
      required: false,
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    customMetricsPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    validateQueryPath: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
  },
 | 
						|
  data() {
 | 
						|
    return {
 | 
						|
      state: 'gettingStarted',
 | 
						|
      elWidth: 0,
 | 
						|
      selectedTimeWindow: '',
 | 
						|
      selectedTimeWindowKey: '',
 | 
						|
      formIsValid: null,
 | 
						|
    };
 | 
						|
  },
 | 
						|
  computed: {
 | 
						|
    canAddMetrics() {
 | 
						|
      return this.customMetricsAvailable && this.customMetricsPath.length;
 | 
						|
    },
 | 
						|
    ...mapState('monitoringDashboard', [
 | 
						|
      'groups',
 | 
						|
      'emptyState',
 | 
						|
      'showEmptyState',
 | 
						|
      'environments',
 | 
						|
      'deploymentData',
 | 
						|
    ]),
 | 
						|
  },
 | 
						|
  created() {
 | 
						|
    this.setEndpoints({
 | 
						|
      metricsEndpoint: this.metricsEndpoint,
 | 
						|
      environmentsEndpoint: this.environmentsEndpoint,
 | 
						|
      deploymentsEndpoint: this.deploymentEndpoint,
 | 
						|
    });
 | 
						|
 | 
						|
    this.timeWindows = timeWindows;
 | 
						|
    this.selectedTimeWindowKey =
 | 
						|
      _.escape(getParameterValues('time_window')[0]) || timeWindowsKeyNames.eightHours;
 | 
						|
 | 
						|
    // Set default time window if the selectedTimeWindowKey is bogus
 | 
						|
    if (!Object.keys(this.timeWindows).includes(this.selectedTimeWindowKey)) {
 | 
						|
      this.selectedTimeWindowKey = timeWindowsKeyNames.eightHours;
 | 
						|
    }
 | 
						|
 | 
						|
    this.selectedTimeWindow = this.timeWindows[this.selectedTimeWindowKey];
 | 
						|
  },
 | 
						|
  beforeDestroy() {
 | 
						|
    if (sidebarMutationObserver) {
 | 
						|
      sidebarMutationObserver.disconnect();
 | 
						|
    }
 | 
						|
  },
 | 
						|
  mounted() {
 | 
						|
    if (!this.hasMetrics) {
 | 
						|
      this.setGettingStartedEmptyState();
 | 
						|
    } else {
 | 
						|
      this.fetchData(getTimeDiff(this.timeWindows.eightHours));
 | 
						|
 | 
						|
      sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
 | 
						|
      sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
 | 
						|
        attributes: true,
 | 
						|
        childList: false,
 | 
						|
        subtree: false,
 | 
						|
      });
 | 
						|
    }
 | 
						|
  },
 | 
						|
  methods: {
 | 
						|
    ...mapActions('monitoringDashboard', [
 | 
						|
      'fetchData',
 | 
						|
      'setGettingStartedEmptyState',
 | 
						|
      'setEndpoints',
 | 
						|
    ]),
 | 
						|
    getGraphAlerts(queries) {
 | 
						|
      if (!this.allAlerts) return {};
 | 
						|
      const metricIdsForChart = queries.map(q => q.metricId);
 | 
						|
      return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId));
 | 
						|
    },
 | 
						|
    getGraphAlertValues(queries) {
 | 
						|
      return Object.values(this.getGraphAlerts(queries));
 | 
						|
    },
 | 
						|
    hideAddMetricModal() {
 | 
						|
      this.$refs.addMetricModal.hide();
 | 
						|
    },
 | 
						|
    onSidebarMutation() {
 | 
						|
      setTimeout(() => {
 | 
						|
        this.elWidth = this.$el.clientWidth;
 | 
						|
      }, sidebarAnimationDuration);
 | 
						|
    },
 | 
						|
    setFormValidity(isValid) {
 | 
						|
      this.formIsValid = isValid;
 | 
						|
    },
 | 
						|
    submitCustomMetricsForm() {
 | 
						|
      this.$refs.customMetricsForm.submit();
 | 
						|
    },
 | 
						|
    activeTimeWindow(key) {
 | 
						|
      return this.timeWindows[key] === this.selectedTimeWindow;
 | 
						|
    },
 | 
						|
    setTimeWindowParameter(key) {
 | 
						|
      return `?time_window=${key}`;
 | 
						|
    },
 | 
						|
  },
 | 
						|
  addMetric: {
 | 
						|
    title: s__('Metrics|Add metric'),
 | 
						|
    modalId: 'add-metric',
 | 
						|
  },
 | 
						|
};
 | 
						|
</script>
 | 
						|
 | 
						|
<template>
 | 
						|
  <div v-if="!showEmptyState" class="prometheus-graphs">
 | 
						|
    <div class="gl-p-3 border-bottom bg-gray-light d-flex justify-content-between">
 | 
						|
      <div
 | 
						|
        v-if="environmentsEndpoint"
 | 
						|
        class="dropdowns d-flex align-items-center justify-content-between"
 | 
						|
      >
 | 
						|
        <div class="d-flex align-items-center">
 | 
						|
          <strong>{{ s__('Metrics|Environment') }}</strong>
 | 
						|
          <gl-dropdown
 | 
						|
            class="prepend-left-10 js-environments-dropdown"
 | 
						|
            toggle-class="dropdown-menu-toggle"
 | 
						|
            :text="currentEnvironmentName"
 | 
						|
            :disabled="environments.length === 0"
 | 
						|
          >
 | 
						|
            <gl-dropdown-item
 | 
						|
              v-for="environment in environments"
 | 
						|
              :key="environment.id"
 | 
						|
              :active="environment.name === currentEnvironmentName"
 | 
						|
              active-class="is-active"
 | 
						|
              >{{ environment.name }}</gl-dropdown-item
 | 
						|
            >
 | 
						|
          </gl-dropdown>
 | 
						|
        </div>
 | 
						|
        <div v-if="showTimeWindowDropdown" class="d-flex align-items-center">
 | 
						|
          <strong>{{ s__('Metrics|Show last') }}</strong>
 | 
						|
          <gl-dropdown
 | 
						|
            class="prepend-left-10 js-time-window-dropdown"
 | 
						|
            toggle-class="dropdown-menu-toggle"
 | 
						|
            :text="selectedTimeWindow"
 | 
						|
          >
 | 
						|
            <gl-dropdown-item
 | 
						|
              v-for="(value, key) in timeWindows"
 | 
						|
              :key="key"
 | 
						|
              :active="activeTimeWindow(key)"
 | 
						|
              ><gl-link :href="setTimeWindowParameter(key)">{{ value }}</gl-link></gl-dropdown-item
 | 
						|
            >
 | 
						|
          </gl-dropdown>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
      <div class="d-flex">
 | 
						|
        <div v-if="isEE && canAddMetrics">
 | 
						|
          <gl-button
 | 
						|
            v-gl-modal-directive="$options.addMetric.modalId"
 | 
						|
            class="js-add-metric-button text-success border-success"
 | 
						|
          >
 | 
						|
            {{ $options.addMetric.title }}
 | 
						|
          </gl-button>
 | 
						|
          <gl-modal
 | 
						|
            ref="addMetricModal"
 | 
						|
            :modal-id="$options.addMetric.modalId"
 | 
						|
            :title="$options.addMetric.title"
 | 
						|
          >
 | 
						|
            <form ref="customMetricsForm" :action="customMetricsPath" method="post">
 | 
						|
              <custom-metrics-form-fields
 | 
						|
                :validate-query-path="validateQueryPath"
 | 
						|
                form-operation="post"
 | 
						|
                @formValidation="setFormValidity"
 | 
						|
              />
 | 
						|
            </form>
 | 
						|
            <div slot="modal-footer">
 | 
						|
              <gl-button @click="hideAddMetricModal">
 | 
						|
                {{ __('Cancel') }}
 | 
						|
              </gl-button>
 | 
						|
              <gl-button
 | 
						|
                :disabled="!formIsValid"
 | 
						|
                variant="success"
 | 
						|
                @click="submitCustomMetricsForm"
 | 
						|
              >
 | 
						|
                {{ __('Save changes') }}
 | 
						|
              </gl-button>
 | 
						|
            </div>
 | 
						|
          </gl-modal>
 | 
						|
        </div>
 | 
						|
        <gl-button
 | 
						|
          v-if="externalDashboardPath.length"
 | 
						|
          class="js-external-dashboard-link prepend-left-8"
 | 
						|
          variant="primary"
 | 
						|
          :href="externalDashboardPath"
 | 
						|
        >
 | 
						|
          {{ __('View full dashboard') }}
 | 
						|
          <icon name="external-link" />
 | 
						|
        </gl-button>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
    <graph-group
 | 
						|
      v-for="(groupData, index) in groups"
 | 
						|
      :key="index"
 | 
						|
      :name="groupData.group"
 | 
						|
      :show-panels="showPanels"
 | 
						|
    >
 | 
						|
      <monitor-area-chart
 | 
						|
        v-for="(graphData, graphIndex) in groupData.metrics"
 | 
						|
        :key="graphIndex"
 | 
						|
        :graph-data="graphData"
 | 
						|
        :deployment-data="deploymentData"
 | 
						|
        :thresholds="getGraphAlertValues(graphData.queries)"
 | 
						|
        :container-width="elWidth"
 | 
						|
        group-id="monitor-area-chart"
 | 
						|
      >
 | 
						|
        <alert-widget
 | 
						|
          v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData"
 | 
						|
          :alerts-endpoint="alertsEndpoint"
 | 
						|
          :relevant-queries="graphData.queries"
 | 
						|
          :alerts-to-manage="getGraphAlerts(graphData.queries)"
 | 
						|
          @setAlerts="setAlerts"
 | 
						|
        />
 | 
						|
      </monitor-area-chart>
 | 
						|
    </graph-group>
 | 
						|
  </div>
 | 
						|
  <empty-state
 | 
						|
    v-else
 | 
						|
    :selected-state="emptyState"
 | 
						|
    :documentation-path="documentationPath"
 | 
						|
    :settings-path="settingsPath"
 | 
						|
    :clusters-path="clustersPath"
 | 
						|
    :empty-getting-started-svg-path="emptyGettingStartedSvgPath"
 | 
						|
    :empty-loading-svg-path="emptyLoadingSvgPath"
 | 
						|
    :empty-no-data-svg-path="emptyNoDataSvgPath"
 | 
						|
    :empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
 | 
						|
  />
 | 
						|
</template>
 |