Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-01-05 15:08:39 +00:00
parent b90cf01a88
commit 5db6a7a014
43 changed files with 697 additions and 297 deletions

View File

@ -389,10 +389,7 @@ export default {
() => {
this.setDiscussions();
if (
this.$store.state.notes.doneFetchingBatchDiscussions &&
window.gon?.features?.paginatedMrDiscussions
) {
if (this.$store.state.notes.doneFetchingBatchDiscussions) {
this.unwatchDiscussions();
}
},
@ -402,10 +399,6 @@ export default {
() => `${this.retrievingBatches}:${this.$store.state.notes.discussions.length}`,
() => {
if (!this.retrievingBatches && this.$store.state.notes.discussions.length) {
if (!window.gon?.features?.paginatedMrDiscussions) {
this.unwatchDiscussions();
}
this.unwatchRetrievingBatches();
}
},

View File

@ -56,11 +56,7 @@ export default {
},
watch: {
discussionTabCounter(val) {
if (this.glFeatures.paginatedMrDiscussions) {
if (this.doneFetchingBatchDiscussions) {
this.discussionCounter = val;
}
} else {
if (this.doneFetchingBatchDiscussions) {
this.discussionCounter = val;
}
},

View File

@ -19,6 +19,25 @@ export default {
artifactsLabel: __('Artifacts'),
parametersLabel: __('Parameters'),
metricsLabel: __('Metrics'),
metadataLabel: __('Metadata'),
},
computed: {
sections() {
return [
{
sectionName: this.$options.i18n.parametersLabel,
sectionValues: this.candidate.params,
},
{
sectionName: this.$options.i18n.metricsLabel,
sectionValues: this.candidate.metrics,
},
{
sectionName: this.$options.i18n.metadataLabel,
sectionValues: this.candidate.metadata,
},
];
},
},
};
</script>
@ -67,27 +86,18 @@ export default {
</td>
</tr>
<tr class="divider"></tr>
<template v-for="{ sectionName, sectionValues } in sections">
<tr :key="sectionName" class="divider"></tr>
<tr v-for="(param, index) in candidate.params" :key="param.name">
<td v-if="index == 0" class="gl-text-secondary gl-font-weight-bold">
{{ $options.i18n.parametersLabel }}
</td>
<td v-else></td>
<td class="gl-font-weight-bold">{{ param.name }}</td>
<td>{{ param.value }}</td>
</tr>
<tr class="divider"></tr>
<tr v-for="(metric, index) in candidate.metrics" :key="metric.name">
<td v-if="index == 0" class="gl-text-secondary gl-font-weight-bold">
{{ $options.i18n.metricsLabel }}
</td>
<td v-else></td>
<td class="gl-font-weight-bold">{{ metric.name }}</td>
<td>{{ metric.value }}</td>
</tr>
<tr v-for="(item, index) in sectionValues" :key="item.name">
<td v-if="index === 0" class="gl-text-secondary gl-font-weight-bold">
{{ sectionName }}
</td>
<td v-else></td>
<td class="gl-font-weight-bold">{{ item.name }}</td>
<td>{{ item.value }}</td>
</tr>
</template>
</tbody>
</table>
</div>

View File

@ -101,7 +101,7 @@ export const fetchDiscussions = (
if (
getters.noteableType === constants.ISSUE_NOTEABLE_TYPE ||
window.gon?.features?.paginatedMrDiscussions
getters.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
) {
return dispatch('fetchDiscussionsBatch', { path, config, perPage: 20 });
}

View File

@ -20,4 +20,4 @@ export const PROJECT_DATA = {
fullName: 'name_with_namespace',
};
export const SYNTAX_OPTIONS_DOCUMENT = 'drawers/user/search/advanced_search.md';
export const SYNTAX_OPTIONS_DOCUMENT = 'drawers/drawers/advanced_search_syntax.md';

View File

@ -150,7 +150,7 @@ class Import::GithubController < Import::BaseController
end
def client_repos_response
@client_repos_response ||= client_proxy.repos(sanitized_filter_param, pagination_options)
@client_repos_response ||= client_proxy.repos(sanitized_filter_param, fetch_repos_options)
end
def client_repos
@ -160,7 +160,11 @@ class Import::GithubController < Import::BaseController
def sanitized_filter_param
super
@filter = @filter&.tr(' ', '')&.tr(':', '')
@filter = sanitize_query_param(@filter)
end
def sanitize_query_param(value)
value.to_s.first(255).gsub(/[ :]/, '')
end
def verify_import_enabled
@ -222,6 +226,10 @@ class Import::GithubController < Import::BaseController
head :too_many_requests
end
def fetch_repos_options
pagination_options.merge(relation_options)
end
def pagination_options
{
before: params[:before].presence,
@ -233,6 +241,13 @@ class Import::GithubController < Import::BaseController
per_page: PAGE_LENGTH
}
end
def relation_options
{
relation_type: params[:relation_type],
organization_login: sanitize_query_param(params[:organization_login])
}
end
end
Import::GithubController.prepend_mod_with('Import::GithubController')

View File

@ -40,7 +40,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:refactor_security_extension, @project)
push_frontend_feature_flag(:refactor_code_quality_inline_findings, project)
push_frontend_feature_flag(:moved_mr_sidebar, project)
push_frontend_feature_flag(:paginated_mr_discussions, project)
push_frontend_feature_flag(:mr_review_submit_comment, project)
push_frontend_feature_flag(:mr_experience_survey, project)
push_frontend_feature_flag(:realtime_reviewers, project)

View File

@ -58,8 +58,9 @@ module EnvironmentHelper
s_('Deployment|blocked')
end
klass = "ci-status ci-#{status.dasherize}"
text = "#{ci_icon_for_status(status)} #{status_text}".html_safe
ci_icon_utilities = "gl-display-inline-flex gl-align-items-center gl-line-height-0 gl-px-3 gl-py-2 gl-rounded-base"
klass = "ci-status ci-#{status.dasherize} #{ci_icon_utilities}"
text = "#{ci_icon_for_status(status)} <span class=\"gl-ml-2\">#{status_text}</span>".html_safe
if deployment.deployable
link_to(text, deployment_path(deployment), class: klass)

View File

@ -32,7 +32,8 @@ module Projects
experiment_name: candidate.experiment.name,
path_to_experiment: link_to_experiment(candidate),
status: candidate.status
}
},
metadata: candidate.metadata
}
Gitlab::Json.generate(data)

View File

@ -36,6 +36,8 @@ module BulkImports
end
def execute
validate!
bulk_import = create_bulk_import
Gitlab::Tracking.event(self.class.name, 'create', label: 'bulk_import_group')
@ -43,7 +45,8 @@ module BulkImports
BulkImportWorker.perform_async(bulk_import.id)
ServiceResponse.success(payload: bulk_import)
rescue ActiveRecord::RecordInvalid, BulkImports::NetworkError => e
rescue ActiveRecord::RecordInvalid, BulkImports::Error, BulkImports::NetworkError => e
ServiceResponse.error(
message: e.message,
http_status: :unprocessable_entity
@ -52,6 +55,11 @@ module BulkImports
private
def validate!
client.validate_instance_version!
client.validate_import_scopes!
end
def create_bulk_import
BulkImport.transaction do
bulk_import = BulkImport.create!(

View File

@ -49,7 +49,6 @@ module Issuable
def paginator
return if params[:per_page].blank?
return if issuable.instance_of?(MergeRequest) && Feature.disabled?(:paginated_mr_discussions, issuable.project)
strong_memoize(:paginator) do
issuable

View File

@ -69,7 +69,7 @@
= render "projects/merge_requests/awards_block"
= render "projects/merge_requests/widget"
- if mr_action === "show"
- add_page_startup_api_call Feature.enabled?(:paginated_mr_discussions, @project) ? discussions_path(@merge_request, per_page: 20) : discussions_path(@merge_request)
- add_page_startup_api_call discussions_path(@merge_request, per_page: 20)
- add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json)
- add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json)
#js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json,

View File

@ -71,14 +71,14 @@ environment variable.
An example configuration file for Redis is in this directory under the name
`resque.yml.example`.
| Name | Fallback instance | Purpose |
| --- | --- | --- |
| `cache` | | Volatile non-persistent data |
| `queues` | | Background job processing queues |
| `shared_state` | | Persistent application state |
| `trace_chunks` | `shared_state` | [CI trace chunks](https://docs.gitlab.com/ee/administration/job_logs.html#incremental-logging-architecture) |
| `rate_limiting` | `cache` | [Rate limiting](https://docs.gitlab.com/ee/user/admin_area/settings/user_and_ip_rate_limits.html) state |
| `sessions` | `shared_state` | [Sessions](https://docs.gitlab.com/ee/development/session.html#redis)|
| Name | Fallback instance | Purpose |
|--------------------|-------------------|-------------------------------------------------------------------------------------------------------------|
| `cache` | | Volatile non-persistent data |
| `queues` | | Background job processing queues |
| `shared_state` | | Persistent application state |
| `trace_chunks` | `shared_state` | [CI trace chunks](https://docs.gitlab.com/ee/administration/job_logs.html#incremental-logging-architecture) |
| `rate_limiting` | `cache` | [Rate limiting](https://docs.gitlab.com/ee/user/admin_area/settings/user_and_ip_rate_limits.html) state |
| `sessions` | `shared_state` | [Sessions](https://docs.gitlab.com/ee/development/session.html#redis) |
If no configuration is found, or no URL is found in the configuration
file, the default URL used is:

View File

@ -59,6 +59,7 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/redis/trace_chunks')
require_dependency Rails.root.join('lib/gitlab/redis/rate_limiting')
require_dependency Rails.root.join('lib/gitlab/redis/sessions')
require_dependency Rails.root.join('lib/gitlab/redis/repository_cache')
require_dependency Rails.root.join('lib/gitlab/current_settings')
require_dependency Rails.root.join('lib/gitlab/middleware/read_only')
require_dependency Rails.root.join('lib/gitlab/middleware/compressed_json')

View File

@ -1,8 +0,0 @@
---
name: paginated_mr_discussions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88905
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364497
milestone: '15.1'
type: development
group: group::code review
default_enabled: true

View File

@ -3488,7 +3488,7 @@ any subkeys. All additional details and related topics are the same.
**Possible inputs**:
- An array of file paths. In GitLab 13.6 and later, [file paths can include variables](../jobs/job_control.md#variables-in-ruleschanges).
- An array of file paths. [File paths can include variables](../jobs/job_control.md#variables-in-ruleschanges).
**Example of `rules:changes:paths`**:

View File

@ -456,7 +456,13 @@ Log in to your **primary** node, executing the following:
1. To get the database migrations and latest code in place, run:
```shell
sudo SKIP_POST_DEPLOYMENT_MIGRATIONS=true gitlab-ctl reconfigure
sudo gitlab-ctl reconfigure
```
1. After the node is updated and reconfigure finished successfully, complete the migrations:
```shell
sudo SKIP_POST_DEPLOYMENT_MIGRATIONS=true gitlab-rake db:migrate
```
### Update the Geo secondary site
@ -486,7 +492,13 @@ On each **secondary** node, executing the following:
1. To get the database migrations and latest code in place, run:
```shell
sudo SKIP_POST_DEPLOYMENT_MIGRATIONS=true gitlab-ctl reconfigure
sudo gitlab-ctl reconfigure
```
1. After the node is updated and reconfigure finished successfully, complete the migrations:
```shell
sudo SKIP_POST_DEPLOYMENT_MIGRATIONS=true gitlab-rake db:migrate
```
1. Run post-deployment database migrations, specific to the Geo database:

View File

@ -159,13 +159,6 @@ the default option of one corpus per job.
The corpus registry uses the package registry to store the project's corpuses. Corpuses stored in
the registry are hidden to ensure data integrity.
In the GitLab UI, with corpus management you can:
- View details of the corpus registry.
- Download a corpus.
- Delete a corpus.
- Create a new corpus.
When you download a corpus, the file is named `artifacts.zip`, regardless of the filename used when
the corpus was initially uploaded. This file contains only the corpus, which is different to the
artifacts files you can download from the CI/CD pipeline. Also, a project member with a Reporter or above privilege can download the corpus using the direct download link.

View File

@ -363,12 +363,7 @@ Threads on lines that don't change and top-level resolvable threads are not reso
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340172) in GitLab 15.1 [with a flag](../../administration/feature_flags.md) named `paginated_mr_discussions`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/364497) in GitLab 15.2.
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/364497) in GitLab 15.3.
FLAG:
On self-managed GitLab, by default this feature is available. To hide the feature
per project or for your entire instance, ask an administrator to
[disable the feature flag](../../administration/feature_flags.md) named `paginated_mr_discussions`.
On GitLab.com, this feature is available.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/370075) in GitLab 15.8. Feature flag `paginated_mr_discussions` removed.
A merge request can have many discussions. Loading them all in a single request
can be slow. To improve the performance of loading discussions, they are split into multiple

View File

@ -8,6 +8,7 @@ module BulkImports
API_VERSION = 'v4'
DEFAULT_PAGE = 1
DEFAULT_PER_PAGE = 30
PAT_ENDPOINT_MIN_VERSION = '15.5.0'
def initialize(url:, token:, page: DEFAULT_PAGE, per_page: DEFAULT_PER_PAGE, api_version: API_VERSION)
@url = url
@ -66,38 +67,57 @@ module BulkImports
instance_version >= BulkImport.min_gl_version_for_project_migration
end
private
def options
{ headers: { 'Content-Type' => 'application/json' }, query: { private_token: @token } }
end
def validate_import_scopes!
return true unless instance_version >= ::Gitlab::VersionInfo.parse(PAT_ENDPOINT_MIN_VERSION)
response = with_error_handling do
Gitlab::HTTP.get(resource_url("personal_access_tokens/self"), options)
end
return true if response['scopes']&.include?('api')
raise ::BulkImports::Error.scope_validation_failure
end
def validate_instance_version!
return if @compatible_instance_version
return true unless instance_version.major < BulkImport::MIN_MAJOR_VERSION
if instance_version.major < BulkImport::MIN_MAJOR_VERSION
raise ::BulkImports::Error.unsupported_gitlab_version
else
@compatible_instance_version = true
end
raise ::BulkImports::Error.unsupported_gitlab_version
end
private
def metadata
response = begin
with_error_handling do
Gitlab::HTTP.get(resource_url(:version), default_options)
Gitlab::HTTP.get(resource_url(:version), options)
end
rescue BulkImports::NetworkError
# `version` endpoint is not available, try `metadata` endpoint instead
with_error_handling do
Gitlab::HTTP.get(resource_url(:metadata), default_options)
Gitlab::HTTP.get(resource_url(:metadata), options)
end
end
response.parsed_response
rescue BulkImports::NetworkError => e
case e&.response&.code
when 401, 403
raise ::BulkImports::Error.scope_validation_failure
when 404
raise ::BulkImports::Error.invalid_url
else
raise
end
end
strong_memoize_attr :metadata
# rubocop:disable GitlabSecurity/PublicSend
def request(method, resource, options = {}, &block)
validate_instance_version!
with_error_handling do
Gitlab::HTTP.public_send(
method,
@ -134,9 +154,10 @@ module BulkImports
def with_error_handling
response = yield
raise ::BulkImports::NetworkError.new("Unsuccessful response #{response.code} from #{response.request.path.path}. Body: #{response.parsed_response}", response: response) unless response.success?
return response if response.success?
raise ::BulkImports::NetworkError.new("Unsuccessful response #{response.code} from #{response.request.path.path}. Body: #{response.parsed_response}", response: response)
response
rescue *Gitlab::HTTP::HTTP_ERRORS => e
raise ::BulkImports::NetworkError, e
end

View File

@ -5,5 +5,13 @@ module BulkImports
def self.unsupported_gitlab_version
self.new("Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MIN_MAJOR_VERSION}.")
end
def self.scope_validation_failure
self.new("Import aborted as the provided personal access token does not have the required 'api' scope.")
end
def self.invalid_url
self.new("Import aborted as it was not possible to connect to the provided GitLab instance URL.")
end
end
end

View File

@ -30,6 +30,8 @@ module Gitlab
secret_detection: VERSIONS_TO_REMOVE_IN_16_0
}.freeze
CURRENT_VERSIONS = SUPPORTED_VERSIONS.to_h { |k, v| [k, v - DEPRECATED_VERSIONS[k]] }
class Schema
def root_path
File.join(__dir__, 'schemas')
@ -187,11 +189,15 @@ module Gitlab
def add_deprecated_report_version_message
log_warnings(problem_type: 'using_deprecated_schema_version')
template = _("Version %{report_version} for report type %{report_type} has been deprecated,"\
" supported versions for this report type are: %{supported_schema_versions}."\
" GitLab will attempt to parse and ingest this report if valid.")
template = _("version %{report_version} for report type %{report_type} is deprecated. "\
"However, GitLab will still attempt to parse and ingest this report. "\
"Upgrade the security report to one of the following versions: %{current_schema_versions}.")
message = format(template, report_version: report_version, report_type: report_type, supported_schema_versions: supported_schema_versions)
message = format(
template,
report_version: report_version,
report_type: report_type,
current_schema_versions: current_schema_versions)
add_message_as(level: :deprecation_warning, message: message)
end
@ -212,6 +218,10 @@ module Gitlab
)
end
def current_schema_versions
CURRENT_VERSIONS[report_type].join(", ")
end
def supported_schema_versions
SUPPORTED_VERSIONS[report_type].join(", ")
end

View File

@ -264,18 +264,6 @@ module Gitlab
private
def collaborations_subquery
each_object(:repos, nil, { affiliation: 'collaborator' })
.map { |repo| "repo:#{repo[:full_name]}" }
.join(' ')
end
def organizations_subquery
each_object(:organizations)
.map { |org| "org:#{org[:login]}" }
.join(' ')
end
def with_retry
Retriable.retriable(on: CLIENT_CONNECTION_ERROR, on_retry: on_retry) do
yield

View File

@ -10,24 +10,24 @@ module Gitlab
@client = pick_client(access_token, client_options)
end
def repos(search_text, pagination_options)
def repos(search_text, options)
return { repos: filtered(client.repos, search_text) } if use_legacy?
if use_graphql?
fetch_repos_via_graphql(search_text, pagination_options)
fetch_repos_via_graphql(search_text, options)
else
fetch_repos_via_rest(search_text, pagination_options)
fetch_repos_via_rest(search_text, options)
end
end
private
def fetch_repos_via_rest(search_text, pagination_options)
{ repos: client.search_repos_by_name(search_text, pagination_options)[:items] }
def fetch_repos_via_rest(search_text, options)
{ repos: client.search_repos_by_name(search_text, options)[:items] }
end
def fetch_repos_via_graphql(search_text, pagination_options)
response = client.search_repos_by_name_graphql(search_text, pagination_options)
def fetch_repos_via_graphql(search_text, options)
response = client.search_repos_by_name_graphql(search_text, options)
{
repos: response.dig(:data, :search, :nodes),
page_info: response.dig(:data, :search, :pageInfo)

View File

@ -14,18 +14,17 @@ module Gitlab
end
def search_repos_by_name(name, options = {})
search_query = search_repos_query(name, options)
with_retry do
octokit.search_repositories(
search_repos_query(str: name, type: :name),
options
).to_h
octokit.search_repositories(search_query, options).to_h
end
end
private
def graphql_search_repos_body(name, options)
query = search_repos_query(str: name, type: :name)
query = search_repos_query(name, options)
query = "query: \"#{query}\""
first = options[:first].present? ? ", first: #{options[:first]}" : ''
after = options[:after].present? ? ", after: \"#{options[:after]}\"" : ''
@ -52,13 +51,49 @@ module Gitlab
TEXT
end
def search_repos_query(str:, type:, include_collaborations: true, include_orgs: true)
query = "#{str} in:#{type} is:public,private user:#{octokit.user.to_h[:login]}"
def search_repos_query(string, options = {})
base = "#{string} in:name is:public,private"
query = [query, collaborations_subquery].join(' ') if include_collaborations
query = [query, organizations_subquery].join(' ') if include_orgs
case options[:relation_type]
when 'organization' then organization_repos_query(base, options)
when 'collaborated' then collaborated_repos_query(base)
when 'owned' then owned_repos_query(base)
# TODO: remove after https://gitlab.com/gitlab-org/gitlab/-/issues/385113 get done
else legacy_all_repos_query(base)
end
end
query
def organization_repos_query(search_string, options)
"#{search_string} org:#{options[:organization_login]}"
end
def collaborated_repos_query(search_string)
"#{search_string} #{collaborations_subquery}"
end
def owned_repos_query(search_string)
"#{search_string} user:#{octokit.user.to_h[:login]}"
end
def legacy_all_repos_query(search_string)
[
search_string,
"user:#{octokit.user.to_h[:login]}",
collaborations_subquery,
organizations_subquery
].join(' ')
end
def collaborations_subquery
each_object(:repos, nil, { affiliation: 'collaborator' })
.map { |repo| "repo:#{repo[:full_name]}" }
.join(' ')
end
def organizations_subquery
each_object(:organizations)
.map { |org| "org:#{org[:login]}" }
.join(' ')
end
end
end

View File

@ -11,6 +11,7 @@ module Gitlab
Gitlab::Redis::Cache,
Gitlab::Redis::Queues,
Gitlab::Redis::RateLimiting,
Gitlab::Redis::RepositoryCache,
Gitlab::Redis::Sessions,
Gitlab::Redis::SharedState,
Gitlab::Redis::TraceChunks

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
module Gitlab
module Redis
class RepositoryCache < ::Gitlab::Redis::Wrapper
# The data we store on RepositoryCache used to be stored on Cache.
def self.config_fallback
Cache
end
end
end
end

View File

@ -26325,6 +26325,9 @@ msgstr ""
msgid "Messages"
msgstr ""
msgid "Metadata"
msgstr ""
msgid "Method"
msgstr ""
@ -45892,9 +45895,6 @@ msgstr ""
msgid "Version"
msgstr ""
msgid "Version %{report_version} for report type %{report_type} has been deprecated, supported versions for this report type are: %{supported_schema_versions}. GitLab will attempt to parse and ingest this report if valid."
msgstr ""
msgid "Version %{report_version} for report type %{report_type} is unsupported, supported versions for this report type are: %{supported_schema_versions}. GitLab will attempt to validate this report against the earliest supported versions of this report type, to show all the errors but will not ingest the report"
msgstr ""
@ -50785,6 +50785,9 @@ msgstr ""
msgid "verify ownership"
msgstr ""
msgid "version %{report_version} for report type %{report_type} is deprecated. However, GitLab will still attempt to parse and ingest this report. Upgrade the security report to one of the following versions: %{current_schema_versions}."
msgstr ""
msgid "version %{versionIndex}"
msgstr ""

View File

@ -102,6 +102,18 @@ RSpec.describe Import::BulkImportsController, feature_category: :importers do
)
end
let(:source_version) do
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION,
::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT)
end
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
allow(instance).to receive(:instance_enterprise).and_return(false)
end
end
it 'returns serialized group data' do
get_status
@ -203,8 +215,15 @@ RSpec.describe Import::BulkImportsController, feature_category: :importers do
end
context 'when connection error occurs' do
let(:source_version) do
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION,
::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT)
end
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
allow(instance).to receive(:instance_enterprise).and_return(false)
allow(instance).to receive(:get).and_raise(BulkImports::Error)
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Import::GithubController do
RSpec.describe Import::GithubController, feature_category: :import do
include ImportSpecHelper
let(:provider) { :github }
@ -138,7 +138,7 @@ RSpec.describe Import::GithubController do
it 'calls repos list from provider with expected args' do
expect_next_instance_of(Gitlab::GithubImport::Clients::Proxy) do |client|
expect(client).to receive(:repos)
.with(expected_filter, expected_pagination_options)
.with(expected_filter, expected_options)
.and_return({ repos: [], page_info: {} })
end
@ -155,11 +155,16 @@ RSpec.describe Import::GithubController do
let(:provider_token) { 'asdasd12345' }
let(:client_auth_success) { true }
let(:client_stub) { instance_double(Gitlab::GithubImport::Client, user: { login: 'user' }) }
let(:expected_pagination_options) { pagination_params.merge(first: 25, page: 1, per_page: 25) }
let(:expected_filter) { nil }
let(:params) { nil }
let(:pagination_params) { { before: nil, after: nil } }
let(:relation_params) { { relation_type: nil, organization_login: '' } }
let(:provider_repos) { [] }
let(:expected_filter) { '' }
let(:expected_options) do
pagination_params.merge(relation_params).merge(
first: 25, page: 1, per_page: 25
)
end
before do
allow_next_instance_of(Gitlab::GithubImport::Clients::Proxy) do |proxy|
@ -277,8 +282,34 @@ RSpec.describe Import::GithubController do
context 'when page is specified' do
let(:pagination_params) { { before: nil, after: nil, page: 2 } }
let(:expected_pagination_options) { pagination_params.merge(first: 25, page: 2, per_page: 25) }
let(:params) { pagination_params }
let(:expected_options) do
pagination_params.merge(relation_params).merge(first: 25, page: 2, per_page: 25)
end
it_behaves_like 'calls repos through Clients::Proxy with expected args'
end
end
context 'when relation type params present' do
let(:organization_login) { 'test-login' }
let(:params) { pagination_params.merge(relation_type: 'organization', organization_login: organization_login) }
let(:pagination_defaults) { { first: 25, page: 1, per_page: 25 } }
let(:expected_options) do
pagination_defaults.merge(pagination_params).merge(
relation_type: 'organization', organization_login: organization_login
)
end
it_behaves_like 'calls repos through Clients::Proxy with expected args'
context 'when organization_login is too long and with ":"' do
let(:organization_login) { ":#{Array.new(270) { ('a'..'z').to_a.sample }.join}" }
let(:expected_options) do
pagination_defaults.merge(pagination_params).merge(
relation_type: 'organization', organization_login: organization_login.slice(1, 254)
)
end
it_behaves_like 'calls repos through Clients::Proxy with expected args'
end

View File

@ -72,6 +72,19 @@ RSpec.describe BranchesFinder, feature_category: :source_code_management do
end
end
context 'by string' do
let(:params) { { search: 'add' } }
it 'returns all branches contain name' do
result = subject
result.each do |branch|
expect(branch.name).to include('add')
end
expect(result.count).to eq(5)
end
end
context 'by provided names' do
let(:params) { { names: %w[fix csv lfs does-not-exist] } }

View File

@ -107,6 +107,7 @@ describe('diffs/components/app', () => {
beforeEach(() => {
const fetchResolver = () => {
store.state.diffs.retrievingBatches = false;
store.state.notes.doneFetchingBatchDiscussions = true;
store.state.notes.discussions = 'test';
return Promise.resolve({ real_size: 100 });
};

View File

@ -163,8 +163,8 @@ exports[`MlCandidate renders correctly 1`] = `
class="gl-text-secondary gl-font-weight-bold"
>
Parameters
Parameters
</td>
<td
@ -190,7 +190,6 @@ exports[`MlCandidate renders correctly 1`] = `
3
</td>
</tr>
<tr
class="divider"
/>
@ -200,8 +199,8 @@ exports[`MlCandidate renders correctly 1`] = `
class="gl-text-secondary gl-font-weight-bold"
>
Metrics
Metrics
</td>
<td
@ -227,6 +226,42 @@ exports[`MlCandidate renders correctly 1`] = `
.99
</td>
</tr>
<tr
class="divider"
/>
<tr>
<td
class="gl-text-secondary gl-font-weight-bold"
>
Metadata
</td>
<td
class="gl-font-weight-bold"
>
FileName
</td>
<td>
test.py
</td>
</tr>
<tr>
<td />
<td
class="gl-font-weight-bold"
>
ExecutionTime
</td>
<td>
.0856
</td>
</tr>
</tbody>
</table>
</div>

View File

@ -15,6 +15,10 @@ describe('MlCandidate', () => {
{ name: 'AUC', value: '.55' },
{ name: 'Accuracy', value: '.99' },
],
metadata: [
{ name: 'FileName', value: 'test.py' },
{ name: 'ExecutionTime', value: '.0856' },
],
info: {
iid: 'candidate_iid',
artifact_link: 'path_to_artifact',

View File

@ -1442,7 +1442,7 @@ describe('Actions Notes Store', () => {
return testAction(
actions.fetchDiscussions,
{},
{ noteableType: notesConstants.MERGE_REQUEST_NOTEABLE_TYPE },
{ noteableType: notesConstants.EPIC_NOTEABLE_TYPE },
[
{ type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } },
{ type: mutationTypes.SET_FETCHING_DISCUSSIONS, payload: false },
@ -1472,9 +1472,7 @@ describe('Actions Notes Store', () => {
);
});
it('dispatches `fetchDiscussionsBatch` action if `paginatedMrDiscussions` feature flag is enabled', () => {
window.gon = { features: { paginatedMrDiscussions: true } };
it('dispatches `fetchDiscussionsBatch` action if noteable is a MergeRequest', () => {
return testAction(
actions.fetchDiscussions,
{ path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' },

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe BulkImports::Clients::HTTP do
RSpec.describe BulkImports::Clients::HTTP, feature_category: :importers do
include ImportSpecHelper
let(:url) { 'http://gitlab.example' }
@ -22,12 +22,6 @@ RSpec.describe BulkImports::Clients::HTTP do
)
end
before do
allow(Gitlab::HTTP).to receive(:get)
.with('http://gitlab.example/api/v4/version', anything)
.and_return(metadata_response)
end
subject { described_class.new(url: url, token: token) }
shared_examples 'performs network request' do
@ -39,7 +33,7 @@ RSpec.describe BulkImports::Clients::HTTP do
context 'error handling' do
context 'when error occurred' do
it 'raises BulkImports::Error' do
it 'raises BulkImports::NetworkError' do
allow(Gitlab::HTTP).to receive(method).and_raise(Errno::ECONNREFUSED)
expect { subject.public_send(method, resource) }.to raise_exception(BulkImports::NetworkError)
@ -47,7 +41,7 @@ RSpec.describe BulkImports::Clients::HTTP do
end
context 'when response is not success' do
it 'raises BulkImports::Error' do
it 'raises BulkImports::NetworkError' do
response_double = double(code: 503, success?: false, parsed_response: 'Error', request: double(path: double(path: '/test')))
allow(Gitlab::HTTP).to receive(method).and_return(response_double)
@ -210,33 +204,149 @@ RSpec.describe BulkImports::Clients::HTTP do
describe '#instance_version' do
it 'returns version as an instance of Gitlab::VersionInfo' do
response = { version: version }
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token')
.to_return(status: 200, body: response.to_json, headers: { 'Content-Type' => 'application/json' })
expect(subject.instance_version).to eq(Gitlab::VersionInfo.parse(version))
end
context 'when /version endpoint is not available' do
it 'requests /metadata endpoint' do
response_double = double(code: 404, success?: false, parsed_response: 'Not Found', request: double(path: double(path: '/version')))
response = { version: version }
allow(Gitlab::HTTP).to receive(:get)
.with('http://gitlab.example/api/v4/version', anything)
.and_return(response_double)
expect(Gitlab::HTTP).to receive(:get)
.with('http://gitlab.example/api/v4/metadata', anything)
.and_return(metadata_response)
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 200, body: response.to_json, headers: { 'Content-Type' => 'application/json' })
expect(subject.instance_version).to eq(Gitlab::VersionInfo.parse(version))
end
context 'when /metadata endpoint returns a 401' do
it 'raises a BulkImports:Error' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 401, body: "", headers: { 'Content-Type' => 'application/json' })
expect { subject.instance_version }.to raise_exception(BulkImports::Error, "Import aborted as the provided personal access token does not have the required 'api' scope.")
end
end
context 'when /metadata endpoint returns a 403' do
it 'raises a BulkImports:Error' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 403, body: "", headers: { 'Content-Type' => 'application/json' })
expect { subject.instance_version }.to raise_exception(BulkImports::Error, "Import aborted as the provided personal access token does not have the required 'api' scope.")
end
end
context 'when /metadata endpoint returns a 404' do
it 'raises a BulkImports:Error' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 404, body: "", headers: { 'Content-Type' => 'application/json' })
expect { subject.instance_version }.to raise_exception(BulkImports::Error, 'Import aborted as it was not possible to connect to the provided GitLab instance URL.')
end
end
context 'when /metadata endpoint returns any other BulkImports::NetworkError' do
it 'raises a BulkImports:NetworkError' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 418, body: "", headers: { 'Content-Type' => 'application/json' })
expect { subject.instance_version }.to raise_exception(BulkImports::NetworkError)
end
end
end
end
describe '#validate_instance_version!' do
before do
allow(subject).to receive(:instance_version).and_return(source_version)
end
context 'when instance version is greater than or equal to the minimum major version' do
let(:source_version) { Gitlab::VersionInfo.new(14) }
it { expect(subject.validate_instance_version!).to eq(true) }
end
context 'when instance version is less than the minimum major version' do
let(:source_version) { Gitlab::VersionInfo.new(13, 10, 0) }
it { expect { subject.validate_instance_version! }.to raise_exception(BulkImports::Error) }
end
end
describe '#validate_import_scopes!' do
context 'when the source_version is < 15.5' do
let(:source_version) { Gitlab::VersionInfo.new(15, 0) }
it 'skips validation' do
allow(subject).to receive(:instance_version).and_return(source_version)
expect(subject.validate_import_scopes!).to eq(true)
end
end
context 'when source version is 15.5 or higher' do
let(:source_version) { Gitlab::VersionInfo.new(15, 6) }
before do
allow(subject).to receive(:instance_version).and_return(source_version)
end
context 'when an HTTP error is raised' do
let(:response) { { enterprise: false } }
it 'raises BulkImports::NetworkError' do
stub_request(:get, 'http://gitlab.example/api/v4/personal_access_tokens/self?private_token=token')
.to_return(status: 404)
expect { subject.validate_import_scopes! }.to raise_exception(BulkImports::NetworkError)
end
end
context 'when scopes are valid' do
it 'returns true' do
stub_request(:get, 'http://gitlab.example/api/v4/personal_access_tokens/self?private_token=token')
.to_return(status: 200, body: { 'scopes' => ['api'] }.to_json, headers: { 'Content-Type' => 'application/json' })
expect(subject.validate_import_scopes!).to eq(true)
end
end
context 'when scopes are invalid' do
it 'raises a BulkImports error' do
stub_request(:get, 'http://gitlab.example/api/v4/personal_access_tokens/self?private_token=token')
.to_return(status: 200, body: { 'scopes' => ['read_user'] }.to_json, headers: { 'Content-Type' => 'application/json' })
expect(subject.instance_version).to eq(Gitlab::VersionInfo.parse(source_version))
expect { subject.validate_import_scopes! }.to raise_exception(BulkImports::Error)
end
end
end
end
describe '#instance_enterprise' do
let(:response) { { enterprise: false } }
before do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token')
.to_return(status: 200, body: response.to_json, headers: { 'Content-Type' => 'application/json' })
end
it 'returns source instance enterprise information' do
expect(subject.instance_enterprise).to eq(false)
end
context 'when enterprise information is missing' do
let(:enterprise) { nil }
let(:response) { {} }
it 'defaults to true' do
expect(subject.instance_enterprise).to eq(true)
@ -245,14 +355,20 @@ RSpec.describe BulkImports::Clients::HTTP do
end
describe '#compatible_for_project_migration?' do
before do
allow(subject).to receive(:instance_version).and_return(Gitlab::VersionInfo.parse(version))
end
context 'when instance version is lower the the expected minimum' do
let(:version) { '14.3.0' }
it 'returns false' do
expect(subject.compatible_for_project_migration?).to be false
end
end
context 'when instance version is at least the expected minimum' do
let(:version) { "14.4.4" }
let(:version) { '14.4.4' }
it 'returns true' do
expect(subject.compatible_for_project_migration?).to be true
@ -260,18 +376,6 @@ RSpec.describe BulkImports::Clients::HTTP do
end
end
context 'when source instance is incompatible' do
let(:version) { '13.0.0' }
it 'raises an error' do
expect { subject.get(resource) }
.to raise_error(
::BulkImports::Error,
"Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MIN_MAJOR_VERSION}."
)
end
end
context 'when url is relative' do
let(:url) { 'http://website.example/gitlab' }

View File

@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator, feature_category: :vulnerability_management do
let_it_be(:project) { create(:project) }
let(:current_dast_versions) { described_class::CURRENT_VERSIONS[:dast].join(', ') }
let(:supported_dast_versions) { described_class::SUPPORTED_VERSIONS[:dast].join(', ') }
let(:deprecated_schema_version_message) {}
let(:missing_schema_version_message) do
@ -466,8 +467,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator, featu
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
let(:expected_deprecation_message) do
"Version #{report_version} for report type #{report_type} has been deprecated, supported versions for this "\
"report type are: #{supported_dast_versions}. GitLab will attempt to parse and ingest this report if valid."
"version #{report_version} for report type #{report_type} is deprecated. "\
"However, GitLab will still attempt to parse and ingest this report. "\
"Upgrade the security report to one of the following versions: #{current_dast_versions}."
end
let(:expected_deprecation_warnings) do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::GithubImport::Client do
RSpec.describe Gitlab::GithubImport::Client, feature_category: :importer do
subject(:client) { described_class.new('foo', parallel: parallel) }
let(:parallel) { true }
@ -614,6 +614,46 @@ RSpec.describe Gitlab::GithubImport::Client do
client.search_repos_by_name_graphql('test')
end
context 'when relation type option present' do
context 'when relation type is owned' do
let(:expected_query) { 'test in:name is:public,private user:user' }
it 'searches for repositories within the organization based on name' do
expect(client.octokit).to receive(:post).with(
'/graphql', { query: expected_graphql }.to_json
)
client.search_repos_by_name_graphql('test', relation_type: 'owned')
end
end
context 'when relation type is organization' do
let(:expected_query) { 'test in:name is:public,private org:test-login' }
it 'searches for repositories within the organization based on name' do
expect(client.octokit).to receive(:post).with(
'/graphql', { query: expected_graphql }.to_json
)
client.search_repos_by_name_graphql(
'test', relation_type: 'organization', organization_login: 'test-login'
)
end
end
context 'when relation type is collaborated' do
let(:expected_query) { 'test in:name is:public,private repo:repo1 repo:repo2' }
it 'searches for collaborated repositories based on name' do
expect(client.octokit).to receive(:post).with(
'/graphql', { query: expected_graphql }.to_json
)
client.search_repos_by_name_graphql('test', relation_type: 'collaborated')
end
end
end
context 'when pagination options present' do
context 'with "first" option' do
let(:expected_graphql_params) do

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Redis::RepositoryCache, feature_category: :scalability do
include_examples "redis_new_instance_shared_examples", 'repository_cache', Gitlab::Redis::Cache
end

View File

@ -91,22 +91,6 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :source_code
expect(discussions.count).to eq(1)
expect(notes).to match([a_hash_including('id' => discussion_2.id.to_s)])
end
context 'when paginated_mr_discussions is disabled' do
before do
stub_feature_flags(paginated_mr_discussions: false)
end
it 'returns all discussions and ignores per_page param' do
get_discussions(per_page: 2)
discussions = Gitlab::Json.parse(response.body)
notes = discussions.flat_map { |d| d['notes'] }
expect(discussions.count).to eq(4)
expect(notes.count).to eq(5)
end
end
end
end

View File

@ -13,19 +13,19 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
source_type: 'group_entity',
source_full_path: 'full/path/to/group1',
destination_slug: 'destination group 1',
destination_namespace: 'full/path/to/destination1'
destination_namespace: 'parent-group'
},
{
source_type: 'group_entity',
source_full_path: 'full/path/to/group2',
destination_slug: 'destination group 2',
destination_namespace: 'full/path/to/destination2'
destination_namespace: 'parent-group'
},
{
source_type: 'project_entity',
source_full_path: 'full/path/to/project1',
destination_slug: 'destination project 1',
destination_namespace: 'full/path/to/destination1'
destination_namespace: 'parent-group'
}
]
end
@ -33,129 +33,186 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
subject { described_class.new(user, params, credentials) }
describe '#execute' do
let_it_be(:source_version) do
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION,
::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT)
end
context 'when gitlab version is 15.5 or higher' do
let(:source_version) { { version: "15.6.0", enterprise: false } }
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
allow(instance).to receive(:instance_enterprise).and_return(false)
end
end
context 'when a BulkImports::Error is raised while validating the instance version' do
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client)
.to receive(:validate_instance_version!)
.and_raise(BulkImports::Error, "This is a BulkImports error.")
end
end
it 'creates bulk import' do
parent_group.add_owner(user)
expect { subject.execute }.to change { BulkImport.count }.by(1)
it 'rescues the error and raises a ServiceResponse::Error' do
result = subject.execute
last_bulk_import = BulkImport.last
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_version).to eq(source_version.to_s)
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_enterprise).to eq(false)
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'bulk_import_group'
)
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'import_access_level',
user: user,
extra: { user_role: 'Owner', import_type: 'bulk_import_group' }
)
end
it 'creates bulk import entities' do
expect { subject.execute }.to change { BulkImports::Entity.count }.by(3)
end
it 'creates bulk import configuration' do
expect { subject.execute }.to change { BulkImports::Configuration.count }.by(1)
end
it 'enqueues BulkImportWorker' do
expect(BulkImportWorker).to receive(:perform_async)
subject.execute
end
it 'returns success ServiceResponse' do
result = subject.execute
expect(result).to be_a(ServiceResponse)
expect(result).to be_success
end
it 'returns ServiceResponse with error if validation fails' do
params[0][:source_full_path] = nil
result = subject.execute
expect(result).to be_a(ServiceResponse)
expect(result).to be_error
expect(result.message).to eq("Validation failed: Source full path can't be blank")
end
context 'when the token is invalid' do
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:instance_version).and_raise(BulkImports::NetworkError, "401 Unauthorized")
expect(result).to be_a(ServiceResponse)
expect(result).to be_error
expect(result.message).to eq("This is a BulkImports error.")
end
end
it 'rescues the error and raises a ServiceResponse::Error' do
result = subject.execute
context 'when required scopes are not present' do
it 'returns ServiceResponse with error if token does not have api scope' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(
status: 200,
body: source_version.to_json,
headers: { 'Content-Type' => 'application/json' }
)
expect(result).to be_a(ServiceResponse)
expect(result).to be_error
expect(result.message).to eq("401 Unauthorized")
end
end
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:validate_instance_version!).and_raise(BulkImports::Error.scope_validation_failure)
end
describe '#user-role' do
context 'when there is a parent_namespace and the user is a member' do
let(:group2) { create(:group, path: 'destination200', source_id: parent_group.id ) }
let(:params) do
[
{
source_type: 'group_entity',
source_full_path: 'full/path/to/group1',
destination_slug: 'destination200',
destination_namespace: 'parent-group'
}
]
result = subject.execute
expect(result).to be_a(ServiceResponse)
expect(result).to be_error
expect(result.message)
.to eq(
"Import aborted as the provided personal access token does not have the required 'api' scope."
)
end
end
it 'defines access_level from parent namespace membership' do
parent_group.add_guest(user)
subject.execute
context 'when token validation succeeds' do
it 'creates bulk import' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 200, body: source_version.to_json, headers: { 'Content-Type' => 'application/json' })
stub_request(:get, 'http://gitlab.example/api/v4/personal_access_tokens/self?private_token=token')
.to_return(
status: 200,
body: { 'scopes' => ['api'] }.to_json,
headers: { 'Content-Type' => 'application/json' }
)
parent_group.add_owner(user)
expect { subject.execute }.to change { BulkImport.count }.by(1)
last_bulk_import = BulkImport.last
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_version).to eq(source_version[:version])
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_enterprise).to eq(false)
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'bulk_import_group'
)
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'import_access_level',
user: user,
extra: { user_role: 'Guest', import_type: 'bulk_import_group' }
extra: { user_role: 'Owner', import_type: 'bulk_import_group' }
)
end
end
end
context 'when there is a parent_namespace and the user is not a member' do
let(:params) do
[
{
source_type: 'group_entity',
source_full_path: 'full/path/to/group1',
destination_slug: 'destination-group-1',
destination_namespace: 'parent-group'
}
]
context 'when gitlab version is lower than 15.5' do
let(:source_version) do
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION,
::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT)
end
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
allow(instance).to receive(:instance_enterprise).and_return(false)
end
end
it 'creates bulk import' do
parent_group.add_owner(user)
expect { subject.execute }.to change { BulkImport.count }.by(1)
last_bulk_import = BulkImport.last
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_version).to eq(source_version.to_s)
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_enterprise).to eq(false)
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'bulk_import_group'
)
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'import_access_level',
user: user,
extra: { user_role: 'Owner', import_type: 'bulk_import_group' }
)
end
it 'creates bulk import entities' do
expect { subject.execute }.to change { BulkImports::Entity.count }.by(3)
end
it 'creates bulk import configuration' do
expect { subject.execute }.to change { BulkImports::Configuration.count }.by(1)
end
it 'enqueues BulkImportWorker' do
expect(BulkImportWorker).to receive(:perform_async)
subject.execute
end
it 'returns success ServiceResponse' do
result = subject.execute
expect(result).to be_a(ServiceResponse)
expect(result).to be_success
end
it 'returns ServiceResponse with error if validation fails' do
params[0][:source_full_path] = nil
result = subject.execute
expect(result).to be_a(ServiceResponse)
expect(result).to be_error
expect(result.message).to eq("Validation failed: Source full path can't be blank")
end
describe '#user-role' do
context 'when there is a parent_namespace and the user is a member' do
let(:group2) { create(:group, path: 'destination200', source_id: parent_group.id ) }
let(:params) do
[
{
source_type: 'group_entity',
source_full_path: 'full/path/to/group1',
destination_slug: 'destination200',
destination_namespace: 'parent-group'
}
]
end
it 'defines access_level from parent namespace membership' do
parent_group.add_guest(user)
subject.execute
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
label: 'import_access_level',
user: user,
extra: { user_role: 'Guest', import_type: 'bulk_import_group' }
)
end
end
it 'defines access_level as not a member' do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe BulkImports::GetImportableDataService do
RSpec.describe BulkImports::GetImportableDataService, feature_category: :importers do
describe '#execute' do
include_context 'bulk imports requests context', 'https://gitlab.example.com'
@ -34,6 +34,18 @@ RSpec.describe BulkImports::GetImportableDataService do
]
end
let(:source_version) do
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION,
::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT)
end
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
allow(instance).to receive(:instance_enterprise).and_return(false)
end
end
subject do
described_class.new(params, query_params, credentials).execute
end

View File

@ -15,7 +15,7 @@ RSpec.shared_context 'bulk imports requests context' do |url|
let(:request_headers) { { 'Content-Type' => 'application/json' } }
before do
stub_request(:get, "#{url}/api/v4/version?page=1&per_page=20&private_token=demo-pat")
stub_request(:get, "#{url}/api/v4/version?private_token=demo-pat")
.with(headers: request_headers)
.to_return(
status: 200,