Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue')
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue392
1 files changed, 392 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
new file mode 100644
index 00000000000..bcea7ca654e
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
@@ -0,0 +1,392 @@
+<script>
+import {
+ GlAlert,
+ GlBadge,
+ GlIcon,
+ GlLink,
+ GlLoadingIcon,
+ GlSprintf,
+ GlTabs,
+ GlTab,
+ GlButton,
+ GlSafeHtmlDirective,
+} from '@gitlab/ui';
+import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
+import { fetchPolicies } from '~/lib/graphql';
+import { toggleContainerClasses } from '~/lib/utils/dom_utils';
+import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
+import { s__ } from '~/locale';
+import * as Sentry from '~/sentry/wrapper';
+import Tracking from '~/tracking';
+import initUserPopovers from '~/user_popovers';
+import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import { SEVERITY_LEVELS } from '../constants';
+import createIssueMutation from '../graphql/mutations/alert_issue_create.mutation.graphql';
+import toggleSidebarStatusMutation from '../graphql/mutations/alert_sidebar_status.mutation.graphql';
+import alertQuery from '../graphql/queries/alert_details.query.graphql';
+import sidebarStatusQuery from '../graphql/queries/alert_sidebar_status.query.graphql';
+import AlertMetrics from './alert_metrics.vue';
+import AlertSidebar from './alert_sidebar.vue';
+import AlertSummaryRow from './alert_summary_row.vue';
+import SystemNote from './system_notes/system_note.vue';
+
+const containerEl = document.querySelector('.page-with-contextual-sidebar');
+
+export default {
+ i18n: {
+ errorMsg: s__(
+ 'AlertManagement|There was an error displaying the alert. Please refresh the page to try again.',
+ ),
+ reportedAt: s__('AlertManagement|Reported %{when}'),
+ reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'),
+ },
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ severityLabels: SEVERITY_LEVELS,
+ tabsConfig: [
+ {
+ id: 'overview',
+ title: s__('AlertManagement|Alert details'),
+ },
+ {
+ id: 'metrics',
+ title: s__('AlertManagement|Metrics'),
+ },
+ {
+ id: 'activity',
+ title: s__('AlertManagement|Activity feed'),
+ },
+ ],
+ components: {
+ AlertDetailsTable,
+ AlertSummaryRow,
+ GlBadge,
+ GlAlert,
+ GlIcon,
+ GlLink,
+ GlLoadingIcon,
+ GlSprintf,
+ GlTab,
+ GlTabs,
+ GlButton,
+ TimeAgoTooltip,
+ AlertSidebar,
+ SystemNote,
+ AlertMetrics,
+ },
+ inject: {
+ projectPath: {
+ default: '',
+ },
+ alertId: {
+ default: '',
+ },
+ isThreatMonitoringPage: {
+ default: false,
+ },
+ projectId: {
+ default: '',
+ },
+ projectIssuesPath: {
+ default: '',
+ },
+ trackAlertsDetailsViewsOptions: {
+ default: null,
+ },
+ },
+ apollo: {
+ alert: {
+ fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
+ query: alertQuery,
+ variables() {
+ return {
+ fullPath: this.projectPath,
+ alertId: this.alertId,
+ };
+ },
+ update(data) {
+ return data?.project?.alertManagementAlerts?.nodes?.[0] ?? null;
+ },
+ error(error) {
+ this.errored = true;
+ Sentry.captureException(error);
+ },
+ },
+ sidebarStatus: {
+ query: sidebarStatusQuery,
+ },
+ },
+ data() {
+ return {
+ alert: null,
+ errored: false,
+ sidebarStatus: false,
+ isErrorDismissed: false,
+ createIncidentError: '',
+ incidentCreationInProgress: false,
+ sidebarErrorMessage: '',
+ };
+ },
+ computed: {
+ loading() {
+ return this.$apollo.queries.alert.loading;
+ },
+ reportedAtMessage() {
+ return this.alert?.monitoringTool
+ ? this.$options.i18n.reportedAtWithTool
+ : this.$options.i18n.reportedAt;
+ },
+ showErrorMsg() {
+ return this.errored && !this.isErrorDismissed;
+ },
+ activeTab() {
+ return this.$route.params.tabId || this.$options.tabsConfig[0].id;
+ },
+ currentTabIndex: {
+ get() {
+ return this.$options.tabsConfig.findIndex((tab) => tab.id === this.activeTab);
+ },
+ set(tabIdx) {
+ const tabId = this.$options.tabsConfig[tabIdx].id;
+ this.$router.replace({ name: 'tab', params: { tabId } });
+ },
+ },
+ environmentName() {
+ return this.alert?.environment?.name;
+ },
+ environmentPath() {
+ return this.alert?.environment?.path;
+ },
+ },
+ mounted() {
+ if (this.trackAlertsDetailsViewsOptions) {
+ this.trackPageViews();
+ }
+ toggleContainerClasses(containerEl, {
+ 'issuable-bulk-update-sidebar': true,
+ 'right-sidebar-expanded': true,
+ });
+ },
+ updated() {
+ this.$nextTick(() => {
+ highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'));
+ initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
+ });
+ },
+ methods: {
+ dismissError() {
+ this.isErrorDismissed = true;
+ this.sidebarErrorMessage = '';
+ },
+ toggleSidebar() {
+ this.$apollo.mutate({ mutation: toggleSidebarStatusMutation });
+ toggleContainerClasses(containerEl, {
+ 'right-sidebar-collapsed': !this.sidebarStatus,
+ 'right-sidebar-expanded': this.sidebarStatus,
+ });
+ },
+ handleAlertSidebarError(errorMessage) {
+ this.errored = true;
+ this.sidebarErrorMessage = errorMessage;
+ },
+ createIncident() {
+ this.incidentCreationInProgress = true;
+
+ this.$apollo
+ .mutate({
+ mutation: createIssueMutation,
+ variables: {
+ iid: this.alert.iid,
+ projectPath: this.projectPath,
+ },
+ })
+ .then(
+ ({
+ data: {
+ createAlertIssue: { errors, issue },
+ },
+ }) => {
+ if (errors?.length) {
+ [this.createIncidentError] = errors;
+ this.incidentCreationInProgress = false;
+ } else if (issue) {
+ visitUrl(this.incidentPath(issue.iid));
+ }
+ },
+ )
+ .catch((error) => {
+ this.createIncidentError = error;
+ this.incidentCreationInProgress = false;
+ });
+ },
+ incidentPath(issueId) {
+ return joinPaths(this.projectIssuesPath, issueId);
+ },
+ trackPageViews() {
+ const { category, action } = this.trackAlertsDetailsViewsOptions;
+ Tracking.event(category, action);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError">
+ <p v-safe-html="sidebarErrorMessage || $options.i18n.errorMsg"></p>
+ </gl-alert>
+ <gl-alert
+ v-if="createIncidentError"
+ variant="danger"
+ data-testid="incidentCreationError"
+ @dismiss="createIncidentError = null"
+ >
+ {{ createIncidentError }}
+ </gl-alert>
+ <div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div>
+ <div
+ v-if="alert"
+ class="alert-management-details gl-relative"
+ :class="{ 'pr-sm-8': sidebarStatus }"
+ >
+ <div
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-px-1 py-3 py-md-4 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-flex-direction-column gl-sm-flex-direction-row"
+ >
+ <div data-testid="alert-header">
+ <gl-badge class="gl-mr-3">
+ <strong>{{ s__('AlertManagement|Alert') }}</strong>
+ </gl-badge>
+ <span>
+ <gl-sprintf :message="reportedAtMessage">
+ <template #when>
+ <time-ago-tooltip :time="alert.createdAt" />
+ </template>
+ <template #tool>{{ alert.monitoringTool }}</template>
+ </gl-sprintf>
+ </span>
+ </div>
+ <gl-button
+ v-if="alert.issueIid"
+ class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-incident-button"
+ data-testid="viewIncidentBtn"
+ :href="incidentPath(alert.issueIid)"
+ category="primary"
+ variant="success"
+ >
+ {{ s__('AlertManagement|View incident') }}
+ </gl-button>
+ <gl-button
+ v-else
+ class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-incident-button"
+ data-testid="createIncidentBtn"
+ :loading="incidentCreationInProgress"
+ category="primary"
+ variant="success"
+ @click="createIncident()"
+ >
+ {{ s__('AlertManagement|Create incident') }}
+ </gl-button>
+ <gl-button
+ :aria-label="__('Toggle sidebar')"
+ category="primary"
+ variant="default"
+ class="d-sm-none gl-absolute toggle-sidebar-mobile-button"
+ type="button"
+ icon="chevron-double-lg-left"
+ @click="toggleSidebar"
+ />
+ </div>
+ <div
+ v-if="alert"
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-center"
+ >
+ <h2 data-testid="title">{{ alert.title }}</h2>
+ </div>
+ <gl-tabs v-if="alert" v-model="currentTabIndex" data-testid="alertDetailsTabs">
+ <gl-tab :data-testid="$options.tabsConfig[0].id" :title="$options.tabsConfig[0].title">
+ <alert-summary-row v-if="alert.severity" :label="`${s__('AlertManagement|Severity')}:`">
+ <span data-testid="severity">
+ <gl-icon
+ class="gl-vertical-align-middle"
+ :size="12"
+ :name="`severity-${alert.severity.toLowerCase()}`"
+ :class="`icon-${alert.severity.toLowerCase()}`"
+ />
+ {{ $options.severityLabels[alert.severity] }}
+ </span>
+ </alert-summary-row>
+ <alert-summary-row
+ v-if="environmentName"
+ :label="`${s__('AlertManagement|Environment')}:`"
+ >
+ <gl-link
+ v-if="environmentPath"
+ class="gl-display-inline-block"
+ data-testid="environmentPath"
+ :href="environmentPath"
+ >
+ {{ environmentName }}
+ </gl-link>
+ <span v-else data-testid="environmentName">{{ environmentName }}</span>
+ </alert-summary-row>
+ <alert-summary-row
+ v-if="alert.startedAt"
+ :label="`${s__('AlertManagement|Start time')}:`"
+ >
+ <time-ago-tooltip data-testid="startTimeItem" :time="alert.startedAt" />
+ </alert-summary-row>
+ <alert-summary-row
+ v-if="alert.eventCount"
+ :label="`${s__('AlertManagement|Events')}:`"
+ data-testid="eventCount"
+ >
+ {{ alert.eventCount }}
+ </alert-summary-row>
+ <alert-summary-row
+ v-if="alert.monitoringTool"
+ :label="`${s__('AlertManagement|Tool')}:`"
+ data-testid="monitoringTool"
+ >
+ {{ alert.monitoringTool }}
+ </alert-summary-row>
+ <alert-summary-row
+ v-if="alert.service"
+ :label="`${s__('AlertManagement|Service')}:`"
+ data-testid="service"
+ >
+ {{ alert.service }}
+ </alert-summary-row>
+ <alert-summary-row
+ v-if="alert.runbook"
+ :label="`${s__('AlertManagement|Runbook')}:`"
+ data-testid="runbook"
+ >
+ {{ alert.runbook }}
+ </alert-summary-row>
+ <alert-details-table :alert="alert" :loading="loading" />
+ </gl-tab>
+ <gl-tab
+ v-if="isThreatMonitoringPage"
+ :data-testid="$options.tabsConfig[1].id"
+ :title="$options.tabsConfig[1].title"
+ >
+ <alert-metrics :dashboard-url="alert.metricsDashboardUrl" />
+ </gl-tab>
+ <gl-tab :data-testid="$options.tabsConfig[2].id" :title="$options.tabsConfig[2].title">
+ <div v-if="alert.notes.nodes.length > 0" class="issuable-discussion">
+ <ul class="notes main-notes-list timeline">
+ <system-note v-for="note in alert.notes.nodes" :key="note.id" :note="note" />
+ </ul>
+ </div>
+ </gl-tab>
+ </gl-tabs>
+ <alert-sidebar
+ :alert="alert"
+ @toggle-sidebar="toggleSidebar"
+ @alert-error="handleAlertSidebarError"
+ />
+ </div>
+ </div>
+</template>