Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-06-19 00:09:12 +00:00
parent d7b7232142
commit b9f288cdfa
20 changed files with 351 additions and 109 deletions

View file

@ -0,0 +1,41 @@
import $ from 'jquery';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
/**
* This behavior collapses the right sidebar
* if the window size changes
*
* @sentrify
*/
export default () => {
const $sidebarGutterToggle = $('.js-sidebar-toggle');
let bootstrapBreakpoint = bp.getBreakpointSize();
$(window).on('resize.app', () => {
const oldBootstrapBreakpoint = bootstrapBreakpoint;
bootstrapBreakpoint = bp.getBreakpointSize();
if (bootstrapBreakpoint !== oldBootstrapBreakpoint) {
const breakpointSizes = ['md', 'sm', 'xs'];
if (breakpointSizes.includes(bootstrapBreakpoint)) {
const $gutterIcon = $sidebarGutterToggle.find('i');
if ($gutterIcon.hasClass('fa-angle-double-right')) {
$sidebarGutterToggle.trigger('click');
}
const sidebarGutterVueToggleEl = document.querySelector('.js-sidebar-vue-toggle');
// Sidebar has an icon which corresponds to collapsing the sidebar
// only then trigger the click.
if (sidebarGutterVueToggleEl) {
const collapseIcon = sidebarGutterVueToggleEl.querySelector('i.fa-angle-double-right');
if (collapseIcon) {
collapseIcon.click();
}
}
}
}
});
};

View file

@ -11,9 +11,13 @@ import './requires_input';
import initPageShortcuts from './shortcuts';
import './toggler_behavior';
import './preview_markdown';
import initCollapseSidebarOnWindowResize from './collapse_sidebar_on_window_resize';
import initSelect2Dropdowns from './select2';
installGlEmojiElement();
initGFMInput();
initCopyAsGFM();
initCopyToClipboard();
initPageShortcuts();
initCollapseSidebarOnWindowResize();
initSelect2Dropdowns();

View file

@ -0,0 +1,23 @@
import $ from 'jquery';
export default () => {
if ($('select.select2').length) {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
$('select.select2').select2({
width: 'resolve',
minimumResultsForSearch: 10,
dropdownAutoWidth: true,
});
// Close select2 on escape
$('.js-select2').on('select2-close', () => {
setTimeout(() => {
$('.select2-container-active').removeClass('select2-container-active');
$(':focus').blur();
}, 1);
});
})
.catch(() => {});
}
};

View file

