Commit ed265429 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 082c051b2db8
...@@ -11,18 +11,19 @@ import { ...@@ -11,18 +11,19 @@ import {
GlModalDirective, GlModalDirective,
GlTooltipDirective, GlTooltipDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { getParameterValues, mergeUrlParams, redirectTo } from '~/lib/utils/url_utility'; import { getParameterValues, mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url'; import invalidUrl from '~/lib/utils/invalid_url';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import DateTimePicker from './date_time_picker/date_time_picker.vue'; import DateTimePicker from './date_time_picker/date_time_picker.vue';
import MonitorTimeSeriesChart from './charts/time_series.vue'; import MonitorTimeSeriesChart from './charts/time_series.vue';
import MonitorSingleStatChart from './charts/single_stat.vue'; import MonitorSingleStatChart from './charts/single_stat.vue';
import GraphGroup from './graph_group.vue'; import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import { getTimeDiff, isValidDate } from '../utils'; import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getTimeDiff, isValidDate, getAddMetricTrackingOptions } from '../utils';
export default { export default {
components: { components: {
...@@ -43,6 +44,7 @@ export default { ...@@ -43,6 +44,7 @@ export default {
directives: { directives: {
GlModal: GlModalDirective, GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
TrackEvent: TrackEventDirective,
}, },
props: { props: {
externalDashboardUrl: { externalDashboardUrl: {
...@@ -298,6 +300,7 @@ export default { ...@@ -298,6 +300,7 @@ export default {
onDateTimePickerApply(timeWindowUrlParams) { onDateTimePickerApply(timeWindowUrlParams) {
return redirectTo(mergeUrlParams(timeWindowUrlParams, window.location.href)); return redirectTo(mergeUrlParams(timeWindowUrlParams, window.location.href));
}, },
getAddMetricTrackingOptions,
}, },
addMetric: { addMetric: {
title: s__('Metrics|Add metric'), title: s__('Metrics|Add metric'),
...@@ -389,9 +392,10 @@ export default { ...@@ -389,9 +392,10 @@ export default {
</gl-button> </gl-button>
<gl-button <gl-button
v-if="addingMetricsAvailable" v-if="addingMetricsAvailable"
ref="addMetricBtn"
v-gl-modal="$options.addMetric.modalId" v-gl-modal="$options.addMetric.modalId"
variant="outline-success" variant="outline-success"
class="mr-2 mt-1 js-add-metric-button" class="mr-2 mt-1"
> >
{{ $options.addMetric.title }} {{ $options.addMetric.title }}
</gl-button> </gl-button>
...@@ -411,6 +415,8 @@ export default { ...@@ -411,6 +415,8 @@ export default {
<div slot="modal-footer"> <div slot="modal-footer">
<gl-button @click="hideAddMetricModal">{{ __('Cancel') }}</gl-button> <gl-button @click="hideAddMetricModal">{{ __('Cancel') }}</gl-button>
<gl-button <gl-button
ref="submitCustomMetricsFormBtn"
v-track-event="getAddMetricTrackingOptions()"
:disabled="!formIsValid" :disabled="!formIsValid"
variant="success" variant="success"
@click="submitCustomMetricsForm" @click="submitCustomMetricsForm"
......
...@@ -115,6 +115,7 @@ export const generateLinkToChartOptions = chartLink => { ...@@ -115,6 +115,7 @@ export const generateLinkToChartOptions = chartLink => {
/** /**
* Tracks snowplow event when user downloads CSV of cluster metric * Tracks snowplow event when user downloads CSV of cluster metric
* @param {String} chart title that will be sent as a property for the event * @param {String} chart title that will be sent as a property for the event
* @return {Object} config object for event tracking
*/ */
export const downloadCSVOptions = title => { export const downloadCSVOptions = title => {
const isCLusterHealthBoard = isClusterHealthBoard(); const isCLusterHealthBoard = isClusterHealthBoard();
...@@ -129,6 +130,18 @@ export const downloadCSVOptions = title => { ...@@ -129,6 +130,18 @@ export const downloadCSVOptions = title => {
return { category, action, label: 'Chart title', property: title }; return { category, action, label: 'Chart title', property: title };
}; };
/**
* Generate options for snowplow to track adding a new metric via the dashboard
* custom metric modal
* @return {Object} config object for event tracking
*/
export const getAddMetricTrackingOptions = () => ({
category: document.body.dataset.page,
action: 'click_button',
label: 'add_new_metric',
property: 'modal',
});
/** /**
* This function validates the graph data contains exactly 3 metrics plus * This function validates the graph data contains exactly 3 metrics plus
* value validations from graphDataValidatorForValues. * value validations from graphDataValidatorForValues.
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
} }
.tree-controls { .tree-controls {
display: flex;
text-align: right; text-align: right;
.btn { .btn {
......
...@@ -55,6 +55,7 @@ def render_index_json ...@@ -55,6 +55,7 @@ def render_index_json
render json: { render json: {
errors: serialize_errors(result[:issues]), errors: serialize_errors(result[:issues]),
pagination: result[:pagination],
external_url: service.external_url external_url: service.external_url
} }
end end
...@@ -111,7 +112,7 @@ def handle_errors(result) ...@@ -111,7 +112,7 @@ def handle_errors(result)
end end
def list_issues_params def list_issues_params
params.permit([:search_term, :sort]) params.permit(:search_term, :sort, :cursor)
end end
def list_projects_params def list_projects_params
......
...@@ -301,7 +301,8 @@ def visible_attributes ...@@ -301,7 +301,8 @@ def visible_attributes
:snowplow_iglu_registry_url, :snowplow_iglu_registry_url,
:push_event_hooks_limit, :push_event_hooks_limit,
:push_event_activities_limit, :push_event_activities_limit,
:custom_http_clone_url_root :custom_http_clone_url_root,
:snippet_size_limit
] ]
end end
......
...@@ -226,6 +226,8 @@ class ApplicationSetting < ApplicationRecord ...@@ -226,6 +226,8 @@ class ApplicationSetting < ApplicationRecord
validates :push_event_activities_limit, validates :push_event_activities_limit,
numericality: { greater_than_or_equal_to: 0 } numericality: { greater_than_or_equal_to: 0 }
validates :snippet_size_limit, numericality: { only_integer: true, greater_than: 0 }
SUPPORTED_KEY_TYPES.each do |type| SUPPORTED_KEY_TYPES.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type } validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
end end
......
...@@ -26,7 +26,8 @@ module ApplicationSettingImplementation ...@@ -26,7 +26,8 @@ module ApplicationSettingImplementation
'/users', '/users',
'/users/confirmation', '/users/confirmation',
'/unsubscribes/', '/unsubscribes/',
'/import/github/personal_access_token' '/import/github/personal_access_token',
'/admin/session'
].freeze ].freeze
class_methods do class_methods do
...@@ -139,7 +140,8 @@ def defaults ...@@ -139,7 +140,8 @@ def defaults
snowplow_app_id: nil, snowplow_app_id: nil,
snowplow_iglu_registry_url: nil, snowplow_iglu_registry_url: nil,
custom_http_clone_url_root: nil, custom_http_clone_url_root: nil,
productivity_analytics_start_date: Time.now productivity_analytics_start_date: Time.now,
snippet_size_limit: 50.megabytes
} }
end end
......
...@@ -104,7 +104,7 @@ def issue_latest_event(opts = {}) ...@@ -104,7 +104,7 @@ def issue_latest_event(opts = {})
def calculate_reactive_cache(request, opts) def calculate_reactive_cache(request, opts)
case request case request
when 'list_issues' when 'list_issues'
{ issues: sentry_client.list_issues(**opts.symbolize_keys) } sentry_client.list_issues(**opts.symbolize_keys)
when 'issue_details' when 'issue_details'
{ {
issue: sentry_client.issue_details(**opts.symbolize_keys) issue: sentry_client.issue_details(**opts.symbolize_keys)
......
...@@ -170,6 +170,7 @@ class Project < ApplicationRecord ...@@ -170,6 +170,7 @@ class Project < ApplicationRecord
has_one :microsoft_teams_service has_one :microsoft_teams_service
has_one :packagist_service has_one :packagist_service
has_one :hangouts_chat_service has_one :hangouts_chat_service
has_one :unify_circuit_service
has_one :root_of_fork_network, has_one :root_of_fork_network,
foreign_key: 'root_project_id', foreign_key: 'root_project_id',
......
# frozen_string_literal: true
class UnifyCircuitService < ChatNotificationService
def title
'Unify Circuit'
end
def description
'Receive event notifications in Unify Circuit'
end
def self.to_param
'unify_circuit'
end
def help
'This service sends notifications about projects events to a Unify Circuit conversation.<br />
To set up this service:
<ol>
<li><a href="https://www.circuit.com/unifyportalfaqdetail?articleId=164448">Set up an incoming webhook for your conversation</a>. All notifications will come to this conversation.</li>
<li>Paste the <strong>Webhook URL</strong> into the field below.</li>
<li>Select events below to enable notifications.</li>
</ol>'
end
def event_field(event)
end
def default_channel_placeholder
end
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
end
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: "e.g. https://circuit.com/rest/v2/webhooks/incoming/…", required: true },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{ type: 'select', name: 'branches_to_be_notified', choices: BRANCH_CHOICES }
]
end
private
def notify(message, opts)
response = Gitlab::HTTP.post(webhook, body: {
subject: message.project_name,
text: message.pretext,
markdown: true
}.to_json)
response if response.success?
end
def custom_data(data)
super(data).merge(markdown: true)
end
end
...@@ -289,6 +289,7 @@ def self.available_services_names ...@@ -289,6 +289,7 @@ def self.available_services_names
slack slack
teamcity teamcity
microsoft_teams microsoft_teams
unify_circuit
] ]
if Rails.env.development? if Rails.env.development?
......
...@@ -46,6 +46,18 @@ def content_html_invalidated? ...@@ -46,6 +46,18 @@ def content_html_invalidated?
length: { maximum: 255 } length: { maximum: 255 }
validates :content, presence: true validates :content, presence: true
validates :content,
length: {
maximum: ->(_) { Gitlab::CurrentSettings.snippet_size_limit },
message: -> (_, data) do
current_value = ActiveSupport::NumberHelper.number_to_human_size(data[:value].size)
max_size = ActiveSupport::NumberHelper.number_to_human_size(Gitlab::CurrentSettings.snippet_size_limit)
_("is too long (%{current_value}). The maximum size is %{max_size}.") % { current_value: current_value, max_size: max_size }
end
},
if: :content_changed?
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
# Scopes # Scopes
......
...@@ -6,37 +6,24 @@ class ListIssuesService < ErrorTracking::BaseService ...@@ -6,37 +6,24 @@ class ListIssuesService < ErrorTracking::BaseService
DEFAULT_LIMIT = 20 DEFAULT_LIMIT = 20
DEFAULT_SORT = 'last_seen' DEFAULT_SORT = 'last_seen'
def execute
return error('Error Tracking is not enabled') unless enabled?
return error('Access denied', :unauthorized) unless can_read?
result = project_error_tracking_setting.list_sentry_issues(
issue_status: issue_status,
limit: limit,
search_term: search_term,
sort: sort
)
# our results are not yet ready
unless result
return error('Not ready. Try again later', :no_content)
end
if result[:error].present?
return error(result[:error], http_status_for(result[:error_type]))
end
success(issues: result[:issues])
end
def external_url def external_url
project_error_tracking_setting&.sentry_external_url project_error_tracking_setting&.sentry_external_url
end end
private private
def fetch
project_error_tracking_setting.list_sentry_issues(
issue_status: issue_status,
limit: limit,
search_term: params[:search_term].presence,
sort: sort,
cursor: params[:cursor].presence
)
end
def parse_response(response) def parse_response(response)
{ issues: response[:issues] } response.slice(:issues, :pagination)
end end
def issue_status def issue_status
...@@ -47,18 +34,6 @@ def limit ...@@ -47,18 +34,6 @@ def limit
params[:limit] || DEFAULT_LIMIT params[:limit] || DEFAULT_LIMIT
end end
def search_term
params[:search_term].presence
end
def enabled?
project_error_tracking_setting&.enabled?
end
def can_read?
can?(current_user, :read_sentry_issue, project)
end
def sort def sort
params[:sort] || DEFAULT_SORT params[:sort] || DEFAULT_SORT
end end
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
%li.breadcrumb-item %li.breadcrumb-item
= link_to truncate(title, length: 40), browse_project_job_artifacts_path(@project, @build, path) = link_to truncate(title, length: 40), browse_project_job_artifacts_path(@project, @build, path)
.tree-controls .tree-controls<
= link_to download_project_job_artifacts_path(@project, @build), = link_to download_project_job_artifacts_path(@project, @build),
rel: 'nofollow', download: '', class: 'btn btn-default download' do rel: 'nofollow', download: '', class: 'btn btn-default download' do
= sprite_icon('download') = sprite_icon('download')
......
...@@ -17,21 +17,19 @@ ...@@ -17,21 +17,19 @@
- else - else
= link_to title, project_tree_path(@project, tree_join(@ref, path)) = link_to title, project_tree_path(@project, tree_join(@ref, path))
.tree-controls .tree-controls<
= render 'projects/find_file_link' = render 'projects/find_file_link'
-# only show normal/blame view links for text files
- if blob.readable_text?
- if blame
= link_to 'Normal view', project_blob_path(@project, @id),
class: 'btn'
- else
= link_to 'Blame', project_blame_path(@project, @id),
class: 'btn js-blob-blame-link' unless blob.empty?
.btn-group{ role: "group" }< = link_to 'History', project_commits_path(@project, @id),
-# only show normal/blame view links for text files class: 'btn'
- if blob.readable_text?
- if blame
= link_to 'Normal view', project_blob_path(@project, @id),
class: 'btn'
- else
= link_to 'Blame', project_blame_path(@project, @id),
class: 'btn js-blob-blame-link' unless blob.empty?
= link_to 'History', project_commits_path(@project, @id), = link_to 'Permalink', project_blob_path(@project,
class: 'btn' tree_join(@commit.sha, @path)), class: 'btn js-data-file-blob-permalink-url'
= link_to 'Permalink', project_blob_path(@project,
tree_join(@commit.sha, @path)), class: 'btn js-data-file-blob-permalink-url'
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- show_menu = can_create_issue || can_create_project_snippet || can_push_code || create_mr_from_new_fork || merge_project - show_menu = can_create_issue || can_create_project_snippet || can_push_code || create_mr_from_new_fork || merge_project
- if show_menu - if show_menu
.project-action-button.dropdown.inline .project-action-button.dropdown.inline<
%a.btn.dropdown-toggle.has-tooltip.qa-create-new-dropdown{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' } %a.btn.dropdown-toggle.has-tooltip.qa-create-new-dropdown{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' }
= icon('plus') = icon('plus')
= icon("caret-down") = icon("caret-down")
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
%ul.breadcrumb.repo-breadcrumb %ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs = commits_breadcrumbs
.tree-controls.d-none.d-sm-none.d-md-block .tree-controls.d-none.d-sm-none.d-md-block<
- if @merge_request.present? - if @merge_request.present?
.control .control
= link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn' = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn'
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
= link_to new_project_tag_path(@project) do = link_to new_project_tag_path(@project) do
#{ _('New tag') } #{ _('New tag') }
.tree-controls .tree-controls<
= render_if_exists 'projects/tree/lock_link' = render_if_exists 'projects/tree/lock_link'
- if vue_file_list_enabled? - if vue_file_list_enabled?
#js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } } #js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } }
...@@ -85,20 +85,19 @@ ...@@ -85,20 +85,19 @@
= render 'projects/find_file_link' = render 'projects/find_file_link'
- if can_create_mr_from_fork - if can_create_mr_from_fork
= succeed " " do - if can_collaborate || current_user&.already_forked?(@project)
- if can_collaborate || current_user&.already_forked?(@project) - if vue_file_list_enabled?
- if vue_file_list_enabled? #js-tree-web-ide-link.d-inline-block
#js-tree-web-ide-link.d-inline-block
- else
= link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
= _('Web IDE')
- else - else
= link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
= _('Web IDE') = _('Web IDE')
= render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path) - else
= link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do
= _('Web IDE')
= render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path)
- if show_xcode_link?(@project) - if show_xcode_link?(@project)
.project-action-button.project-xcode.inline .project-action-button.project-xcode.inline<
= render "projects/buttons/xcode_link" = render "projects/buttons/xcode_link"
= render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/download', project: @project, ref: @ref
---
title: Track adding metric via monitoring dashboard
merge_request: 20818
author:
type: added
---
title: Remove whitespaces between tree-controls elements
merge_request: 20952
author:
type: other
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment