提交 2a53c24c 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 8e812185
......@@ -6,6 +6,7 @@ require_relative 'lib/gitlab/danger/request_helper'
danger.import_plugin('danger/plugins/helper.rb')
danger.import_plugin('danger/plugins/roulette.rb')
danger.import_plugin('danger/plugins/changelog.rb')
danger.import_plugin('danger/plugins/sidekiq_queues.rb')
return if helper.release_automation?
......
......@@ -64,6 +64,11 @@ export default {
type: Boolean,
required: true,
},
isDisabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -118,6 +123,9 @@ export default {
.then(() => {
this.activated = value;
this.loadingActivated = false;
if (value) {
window.location.reload();
}
})
.catch(() => {
createFlash(__('Update failed. Please try again.'));
......@@ -142,7 +150,7 @@ export default {
<gl-form-group :label="__('Active')" label-for="activated" label-class="label-bold">
<toggle-button
id="activated"
:disabled-input="loadingActivated"
:disabled-input="loadingActivated || isDisabled"
:is-loading="loadingActivated"
:value="activated"
@change="toggleActivated"
......@@ -152,7 +160,11 @@ export default {
<div class="input-group">
<gl-form-input id="url" :readonly="true" :value="url" />
<span class="input-group-append">
<clipboard-button :text="url" :title="$options.COPY_TO_CLIPBOARD" />
<clipboard-button
:text="url"
:title="$options.COPY_TO_CLIPBOARD"
:disabled="isDisabled"
/>
</span>
</div>
</gl-form-group>
......@@ -164,10 +176,16 @@ export default {
<div class="input-group">
<gl-form-input id="authorization-key" :readonly="true" :value="authorizationKey" />
<span class="input-group-append">
<clipboard-button :text="authorizationKey" :title="$options.COPY_TO_CLIPBOARD" />
<clipboard-button
:text="authorizationKey"
:title="$options.COPY_TO_CLIPBOARD"
:disabled="isDisabled"
/>
</span>
</div>
<gl-button v-gl-modal.authKeyModal class="mt-2">{{ $options.RESET_KEY }}</gl-button>
<gl-button v-gl-modal.authKeyModal class="mt-2" :disabled="isDisabled">{{
$options.RESET_KEY
}}</gl-button>
<gl-modal
modal-id="authKeyModal"
:title="$options.RESET_KEY"
......
......@@ -14,8 +14,11 @@ export default el => {
formPath,
authorizationKey,
url,
disabled,
} = el.dataset;
const activated = parseBoolean(activatedStr);
const isDisabled = parseBoolean(disabled);
return new Vue({
el,
......@@ -28,6 +31,7 @@ export default el => {
formPath,
initialAuthorizationKey: authorizationKey,
url,
isDisabled,
},
});
},
......
import mountErrorTrackingForm from '~/error_tracking_settings';
import initAlertsSettings from '~/alerts_service_settings';
import mountOperationSettings from '~/operation_settings';
import mountGrafanaIntegration from '~/grafana_integration';
import initSettingsPanels from '~/settings_panels';
......@@ -10,4 +11,5 @@ document.addEventListener('DOMContentLoaded', () => {
if (!IS_EE) {
initSettingsPanels();
}
initAlertsSettings(document.querySelector('.js-alerts-service-settings'));
});
......@@ -5,6 +5,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-users-over-license-callout',
'.js-admin-licensed-user-count-threshold',
'.js-buy-pipeline-minutes-notification-callout',
'.js-alerts-moved-alert',
];
const initCallouts = () => {
......
......@@ -46,3 +46,8 @@
// Gitlab UI util classes
@import "@gitlab/ui/src/scss/utilities";
/* print styles */
@media print {
@import "print";
}
......@@ -11,7 +11,7 @@ module Projects
helper_method :error_tracking_setting
def show
render locals: { prometheus_service: prometheus_service }
render locals: { prometheus_service: prometheus_service, alerts_service: alerts_service }
end
def update
......@@ -52,6 +52,10 @@ module Projects
project.find_or_initialize_service(::PrometheusService.to_param)
end
def alerts_service
project.find_or_initialize_service(::AlertsService.to_param)
end
def render_update_response(result)
respond_to do |format|
format.html do
......
......@@ -7,6 +7,7 @@ module UserCalloutsHelper
SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed'
TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight'
WEBHOOKS_MOVED = 'webhooks_moved'
ALERTS_MOVED = 'alerts_moved'
def show_admin_integrations_moved?
!user_dismissed?(ADMIN_INTEGRATIONS_MOVED)
......@@ -43,6 +44,10 @@ module UserCalloutsHelper
!user_dismissed?(WEBHOOKS_MOVED)
end
def show_alerts_moved_alert?
!user_dismissed?(ALERTS_MOVED)
end
private
def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil)
......
......@@ -79,6 +79,7 @@ class Namespace < ApplicationRecord
'COALESCE(SUM(ps.storage_size), 0) AS storage_size',
'COALESCE(SUM(ps.repository_size), 0) AS repository_size',
'COALESCE(SUM(ps.wiki_size), 0) AS wiki_size',
'COALESCE(SUM(ps.snippets_size), 0) AS snippets_size',
'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size',
'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size',
'COALESCE(SUM(ps.packages_size), 0) AS packages_size'
......
......@@ -7,15 +7,11 @@ class ProjectStatistics < ApplicationRecord
belongs_to :namespace
default_value_for :wiki_size, 0
# older migrations fail due to non-existent attribute without this
def wiki_size
has_attribute?(:wiki_size) ? super : 0
end
default_value_for :snippets_size, 0
before_save :update_storage_size
COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count].freeze
COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size].freeze
INCREMENTABLE_COLUMNS = { build_artifacts_size: %i[storage_size], packages_size: %i[storage_size] }.freeze
NAMESPACE_RELATABLE_COLUMNS = [:repository_size, :wiki_size, :lfs_objects_size].freeze
......@@ -54,17 +50,32 @@ class ProjectStatistics < ApplicationRecord
self.wiki_size = project.wiki.repository.size * 1.megabyte
end
def update_snippets_size
self.snippets_size = project.snippets.with_statistics.sum(:repository_size)
end
def update_lfs_objects_size
self.lfs_objects_size = project.lfs_objects.sum(:size)
end
# older migrations fail due to non-existent attribute without this
def packages_size
has_attribute?(:packages_size) ? super : 0
# `wiki_size` and `snippets_size` have no default value in the database
# and the column can be nil.
# This means that, when the columns were added, all rows had nil
# values on them.
# Therefore, any call to any of those methods will return nil instead
# of 0, because `default_value_for` works with new records, not existing ones.
#
# These two methods provide consistency and avoid returning nil.
def wiki_size
super.to_i
end
def snippets_size
super.to_i
end
def update_storage_size
self.storage_size = repository_size + wiki_size.to_i + lfs_objects_size + build_artifacts_size + packages_size
self.storage_size = repository_size + wiki_size + lfs_objects_size + build_artifacts_size + packages_size + snippets_size
end
# Since this incremental update method does not call update_storage_size above,
......
......@@ -79,6 +79,7 @@ class Snippet < ApplicationRecord
scope :fresh, -> { order("created_at DESC") }
scope :inc_author, -> { includes(:author) }
scope :inc_relations_for_view, -> { includes(author: :status) }
scope :with_statistics, -> { joins(:statistics) }
attr_mentionable :description
......
......@@ -17,7 +17,8 @@ module UserCalloutEnums
suggest_popover_dismissed: 9,
tabs_position_highlight: 10,
webhooks_moved: 13,
admin_integrations_moved: 15
admin_integrations_moved: 15,
alerts_moved: 20
}
end
end
......
......@@ -53,7 +53,6 @@
= stylesheet_link_tag "application_dark", media: "all"
- else
= stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print"
= stylesheet_link_tag "disable_animations", media: "all" if Rails.env.test? || Gitlab.config.gitlab['disable_animations']
= stylesheet_link_tag 'performance_bar' if performance_bar_enabled?
......
- if lookup_context.template_exists?('top', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/top"
.row.gl-mt-3.gl-mb-3
.col-lg-4
%h4.gl-mt-0
......@@ -12,7 +15,7 @@
.col-lg-8
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, integration: @service
.footer-block.row-content-block
.footer-block.row-content-block{ :class => "#{'gl-display-none' if @service.is_a?(AlertsService)}" }
%input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer }
= service_save_button
&nbsp;
......
......@@ -2,5 +2,6 @@
form_path: scoped_integration_path(@service),
authorization_key: @service.token,
url: @service.url || _('<namespace / project>'),
disabled: 'true',
alerts_setup_url: help_page_path('user/project/integrations/generic_alerts.html', anchor: 'setting-up-generic-alerts'),
alerts_usage_url: help_page_path('user/project/operations/alert_management.html') } }
- return unless show_alerts_moved_alert?
.row
.col-lg-12
.gl-alert.gl-alert-info.js-alerts-moved-alert{ role: 'alert', data: { feature_id: UserCalloutsHelper::ALERTS_MOVED, dismiss_endpoint: user_callouts_path } }
= sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
%button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
= sprite_icon('close', size: 16, css_class: 'gl-icon')
.gl-alert-body
= _('You can now manage alert endpoint configuration in the Alerts section on the Operations settings page. Fields on this page have been deprecated.')
.gl-alert-actions
= link_to _('Visit settings page'), project_settings_operations_path(@project), class: 'btn gl-alert-action btn-info new-gl-button'
- return unless can?(current_user, :admin_operations, @project)
%section.settings.no-animate.js-alert-management-settings
.settings-header
%h3{ :class => "h4" }
= _('Alerts')
%button.btn.js-settings-toggle{ type: 'button' }
= _('Expand')
%p
= _('Display alerts from all your monitoring tools directly within GitLab.')
= link_to _('More information'), help_page_path('user/project/operations/alert_management'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
.js-alerts-service-settings{ data: { activated: service.activated?.to_s,
form_path: scoped_integration_path(service),
authorization_key: service.token,
url: service.url || _('<namespace / project>'),
alerts_setup_url: help_page_path('user/project/integrations/generic_alerts.html', anchor: 'setting-up-generic-alerts'),
alerts_usage_url: project_alert_management_index_path(@project) } }
......@@ -2,6 +2,7 @@
- page_title _('Operations Settings')
- breadcrumb_title _('Operations Settings')
= render 'projects/settings/operations/alert_management', service: alerts_service
= render 'projects/settings/operations/incidents'
= render 'projects/settings/operations/error_tracking'
= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service)
......
---
title: Create schema for static partitions
merge_request: 35268
author:
type: other
---
title: Move configuration for Alerts endpoint from "Settings > Integration" to "Settings > Operations > Alerts"
merge_request: 35187
author:
type: other
---
title: Include snippets size in project statistics
merge_request: 35120
author:
type: changed
---
title: Add migration for experimental product analytics table
merge_request: 35168
author:
type: added
# frozen_string_literal: true
require_relative '../../lib/gitlab/danger/sidekiq_queues'
module Danger
class SidekiqQueues < Plugin
# Put the helper code somewhere it can be tested
include Gitlab::Danger::SidekiqQueues
end
end
# frozen_string_literal: true
SCALABILITY_REVIEW_MESSAGE = <<~MSG
## Sidekiq queue changes
This merge request contains changes to Sidekiq queues. Please follow the [documentation on changing a queue's urgency](https://docs.gitlab.com/ee/development/sidekiq_style_guide.html#changing-a-queues-urgency).
MSG
ADDED_QUEUES_MESSAGE = <<~MSG
These queues were added:
MSG
CHANGED_QUEUES_MESSAGE = <<~MSG
These queues had their attributes changed:
MSG
if sidekiq_queues.added_queue_names.any? || sidekiq_queues.changed_queue_names.any?
markdown(SCALABILITY_REVIEW_MESSAGE)
if sidekiq_queues.added_queue_names.any?
markdown(ADDED_QUEUES_MESSAGE + helper.markdown_list(sidekiq_queues.added_queue_names))
end
if sidekiq_queues.changed_queue_names.any?
markdown(CHANGED_QUEUES_MESSAGE + helper.markdown_list(sidekiq_queues.changed_queue_names))
end
end
# frozen_string_literal: true
class CreateStaticPartitionsSchema < ActiveRecord::Migration[6.0]
include Gitlab::Database::SchemaHelpers
DOWNTIME = false
def up
execute 'CREATE SCHEMA gitlab_partitions_static'
create_comment(:schema, :gitlab_partitions_static, <<~EOS.strip)
Schema to hold static partitions, e.g. for hash partitioning
EOS
end
def down
execute 'DROP SCHEMA gitlab_partitions_static'
end
end
# frozen_string_literal: true
class AddProductAnalyticsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# Table is based on https://github.com/snowplow/snowplow/blob/master/4-storage/postgres-storage/sql/atomic-def.sql 6e07b1c, with the following differences:
# * app_id varchar -> project_id integer (+ FK)
# * Add `id bigserial`
# * Hash partitioning based on `project_id`
# * Timestamp columns: Change type to timestamp with time zone
#
# This table is part of the "product analytics experiment" and as such marked "experimental". The goal here is to
# explore the product analytics as a MVP feature more. We are explicitly not spending time on relational modeling
# here.
#
# We expect significant changes to the database part of this once the feature has been validated.
# Therefore, we expect to drop the table when feature validation is complete. All data will be lost.
def up
with_lock_retries do
execute <<~SQL
CREATE TABLE "product_analytics_events_experimental" (
id bigserial NOT NULL,
-- App
"project_id" integer NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
"platform" varchar(255),
-- Date/time
"etl_tstamp" timestamp with time zone,
"collector_tstamp" timestamp with time zone NOT NULL,
"dvce_created_tstamp" timestamp with time zone,
-- Date/time
"event" varchar(128),
"event_id" char(36) NOT NULL,
"txn_id" integer,
-- Versioning
"name_tracker" varchar(128),
"v_tracker" varchar(100),
"v_collector" varchar(100) NOT NULL,
"v_etl" varchar(100) NOT NULL,
-- User and visit
"user_id" varchar(255),
"user_ipaddress" varchar(45),
"user_fingerprint" varchar(50),
"domain_userid" varchar(36),
"domain_sessionidx" smallint,
"network_userid" varchar(38),
-- Location
"geo_country" char(2),
"geo_region" char(3),
"geo_city" varchar(75),
"geo_zipcode" varchar(15),
"geo_latitude" double precision,
"geo_longitude" double precision,
"geo_region_name" varchar(100),
-- IP lookups
"ip_isp" varchar(100),
"ip_organization" varchar(100),
"ip_domain" varchar(100),
"ip_netspeed" varchar(100),
-- Page
"page_url" text,
"page_title" varchar(2000),
"page_referrer" text,
-- Page URL components
"page_urlscheme" varchar(16),
"page_urlhost" varchar(255),
"page_urlport" integer,
"page_urlpath" varchar(3000),
"page_urlquery" varchar(6000),
"page_urlfragment" varchar(3000),
-- Referrer URL components
"refr_urlscheme" varchar(16),
"refr_urlhost" varchar(255),
"refr_urlport" integer,
"refr_urlpath" varchar(6000),
"refr_urlquery" varchar(6000),
"refr_urlfragment" varchar(3000),
-- Referrer details
"refr_medium" varchar(25),
"refr_source" varchar(50),
"refr_term" varchar(255),
-- Marketing
"mkt_medium" varchar(255),
"mkt_source" varchar(255),
"mkt_term" varchar(255),
"mkt_content" varchar(500),
"mkt_campaign" varchar(255),
-- Custom structured event
"se_category" varchar(1000),
"se_action" varchar(1000),
"se_label" varchar(1000),
"se_property" varchar(1000),
"se_value" double precision,
-- Ecommerce
"tr_orderid" varchar(255),
"tr_affiliation" varchar(255),
"tr_total" decimal(18,2),
"tr_tax" decimal(18,2),
"tr_shipping" decimal(18,2),
"tr_city" varchar(255),
"tr_state" varchar(255),
"tr_country" varchar(255),
"ti_orderid" varchar(255),
"ti_sku" varchar(255),
"ti_name" varchar(255),
"ti_category" varchar(255),
"ti_price" decimal(18,2),
"ti_quantity" integer,
-- Page ping
"pp_xoffset_min" integer,
"pp_xoffset_max" integer,
"pp_yoffset_min" integer,
"pp_yoffset_max" integer,
-- User Agent
"useragent" varchar(1000),
-- Browser
"br_name" varchar(50),
"br_family" varchar(50),
"br_version" varchar(50),
"br_type" varchar(50),
"br_renderengine" varchar(50),
"br_lang" varchar(255),
"br_features_pdf" boolean,
"br_features_flash" boolean,
"br_features_java" boolean,
"br_features_director" boolean,
"br_features_quicktime" boolean,
"br_features_realplayer" boolean,
"br_features_windowsmedia" boolean,
"br_features_gears" boolean,
"br_features_silverlight" boolean,
"br_cookies" boolean,
"br_colordepth" varchar(12),
"br_viewwidth" integer,
"br_viewheight" integer,
-- Operating System
"os_name" varchar(50),
"os_family" varchar(50),
"os_manufacturer" varchar(50),
"os_timezone" varchar(50),
-- Device/Hardware
"dvce_type" varchar(50),
"dvce_ismobile" boolean,
"dvce_screenwidth" integer,
"dvce_screenheight" integer,
-- Document
"doc_charset" varchar(128),
"doc_width" integer,
"doc_height" integer,
-- Currency
"tr_currency" char(3),
"tr_total_base" decimal(18, 2),
"tr_tax_base" decimal(18, 2),
"tr_shipping_base" decimal(18, 2),
"ti_currency" char(3),
"ti_price_base" decimal(18, 2),
"base_currency" char(3),
-- Geolocation
"geo_timezone" varchar(64),
-- Click ID
"mkt_clickid" varchar(128),
"mkt_network" varchar(64),
-- ETL tags
"etl_tags" varchar(500),
-- Time event was sent
"dvce_sent_tstamp" timestamp with time zone,
-- Referer
"refr_domain_userid" varchar(36),
"refr_dvce_tstamp" timestamp with time zone,
-- Session ID
"domain_sessionid" char(36),
-- Derived timestamp
"derived_tstamp" timestamp with time zone,
-- Event schema
"event_vendor" varchar(1000),
"event_name" varchar(1000),
"event_format" varchar(128),
"event_version" varchar(128),
-- Event fingerprint
"event_fingerprint" varchar(128),
-- True timestamp
"true_tstamp" timestamp with time zone,
PRIMARY KEY (id, project_id)
) PARTITION BY HASH (project_id)
WITHOUT OIDS;
CREATE INDEX index_product_analytics_events_experimental_project_and_time ON product_analytics_events_experimental (project_id, collector_tstamp);
SQL
create_hash_partitions :product_analytics_events_experimental, 64
end
end
def down
with_lock_retries do
execute 'DROP TABLE product_analytics_events_experimental'
end
end
end
此差异已折叠。
......@@ -56,9 +56,7 @@ From there, you can see the following actions:
- User sign-in via [Group SAML](../user/group/saml_sso/index.md)
- Permissions changes of a user assigned to a group
- Removed user from group
- Project imported in to group
- Project added to group and with which visibility level
- Project removed from group
- Project repository imported into group
- [Project shared with group](../user/project/members/share_project_with_groups.md)
and with which [permissions](../user/permissions.md)
- Removal of a previously shared group with a project
......@@ -80,7 +78,7 @@ To view a project's audit events, navigate to **Project > Settings > Audit Event
From there, you can see the following actions:
- Added or removed deploy keys
- Project created, deleted, renamed, moved(transferred), changed path
- Project created, deleted, renamed, moved (transferred), changed path
- Project changed visibility level
- User was added to project and with which [permissions](../user/permissions.md)
- Permission changes of a user assigned to a project
......
......@@ -491,6 +491,28 @@ GET /api/v4/projects/1/branches/my%2Fbranch/commits
GET /api/v4/projects/1/repository/tags/my%2Ftag
```
## Request Payload
API Requests can use parameters sent as [query strings](https://en.wikipedia.org/wiki/Query_string)
or as a [payload body](https://tools.ietf.org/html/draft-ietf-httpbis-p3-payload-14#section-3.2).
GET requests usually send a query string, while PUT/POST requests usually send the payload body:
- Query string:
```shell
curl --request POST "https://gitlab/api/v4/projects?name=<example-name>&description=<example-description>"
```
- Request payload (JSON):
```shell
curl --request POST --header "Content-Type: application/json" --data '{"name":"<example-name>", "description":"<example-description"}' "https://gitlab/api/v4/projects"
```
URL encoded query strings have a length limitation. Requests that are too large will
result in a `414 Request-URI Too Large` error message. This can be resolved by using
a payload body instead.
## Encoding API parameters of `array` and `hash` types
We can call the API with `array` and `hash` types parameters as shown below:
......
......@@ -49,3 +49,10 @@ metrics:
reports:
metrics: metrics.txt
```
## Advanced Example
An advanced example of an OpenMetrics text file (from the [Prometheus documentation](https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md#text-format-example))
renders in the merge request widget as:
![Metrics Reports Advanced](img/metrics_reports_advanced_v13_0.png)
......@@ -332,12 +332,6 @@ The current permissions on the Project Value Stream Analytics dashboard are:
You can [read more about permissions](../../ci/yaml/README.md) in general.
NOTE: **Note:**
As of GitLab 12.3, the project-level page is deprecated. You should access
project-level Value Stream Analytics from **Analytics > Value Stream Analytics** in the top
navigation bar. We will ensure that the same project-level functionality is available
to CE users in the new analytics space.
For Value Stream Analytics functionality introduced in GitLab 12.3 and later:
- Users must have Reporter access or above.
......
......@@ -32,7 +32,7 @@ You can enable container scanning by doing one of the following:
GitLab compares the found vulnerabilities between the source and target branches, and shows the
information directly in the merge request.
![Container Scanning Widget](img/container_scanning_v13_0.png)
![Container Scanning Widget](img/container_scanning_v13_1.png)
<!-- NOTE: The container scanning tool references the following heading in the code, so if you
make a change to this heading, make sure to update the documentation URLs used in the
......
......@@ -36,7 +36,7 @@ NOTE: **Note:**
This comparison logic uses only the latest pipeline executed for the target branch's base commit.
Running the pipeline on any other commit has no effect on the merge request.
![DAST Widget](img/dast_all_v13_0.png)
![DAST Widget](img/dast_all_v13_1.png)
By clicking on one of the detected linked vulnerabilities, you can
see the details and the URL(s) affected.
......
......@@ -27,7 +27,7 @@ GitLab checks the Dependency Scanning report, compares the found vulnerabilities
between the source and target branches, and shows the information on the
merge request.
![Dependency Scanning Widget](img/dependency_scanning_v13_0.png)
![Dependency Scanning Widget](img/dependency_scanning_v13_1.png)
The results are sorted by the severity of the vulnerability:
......
......@@ -28,7 +28,7 @@ You can take advantage of SAST by doing one of the following:
GitLab checks the SAST report, compares the found vulnerabilities between the
source and target branches, and shows the information right on the merge request.
![SAST Widget](img/sast_v13_0.png)
![SAST Widget](img/sast_v13_1.png)
The results are sorted by the priority of the vulnerability:
......
# frozen_string_literal: true
module Gitlab
module Danger
module SidekiqQueues
def changed_queue_files
@changed_queue_files ||= git.modified_files.grep(%r{\A(ee/)?app/workers/all_queues\.yml})
end
def added_queue_names
@added_queue_names ||= new_queues.keys - old_queues.keys
end
def changed_queue_names
@changed_queue_names ||=
(new_queues.values_at(*old_queues.keys) - old_queues.values)
.map { |queue| queue[:name] }
end
private
def old_queues
@old_queues ||= queues_for(gitlab.base_commit)
end
def new_queues
@new_queues ||= queues_for(gitlab.head_commit)
end
def queues_for(branch)
changed_queue_files
.flat_map { |file| YAML.safe_load(`git show #{branch}:#{file}`, permitted_classes: [Symbol]) }
.to_h { |queue| [queue[:name], queue] }
end
end
end
end
......@@ -22,12 +22,15 @@ module Gitlab
MIN_SCHEMA_VERSION = 20190506135400
MIN_SCHEMA_GITLAB_VERSION = '11.11.0'
# Schema we store dynamically managed partitions in
# Schema we store dynamically managed partitions in (e.g. for time partitioning)
DYNAMIC_PARTITIONS_SCHEMA = :gitlab_partitions_dynamic
# Schema we store static partitions in (e.g. for hash partitioning)
STATIC_PARTITIONS_SCHEMA = :gitlab_partitions_static
# This is an extensive list of postgres schemas owned by GitLab
# It does not include the default public schema
EXTRA_SCHEMAS = [DYNAMIC_PARTITIONS_SCHEMA].freeze
EXTRA_SCHEMAS = [DYNAMIC_PARTITIONS_SCHEMA, STATIC_PARTITIONS_SCHEMA].freeze
define_histogram :gitlab_database_transaction_seconds do
docstring "Time spent in database transactions, in seconds"
......
......@@ -70,6 +70,23 @@ module Gitlab
drop_table(part_table_name)
end
def create_hash_partitions(table_name, number_of_partitions)
transaction do
(0..number_of_partitions - 1).each do |partition|
decimals = Math.log10(number_of_partitions).ceil
suffix = "%0#{decimals}d" % partition
partition_name = "#{table_name}_#{suffix}"
schema = Gitlab::Database::STATIC_PARTITIONS_SCHEMA
execute(<<~SQL)
CREATE TABLE #{schema}.#{partition_name}
PARTITION OF #{table_name}
FOR VALUES WITH (MODULUS #{number_of_partitions}, REMAINDER #{partition});
SQL
end
end
end
private
def assert_table_is_allowed(table_name)
......
......@@ -21,6 +21,7 @@ class GitlabDanger
specs
roulette
ce_ee_vue_templates
sidekiq_queues
].freeze
MESSAGE_PREFIX = '==>'.freeze
......
......@@ -534,6 +534,36 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
msgid "%{reportType} %{status} detected %{critical} critical and %{high} high severity vulnerabilities out of %{total}."
msgstr ""
msgid "%{reportType} %{status} detected %{critical} critical and %{high} high severity vulnerabilities."
msgstr ""
msgid "%{reportType} %{status} detected %{critical} critical severity vulnerabilities out of %{total}."
msgstr ""
msgid "%{reportType} %{status} detected %{critical} critical severity vulnerability."
msgid_plural "%{reportType} %{status} detected %{critical} critical severity vulnerabilities."
msgstr[0] ""
msgstr[1] ""
msgid "%{reportType} %{status} detected %{high} high severity vulnerabilities out of %{total}."
msgstr ""
msgid "%{reportType} %{status} detected %{high} high severity vulnerability."
msgid_plural "%{reportType} %{status} detected %{high} high severity vulnerabilities."
msgstr[0] ""
msgstr[1] ""
msgid "%{reportType} %{status} detected %{other} vulnerability."
msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
msgstr[0] ""
msgstr[1] ""
msgid "%{reportType} %{status} detected no new vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}"
msgstr ""
......@@ -8009,6 +8039,9 @@ msgstr ""
msgid "Dismissed on pipeline %{pipelineLink} at %{projectLink}"
msgstr ""
msgid "Display alerts from all your monitoring tools directly within GitLab."
msgstr ""
msgid "Display name"
msgstr ""
......@@ -25389,6 +25422,9 @@ msgstr ""
msgid "VisibilityLevel|Unknown"
msgstr ""
msgid "Visit settings page"
msgstr ""
msgid "VisualReviewApp|%{stepStart}Step 1%{stepEnd}. Copy the following script:"
msgstr ""
......@@ -26111,6 +26147,9 @@ msgstr ""
msgid "You can now export your security dashboard to a CSV report."
msgstr ""
msgid "You can now manage alert endpoint configuration in the Alerts section on the Operations settings page. Fields on this page have been deprecated."
msgstr ""
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
......@@ -26779,55 +26818,6 @@ msgstr ""
msgid "ciReport|%{remainingPackagesCount} more"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerability"
msgid_plural "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgid "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerability for the source branch only"
msgid_plural "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerabilities for the source branch only"
msgstr[0] ""
msgstr[1] ""
msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed, and %{dismissedCount} dismissed vulnerabilities"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgid "ciReport|%{reportType} %{status} detected %{newCount} new, %{fixedCount} fixed, and %{dismissedCount} dismissed vulnerabilities"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{dismissedCount} dismissed vulnerabilities"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{dismissedCount} dismissed vulnerabilities for the source branch only"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected %{newCount} vulnerability for the source branch only"
msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} vulnerabilities for the source branch only"
msgstr[0] ""
msgstr[1] ""
msgid "ciReport|%{reportType} %{status} detected no new vulnerabilities"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected no vulnerabilities"
msgstr ""
msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
msgid "ciReport|%{reportType} is loading"
msgstr ""
......
......@@ -79,6 +79,7 @@ module QA
autoload :PersonalAccessToken, 'qa/resource/personal_access_token'
autoload :User, 'qa/resource/user'
autoload :ProjectMilestone, 'qa/resource/project_milestone'
autoload :GroupMilestone, 'qa/resource/group_milestone'
autoload :Members, 'qa/resource/members'
autoload :File, 'qa/resource/file'
autoload :Fork, 'qa/resource/fork'
......
# frozen_string_literal: true
module QA
module Resource
class GroupMilestone < Base
attr_writer :start_date, :due_date
attribute :id
attribute :title
attribute :group do
Group.fabricate_via_api! do |resource|
resource.name = 'group-with-milestone'
end
end
def initialize
@title = "group-milestone-#{SecureRandom.hex(4)}"
end
def api_get_path
"/groups/#{group.id}/milestones/#{id}"
end
def api_post_path
"/groups/#{group.id}/milestones"
end
def api_post_body
{
title: title
}.tap do |hash|
hash[:start_date] = @start_date if @start_date
hash[:due_date] = @due_date if @due_date
end
end
end
end
end
# frozen_string_literal: true
module QA
context 'Plan' do
describe 'Milestones' do
include Support::Dates
let(:start_date) { current_date_yyyy_mm_dd }
let(:due_date) { next_month_yyyy_mm_dd }
let(:group) do
Resource::Group.fabricate_via_api! do |group|
group.name = 'group-to-test-milestones'
end
end
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-to-test-milestones'
end
end
let(:issue) do
Resource::Issue.fabricate_via_api! do |issue|
issue.project = project
end
end
before do
Flow::Login.sign_in
end
shared_examples 'milestone assigned to existing issue' do
it 'is assigned to an existing issue' do
issue.visit!
Page::Project::Issue::Show.perform do |existing_issue|
existing_issue.assign_milestone(milestone)
expect(existing_issue).to have_milestone(milestone.title)
end
end
end
shared_examples 'milestone assigned to new issue' do
it 'is assigned to a new issue' do
Resource::Issue.fabricate_via_browser_ui! do |new_issue|
new_issue.project = project
new_issue.milestone = milestone
end
Page::Project::Issue::Show.perform do |issue|
expect(issue).to have_milestone(milestone.title)
end
end
end
context 'Group milestone' do
let(:milestone) do
Resource::GroupMilestone.fabricate_via_api! do |milestone|
milestone.group = group
milestone.start_date = start_date
milestone.due_date = due_date
end
end
it_behaves_like 'milestone assigned to existing issue'
it_behaves_like 'milestone assigned to new issue'
end
context 'Project milestone' do
let(:milestone) do
Resource::ProjectMilestone.fabricate_via_api! do |milestone|
milestone.project = project
milestone.start_date = start_date
milestone.due_date = due_date
end
end
it_behaves_like 'milestone assigned to existing issue'
it_behaves_like 'milestone assigned to new issue'
end
end
end
end
# frozen_string_literal: true
module QA
context 'Plan' do
describe 'Project milestone' do
include Support::Dates
let(:title) { 'Project milestone' }
let(:start_date) { current_date_yyyy_mm_dd }
let(:due_date) { next_month_yyyy_mm_dd }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-to-test-milestones'
end
end
let(:issue) do
Resource::Issue.fabricate_via_api! do |issue|
issue.project = project
end
end
let(:project_milestone) do
Resource::ProjectMilestone.fabricate_via_api! do |milestone|
milestone.project = project
milestone.start_date = start_date
milestone.due_date = due_date
end
end
before do
Flow::Login.sign_in
end
it 'assigns a project milestone to an existing issue' do
issue.visit!
Page::Project::Issue::Show.perform do |existing_issue|
existing_issue.assign_milestone(project_milestone)
expect(existing_issue).to have_milestone(project_milestone.title)
end
end
it 'assigns a project milestone to a new issue' do
Resource::Issue.fabricate_via_browser_ui! do |issue|
issue.project = project
issue.milestone = project_milestone
end
Page::Project::Issue::Show.perform do |issue|
expect(issue).to have_milestone(project_milestone.title)
end
end
end
end
end
......@@ -10,6 +10,7 @@ module RuboCop
ALLOWED_MIGRATION_METHODS = %i[
create_table
create_hash_partitions
drop_table
add_foreign_key
remove_foreign_key
......
......@@ -63,6 +63,7 @@ RSpec.describe 'Database schema' do
oauth_access_tokens: %w[resource_owner_id application_id],
oauth_applications: %w[owner_id],
open_project_tracker_data: %w[closed_status_id],
product_analytics_events_experimental: %w[event_id txn_id user_id],
project_group_links: %w[group_id],
project_statistics: %w[namespace_id],
projects: %w[creator_id ci_id mirror_user_id],
......
......@@ -21,6 +21,7 @@ FactoryBot.define do
project_statistics.lfs_objects_size = evaluator.size_multiplier * 3
project_statistics.build_artifacts_size = evaluator.size_multiplier * 4
project_statistics.packages_size = evaluator.size_multiplier * 5
project_statistics.snippets_size = evaluator.size_multiplier * 6
end
end
end
......
......@@ -15,35 +15,32 @@ RSpec.describe 'User activates Alerts', :js do
end
context 'when service is deactivated' do
it 'activates service' do
it 'user cannot activate service' do
visit_project_services
expect(page).to have_link(service_title)
click_link(service_title)
expect(page).to have_callout_message
expect(page).not_to have_active_service
click_activate_service
wait_for_requests
expect(page).to have_active_service
expect(page).to have_toggle_active_disabled
end
end
context 'when service is activated' do
let_it_be(:activated_alerts_service) do
create(:alerts_service, :active, project: project)
end
before do
visit_alerts_service
click_activate_service
end
it 're-generates key' do
expect(reset_key.value).to be_blank
click_reset_key
click_confirm_reset_key
wait_for_requests
expect(reset_key.value).to be_present
it 'user cannot change settings' do
expect(page).to have_callout_message
expect(page).to have_active_service
expect(page).to have_toggle_active_disabled
expect(page).to have_button_reset_key_disabled
end
end
......@@ -57,25 +54,21 @@ RSpec.describe 'User activates Alerts', :js do
visit(edit_project_service_path(project, service_name))
end
def click_activate_service
find('#activated').click
end
def click_reset_key
click_button('Reset key')
def have_callout_message
within('.gl-alert') do
have_content('You can now manage alert endpoint configuration in the Alerts section on the Operations settings page.')
end
end
def click_confirm_reset_key
within '.modal-content' do
click_reset_key
end
def have_active_service
have_selector('.js-service-active-status[data-value="true"]')
end
def reset_key
find_field('Authorization key')
def have_toggle_active_disabled
have_selector('#activated .project-feature-toggle.is-disabled')
end
def have_active_service
have_selector('.js-service-active-status[data-value="true"]')
def have_button_reset_key_disabled
have_button('Reset key', disabled: true)
end
end
......@@ -15,6 +15,7 @@ const defaultProps = {
alertsSetupUrl: 'http://invalid',
alertsUsageUrl: 'http://invalid',
initialActivated: false,
isDisabled: false,
};
describe('AlertsServiceForm', () => {
......@@ -166,4 +167,17 @@ describe('AlertsServiceForm', () => {
});
});
});
describe('form is disabled', () => {
beforeEach(() => {
createComponent({ isDisabled: true });
});
it('cannot be toggled', () => {
wrapper.find(ToggleButton).vm.$emit('change');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(ToggleButton).props('disabledInput')).toBe(true);
});
});
});
});
......@@ -3,14 +3,14 @@
require "spec_helper"
RSpec.describe UserCalloutsHelper do
let(:user) { create(:user) }
let_it_be(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
describe '.show_gke_cluster_integration_callout?' do
let(:project) { create(:project) }
let_it_be(:project) { create(:project) }
subject { helper.show_gke_cluster_integration_callout?(project) }
......@@ -67,6 +67,26 @@ RSpec.describe UserCalloutsHelper do
end
end
describe '.show_alerts_moved_alert?' do
subject { helper.show_alerts_moved_alert? }
context 'when user has not dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::ALERTS_MOVED) { false }
end
it { is_expected.to be true }
end
context 'when user dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::ALERTS_MOVED) { true }
end
it { is_expected.to be false }
end
end
describe '.render_flash_user_callout' do
it 'renders the flash_user_callout partial' do
expect(helper).to receive(:render)
......
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rspec-parameterized'
require_relative 'danger_spec_helper'
require 'gitlab/danger/sidekiq_queues'
RSpec.describe Gitlab::Danger::SidekiqQueues do
using RSpec::Parameterized::TableSyntax
include DangerSpecHelper
let(:fake_git) { double('fake-git') }
let(:fake_danger) { new_fake_danger.include(described_class) }
subject(:sidekiq_queues) { fake_danger.new(git: fake_git) }
describe '#changed_queue_files' do
where(:modified_files, :changed_queue_files) do
%w(app/workers/all_queues.yml ee/app/workers/all_queues.yml foo) | %w(app/workers/all_queues.yml ee/app/workers/all_queues.yml)
%w(app/workers/all_queues.yml ee/app/workers/all_queues.yml) | %w(app/workers/all_queues.yml ee/app/workers/all_queues.yml)
%w(app/workers/all_queues.yml foo) | %w(app/workers/all_queues.yml)
%w(ee/app/workers/all_queues.yml foo) | %w(ee/app/workers/all_queues.yml)
%w(foo) | %w()
%w() | %w()
end
with_them do
it do
allow(fake_git).to receive(:modified_files).and_return(modified_files)
expect(sidekiq_queues.changed_queue_files).to match_array(changed_queue_files)
end
end
end
describe '#added_queue_names' do
it 'returns queue names added by this change' do
old_queues = { post_receive: nil }
allow(sidekiq_queues).to receive(:old_queues).and_return(old_queues)
allow(sidekiq_queues).to receive(:new_queues).and_return(old_queues.merge(merge: nil, process_commit: nil))
expect(sidekiq_queues.added_queue_names).to contain_exactly(:merge, :process_commit)
end
end
describe '#changed_queue_names' do
it 'returns names for queues whose attributes were changed' do
old_queues = {
merge: { name: :merge, urgency: :low },
post_receive: { name: :post_receive, urgency: :high },
process_commit: { name: :process_commit, urgency: :high }
}
new_queues = old_queues.merge(mailers: { name: :mailers, urgency: :high },
post_receive: { name: :post_receive, urgency: :low },
process_commit: { name: :process_commit, urgency: :low })
allow(sidekiq_queues).to receive(:old_queues).and_return(old_queues)
allow(sidekiq_queues).to receive(:new_queues).and_return(new_queues)
expect(sidekiq_queues.changed_queue_names).to contain_exactly(:post_receive, :process_commit)
end
end
end
......@@ -320,6 +320,38 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe
end
end
describe '#create_hash_partitions' do
before do
connection.execute(<<~SQL)
CREATE TABLE #{partitioned_table}
(id serial not null, some_id integer not null, PRIMARY KEY (id, some_id))
PARTITION BY HASH (some_id);
SQL
end
it 'creates partitions for the full hash space (8 partitions)' do
partitions = 8
migration.create_hash_partitions(partitioned_table, partitions)
(0..partitions - 1).each do |partition|
partition_name = "#{partitioned_table}_#{"%01d" % partition}"
expect_hash_partition_of(partition_name, partitioned_table, partitions, partition)
end
end
it 'creates partitions for the full hash space (16 partitions)' do
partitions = 16
migration.create_hash_partitions(partitioned_table, partitions)
(0..partitions - 1).each do |partition|
partition_name = "#{partitioned_table}_#{"%02d" % partition}"
expect_hash_partition_of(partition_name, partitioned_table, partitions, partition)
end
end
end
def filter_columns_by_name(columns, names)
columns.reject { |c| names.include?(c.name) }
end
......
......@@ -153,7 +153,8 @@ RSpec.describe Namespace do
wiki_size: 505,
lfs_objects_size: 202,
build_artifacts_size: 303,
packages_size: 404))
packages_size: 404,
snippets_size: 605))
end
let(:project2) do
......@@ -164,7 +165,8 @@ RSpec.describe Namespace do
wiki_size: 50,
lfs_objects_size: 20,
build_artifacts_size: 30,
packages_size: 40))
packages_size: 40,
snippets_size: 60))
end
it "sums all project storage counters in the namespace" do
......@@ -172,12 +174,13 @@ RSpec.describe Namespace do
project2
statistics = described_class.with_statistics.find(namespace.id)
expect(statistics.storage_size).to eq 1665
expect(statistics.storage_size).to eq 2330
expect(statistics.repository_size).to eq 111
expect(statistics.wiki_size).to eq 555
expect(statistics.lfs_objects_size).to eq 222
expect(statistics.build_artifacts_size).to eq 333
expect(statistics.packages_size).to eq 444
expect(statistics.snippets_size).to eq 665
end
it "correctly handles namespaces without projects" do
......@@ -189,6 +192,7 @@ RSpec.describe Namespace do
expect(statistics.lfs_objects_size).to eq 0
expect(statistics.build_artifacts_size).to eq 0
expect(statistics.packages_size).to eq 0
expect(statistics.snippets_size).to eq 0
end
end
......
......@@ -32,7 +32,8 @@ RSpec.describe ProjectStatistics do
repository_size: 2.exabytes,
wiki_size: 1.exabytes,
lfs_objects_size: 2.exabytes,
build_artifacts_size: 3.exabytes - 1
build_artifacts_size: 2.exabytes - 1,
snippets_size: 1.exabyte
)
statistics.reload
......@@ -41,8 +42,9 @@ RSpec.describe ProjectStatistics do
expect(statistics.repository_size).to eq(2.exabytes)
expect(statistics.wiki_size).to eq(1.exabytes)
expect(statistics.lfs_objects_size).to eq(2.exabytes)
expect(statistics.build_artifacts_size).to eq(3.exabytes - 1)
expect(statistics.build_artifacts_size).to eq(2.exabytes - 1)
expect(statistics.storage_size).to eq(8.exabytes - 1)
expect(statistics.snippets_size).to eq(1.exabyte)
end
end
......@@ -52,23 +54,47 @@ RSpec.describe ProjectStatistics do
statistics.wiki_size = 6
statistics.lfs_objects_size = 3
statistics.build_artifacts_size = 4
statistics.snippets_size = 5
expect(statistics.total_repository_size).to eq 5
end
end
describe '#wiki_size' do
it "is initialized with not null value" do
it 'is initialized with not null value' do
expect(statistics.attributes['wiki_size']).to be_zero
expect(statistics.wiki_size).to be_zero
end
it 'coerces any nil value to 0' do
statistics.update!(wiki_size: nil)
expect(statistics.attributes['wiki_size']).to be_nil
expect(statistics.wiki_size).to eq 0
end
end
describe '#snippets_size' do
it 'is initialized with not null value' do
expect(statistics.attributes['snippets_size']).to be_zero
expect(statistics.snippets_size).to be_zero
end
it 'coerces any nil value to 0' do
statistics.update!(snippets_size: nil)
expect(statistics.attributes['snippets_size']).to be_nil
expect(statistics.snippets_size).to eq 0
end
end
describe '#refresh!' do
before do
allow(statistics).to receive(:update_commit_count)
allow(statistics).to receive(:update_repository_size)
allow(statistics).to receive(:update_wiki_size)
allow(statistics).to receive(:update_lfs_objects_size)
allow(statistics).to receive(:update_snippets_size)
allow(statistics).to receive(:update_storage_size)
end
......@@ -82,6 +108,7 @@ RSpec.describe ProjectStatistics do
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
expect(statistics).to have_received(:update_lfs_objects_size)
expect(statistics).to have_received(:update_snippets_size)
end
end
......@@ -95,6 +122,7 @@ RSpec.describe ProjectStatistics do
expect(statistics).not_to have_received(:update_commit_count)
expect(statistics).not_to have_received(:update_repository_size)
expect(statistics).not_to have_received(:update_wiki_size)
expect(statistics).not_to have_received(:update_snippets_size)
end
end
......@@ -108,9 +136,11 @@ RSpec.describe ProjectStatistics do
expect(statistics).to have_received(:update_commit_count)
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
expect(statistics).to have_received(:update_snippets_size)
expect(statistics.repository_size).to eq(0)
expect(statistics.commit_count).to eq(0)
expect(statistics.wiki_size).to eq(0)
expect(statistics.snippets_size).to eq(0)
end
end
......@@ -130,9 +160,11 @@ RSpec.describe ProjectStatistics do
expect(statistics).to have_received(:update_commit_count)
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
expect(statistics).to have_received(:update_snippets_size)
expect(statistics.repository_size).to eq(0)
expect(statistics.commit_count).to eq(0)
expect(statistics.wiki_size).to eq(0)
expect(statistics.snippets_size).to eq(0)
end
end
......@@ -202,6 +234,33 @@ RSpec.describe ProjectStatistics do
end
end
describe '#update_snippets_size' do
before do
create_list(:project_snippet, 2, project: project)
SnippetStatistics.update_all(repository_size: 10)
end
it 'stores the size of snippets' do
# Snippet not associated with the project
snippet = create(:project_snippet)
snippet.statistics.update!(repository_size: 40)
statistics.update_snippets_size
expect(statistics.update_snippets_size).to eq 20
end
context 'when not all snippets has statistics' do
it 'stores the size of snippets with statistics' do
SnippetStatistics.last.delete
statistics.update_snippets_size
expect(statistics.update_snippets_size).to eq 10
end
end
end
describe '#update_lfs_objects_size' do
let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) }
let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) }
......@@ -222,12 +281,13 @@ RSpec.describe ProjectStatistics do
statistics.update!(
repository_size: 2,
wiki_size: 4,
lfs_objects_size: 3
lfs_objects_size: 3,
snippets_size: 2
)
statistics.reload
expect(statistics.storage_size).to eq 9
expect(statistics.storage_size).to eq 11
end
it 'works during wiki_size backfill' do
......@@ -241,6 +301,21 @@ RSpec.describe ProjectStatistics do
expect(statistics.storage_size).to eq 5
end
context 'when nullable columns are nil' do
it 'does not raise any error' do
expect do
statistics.update!(
repository_size: 2,
wiki_size: nil,
lfs_objects_size: 3,
snippets_size: nil
)
end.not_to raise_error
expect(statistics.storage_size).to eq 5
end
end
end
describe '.increment_statistic' do
......
......@@ -31,6 +31,14 @@ module PartitioningHelpers
expect_total_partitions(table_name, partitions.size, schema: Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA)
end
def expect_hash_partition_of(partition_name, table_name, modulus, remainder)
definition = find_partition_definition(partition_name, schema: Gitlab::Database::STATIC_PARTITIONS_SCHEMA)
expect(definition).not_to be_nil
expect(definition['base_table']).to eq(table_name.to_s)
expect(definition['condition']).to eq("FOR VALUES WITH (modulus #{modulus}, remainder #{remainder})")
end
private
def find_partitioned_columns(table)
......@@ -55,7 +63,7 @@ module PartitioningHelpers
SQL
end
def find_partition_definition(partition, schema: Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA)
def find_partition_definition(partition, schema: )
connection.select_one(<<~SQL)
select
parent_class.relname as base_table,
......
......@@ -3,8 +3,15 @@
require 'spec_helper'
RSpec.describe 'projects/settings/operations/show' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let(:operations_show_locals) do
{
prometheus_service: project.find_or_initialize_service('prometheus'),
alerts_service: project.find_or_initialize_service('alerts')
}
end
before do
assign :project, project
......@@ -20,13 +27,13 @@ RSpec.describe 'projects/settings/operations/show' do
allow(view).to receive(:incident_management_available?) { false }
end
let!(:error_tracking_setting) do
let_it_be(:error_tracking_setting) do
create(:project_error_tracking_setting, project: project)
end
context 'Settings page ' do
it 'renders the Operations Settings page' do
render template: "projects/settings/operations/show", locals: { prometheus_service: project.find_or_initialize_service('prometheus') }
render template: 'projects/settings/operations/show', locals: operations_show_locals
expect(rendered).to have_content _('Error Tracking')
expect(rendered).to have_content _('To link Sentry to GitLab, enter your Sentry URL and Auth Token')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册