@ -62,12 +62,12 @@ function disableJQueryAnimations() {
}
// Disable jQuery animations
if (gon && gon.disable_animations) {
if (gon?.disable_animations) {
disableJQueryAnimations();
}
// inject test utilities if necessary
if (process.env.NODE_ENV !== 'production' && gon && gon.test_env) {
if (process.env.NODE_ENV !== 'production' && gon?.test_env) {
disableJQueryAnimations();
import(/* webpackMode: "eager" */ './test_utils/'); // eslint-disable-line no-unused-expressions
}
@ -132,27 +132,6 @@ function deferredInitialisation() {
.fadeOut();
});
// Initialize select2 selects
if ($('select.select2').length) {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
$('select.select2').select2({
width: 'resolve',
minimumResultsForSearch: 10,
dropdownAutoWidth: true,
});
// Close select2 on escape
$('.js-select2').on('select2-close', () => {
setTimeout(() => {
$('.select2-container-active').removeClass('select2-container-active');
$(':focus').blur();
}, 1);
});
})
.catch(() => {});
}
const glTooltipDelay = localStorage.getItem('gl-tooltip-delay');
const delay = glTooltipDelay ? JSON.parse(glTooltipDelay) : 0;
@ -179,9 +158,7 @@ function deferredInitialisation() {
document.addEventListener('DOMContentLoaded', () => {
const $body = $('body');
const $document = $(document);
const $window = $(window);
const $sidebarGutterToggle = $('.js-sidebar-toggle');
let bootstrapBreakpoint = bp.getBreakpointSize();
const bootstrapBreakpoint = bp.getBreakpointSize();
if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' });
@ -199,6 +176,15 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
/**
* TODO: Apparently we are collapsing the right sidebar on certain screensizes per default
* except on issue board pages. Why can't we do it with CSS?
*
* Proposal: Expose a global sidebar API, which we could import wherever we are manipulating
* the visibility of the sidebar.
*
* Quick fix: Get rid of jQuery for this implementation
*/
const isBoardsPage = /(projects|groups):boards:show/.test(document.body.dataset.page);
if (!isBoardsPage && (bootstrapBreakpoint === 'sm' || bootstrapBreakpoint === 'xs')) {
const $rightSidebar = $('aside.right-sidebar');
@ -225,14 +211,12 @@ document.addEventListener('DOMContentLoaded', () => {
localTimeAgo($('abbr.timeago, .js-timeago'), true);
// Form submitter
$('.trigger-submit').on('change', function triggerSubmitCallback() {
$(this)
.parents('form')
.submit();
});
// Disable form buttons while a form is submitting
/**
* This disables form buttons while a form is submitting
* We do not difinitively know all of the places where this is used
*
* TODO: Defer execution, migrate to behaviors, and add sentry logging
*/
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function ajaxCompleteCallback(e) {
const $buttons = $('[type="submit"], .js-disable-on-submit', this).not('.js-no-auto-disable');
switch (e.type) {
@ -259,7 +243,11 @@ document.addEventListener('DOMContentLoaded', () => {
$('.header-content').toggleClass('menu-expanded');
});
// Commit show suppressed diff
/**
* Show suppressed commit diff
*
* TODO: Move to commit diff pages
*/
$document.on('click', '.diff-content .js-show-suppressed-diff', function showDiffCallback() {
const $container = $(this).parent();
$container.next('table').show();
@ -290,39 +278,6 @@ document.addEventListener('DOMContentLoaded', () => {
$(document).trigger('toggle.comments');
});
$document.on('breakpoint:change', (e, breakpoint) => {
const breakpointSizes = ['md', 'sm', 'xs'];
if (breakpointSizes.includes(breakpoint)) {
const $gutterIcon = $sidebarGutterToggle.find('i');
if ($gutterIcon.hasClass('fa-angle-double-right')) {
$sidebarGutterToggle.trigger('click');
}
const sidebarGutterVueToggleEl = document.querySelector('.js-sidebar-vue-toggle');
// Sidebar has an icon which corresponds to collapsing the sidebar
// only then trigger the click.
if (sidebarGutterVueToggleEl) {
const collapseIcon = sidebarGutterVueToggleEl.querySelector('i.fa-angle-double-right');
if (collapseIcon) {
collapseIcon.click();
}
}
}
});
function fitSidebarForSize() {
const oldBootstrapBreakpoint = bootstrapBreakpoint;
bootstrapBreakpoint = bp.getBreakpointSize();
if (bootstrapBreakpoint !== oldBootstrapBreakpoint) {
$document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
}
$window.on('resize.app', fitSidebarForSize);
$('form.filter-form').on('submit', function filterFormSubmitCallback(event) {
const link = document.createElement('a');
link.href = this.action;

View file

@ -7,7 +7,7 @@ module PerformanceMonitoring
attr_accessor :dashboard, :panel_groups, :path, :environment, :priority, :templating, :links
validates :dashboard, presence: true
validates :panel_groups, presence: true
validates :panel_groups, array_members: { member_class: PerformanceMonitoring::PrometheusPanelGroup }
class << self
def from_json(json_content)
@ -35,9 +35,15 @@ module PerformanceMonitoring
new(
dashboard: attributes['dashboard'],
panel_groups: attributes['panel_groups']&.map { |group| PrometheusPanelGroup.from_json(group) }
panel_groups: initialize_children_collection(attributes['panel_groups'])
)
end
def initialize_children_collection(children)
return unless children.is_a?(Array)
children.map { |group| PerformanceMonitoring::PrometheusPanelGroup.from_json(group) }
end
end
def to_yaml
@ -47,7 +53,7 @@ module PerformanceMonitoring
# This method is planned to be refactored as a part of https://gitlab.com/gitlab-org/gitlab/-/issues/219398
# implementation. For new existing logic was reused to faster deliver MVC
def schema_validation_warnings
self.class.from_json(self.as_json)
self.class.from_json(reload_schema)
nil
rescue ActiveModel::ValidationError => exception
exception.model.errors.map { |attr, error| "#{attr}: #{error}" }
@ -55,6 +61,14 @@ module PerformanceMonitoring
private
# dashboard finder methods are somehow limited, #find includes checking if
# user is authorised to view selected dashboard, but modifies schema, which in some cases may
# cause false positives returned from validation, and #find_raw does not authorise users
def reload_schema
project = environment&.project
project.nil? ? self.as_json : Gitlab::Metrics::Dashboard::Finder.find_raw(project, dashboard_path: path)
end
def yaml_valid_attributes
%w(panel_groups panels metrics group priority type title y_label weight id unit label query query_range dashboard)
end

View file

@ -7,7 +7,8 @@ module PerformanceMonitoring
attr_accessor :type, :title, :y_label, :weight, :metrics, :y_axis, :max_value
validates :title, presence: true
validates :metrics, presence: true
validates :metrics, array_members: { member_class: PerformanceMonitoring::PrometheusMetric }
class << self
def from_json(json_content)
build_from_hash(json_content).tap(&:validate!)
@ -23,9 +24,15 @@ module PerformanceMonitoring
title: attributes['title'],
y_label: attributes['y_label'],
weight: attributes['weight'],
metrics: attributes['metrics']&.map { |metric| PrometheusMetric.from_json(metric) }
metrics: initialize_children_collection(attributes['metrics'])
)
end
def initialize_children_collection(children)
return unless children.is_a?(Array)
children.map { |metrics| PerformanceMonitoring::PrometheusMetric.from_json(metrics) }
end
end
def id(group_title)

View file

@ -7,7 +7,8 @@ module PerformanceMonitoring
attr_accessor :group, :priority, :panels
validates :group, presence: true
validates :panels, presence: true
validates :panels, array_members: { member_class: PerformanceMonitoring::PrometheusPanel }
class << self
def from_json(json_content)
build_from_hash(json_content).tap(&:validate!)
@ -21,9 +22,15 @@ module PerformanceMonitoring
new(
group: attributes['group'],
priority: attributes['priority'],
panels: attributes['panels']&.map { |panel| PrometheusPanel.from_json(panel) }
panels: initialize_children_collection(attributes['panels'])
)
end
def initialize_children_collection(children)
return unless children.is_a?(Array)
children.map { |panels| PerformanceMonitoring::PrometheusPanel.from_json(panels) }
end
end
end
end

View file

@ -0,0 +1,21 @@
# frozen_string_literal: true
# ArrayMembersValidator
#
# Custom validator that checks if validated
# attribute contains non empty array, which every
# element is an instances of :member_class
#
# Example:
#
# class Config::Root < ActiveRecord::Base
# validates :nodes, member_class: Config::Node
# end
#
class ArrayMembersValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if !value.is_a?(Array) || value.empty? || value.any? { |child| !child.instance_of?(options[:member_class]) }
record.errors.add(attribute, _("should be an array of %{object_name} objects") % { object_name: options.fetch(:object_name, attribute) })
end
end
end

View file

@ -0,0 +1,5 @@
---
title: Fix 500 errors and false positive warnings during metrics dashboard validation.
merge_request: 34166
author:
type: fixed

View file

@ -322,6 +322,8 @@ application server, or a Gitaly node.
}
```
1. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2013) in GitLab 13.1 and later, enable [distribution of reads](#distributed-reads).
1. Save the changes to `/etc/gitlab/gitlab.rb` and [reconfigure
Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure):
@ -712,6 +714,43 @@ To get started quickly:
Congratulations! You've configured an observable highly available Praefect
cluster.
## Distributed reads
> Introduced in GitLab 13.1 in [beta](https://about.gitlab.com/handbook/product/#alpha-beta-ga) with feature flag `gitaly_distributed_reads` set to disabled.
Praefect supports distribution of read operations across Gitaly nodes that are
configured for the virtual node.
To allow for [performance testing](https://gitlab.com/gitlab-org/quality/performance/-/issues/231),
distributed reads are currently in
[beta](https://about.gitlab.com/handbook/product/#alpha-beta-ga) and disabled by
default. To enable distributed reads, the `gitaly_distributed_reads`
[feature flag](../feature_flags.md) must be enabled in a Ruby console:
```ruby
Feature.enable(:gitaly_distributed_reads)
```
If enabled, all RPCs marked with `ACCESSOR` option like
[GetBlob](https://gitlab.com/gitlab-org/gitaly/-/blob/v12.10.6/proto/blob.proto#L16)
are redirected to an up to date and healthy Gitaly node.
_Up to date_ in this context means that:
- There is no replication operations scheduled for this node.
- The last replication operation is in _completed_ state.
If there is no such nodes, or any other error occurs during node selection, the primary
node will be chosen to serve the request.
To track distribution of read operations, you can use the `gitaly_praefect_read_distribution`
Prometheus counter metric. It has two labels:
- `virtual_storage`.
- `storage`.
They reflect configuration defined for this instance of Praefect.
## Automatic failover and leader election
Praefect regularly checks the health of each backend Gitaly node. This

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -34,3 +34,37 @@ Merge request approval rules that can be set at an instance level are:
- **Prevent users from modifying merge request approvers list**. Prevents project
maintainers from allowing users to modify the approvers list in project settings
or in individual merge requests.
## Scope rules to compliance-labeled projects
> Introduced in [GitLab Premium](https://gitlab.com/groups/gitlab-org/-/epics/3432) 13.1.
Merge request approval rules can be further scoped to specific compliance frameworks.
When the compliance framework label is selected and the project is assigned the compliance
label, the instance-level MR approval settings will take effect and
[project-level settings](../project/merge_requests/merge_request_approvals.md#adding--editing-a-default-approval-rule)
is locked for modification.
When the compliance framework label is not selected or the project is not assigned the
compliance label, the project-level MR approval settings will take effect and the users with
Maintainer role and above can modify these.
| Instance-level | Project-level |
| -------------- | ------------- |
| ![Scope MR approval settings to compliance frameworks](img/scope_mr_approval_settings_v13_1.png) | ![MR approval settings on compliance projects](img/mr_approval_settings_compliance_project_v13_1.png) |
### Enabling the feature
This feature comes with two feature flags which are disabled by default.
- The configuration in Admin area is controlled via `admin_merge_request_approval_settings`
- The application of these rules is controlled via `project_merge_request_approval_settings`
These feature flags can be managed by feature flag [API endpoint](../../api/features.md#set-or-create-a-feature) or
by [GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) with the following commands:
```ruby
Feature.enable(:admin_merge_request_approval_settings)
Feature.enable(:project_merge_request_approval_settings)
```

View file

@ -353,11 +353,11 @@ and files with invalid syntax display **Metrics Dashboard YAML definition is inv
When **Metrics Dashboard YAML definition is invalid** at least one of the following messages is displayed:
1. `dashboard: can't be blank` [learn more](#dashboard-top-level-properties)
1. `panel_groups: can't be blank` [learn more](#dashboard-top-level-properties)
1. `panel_groups: should be an array of panel_groups objects` [learn more](#dashboard-top-level-properties)
1. `group: can't be blank` [learn more](#panel-group-panel_groups-properties)
1. `panels: can't be blank` [learn more](#panel-group-panel_groups-properties)
1. `metrics: can't be blank` [learn more](#panel-panels-properties)
1. `panels: should be an array of panels objects` [learn more](#panel-group-panel_groups-properties)
1. `title: can't be blank` [learn more](#panel-panels-properties)
1. `metrics: should be an array of metrics objects` [learn more](#panel-panels-properties)
1. `query: can't be blank` [learn more](#metrics-metrics-properties)
1. `query_range: can't be blank` [learn more](#metrics-metrics-properties)
1. `unit: can't be blank` [learn more](#metrics-metrics-properties)

View file

@ -1,25 +1,45 @@
# Slack Notifications Service
The Slack Notifications Service allows your GitLab project to send events (e.g. issue created) to your existing Slack team as notifications. This requires configurations in both Slack and GitLab.
The Slack Notifications Service allows your GitLab project to send events
(such as issue creation) to your existing Slack team as notifications. Setting up
Slack notifications requires configuration changes for both Slack and GitLab.
> Note: You can also use Slack slash commands to control GitLab inside Slack. This is the separately configured [Slack slash commands](slack_slash_commands.md).
NOTE: **Note:**
You can also use Slack slash commands to control GitLab inside Slack. This is the
separately configured [Slack slash commands](slack_slash_commands.md).
## Slack Configuration
## Slack configuration
1. Sign in to your Slack team and [start a new Incoming WebHooks configuration](https://my.slack.com/services/new/incoming-webhook).
1. Select the Slack channel where notifications will be sent to by default. Click the **Add Incoming WebHooks integration** button to add the configuration.
1. Copy the **Webhook URL**, which we'll use later in the GitLab configuration.
1. Select the Slack channel where notifications will be sent to by default.
Click the **Add Incoming WebHooks integration** button to add the configuration.
1. Copy the **Webhook URL**, which we will use later in the GitLab configuration.
## GitLab Configuration
## GitLab configuration
1. Navigate to the [Integrations page](overview.md#accessing-integrations) in your project's settings, i.e. **Project > Settings > Integrations**.
1. Open your project's page, and navigate to your project's
[Integrations page](overview.md#accessing-integrations) at
**{settings}** **Settings > Integrations**.
1. Select the **Slack notifications** integration to configure it.
1. Ensure that the **Active** toggle is enabled.
1. Check the checkboxes corresponding to the GitLab events you want to send to Slack as a notification.
1. For each event, optionally enter the Slack channel names where you want to send the event, separated by a comma. If left empty, the event will be sent to the default channel that you configured in the Slack Configuration step. **Note:** Usernames and private channels are not supported. To send direct messages, use the Member ID found under user's Slack profile.
1. Paste the **Webhook URL** that you copied from the Slack Configuration step.
1. Optionally customize the Slack bot username that will be sending the notifications.
1. Configure the remaining options and click `Save changes`.
1. Click **Enable integration**.
1. In **Trigger**, select the checkboxes for each type of GitLab event to send to Slack as a
notification. See [Triggers available for Slack notifications](#triggers-available-for-slack-notifications)
for a full list. By default, messages are sent to the channel you configured during
[Slack integration](#slack-configuration).
1. (Optional) To send messages to a different channel, multiple channels, or as a direct message:
- To send messages to channels, enter the Slack channel names, separated by commas.
- To send direct messages, use the Member ID found in the user's Slack profile.
NOTE: **Note:**
Usernames and private channels are not supported.
1. In **Webhook**, provide the webhook URL that you copied from the
[Slack integration](#slack-configuration) step.
1. (Optional) In **Username**, provide the username of the Slack bot that sends the notifications.
1. Select the **Notify only broken pipelines** check box to only notify on failures.
1. In the **Branches to be notified** select box, choose which types of branches
to send notifications for.
1. Click **Test settings and save changes**.
Your Slack team will now start receiving GitLab event notifications as configured.
@ -43,14 +63,14 @@ The following triggers are available for Slack notifications:
## Troubleshooting
If you're having trouble with the Slack integration not working, then start by
If your Slack integration is not working, start troubleshooting by
searching through the [Sidekiq logs](../../../administration/logs.md#sidekiqlog)
for errors relating to your Slack service.
### Something went wrong on our end
This is a generic error shown in the GitLab UI and doesn't mean much by itself.
You'll need to look in [the logs](../../../administration/logs.md#productionlog) to find
This is a generic error shown in the GitLab UI and does not mean much by itself.
Review [the logs](../../../administration/logs.md#productionlog) to find
an error message and keep troubleshooting from there.
### `certificate verify failed`
@ -83,10 +103,10 @@ result = Net::HTTP.get(URI('https://<SLACK URL>'));0
result = Net::HTTP.get(URI('https://<GITLAB URL>'));0
```
If it's an issue with GitLab not trusting HTTPS connections to itself, then you may simply
If GitLab is not trusting HTTPS connections to itself, then you may
need to [add your certificate to GitLab's trusted certificates](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
If it's an issue with GitLab not trusting connections to Slack, then the GitLab
OpenSSL trust store probably got messed up somehow. Typically this is from overriding
the trust store with `gitlab_rails['env'] = {"SSL_CERT_FILE" => "/path/to/file.pem"}`
If GitLab is not trusting connections to Slack, then the GitLab
OpenSSL trust store is incorrect. Some typical causes: overriding
the trust store with `gitlab_rails['env'] = {"SSL_CERT_FILE" => "/path/to/file.pem"}`,
or by accidentally modifying the default CA bundle `/opt/gitlab/embedded/ssl/certs/cacert.pem`.

View file

@ -27624,6 +27624,9 @@ msgstr ""
msgid "severity|Unknown"
msgstr ""
msgid "should be an array of %{object_name} objects"
msgstr ""
msgid "should be greater than or equal to %{access} inherited membership from group %{group_name}"
msgstr ""

View file

@ -593,7 +593,7 @@ RSpec.describe 'File blob', :js do
aggregate_failures do
# shows that dashboard yaml is invalid
expect(page).to have_content('Metrics Dashboard YAML definition is invalid:')
expect(page).to have_content("panel_groups: can't be blank")
expect(page).to have_content("panel_groups: should be an array of panel_groups objects")
# shows a learn more link
expect(page).to have_link('Learn more')

View file

@ -50,19 +50,19 @@ describe PerformanceMonitoring::PrometheusDashboard do
context 'dashboard content is missing' do
let(:json_content) { nil }
it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'dashboard content is NOT a hash' do
let(:json_content) { YAML.safe_load("'test'") }
it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'content is an array' do
let(:json_content) { [{ "dashboard" => "Dashboard Title" }] }
it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'dashboard definition is missing panels_groups and dashboard keys' do
@ -72,7 +72,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
}
end
it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'group definition is missing panels and group keys' do
@ -88,7 +88,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
}
end
it_behaves_like 'validation failed', panels: ["can't be blank"], group: ["can't be blank"]
it_behaves_like 'validation failed', panels: ["should be an array of panels objects"], group: ["can't be blank"]
end
context 'panel definition is missing metrics and title keys' do
@ -110,7 +110,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
}
end
it_behaves_like 'validation failed', metrics: ["can't be blank"], title: ["can't be blank"]
it_behaves_like 'validation failed', metrics: ["should be an array of metrics objects"], title: ["can't be blank"]
end
context 'metrics definition is missing unit, query and query_range keys' do
@ -180,7 +180,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
describe '.find_for' do
let(:project) { build_stubbed(:project) }
let(:user) { build_stubbed(:user) }
let(:environment) { build_stubbed(:environment) }
let(:environment) { build_stubbed(:environment, project: project) }
let(:path) { ::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH }
context 'dashboard has been found' do

View file

@ -62,12 +62,12 @@ describe 'Getting Metrics Dashboard' do
context 'invalid dashboard' do
let(:path) { '.gitlab/dashboards/metrics.yml' }
let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndasboard: ''" }) }
let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) }
it 'returns metrics dashboard' do
dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: can't be blank"])
expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["panel_groups: should be an array of panel_groups objects"])
end
end
@ -78,7 +78,7 @@ describe 'Getting Metrics Dashboard' do
it 'returns metrics dashboard' do
dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: can't be blank"])
expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: should be an array of panel_groups objects"])
end
end
end

View file

@ -0,0 +1,69 @@
# frozen_string_literal: true
require 'spec_helper'
describe ArrayMembersValidator do
using RSpec::Parameterized::TableSyntax
child_class = Class.new
subject(:test_class) do
Class.new do
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :children
validates :children, array_members: { member_class: child_class }
end
end
where(:children, :is_valid) do
[child_class.new] | true
[Class.new.new] | false
[child_class.new, Class.new.new] | false
[] | false
child_class.new | false
[Class.new(child_class).new] | false
end
with_them do
it 'only accepts valid children nodes' do
expect(test_class.new(children: children).valid?).to eq(is_valid)
end
end
context 'validation message' do
subject(:test_class) do
Class.new do
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :children
end
end
context 'with default object name' do
it 'uses attribute name', :aggregate_failures do
test_class.class_eval do
validates :children, array_members: { member_class: child_class }
end
object = test_class.new(children: [])
expect(object.valid?).to be_falsey
expect(object.errors.messages).to eql(children: ['should be an array of children objects'])
end
end
context 'with custom object name' do
it 'uses that name', :aggregate_failures do
test_class.class_eval do
validates :children, array_members: { member_class: child_class, object_name: 'test' }
end
object = test_class.new(children: [])
expect(object.valid?).to be_falsey
expect(object.errors.messages).to eql(children: ['should be an array of test objects'])
end
end
end
end