diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-11 18:09:37 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-11 18:09:37 +0300 |
commit | 6217d19741a8ea4351813b13c6fb39cc6a746602 (patch) | |
tree | 148b37b12e23835691de2a6dad4a425e678bc33d /app | |
parent | e3190840bc2e05ed04a49869978a54b7b518edf1 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
25 files changed, 207 insertions, 30 deletions
diff --git a/app/assets/javascripts/lib/utils/downloader.js b/app/assets/javascripts/lib/utils/downloader.js new file mode 100644 index 00000000000..2297f5f90ce --- /dev/null +++ b/app/assets/javascripts/lib/utils/downloader.js @@ -0,0 +1,20 @@ +/** + * Helper function to trigger a download. + * + * - If the `fileName` is `_blank` it will open the file in a new tab. + * - If `fileData` is provided, it will inline the content and use data URLs to + * download the file. In this case the `url` property will be ignored. Please + * note that `fileData` needs to be Base64 encoded. + */ +export default ({ fileName, url, fileData }) => { + let href = url; + + if (fileData) { + href = `data:text/plain;base64,${fileData}`; + } + + const anchor = document.createElement('a'); + anchor.download = fileName; + anchor.href = href; + anchor.click(); +}; diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index e2e950f7790..2b5a10907f5 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -30,6 +30,7 @@ import GraphGroup from './graph_group.vue'; import EmptyState from './empty_state.vue'; import GroupEmptyState from './group_empty_state.vue'; import DashboardsDropdown from './dashboards_dropdown.vue'; +import VariablesSection from './variables_section.vue'; import TrackEventDirective from '~/vue_shared/directives/track_event'; import { @@ -64,6 +65,8 @@ export default { EmptyState, GroupEmptyState, DashboardsDropdown, + + VariablesSection, }, directives: { GlModal: GlModalDirective, @@ -222,6 +225,7 @@ export default { 'allDashboards', 'environmentsLoading', 'expandedPanel', + 'promVariables', ]), ...mapGetters('monitoringDashboard', ['getMetricStates', 'filteredEnvironments']), firstDashboard() { @@ -243,6 +247,9 @@ export default { shouldShowEnvironmentsDropdownNoMatchedMsg() { return !this.environmentsLoading && this.filteredEnvironments.length === 0; }, + shouldShowVariablesSection() { + return Object.keys(this.promVariables).length > 0; + }, }, watch: { dashboard(newDashboard) { @@ -584,7 +591,7 @@ export default { </div> </div> </div> - + <variables-section v-if="shouldShowVariablesSection && !showEmptyState" /> <div v-if="!showEmptyState"> <dashboard-panel v-show="expandedPanel.panel" diff --git a/app/assets/javascripts/monitoring/components/dashboard_panel.vue b/app/assets/javascripts/monitoring/components/dashboard_panel.vue index 64e79f86dce..48825fda5c8 100644 --- a/app/assets/javascripts/monitoring/components/dashboard_panel.vue +++ b/app/assets/javascripts/monitoring/components/dashboard_panel.vue @@ -313,7 +313,12 @@ export default { <template slot="button-content"> <gl-icon name="ellipsis_v" class="text-secondary" /> </template> - <gl-dropdown-item v-if="expandBtnAvailable" ref="expandBtn" @click="onExpand"> + <gl-dropdown-item + v-if="expandBtnAvailable" + ref="expandBtn" + :href="clipboardText" + @click.prevent="onExpand" + > {{ s__('Metrics|Expand panel') }} </gl-dropdown-item> <gl-dropdown-item diff --git a/app/assets/javascripts/monitoring/components/variables_section.vue b/app/assets/javascripts/monitoring/components/variables_section.vue new file mode 100644 index 00000000000..a67bc62e196 --- /dev/null +++ b/app/assets/javascripts/monitoring/components/variables_section.vue @@ -0,0 +1,48 @@ +<script> +import { GlFormGroup, GlFormInput } from '@gitlab/ui'; +import { mapState, mapActions } from 'vuex'; +import { mergeUrlParams, updateHistory } from '~/lib/utils/url_utility'; + +export default { + components: { + GlFormGroup, + GlFormInput, + }, + computed: { + ...mapState('monitoringDashboard', ['promVariables']), + }, + methods: { + ...mapActions('monitoringDashboard', ['fetchDashboardData', 'setVariableData']), + refreshDashboard(event) { + const { name, value } = event.target; + + if (this.promVariables[name] !== value) { + const changedVariable = { [name]: value }; + + this.setVariableData(changedVariable); + + updateHistory({ + url: mergeUrlParams(this.promVariables, window.location.href), + title: document.title, + }); + + this.fetchDashboardData(); + } + }, + }, +}; +</script> +<template> + <div ref="variablesSection" class="d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 variables-section"> + <div v-for="(val, key) in promVariables" :key="key" class="mb-1 pr-2 d-flex d-sm-block"> + <gl-form-group :label="key" class="mb-0 flex-grow-1"> + <gl-form-input + :value="val" + :name="key" + @keyup.native.enter="refreshDashboard" + @blur.native="refreshDashboard" + /> + </gl-form-group> + </div> + </div> +</template> diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 9d18629bf34..0134378868b 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -222,14 +222,17 @@ export const fetchDashboardData = ({ state, dispatch, getters }) => { * * @param {metric} metric */ -export const fetchPrometheusMetric = ({ commit, state }, { metric, defaultQueryParams }) => { +export const fetchPrometheusMetric = ( + { commit, state, getters }, + { metric, defaultQueryParams }, +) => { const queryParams = { ...defaultQueryParams }; if (metric.step) { queryParams.step = metric.step; } - if (state.promVariables.length > 0) { - queryParams.variables = state.promVariables; + if (Object.keys(state.promVariables).length > 0) { + queryParams.variables = getters.getCustomVariablesArray; } commit(types.REQUEST_METRIC_RESULT, { metricId: metric.metricId }); @@ -390,5 +393,11 @@ export const duplicateSystemDashboard = ({ state }, payload) => { }); }; +// Variables manipulation + +export const setVariableData = ({ commit }, updatedVariable) => { + commit(types.UPDATE_VARIABLE_DATA, updatedVariable); +}; + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/monitoring/stores/getters.js b/app/assets/javascripts/monitoring/stores/getters.js index a6d80c5063e..1cadc287204 100644 --- a/app/assets/javascripts/monitoring/stores/getters.js +++ b/app/assets/javascripts/monitoring/stores/getters.js @@ -96,5 +96,17 @@ export const filteredEnvironments = state => env.name.toLowerCase().includes((state.environmentsSearchTerm || '').trim().toLowerCase()), ); +/** + * Maps an variables object to an array + * @param {Object} variables - Custom variables provided by the user + * @returns {Array} The custom variables array to be send to the API + * in the format of [variable1, variable1_value] + */ + +export const getCustomVariablesArray = state => + Object.entries(state.promVariables) + .flat() + .map(encodeURIComponent); + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js index ebe89e93ede..2fd0efa4ab7 100644 --- a/app/assets/javascripts/monitoring/stores/mutation_types.js +++ b/app/assets/javascripts/monitoring/stores/mutation_types.js @@ -3,6 +3,7 @@ export const REQUEST_METRICS_DASHBOARD = 'REQUEST_METRICS_DASHBOARD'; export const RECEIVE_METRICS_DASHBOARD_SUCCESS = 'RECEIVE_METRICS_DASHBOARD_SUCCESS'; export const RECEIVE_METRICS_DASHBOARD_FAILURE = 'RECEIVE_METRICS_DASHBOARD_FAILURE'; export const SET_PROM_QUERY_VARIABLES = 'SET_PROM_QUERY_VARIABLES'; +export const UPDATE_VARIABLE_DATA = 'UPDATE_VARIABLE_DATA'; // Annotations export const RECEIVE_ANNOTATIONS_SUCCESS = 'RECEIVE_ANNOTATIONS_SUCCESS'; diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index c4c15993aa0..8de1430302a 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -1,4 +1,4 @@ -import pick from 'lodash/pick'; +import { pick } from 'lodash'; import * as types from './mutation_types'; import { mapToDashboardViewModel, normalizeQueryResult } from './utils'; import { BACKOFF_TIMEOUT } from '../../lib/utils/common_utils'; @@ -51,18 +51,6 @@ const emptyStateFromError = error => { return metricStates.UNKNOWN_ERROR; }; -/** - * Maps an variables object to an array - * @returns {Array} The custom variables array to be send to the API - * in the format of [variable1, variable1_value] - * @param {Object} variables - Custom variables provided by the user - */ - -const transformVariablesObjectArray = variables => - Object.entries(variables) - .flat() - .map(encodeURIComponent); - export default { /** * Dashboard panels structure and global state @@ -182,6 +170,12 @@ export default { state.expandedPanel.panel = panel; }, [types.SET_PROM_QUERY_VARIABLES](state, variables) { - state.promVariables = transformVariablesObjectArray(variables); + state.promVariables = variables; + }, + [types.UPDATE_VARIABLE_DATA](state, newVariable) { + Object.assign(state.promVariables, { + ...state.promVariables, + ...newVariable, + }); }, }; diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js index 3a63d6279f4..f1b2baf0f74 100644 --- a/app/assets/javascripts/monitoring/stores/state.js +++ b/app/assets/javascripts/monitoring/stores/state.js @@ -33,7 +33,7 @@ export default () => ({ panel: null, }, allDashboards: [], - promVariables: [], + promVariables: {}, // Other project data annotations: [], diff --git a/app/assets/stylesheets/pages/prometheus.scss b/app/assets/stylesheets/pages/prometheus.scss index f61245bed24..d86bf92eac4 100644 --- a/app/assets/stylesheets/pages/prometheus.scss +++ b/app/assets/stylesheets/pages/prometheus.scss @@ -13,6 +13,14 @@ .form-group { margin-bottom: map-get($spacing-scale, 3); } + + .variables-section { + input { + @include media-breakpoint-up(sm) { + width: 160px; + } + } + } } .draggable { diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index da39d64c93d..dcd80f4032e 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -75,4 +75,9 @@ class JwtController < ApplicationController Array(Rack::Utils.parse_query(request.query_string)['scope']) end + + def auth_user + actor = @authentication_result&.actor + actor.is_a?(User) ? actor : nil + end end diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb index a48dadc7fdb..eeb6b5f8491 100644 --- a/app/finders/alert_management/alerts_finder.rb +++ b/app/finders/alert_management/alerts_finder.rb @@ -12,6 +12,7 @@ module AlertManagement return AlertManagement::Alert.none unless authorized? collection = project.alert_management_alerts + collection = by_status(collection) collection = by_iid(collection) sort(collection) end @@ -26,6 +27,12 @@ module AlertManagement collection.for_iid(params[:iid]) end + def by_status(collection) + values = AlertManagement::Alert::STATUSES.values & Array(params[:status]) + + values.present? ? collection.for_status(values) : collection + end + def sort(collection) params[:sort] ? collection.sort_by_attribute(params[:sort]) : collection end diff --git a/app/graphql/resolvers/alert_management_alert_resolver.rb b/app/graphql/resolvers/alert_management_alert_resolver.rb index 6c8f64cc62b..d2f82ece281 100644 --- a/app/graphql/resolvers/alert_management_alert_resolver.rb +++ b/app/graphql/resolvers/alert_management_alert_resolver.rb @@ -6,6 +6,11 @@ module Resolvers required: false, description: 'IID of the alert. For example, "1"' + argument :statuses, [Types::AlertManagement::StatusEnum], + as: :status, + required: false, + description: 'Alerts with the specified statues. For example, [TRIGGERED]' + argument :sort, Types::AlertManagement::AlertSortEnum, description: 'Sort alerts by this criteria', required: false diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb index 6bbdc1645ca..e1a6b507906 100644 --- a/app/models/alert_management/alert.rb +++ b/app/models/alert_management/alert.rb @@ -93,6 +93,7 @@ module AlertManagement end scope :for_iid, -> (iid) { where(iid: iid) } + scope :for_status, -> (status) { where(status: status) } scope :for_fingerprint, -> (project, fingerprint) { where(project: project, fingerprint: fingerprint) } scope :order_start_time, -> (sort_order) { order(started_at: sort_order) } diff --git a/app/models/concerns/state_eventable.rb b/app/models/concerns/state_eventable.rb new file mode 100644 index 00000000000..68129798543 --- /dev/null +++ b/app/models/concerns/state_eventable.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module StateEventable + extend ActiveSupport::Concern + + included do + has_many :resource_state_events + end +end diff --git a/app/models/issue.rb b/app/models/issue.rb index 82643d8f5d6..90443cba81b 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -17,6 +17,7 @@ class Issue < ApplicationRecord include IgnorableColumns include MilestoneEventable include WhereComposite + include StateEventable DueDateStruct = Struct.new(:title, :name).freeze NoDueDate = DueDateStruct.new('No Due Date', '0').freeze diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c962f8c8c26..e24384156c9 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -19,6 +19,7 @@ class MergeRequest < ApplicationRecord include ShaAttribute include IgnorableColumns include MilestoneEventable + include StateEventable sha_attribute :squash_commit_sha diff --git a/app/models/resource_state_event.rb b/app/models/resource_state_event.rb new file mode 100644 index 00000000000..1d6573b180f --- /dev/null +++ b/app/models/resource_state_event.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class ResourceStateEvent < ResourceEvent + include IssueResourceEvent + include MergeRequestResourceEvent + + validate :exactly_one_issuable + + # state is used for issue and merge request states. + enum state: Issue.available_states.merge(MergeRequest.available_states).merge(reopened: 5) + + def self.issuable_attrs + %i(issue merge_request).freeze + end +end diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb index 78d3fb2e4d2..a78e191c85f 100644 --- a/app/services/issuable/clone/attributes_rewriter.rb +++ b/app/services/issuable/clone/attributes_rewriter.rb @@ -20,6 +20,7 @@ module Issuable copy_resource_label_events copy_resource_weight_events copy_resource_milestone_events + copy_resource_state_events end private @@ -47,8 +48,6 @@ module Issuable end def copy_resource_label_events - entity_key = new_entity.class.name.underscore.foreign_key - copy_events(ResourceLabelEvent.table_name, original_entity.resource_label_events) do |event| event.attributes .except('id', 'reference', 'reference_html') @@ -80,9 +79,18 @@ module Issuable end end - def event_attributes_with_milestone(event, milestone) - entity_key = new_entity.class.name.underscore.foreign_key + def copy_resource_state_events + return unless state_events_supported? + + copy_events(ResourceStateEvent.table_name, original_entity.resource_state_events) do |event| + event.attributes + .except('id') + .merge(entity_key => new_entity.id, + 'state' => ResourceStateEvent.states[event.state]) + end + end + def event_attributes_with_milestone(event, milestone) event.attributes .except('id') .merge(entity_key => new_entity.id, @@ -102,12 +110,20 @@ module Issuable end def entity_key - new_entity.class.name.parameterize('_').foreign_key + new_entity.class.name.underscore.foreign_key end def milestone_events_supported? - original_entity.respond_to?(:resource_milestone_events) && - new_entity.respond_to?(:resource_milestone_events) + both_respond_to?(:resource_milestone_events) + end + + def state_events_supported? + both_respond_to?(:resource_state_events) + end + + def both_respond_to?(method) + original_entity.respond_to?(method) && + new_entity.respond_to?(method) end end end diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index e55697eebcc..a2167be7949 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -22,6 +22,8 @@ module Projects import_data + after_execute_hook + success rescue Gitlab::UrlBlocker::BlockedUrlError => e Gitlab::ErrorTracking.track_exception(e, project_path: project.full_path, importer: project.import_type) @@ -37,6 +39,10 @@ module Projects private + def after_execute_hook + # Defined in EE::Projects::ImportService + end + def add_repository_to_project if project.external_import? && !unknown_url? begin @@ -131,3 +137,5 @@ module Projects end end end + +Projects::ImportService.prepend_if_ee('EE::Projects::ImportService') diff --git a/app/views/groups/_flash_messages.html.haml b/app/views/groups/_flash_messages.html.haml new file mode 100644 index 00000000000..fa1a9d2cca4 --- /dev/null +++ b/app/views/groups/_flash_messages.html.haml @@ -0,0 +1,2 @@ += content_for :flash_message do + = render_if_exists 'shared/shared_runners_minutes_limit', namespace: @group, classes: [container_class, ("limit-container-width" unless fluid_layout)] diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index e1cda7dbacd..032766327ca 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -8,6 +8,8 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity") += render partial: 'flash_messages' + %div{ class: [("limit-container-width" unless fluid_layout)] } = render_if_exists 'trials/banner', namespace: @group diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index b9327c4573f..752ebcc6add 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1150,7 +1150,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: true - :name: migrate_external_diffs :feature_category: :source_code_management :has_external_dependencies: diff --git a/app/workers/incident_management/process_alert_worker.rb b/app/workers/incident_management/process_alert_worker.rb index e63bcc4cb08..2ce9fe359b5 100644 --- a/app/workers/incident_management/process_alert_worker.rb +++ b/app/workers/incident_management/process_alert_worker.rb @@ -35,7 +35,7 @@ module IncidentManagement return if alert.update(issue_id: issue_id) - Gitlab::GitLogger.warn( + Gitlab::AppLogger.warn( message: 'Cannot link an Issue with Alert', issue_id: issue_id, alert_id: alert_id, diff --git a/app/workers/merge_request_mergeability_check_worker.rb b/app/workers/merge_request_mergeability_check_worker.rb index a26c1a886f6..1a84efb4e52 100644 --- a/app/workers/merge_request_mergeability_check_worker.rb +++ b/app/workers/merge_request_mergeability_check_worker.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -class MergeRequestMergeabilityCheckWorker # rubocop:disable Scalability/IdempotentWorker +class MergeRequestMergeabilityCheckWorker include ApplicationWorker feature_category :source_code_management + idempotent! def perform(merge_request_id) merge_request = MergeRequest.find_by_id(merge_request_id) |