Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3172281335
commit
87911fabb2
43 changed files with 406 additions and 141 deletions
|
@ -192,7 +192,7 @@ export default {
|
|||
async createBoard() {
|
||||
// TODO: change this to use `createBoard` mutation https://gitlab.com/gitlab-org/gitlab/-/issues/292466 is resolved
|
||||
const boardData = await getBoardsPath(this.endpoints.boardsEndpoint, this.boardPayload);
|
||||
await this.callBoardMutation(fullBoardId(boardData.data.id));
|
||||
this.callBoardMutation(fullBoardId(boardData.data.id));
|
||||
|
||||
return boardData.data || boardData;
|
||||
},
|
||||
|
|
|
@ -286,6 +286,7 @@ export default {
|
|||
handleFilterSubmit() {
|
||||
const filterTokens = uniqueTokens(this.filterValue);
|
||||
this.filterValue = filterTokens;
|
||||
|
||||
if (this.recentSearchesStorageKey) {
|
||||
this.recentSearchesPromise
|
||||
.then(() => {
|
||||
|
@ -302,6 +303,17 @@ export default {
|
|||
this.blurSearchInput();
|
||||
this.$emit('onFilter', this.removeQuotesEnclosure(filterTokens));
|
||||
},
|
||||
historyTokenOptionTitle(historyToken) {
|
||||
const tokenOption = this.tokens
|
||||
.find(token => token.type === historyToken.type)
|
||||
?.options?.find(option => option.value === historyToken.value.data);
|
||||
|
||||
if (!tokenOption?.title) {
|
||||
return historyToken.value.data;
|
||||
}
|
||||
|
||||
return tokenOption.title;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -333,7 +345,7 @@ export default {
|
|||
<span v-if="tokenTitles[token.type]"
|
||||
>{{ tokenTitles[token.type] }} :{{ token.value.operator }}</span
|
||||
>
|
||||
<strong>{{ tokenSymbols[token.type] }}{{ token.value.data }}</strong>
|
||||
<strong>{{ tokenSymbols[token.type] }}{{ historyTokenOptionTitle(token) }}</strong>
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -276,7 +276,7 @@
|
|||
@include fade(left, $gray-light);
|
||||
right: -5px;
|
||||
|
||||
.fa {
|
||||
svg {
|
||||
right: -7px;
|
||||
}
|
||||
}
|
||||
|
@ -286,7 +286,7 @@
|
|||
left: -5px;
|
||||
text-align: center;
|
||||
|
||||
.fa {
|
||||
svg {
|
||||
left: -7px;
|
||||
}
|
||||
}
|
||||
|
@ -337,7 +337,7 @@
|
|||
@include fade(left, $white);
|
||||
right: -5px;
|
||||
|
||||
.fa {
|
||||
svg {
|
||||
right: -7px;
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +346,7 @@
|
|||
@include fade(right, $white);
|
||||
left: -5px;
|
||||
|
||||
.fa {
|
||||
svg {
|
||||
left: -7px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,11 +43,6 @@
|
|||
.dropdown-toggle,
|
||||
.dropdown-menu {
|
||||
color: $gl-text-color-secondary;
|
||||
|
||||
.fa {
|
||||
color: $gl-text-color-secondary;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group.open .btn-default {
|
||||
|
|
|
@ -30,12 +30,6 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.member-controls {
|
||||
.fa {
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&.existing-title {
|
||||
@include media-breakpoint-up(sm) {
|
||||
float: left;
|
||||
|
|
|
@ -163,12 +163,6 @@ $mr-widget-min-height: 69px;
|
|||
|
||||
.btn {
|
||||
font-size: $gl-font-size;
|
||||
|
||||
&.dropdown-toggle {
|
||||
.fa {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accept-merge-holder {
|
||||
|
@ -341,13 +335,6 @@ $mr-widget-min-height: 69px;
|
|||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
.fa {
|
||||
margin-left: 0;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.has-custom-error {
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -552,10 +539,6 @@ $mr-widget-min-height: 69px;
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.dropdown-toggle .fa {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
.git-merge-container {
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
|
|
|
@ -778,13 +778,6 @@ $note-form-margin-left: 72px;
|
|||
outline: none;
|
||||
color: $blue-600;
|
||||
}
|
||||
|
||||
.fa {
|
||||
margin-right: 3px;
|
||||
font-size: 10px;
|
||||
line-height: 18px;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.note-role {
|
||||
|
|
|
@ -35,8 +35,4 @@
|
|||
.notification {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
.fa {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,10 +140,6 @@
|
|||
margin-left: 0;
|
||||
}
|
||||
|
||||
.fa {
|
||||
color: $layout-link-gray;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $layout-link-gray;
|
||||
}
|
||||
|
@ -163,13 +159,6 @@
|
|||
height: 24px;
|
||||
}
|
||||
|
||||
.dropdown-toggle,
|
||||
.clone-dropdown-btn {
|
||||
.fa {
|
||||
color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.home-panel-action-button,
|
||||
.project-action-button {
|
||||
margin: $gl-padding $gl-padding-8 0 0;
|
||||
|
@ -492,7 +481,7 @@
|
|||
top: 0;
|
||||
height: calc(100% - #{$browser-scrollbar-size});
|
||||
|
||||
.fa {
|
||||
svg {
|
||||
top: 50%;
|
||||
margin-top: -$gl-padding-8;
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
@charts = {}
|
||||
@counts = {}
|
||||
|
||||
return unless Feature.enabled?(:graphql_pipeline_analytics)
|
||||
return if Feature.enabled?(:graphql_pipeline_analytics)
|
||||
|
||||
@charts[:week] = Gitlab::Ci::Charts::WeekChart.new(project)
|
||||
@charts[:month] = Gitlab::Ci::Charts::MonthChart.new(project)
|
||||
|
|
|
@ -9,8 +9,8 @@ module Resolvers
|
|||
def resolve
|
||||
authorize!(object)
|
||||
|
||||
BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_discussions_count) do |ids, loader, args|
|
||||
counts = Note.count_for_collection(ids, 'Issue', 'COUNT(DISTINCT discussion_id) as count').index_by(&:noteable_id)
|
||||
BatchLoader::GraphQL.for(object.id).batch do |ids, loader, args|
|
||||
counts = Note.count_for_collection(ids, object.class.name, 'COUNT(DISTINCT discussion_id) as count').index_by(&:noteable_id)
|
||||
|
||||
ids.each do |id|
|
||||
loader.call(id, counts[id]&.count || 0)
|
||||
|
@ -19,7 +19,8 @@ module Resolvers
|
|||
end
|
||||
|
||||
def authorized_resource?(object)
|
||||
context[:current_user].present? && Ability.allowed?(context[:current_user], :read_issue, object)
|
||||
ability = "read_#{object.class.name.underscore}".to_sym
|
||||
context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,8 +9,8 @@ module Resolvers
|
|||
def resolve
|
||||
authorize!(object)
|
||||
|
||||
BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_notes_count) do |ids, loader, args|
|
||||
counts = Note.count_for_collection(ids, 'Issue').index_by(&:noteable_id)
|
||||
BatchLoader::GraphQL.for(object.id).batch(key: :user_notes_count) do |ids, loader, args|
|
||||
counts = Note.count_for_collection(ids, object.class.name).index_by(&:noteable_id)
|
||||
|
||||
ids.each do |id|
|
||||
loader.call(id, counts[id]&.count || 0)
|
||||
|
@ -19,7 +19,8 @@ module Resolvers
|
|||
end
|
||||
|
||||
def authorized_resource?(object)
|
||||
context[:current_user].present? && Ability.allowed?(context[:current_user], :read_issue, object)
|
||||
ability = "read_#{object.class.name.underscore}".to_sym
|
||||
context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,9 +69,11 @@ module Types
|
|||
field :merge_commit_sha, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'SHA of the merge request commit (set once merged)'
|
||||
field :user_notes_count, GraphQL::INT_TYPE, null: true,
|
||||
description: 'User notes count of the merge request'
|
||||
description: 'User notes count of the merge request',
|
||||
resolver: Resolvers::UserNotesCountResolver
|
||||
field :user_discussions_count, GraphQL::INT_TYPE, null: true,
|
||||
description: 'Number of user discussions in the merge request'
|
||||
description: 'Number of user discussions in the merge request',
|
||||
resolver: Resolvers::UserDiscussionsCountResolver
|
||||
field :should_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :should_remove_source_branch?, null: true,
|
||||
description: 'Indicates if the source branch of the merge request will be deleted after merge'
|
||||
field :force_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :force_remove_source_branch?, null: true,
|
||||
|
|
|
@ -102,7 +102,7 @@ module ServicesHelper
|
|||
cancel_path: scoped_integrations_path,
|
||||
can_test: integration.can_test?.to_s,
|
||||
test_path: scoped_test_integration_path(integration),
|
||||
reset_path: reset_integrations?(group: group) ? scoped_reset_integration_path(integration, group: group) : ''
|
||||
reset_path: reset_integration?(integration, group: group) ? scoped_reset_integration_path(integration, group: group) : ''
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -126,8 +126,8 @@ module ServicesHelper
|
|||
!Gitlab.com?
|
||||
end
|
||||
|
||||
def reset_integrations?(group: nil)
|
||||
Feature.enabled?(:reset_integrations, group, type: :development)
|
||||
def reset_integration?(integration, group: nil)
|
||||
integration.persisted? && Feature.enabled?(:reset_integrations, group, type: :development)
|
||||
end
|
||||
|
||||
extend self
|
||||
|
|
|
@ -86,9 +86,9 @@ class CommitCollection
|
|||
|
||||
# Batch load full Commits from the repository
|
||||
# and map to a Hash of id => Commit
|
||||
replacements = Hash[unenriched.map do |c|
|
||||
[c.id, Commit.lazy(container, c.id)]
|
||||
end.compact]
|
||||
replacements = unenriched.each_with_object({}) do |c, result|
|
||||
result[c.id] = Commit.lazy(container, c.id)
|
||||
end.compact
|
||||
|
||||
# Replace the commits, keeping the same order
|
||||
@commits = @commits.map do |original_commit|
|
||||
|
|
|
@ -28,7 +28,7 @@ class Packages::Package < ApplicationRecord
|
|||
validates :project, presence: true
|
||||
validates :name, presence: true
|
||||
|
||||
validates :name, format: { with: Gitlab::Regex.package_name_regex }, unless: -> { conan? || generic? }
|
||||
validates :name, format: { with: Gitlab::Regex.package_name_regex }, unless: -> { conan? || generic? || debian? }
|
||||
|
||||
validates :name,
|
||||
uniqueness: { scope: %i[project_id version package_type] }, unless: :conan?
|
||||
|
@ -40,6 +40,8 @@ class Packages::Package < ApplicationRecord
|
|||
validates :name, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
|
||||
validates :name, format: { with: Gitlab::Regex.generic_package_name_regex }, if: :generic?
|
||||
validates :name, format: { with: Gitlab::Regex.nuget_package_name_regex }, if: :nuget?
|
||||
validates :name, format: { with: Gitlab::Regex.debian_package_name_regex }, if: :debian_package?
|
||||
validates :name, inclusion: { in: %w[incoming] }, if: :debian_incoming?
|
||||
validates :version, format: { with: Gitlab::Regex.nuget_version_regex }, if: :nuget?
|
||||
validates :version, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
|
||||
validates :version, format: { with: Gitlab::Regex.maven_version_regex }, if: -> { version? && maven? }
|
||||
|
@ -51,6 +53,11 @@ class Packages::Package < ApplicationRecord
|
|||
presence: true,
|
||||
format: { with: Gitlab::Regex.generic_package_version_regex },
|
||||
if: :generic?
|
||||
validates :version,
|
||||
presence: true,
|
||||
format: { with: Gitlab::Regex.debian_version_regex },
|
||||
if: :debian_package?
|
||||
validate :forbidden_debian_changes, if: :debian?
|
||||
|
||||
enum package_type: { maven: 1, npm: 2, conan: 3, nuget: 4, pypi: 5, composer: 6, generic: 7, golang: 8, debian: 9 }
|
||||
|
||||
|
@ -184,6 +191,14 @@ class Packages::Package < ApplicationRecord
|
|||
tags.pluck(:name)
|
||||
end
|
||||
|
||||
def debian_incoming?
|
||||
debian? && version.nil?
|
||||
end
|
||||
|
||||
def debian_package?
|
||||
debian? && !version.nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def composer_tag_version?
|
||||
|
@ -228,4 +243,13 @@ class Packages::Package < ApplicationRecord
|
|||
errors.add(:base, _('Package already exists'))
|
||||
end
|
||||
end
|
||||
|
||||
def forbidden_debian_changes
|
||||
return unless persisted?
|
||||
|
||||
# Debian incoming
|
||||
if version_was.nil? || version.nil?
|
||||
errors.add(:version, _('cannot be changed')) if version_changed?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
5
changelogs/unreleased/ab-track-bloat.yml
Normal file
5
changelogs/unreleased/ab-track-bloat.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Track index bloat estimate
|
||||
merge_request: 49822
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Capture subgroup creation failure during Group Import via archive file
|
||||
merge_request: 49484
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/ldap-encrypted-usage-data.yml
Normal file
5
changelogs/unreleased/ldap-encrypted-usage-data.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add ldap encrypted credentials to the usage data
|
||||
merge_request: 48210
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Reduce object allocations for large merge request
|
||||
merge_request: 49563
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddBloatEstimateToReindexAction < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :postgres_reindex_actions, :bloat_estimate_bytes_start, :bigint
|
||||
end
|
||||
end
|
1
db/schema_migrations/20201211145950
Normal file
1
db/schema_migrations/20201211145950
Normal file
|
@ -0,0 +1 @@
|
|||
ecf6b392f35bb0ef905144a4605bcb927ce767240e47ec3b0653a94139b987bd
|
|
@ -15216,6 +15216,7 @@ CREATE TABLE postgres_reindex_actions (
|
|||
ondisk_size_bytes_end bigint,
|
||||
state smallint DEFAULT 0 NOT NULL,
|
||||
index_identifier text NOT NULL,
|
||||
bloat_estimate_bytes_start bigint,
|
||||
CONSTRAINT check_f12527622c CHECK ((char_length(index_identifier) <= 255))
|
||||
);
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36001) in GitLab 13.2.
|
||||
|
||||
Instance-level Kubernetes clusters allow you to connect a Kubernetes cluster to the GitLab instance, which enables you to use the same cluster across multiple projects. [More information](../user/instance/clusters/index.md)
|
||||
|
||||
NOTE:
|
||||
Users need admin access to use these endpoints.
|
||||
|
||||
Use these API endpoints with your instance clusters, which enable you to use the same cluster across multiple projects. [More information](../user/instance/clusters/index.md)
|
||||
|
||||
## List instance clusters
|
||||
|
||||
Returns a list of instance clusters.
|
||||
|
|
|
@ -18,7 +18,8 @@ module Gitlab
|
|||
action = create!(
|
||||
index_identifier: index.identifier,
|
||||
action_start: Time.zone.now,
|
||||
ondisk_size_bytes_start: index.ondisk_size_bytes
|
||||
ondisk_size_bytes_start: index.ondisk_size_bytes,
|
||||
bloat_estimate_bytes_start: index.bloat_size
|
||||
)
|
||||
|
||||
yield
|
||||
|
|
|
@ -74,6 +74,12 @@ module Gitlab
|
|||
group = create_group(group_attributes)
|
||||
|
||||
restore_group(group, group_attributes)
|
||||
rescue => e
|
||||
import_failure_service.log_import_failure(
|
||||
source: 'process_child',
|
||||
relation_key: 'group',
|
||||
exception: e
|
||||
)
|
||||
end
|
||||
|
||||
def create_group(group_attributes)
|
||||
|
@ -83,13 +89,17 @@ module Gitlab
|
|||
|
||||
parent_group = @groups_mapping.fetch(parent_id) { raise(ArgumentError, 'Parent group not found') }
|
||||
|
||||
::Groups::CreateService.new(
|
||||
group = ::Groups::CreateService.new(
|
||||
user,
|
||||
name: name,
|
||||
path: path,
|
||||
parent_id: parent_group.id,
|
||||
visibility_level: sub_group_visibility_level(group_attributes.attributes, parent_group)
|
||||
).execute
|
||||
|
||||
group.validate!
|
||||
|
||||
group
|
||||
end
|
||||
|
||||
def restore_group(group, group_attributes)
|
||||
|
@ -134,6 +144,10 @@ module Gitlab
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
def import_failure_service
|
||||
Gitlab::ImportExport::ImportFailureService.new(@top_level_group)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -236,7 +236,9 @@ module Gitlab
|
|||
|
||||
def system_usage_data_settings
|
||||
{
|
||||
settings: {}
|
||||
settings: {
|
||||
ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -28551,6 +28551,9 @@ msgstr ""
|
|||
msgid "ThreatMonitoring|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear."
|
||||
msgstr ""
|
||||
|
||||
msgid "ThreatMonitoring|There was an error while updating the status of the alert. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "ThreatMonitoring|Threat Monitoring"
|
||||
msgstr ""
|
||||
|
||||
|
@ -32244,6 +32247,9 @@ msgstr ""
|
|||
msgid "cannot be a date in the past"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be changed"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be changed if a personal project has container registry tags."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -22,7 +22,14 @@ FactoryBot.define do
|
|||
end
|
||||
|
||||
factory :debian_package do
|
||||
sequence(:name) { |n| "package-#{n}" }
|
||||
sequence(:version) { |n| "1.0-#{n}" }
|
||||
package_type { :debian }
|
||||
|
||||
factory :debian_incoming do
|
||||
name { 'incoming' }
|
||||
version { nil }
|
||||
end
|
||||
end
|
||||
|
||||
factory :npm_package do
|
||||
|
|
1
spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4351.json
vendored
Normal file
1
spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4351.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"ymg09t5704clnxnqfgaj2h098gz4r7gyx4wc3fzmlqj1en24zf","path":"ymg09t5704clnxnqfgaj2h098gz4r7gyx4wc3fzmlqj1en24zf","owner_id":123,"created_at":"2019-11-20 17:01:53 UTC","updated_at":"2019-11-20 17:05:44 UTC","description":"Group Description","avatar":{"url":null},"membership_lock":false,"share_with_group_lock":false,"visibility_level":0,"request_access_enabled":true,"ldap_sync_status":"ready","ldap_sync_error":null,"ldap_sync_last_update_at":null,"ldap_sync_last_successful_update_at":null,"ldap_sync_last_sync_at":null,"lfs_enabled":null,"parent_id":null,"shared_runners_minutes_limit":null,"repository_size_limit":null,"require_two_factor_authentication":false,"two_factor_grace_period":48,"plan_id":null,"project_creation_level":2,"trial_ends_on":null,"file_template_project_id":null,"saml_discovery_token":"rBKx3ioz","custom_project_templates_group_id":null,"auto_devops_enabled":null,"extra_shared_runners_minutes_limit":null,"last_ci_minutes_notification_at":null,"last_ci_minutes_usage_notification_level":null,"runners_token":"token","runners_token_encrypted":"encrypted","subgroup_creation_level":1,"emails_disabled":null,"max_pages_size":null,"max_artifacts_size":null,"id":4351}
|
1
spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4352.json
vendored
Normal file
1
spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4352.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"a","path":"a","owner_id":null,"created_at":"2019-11-20 17:01:53 UTC","updated_at":"2019-11-20 17:05:44 UTC","description":"","avatar":{"url":null},"membership_lock":false,"share_with_group_lock":false,"visibility_level":0,"request_access_enabled":true,"ldap_sync_status":"ready","ldap_sync_error":null,"ldap_sync_last_update_at":null,"ldap_sync_last_successful_update_at":null,"ldap_sync_last_sync_at":null,"lfs_enabled":null,"parent_id":4351,"shared_runners_minutes_limit":null,"repository_size_limit":null,"require_two_factor_authentication":false,"two_factor_grace_period":48,"plan_id":null,"project_creation_level":2,"trial_ends_on":null,"file_template_project_id":null,"saml_discovery_token":"ki3Xnjw3","custom_project_templates_group_id":null,"auto_devops_enabled":null,"extra_shared_runners_minutes_limit":null,"last_ci_minutes_notification_at":null,"last_ci_minutes_usage_notification_level":null,"subgroup_creation_level":1,"emails_disabled":null,"max_pages_size":null,"max_artifacts_size":null,"id":4352}
|
|
@ -0,0 +1,2 @@
|
|||
4351
|
||||
4352
|
|
@ -4,7 +4,6 @@ import AxiosMockAdapter from 'axios-mock-adapter';
|
|||
import { TEST_HOST } from 'jest/helpers/test_constants';
|
||||
import { GlModal } from '@gitlab/ui';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import BoardScope from 'ee_component/boards/components/board_scope.vue';
|
||||
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
|
@ -64,7 +63,6 @@ describe('BoardForm', () => {
|
|||
const findFormWrapper = () => wrapper.find('[data-testid="board-form-wrapper"]');
|
||||
const findDeleteConfirmation = () => wrapper.find('[data-testid="delete-confirmation-message"]');
|
||||
const findConfigurationOptions = () => wrapper.find(BoardConfigurationOptions);
|
||||
const findBoardScope = () => wrapper.find(BoardScope);
|
||||
const findInput = () => wrapper.find('#board-new-name');
|
||||
|
||||
const createComponent = (props, data) => {
|
||||
|
@ -172,12 +170,6 @@ describe('BoardForm', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('passes a correct collapseScope property to BoardScope component on scoped board', async () => {
|
||||
createComponent({ canAdminBoard: true, scopedIssueBoardFeatureEnabled: true });
|
||||
await waitForPromises();
|
||||
expect(findBoardScope().props('collapseScope')).toBe(true);
|
||||
});
|
||||
|
||||
describe('when submitting a create event', () => {
|
||||
beforeEach(() => {
|
||||
const url = `${endpoints.boardsEndpoint}.json`;
|
||||
|
@ -255,12 +247,6 @@ describe('BoardForm', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('passes a correct collapseScope property to BoardScope component on scoped board', async () => {
|
||||
createComponent({ canAdminBoard: true, scopedIssueBoardFeatureEnabled: true });
|
||||
await waitForPromises();
|
||||
expect(findBoardScope().props('collapseScope')).toBe(false);
|
||||
});
|
||||
|
||||
describe('when submitting an update event', () => {
|
||||
beforeEach(() => {
|
||||
const url = endpoints.boardsEndpoint;
|
||||
|
|
|
@ -17,11 +17,14 @@ import RecentSearchesService from '~/filtered_search/services/recent_searches_se
|
|||
|
||||
import {
|
||||
mockAvailableTokens,
|
||||
mockMembershipToken,
|
||||
mockMembershipTokenOptionsWithoutTitles,
|
||||
mockSortOptions,
|
||||
mockHistoryItems,
|
||||
tokenValueAuthor,
|
||||
tokenValueLabel,
|
||||
tokenValueMilestone,
|
||||
tokenValueMembership,
|
||||
} from './mock_data';
|
||||
|
||||
jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils', () => ({
|
||||
|
@ -412,6 +415,42 @@ describe('FilteredSearchBarRoot', () => {
|
|||
wrapperFullMount.destroy();
|
||||
});
|
||||
|
||||
describe('when token options have `title` attribute defined', () => {
|
||||
it('renders search history items using the provided `title` attribute', async () => {
|
||||
const wrapperFullMount = createComponent({
|
||||
sortOptions: mockSortOptions,
|
||||
tokens: [mockMembershipToken],
|
||||
shallow: false,
|
||||
});
|
||||
|
||||
wrapperFullMount.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]);
|
||||
|
||||
await wrapperFullMount.vm.$nextTick();
|
||||
|
||||
expect(wrapperFullMount.find(GlDropdownItem).text()).toBe('Membership := Direct');
|
||||
|
||||
wrapperFullMount.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when token options have do not have `title` attribute defined', () => {
|
||||
it('renders search history items using the provided `value` attribute', async () => {
|
||||
const wrapperFullMount = createComponent({
|
||||
sortOptions: mockSortOptions,
|
||||
tokens: [mockMembershipTokenOptionsWithoutTitles],
|
||||
shallow: false,
|
||||
});
|
||||
|
||||
wrapperFullMount.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]);
|
||||
|
||||
await wrapperFullMount.vm.$nextTick();
|
||||
|
||||
expect(wrapperFullMount.find(GlDropdownItem).text()).toBe('Membership := exclude');
|
||||
|
||||
wrapperFullMount.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders sort dropdown component', () => {
|
||||
expect(wrapper.find(GlButtonGroup).exists()).toBe(true);
|
||||
expect(wrapper.find(GlDropdown).exists()).toBe(true);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { GlFilteredSearchToken } from '@gitlab/ui';
|
||||
import { mockLabels } from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
|
||||
import Api from '~/api';
|
||||
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
|
||||
|
@ -102,6 +103,21 @@ export const mockMilestoneToken = {
|
|||
fetchMilestones: () => Promise.resolve({ data: mockMilestones }),
|
||||
};
|
||||
|
||||
export const mockMembershipToken = {
|
||||
type: 'with_inherited_permissions',
|
||||
icon: 'group',
|
||||
title: 'Membership',
|
||||
token: GlFilteredSearchToken,
|
||||
unique: true,
|
||||
operators: [{ value: '=', description: 'is' }],
|
||||
options: [{ value: 'exclude', title: 'Direct' }, { value: 'only', title: 'Inherited' }],
|
||||
};
|
||||
|
||||
export const mockMembershipTokenOptionsWithoutTitles = {
|
||||
...mockMembershipToken,
|
||||
options: [{ value: 'exclude' }, { value: 'only' }],
|
||||
};
|
||||
|
||||
export const mockAvailableTokens = [mockAuthorToken, mockLabelToken, mockMilestoneToken];
|
||||
|
||||
export const tokenValueAuthor = {
|
||||
|
@ -128,6 +144,14 @@ export const tokenValueMilestone = {
|
|||
},
|
||||
};
|
||||
|
||||
export const tokenValueMembership = {
|
||||
type: 'with_inherited_permissions',
|
||||
value: {
|
||||
operator: '=',
|
||||
data: 'exclude',
|
||||
},
|
||||
};
|
||||
|
||||
export const tokenValuePlain = {
|
||||
type: 'filtered-search-term',
|
||||
value: { data: 'foo' },
|
||||
|
|
|
@ -7,43 +7,82 @@ RSpec.describe Resolvers::UserNotesCountResolver do
|
|||
|
||||
describe '#resolve' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:private_project) { create(:project, :private) }
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
let_it_be(:private_issue) { create(:issue, project: private_project) }
|
||||
let_it_be(:public_notes) { create_list(:note, 2, noteable: issue, project: project) }
|
||||
let_it_be(:system_note) { create(:note, system: true, noteable: issue, project: project) }
|
||||
let_it_be(:private_notes) { create_list(:note, 3, noteable: private_issue, project: private_project) }
|
||||
let_it_be(:project) { create(:project, :repository, :public) }
|
||||
let_it_be(:private_project) { create(:project, :repository, :private) }
|
||||
|
||||
specify do
|
||||
expect(described_class).to have_nullable_graphql_type(GraphQL::INT_TYPE)
|
||||
end
|
||||
|
||||
context 'when counting notes from a public issue' do
|
||||
subject { batch_sync { resolve_user_notes_count(issue) } }
|
||||
context 'when counting notes from an issue' do
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
let_it_be(:private_issue) { create(:issue, project: private_project) }
|
||||
let_it_be(:public_notes) { create_list(:note, 2, noteable: issue, project: project) }
|
||||
let_it_be(:system_note) { create(:note, system: true, noteable: issue, project: project) }
|
||||
let_it_be(:private_notes) { create_list(:note, 3, noteable: private_issue, project: private_project) }
|
||||
|
||||
it 'returns the number of non-system notes for the issue' do
|
||||
expect(subject).to eq(2)
|
||||
context 'when counting notes from a public issue' do
|
||||
subject { batch_sync { resolve_user_notes_count(issue) } }
|
||||
|
||||
it 'returns the number of non-system notes for the issue' do
|
||||
expect(subject).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a user has permission to view notes' do
|
||||
before do
|
||||
private_project.add_developer(user)
|
||||
end
|
||||
|
||||
subject { batch_sync { resolve_user_notes_count(private_issue) } }
|
||||
|
||||
it 'returns the number of notes for the issue' do
|
||||
expect(subject).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a user does not have permission to view discussions' do
|
||||
subject { batch_sync { resolve_user_notes_count(private_issue) } }
|
||||
|
||||
it 'returns no notes' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a user has permission to view notes' do
|
||||
before do
|
||||
private_project.add_developer(user)
|
||||
context 'when counting notes from a merge request' do
|
||||
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
|
||||
let_it_be(:private_merge_request) { create(:merge_request, source_project: private_project) }
|
||||
let_it_be(:public_notes) { create_list(:note, 2, noteable: merge_request, project: project) }
|
||||
let_it_be(:system_note) { create(:note, system: true, noteable: merge_request, project: project) }
|
||||
let_it_be(:private_notes) { create_list(:note, 3, noteable: private_merge_request, project: private_project) }
|
||||
|
||||
context 'when counting notes from a public merge request' do
|
||||
subject { batch_sync { resolve_user_notes_count(merge_request) } }
|
||||
|
||||
it 'returns the number of non-system notes for the merge request' do
|
||||
expect(subject).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
subject { batch_sync { resolve_user_notes_count(private_issue) } }
|
||||
context 'when a user has permission to view notes' do
|
||||
before do
|
||||
private_project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns the number of notes for the issue' do
|
||||
expect(subject).to eq(3)
|
||||
subject { batch_sync { resolve_user_notes_count(private_merge_request) } }
|
||||
|
||||
it 'returns the number of notes for the merge request' do
|
||||
expect(subject).to eq(3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a user does not have permission to view discussions' do
|
||||
subject { batch_sync { resolve_user_notes_count(private_issue) } }
|
||||
context 'when a user does not have permission to view discussions' do
|
||||
subject { batch_sync { resolve_user_notes_count(private_merge_request) } }
|
||||
|
||||
it 'returns no notes' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
it 'returns no notes' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,34 +49,50 @@ RSpec.describe ServicesHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#reset_integrations?' do
|
||||
describe '#reset_integration?' do
|
||||
let(:group) { nil }
|
||||
|
||||
subject { helper.reset_integrations?(group: group) }
|
||||
subject { helper.reset_integration?(integration, group: group) }
|
||||
|
||||
context 'when `reset_integrations` is not enabled' do
|
||||
it 'returns false' do
|
||||
stub_feature_flags(reset_integrations: false)
|
||||
context 'when integration is existing record' do
|
||||
let_it_be(:integration) { create(:jira_service) }
|
||||
|
||||
is_expected.to eq(false)
|
||||
context 'when `reset_integrations` is not enabled' do
|
||||
it 'returns false' do
|
||||
stub_feature_flags(reset_integrations: false)
|
||||
|
||||
is_expected.to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `reset_integrations` is enabled' do
|
||||
it 'returns true' do
|
||||
stub_feature_flags(reset_integrations: true)
|
||||
|
||||
is_expected.to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `reset_integrations` is enabled for a group' do
|
||||
let(:group) { build_stubbed(:group) }
|
||||
|
||||
it 'returns true' do
|
||||
stub_feature_flags(reset_integrations: group)
|
||||
|
||||
is_expected.to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `reset_integrations` is enabled' do
|
||||
it 'returns true' do
|
||||
stub_feature_flags(reset_integrations: true)
|
||||
context 'when integration is a new record' do
|
||||
let_it_be(:integration) { build(:jira_service) }
|
||||
|
||||
is_expected.to eq(true)
|
||||
end
|
||||
end
|
||||
context 'when `reset_integrations` is enabled' do
|
||||
it 'returns false' do
|
||||
stub_feature_flags(reset_integrations: true)
|
||||
|
||||
context 'when `reset_integrations` is enabled for a group' do
|
||||
let(:group) { build_stubbed(:group) }
|
||||
|
||||
it 'returns true' do
|
||||
stub_feature_flags(reset_integrations: group)
|
||||
|
||||
is_expected.to eq(true)
|
||||
is_expected.to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::Reindexing::ReindexAction, '.keep_track_of' do
|
||||
let(:index) { double('index', identifier: 'public.something', ondisk_size_bytes: 10240, reload: nil) }
|
||||
let(:index) { double('index', identifier: 'public.something', ondisk_size_bytes: 10240, reload: nil, bloat_size: 42) }
|
||||
let(:size_after) { 512 }
|
||||
|
||||
it 'yields to the caller' do
|
||||
|
@ -47,6 +47,12 @@ RSpec.describe Gitlab::Database::Reindexing::ReindexAction, '.keep_track_of' do
|
|||
expect(find_record.ondisk_size_bytes_end).to eq(size_after)
|
||||
end
|
||||
|
||||
it 'creates the record with the indexes bloat estimate' do
|
||||
described_class.keep_track_of(index) do
|
||||
expect(find_record.bloat_estimate_bytes_start).to eq(index.bloat_size)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in case of errors' do
|
||||
it 'sets the state to failed' do
|
||||
expect do
|
||||
|
|
|
@ -75,12 +75,31 @@ RSpec.describe Gitlab::ImportExport::Group::TreeRestorer do
|
|||
|
||||
before do
|
||||
setup_import_export_config('group_exports/child_with_no_parent')
|
||||
|
||||
expect(group_tree_restorer.restore).to be_falsey
|
||||
end
|
||||
|
||||
it 'fails when a child group does not have a valid parent_id' do
|
||||
expect(shared.errors).to include('Parent group not found')
|
||||
it 'captures import failures when a child group does not have a valid parent_id' do
|
||||
group_tree_restorer.restore
|
||||
|
||||
expect(group.import_failures.first.exception_message).to eq('Parent group not found')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when child group creation fails' do
|
||||
let(:user) { create(:user) }
|
||||
let(:group) { create(:group) }
|
||||
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
|
||||
let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
|
||||
|
||||
before do
|
||||
setup_import_export_config('group_exports/child_short_name')
|
||||
end
|
||||
|
||||
it 'captures import failure' do
|
||||
exception_message = 'Validation failed: Group URL is too short (minimum is 2 characters)'
|
||||
|
||||
group_tree_restorer.restore
|
||||
|
||||
expect(group.import_failures.first.exception_message).to eq(exception_message)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1038,6 +1038,14 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".system_usage_data_settings" do
|
||||
subject { described_class.system_usage_data_settings }
|
||||
|
||||
it 'gathers settings usage data', :aggregate_failures do
|
||||
expect(subject[:settings][:ldap_encrypted_secrets_enabled]).to eq(Gitlab::Auth::Ldap::Config.encrypted_secrets.active?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.merge_requests_users', :clean_gitlab_redis_shared_state do
|
||||
|
|
|
@ -111,6 +111,24 @@ RSpec.describe Packages::Package, type: :model do
|
|||
it { is_expected.not_to allow_value('%foo%bar').for(:name) }
|
||||
end
|
||||
|
||||
context 'debian package' do
|
||||
subject { build(:debian_package) }
|
||||
|
||||
it { is_expected.to allow_value('0ad').for(:name) }
|
||||
it { is_expected.to allow_value('g++').for(:name) }
|
||||
it { is_expected.not_to allow_value('a_b').for(:name) }
|
||||
end
|
||||
|
||||
context 'debian incoming' do
|
||||
subject { create(:debian_incoming) }
|
||||
|
||||
# Only 'incoming' is accepted
|
||||
it { is_expected.to allow_value('incoming').for(:name) }
|
||||
it { is_expected.not_to allow_value('0ad').for(:name) }
|
||||
it { is_expected.not_to allow_value('g++').for(:name) }
|
||||
it { is_expected.not_to allow_value('a_b').for(:name) }
|
||||
end
|
||||
|
||||
context 'generic package' do
|
||||
subject { build_stubbed(:generic_package) }
|
||||
|
||||
|
@ -180,6 +198,21 @@ RSpec.describe Packages::Package, type: :model do
|
|||
it { is_expected.to allow_value('2.x-dev').for(:version) }
|
||||
end
|
||||
|
||||
context 'debian package' do
|
||||
subject { build(:debian_package) }
|
||||
|
||||
it { is_expected.to allow_value('2:4.9.5+dfsg-5+deb10u1').for(:version) }
|
||||
it { is_expected.not_to allow_value('1_0').for(:version) }
|
||||
end
|
||||
|
||||
context 'debian incoming' do
|
||||
subject { create(:debian_incoming) }
|
||||
|
||||
it { is_expected.to allow_value(nil).for(:version) }
|
||||
it { is_expected.not_to allow_value('2:4.9.5+dfsg-5+deb10u1').for(:version) }
|
||||
it { is_expected.not_to allow_value('1_0').for(:version) }
|
||||
end
|
||||
|
||||
context 'maven package' do
|
||||
subject { build_stubbed(:maven_package) }
|
||||
|
||||
|
@ -621,6 +654,46 @@ RSpec.describe Packages::Package, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#debian_incoming?' do
|
||||
let(:package) { build(:package) }
|
||||
|
||||
subject { package.debian_incoming? }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
|
||||
context 'with debian_incoming' do
|
||||
let(:package) { create(:debian_incoming) }
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
|
||||
context 'with debian_package' do
|
||||
let(:package) { create(:debian_package) }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#debian_package?' do
|
||||
let(:package) { build(:package) }
|
||||
|
||||
subject { package.debian_package? }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
|
||||
context 'with debian_incoming' do
|
||||
let(:package) { create(:debian_incoming) }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
|
||||
context 'with debian_package' do
|
||||
let(:package) { create(:debian_package) }
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'plan_limits' do
|
||||
Packages::Package.package_types.keys.without('composer').each do |pt|
|
||||
plan_limit_name = if pt == 'generic'
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'overcommit'
|
||||
gem 'gitlab-styles', '~> 5.2.0', require: false
|
||||
gem 'gitlab-styles', '~> 5.3.0', require: false
|
||||
gem 'scss_lint', '~> 0.56.0', require: false
|
||||
gem 'haml_lint', '~> 0.34.0', require: false
|
||||
|
|
|
@ -11,7 +11,7 @@ GEM
|
|||
childprocess (3.0.0)
|
||||
concurrent-ruby (1.1.7)
|
||||
ffi (1.12.2)
|
||||
gitlab-styles (5.2.0)
|
||||
gitlab-styles (5.3.0)
|
||||
rubocop (~> 0.89.1)
|
||||
rubocop-gitlab-security (~> 0.1.0)
|
||||
rubocop-performance (~> 1.8.1)
|
||||
|
@ -88,7 +88,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
gitlab-styles (~> 5.2.0)
|
||||
gitlab-styles (~> 5.3.0)
|
||||
haml_lint (~> 0.34.0)
|
||||
overcommit
|
||||
scss_lint (~> 0.56.0)
|
||||
|
|
Loading…
Reference in a new issue