diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-14 12:09:26 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-14 12:09:26 +0000 |
commit | 2e4c4055181eec9186458dd5dd3219c937032ec7 (patch) | |
tree | eee59e7124ffcf093f1b53369436c69cfe4d9cc5 /app | |
parent | 97f0ae7454597105a27df65ffb772949d9d4f3cb (diff) | |
download | gitlab-ce-2e4c4055181eec9186458dd5dd3219c937032ec7.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
13 files changed, 76 insertions, 83 deletions
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 9db29f327da..15b17f01daf 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -19,12 +19,7 @@ import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue'; import { s__ } from '~/locale'; import createFlash from '~/flash'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { - mergeUrlParams, - redirectTo, - refreshCurrentPage, - updateHistory, -} from '~/lib/utils/url_utility'; +import { mergeUrlParams, redirectTo, updateHistory } from '~/lib/utils/url_utility'; import invalidUrl from '~/lib/utils/invalid_url'; import Icon from '~/vue_shared/components/icon.vue'; import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue'; @@ -273,6 +268,7 @@ export default { ...mapActions('monitoringDashboard', [ 'setTimeRange', 'fetchData', + 'fetchDashboardData', 'setGettingStartedEmptyState', 'setInitialState', 'setPanelGroupMetrics', @@ -360,7 +356,7 @@ export default { }, refreshDashboard() { - refreshCurrentPage(); + this.fetchDashboardData(); }, onTimeRangeZoom({ start, end }) { @@ -475,7 +471,7 @@ export default { ref="refreshDashboardBtn" v-gl-tooltip variant="default" - :title="s__('Metrics|Reload this page')" + :title="s__('Metrics|Refresh dashboard')" @click="refreshDashboard" > <icon name="retry" /> diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue index 44e38089da8..d1394bca447 100644 --- a/app/assets/javascripts/monitoring/components/panel_type.vue +++ b/app/assets/javascripts/monitoring/components/panel_type.vue @@ -4,6 +4,7 @@ import { pickBy } from 'lodash'; import invalidUrl from '~/lib/utils/invalid_url'; import { GlResizeObserverDirective, + GlLoadingIcon, GlDropdown, GlDropdownItem, GlModal, @@ -37,6 +38,7 @@ export default { MonitorStackedColumnChart, MonitorEmptyChart, Icon, + GlLoadingIcon, GlTooltip, GlDropdown, GlDropdownItem, @@ -104,13 +106,17 @@ export default { // This method is extended by ee functionality return false; }, - graphDataHasMetrics() { + graphDataHasResult() { return ( this.graphData.metrics && this.graphData.metrics[0].result && this.graphData.metrics[0].result.length > 0 ); }, + graphDataIsLoading() { + const { metrics = [] } = this.graphData; + return metrics.some(({ loading }) => loading); + }, logsPathWithTimeRange() { const timeRange = this.zoomedTimeRange || this.timeRange; @@ -140,7 +146,7 @@ export default { }, isContextualMenuShown() { return ( - this.graphDataHasMetrics && + this.graphDataHasResult && !this.isPanelType('single-stat') && !this.isPanelType('heatmap') && !this.isPanelType('column') && @@ -193,7 +199,7 @@ export default { </script> <template> <div v-gl-resize-observer="onResize" class="prometheus-graph"> - <div class="prometheus-graph-header"> + <div class="d-flex align-items-center mr-3"> <h5 ref="graphTitle" class="prometheus-graph-title gl-font-size-large font-weight-bold text-truncate append-right-8" @@ -203,23 +209,27 @@ export default { <gl-tooltip :target="() => $refs.graphTitle" :disabled="!showTitleTooltip"> {{ title }} </gl-tooltip> + <alert-widget + v-if="isContextualMenuShown && alertWidgetAvailable" + class="mx-1" + :modal-id="`alert-modal-${index}`" + :alerts-endpoint="alertsEndpoint" + :relevant-queries="graphData.metrics" + :alerts-to-manage="getGraphAlerts(graphData.metrics)" + @setAlerts="setAlerts" + /> + <div class="flex-grow-1"></div> + <div v-if="graphDataIsLoading" class="mx-1 mt-1"> + <gl-loading-icon /> + </div> <div v-if="isContextualMenuShown" - class="prometheus-graph-widgets js-graph-widgets flex-fill" + class="js-graph-widgets" data-qa-selector="prometheus_graph_widgets" > <div class="d-flex align-items-center"> - <alert-widget - v-if="alertWidgetAvailable" - :modal-id="`alert-modal-${index}`" - :alerts-endpoint="alertsEndpoint" - :relevant-queries="graphData.metrics" - :alerts-to-manage="getGraphAlerts(graphData.metrics)" - @setAlerts="setAlerts" - /> <gl-dropdown v-gl-tooltip - class="ml-auto mx-3" toggle-class="btn btn-transparent border-0" data-qa-selector="prometheus_widgets_dropdown" right @@ -275,28 +285,28 @@ export default { </div> <monitor-single-stat-chart - v-if="isPanelType('single-stat') && graphDataHasMetrics" + v-if="isPanelType('single-stat') && graphDataHasResult" :graph-data="graphData" /> <monitor-heatmap-chart - v-else-if="isPanelType('heatmap') && graphDataHasMetrics" + v-else-if="isPanelType('heatmap') && graphDataHasResult" :graph-data="graphData" /> <monitor-bar-chart - v-else-if="isPanelType('bar') && graphDataHasMetrics" + v-else-if="isPanelType('bar') && graphDataHasResult" :graph-data="graphData" /> <monitor-column-chart - v-else-if="isPanelType('column') && graphDataHasMetrics" + v-else-if="isPanelType('column') && graphDataHasResult" :graph-data="graphData" /> <monitor-stacked-column-chart - v-else-if="isPanelType('stacked-column') && graphDataHasMetrics" + v-else-if="isPanelType('stacked-column') && graphDataHasResult" :graph-data="graphData" /> <component :is="timeChartComponent" - v-else-if="graphDataHasMetrics" + v-else-if="graphDataHasResult" ref="timeChart" :graph-data="graphData" :deployment-data="deploymentData" diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js index 6af1d399cfc..8d821c27099 100644 --- a/app/assets/javascripts/monitoring/constants.js +++ b/app/assets/javascripts/monitoring/constants.js @@ -10,7 +10,10 @@ export const metricStates = { OK: 'OK', /** - * Metric data is being fetched + * Metric data is being fetched for the first time. + * + * Not used during data refresh, if data is available in + * the metric, the recommneded state is OK. */ LOADING: 'LOADING', diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 8427a72a68e..06b99f572e7 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -128,7 +128,7 @@ export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response commit(types.RECEIVE_METRICS_DASHBOARD_SUCCESS, dashboard); commit(types.SET_ENDPOINTS, convertObjectPropsToCamelCase(metrics_data)); - return dispatch('fetchPrometheusMetrics'); + return dispatch('fetchDashboardData'); }; export const receiveMetricsDashboardFailure = ({ commit }, error) => { commit(types.RECEIVE_METRICS_DASHBOARD_FAILURE, error); @@ -140,7 +140,7 @@ export const receiveMetricsDashboardFailure = ({ commit }, error) => { * Loads timeseries data: Prometheus data points and deployment data from the project * @param {Object} Vuex store */ -export const fetchPrometheusMetrics = ({ state, dispatch, getters }) => { +export const fetchDashboardData = ({ state, dispatch, getters }) => { dispatch('fetchDeploymentsData'); if (!state.timeRange) { diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index 0a7bb47d533..38c1524d904 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -1,4 +1,3 @@ -import Vue from 'vue'; import pick from 'lodash/pick'; import * as types from './mutation_types'; import { mapToDashboardViewModel, normalizeQueryResult } from './utils'; @@ -27,24 +26,6 @@ const findMetricInDashboard = (metricId, dashboard) => { }; /** - * Set a new state for a metric. - * - * Initally metric data is not populated, so `Vue.set` is - * used to add new properties to the metric. - * - * @param {Object} metric - Metric object as defined in the dashboard - * @param {Object} state - New state - * @param {Array|null} state.result - Array of results - * @param {String} state.error - Error code from metricStates - * @param {Boolean} state.loading - True if the metric is loading - */ -const setMetricState = (metric, { result = null, loading = false, state = null }) => { - Vue.set(metric, 'result', result); - Vue.set(metric, 'loading', loading); - Vue.set(metric, 'state', state); -}; - -/** * Maps a backened error state to a `metricStates` constant * @param {Object} error - Error from backend response */ @@ -116,39 +97,32 @@ export default { */ [types.REQUEST_METRIC_RESULT](state, { metricId }) { const metric = findMetricInDashboard(metricId, state.dashboard); - setMetricState(metric, { - loading: true, - state: metricStates.LOADING, - }); + metric.loading = true; + if (!metric.result) { + metric.state = metricStates.LOADING; + } }, [types.RECEIVE_METRIC_RESULT_SUCCESS](state, { metricId, result }) { - if (!metricId) { - return; - } - + const metric = findMetricInDashboard(metricId, state.dashboard); + metric.loading = false; state.showEmptyState = false; - const metric = findMetricInDashboard(metricId, state.dashboard); if (!result || result.length === 0) { - setMetricState(metric, { - state: metricStates.NO_DATA, - }); + metric.state = metricStates.NO_DATA; + metric.result = null; } else { const normalizedResults = result.map(normalizeQueryResult); - setMetricState(metric, { - result: Object.freeze(normalizedResults), - state: metricStates.OK, - }); + + metric.state = metricStates.OK; + metric.result = Object.freeze(normalizedResults); } }, [types.RECEIVE_METRIC_RESULT_FAILURE](state, { metricId, error }) { - if (!metricId) { - return; - } const metric = findMetricInDashboard(metricId, state.dashboard); - setMetricState(metric, { - state: emptyStateFromError(error), - }); + + metric.state = emptyStateFromError(error); + metric.loading = false; + metric.result = null; }, [types.SET_INITIAL_STATE](state, initialState = {}) { Object.assign(state, pick(initialState, initialStateKeys)); diff --git a/app/assets/javascripts/monitoring/stores/utils.js b/app/assets/javascripts/monitoring/stores/utils.js index d01acdd031b..a212e9be703 100644 --- a/app/assets/javascripts/monitoring/stores/utils.js +++ b/app/assets/javascripts/monitoring/stores/utils.js @@ -76,6 +76,12 @@ const mapToMetricsViewModel = metrics => queryRange: query_range, prometheusEndpointPath: prometheus_endpoint_path, metricId: uniqMetricsId({ metric_id, id }), + + // metric data + loading: false, + result: null, + state: null, + ...metric, })); diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index a58a040fb4e..cd5cfc09ea0 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -1,5 +1,4 @@ <script> -/* eslint-disable @gitlab/vue-require-i18n-strings */ import { mapState, mapActions } from 'vuex'; import { GlSkeletonLoading } from '@gitlab/ui'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; @@ -96,7 +95,7 @@ export default { <td class="old_line diff-line-num"></td> <td class="new_line diff-line-num"></td> <td v-if="error" class="js-error-lazy-load-diff diff-loading-error-block"> - {{ error }} Unable to load the diff + {{ __('Unable to load the diff') }} <button class="btn-link btn-link-retry btn-no-padding js-toggle-lazy-diff-retry-button" @click="fetchDiff" diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue index 65c1f125b55..be7f27f210d 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue @@ -74,7 +74,7 @@ export default { <div class="table-section section-20 section-wrap"> <div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div> - <div class="table-mobile-content">{{ testCase.name }}</div> + <div class="table-mobile-content pr-md-1 text-truncate">{{ testCase.name }}</div> </div> <div class="table-section section-10 section-wrap"> diff --git a/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue b/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue new file mode 100644 index 00000000000..74f988476e3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue @@ -0,0 +1,7 @@ +<template functional> + <footer class="form-actions d-flex justify-content-between"> + <div><slot name="prepend"></slot></div> + <div><slot></slot></div> + <div><slot name="append"></slot></div> + </footer> +</template> diff --git a/app/assets/stylesheets/pages/prometheus.scss b/app/assets/stylesheets/pages/prometheus.scss index 6e5daef3e7d..af0afa9cc3b 100644 --- a/app/assets/stylesheets/pages/prometheus.scss +++ b/app/assets/stylesheets/pages/prometheus.scss @@ -84,13 +84,6 @@ border-radius: $border-radius-default; } -.prometheus-graph-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: $gl-padding-8; -} - .alert-current-setting { max-width: 240px; diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb index ca427928d85..26d9b4b223f 100644 --- a/app/controllers/projects/import/jira_controller.rb +++ b/app/controllers/projects/import/jira_controller.rb @@ -3,14 +3,18 @@ module Projects module Import class JiraController < Projects::ApplicationController + before_action :authenticate_user! + before_action :check_issues_available! + before_action :authorize_read_project! before_action :jira_import_enabled? before_action :jira_integration_configured? + before_action :authorize_admin_project!, only: [:import] def show @is_jira_configured = @project.jira_service.present? return if Feature.enabled?(:jira_issue_import_vue, @project) - unless @project.latest_jira_import&.in_progress? + if !@project.latest_jira_import&.in_progress? && current_user&.can?(:admin_project, @project) jira_client = @project.jira_service.client jira_projects = jira_client.Project.all diff --git a/app/graphql/resolvers/projects/jira_imports_resolver.rb b/app/graphql/resolvers/projects/jira_imports_resolver.rb index b0784b3cdf7..25361c068d9 100644 --- a/app/graphql/resolvers/projects/jira_imports_resolver.rb +++ b/app/graphql/resolvers/projects/jira_imports_resolver.rb @@ -16,7 +16,7 @@ module Resolvers def authorized_resource?(project) return false unless project.jira_issues_import_feature_flag_enabled? - Ability.allowed?(context[:current_user], :admin_project, project) + context[:current_user].present? && Ability.allowed?(context[:current_user], :read_project, project) end end end diff --git a/app/services/jira_import/start_import_service.rb b/app/services/jira_import/start_import_service.rb index 134cef089e7..6accc3cfffc 100644 --- a/app/services/jira_import/start_import_service.rb +++ b/app/services/jira_import/start_import_service.rb @@ -46,6 +46,7 @@ module JiraImport def validate return build_error_response(_('Jira import feature is disabled.')) unless project.jira_issues_import_feature_flag_enabled? return build_error_response(_('You do not have permissions to run the import.')) unless user.can?(:admin_project, project) + return build_error_response(_('Cannot import because issues are not available in this project.')) unless project.feature_available?(:issues, user) return build_error_response(_('Jira integration not configured.')) unless project.jira_service&.active? return build_error_response(_('Unable to find Jira project to import data from.')) if jira_project_key.blank? return build_error_response(_('Jira import is already running.')) if import_in_progress? |