Commit 6980c355 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 417adc72 18172f28
# frozen_string_literal: true
module ShowInheritedLabelsChecker
extend ActiveSupport::Concern
private
def show_inherited_labels?(include_ancestor_groups)
Feature.enabled?(:show_inherited_labels, @project || @group) || include_ancestor_groups # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
class Groups::LabelsController < Groups::ApplicationController class Groups::LabelsController < Groups::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include ShowInheritedLabelsChecker
before_action :label, only: [:edit, :update, :destroy] before_action :label, only: [:edit, :update, :destroy]
before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy] before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy]
...@@ -12,8 +13,9 @@ class Groups::LabelsController < Groups::ApplicationController ...@@ -12,8 +13,9 @@ class Groups::LabelsController < Groups::ApplicationController
def index def index
respond_to do |format| respond_to do |format|
format.html do format.html do
@labels = GroupLabelsFinder # at group level we do not want to list project labels,
.new(current_user, @group, params.merge(sort: sort)).execute # we only want `only_group_labels = false` when pulling labels for label filter dropdowns, fetched through json
@labels = available_labels(params.merge(only_group_labels: true)).page(params[:page])
end end
format.json do format.json do
render json: LabelSerializer.new.represent_appearance(available_labels) render json: LabelSerializer.new.represent_appearance(available_labels)
...@@ -74,7 +76,7 @@ class Groups::LabelsController < Groups::ApplicationController ...@@ -74,7 +76,7 @@ class Groups::LabelsController < Groups::ApplicationController
end end
def label def label
@label ||= @group.labels.find(params[:id]) @label ||= available_labels(params.merge(only_group_labels: true)).find(params[:id])
end end
alias_method :subscribable_resource, :label alias_method :subscribable_resource, :label
...@@ -102,15 +104,17 @@ class Groups::LabelsController < Groups::ApplicationController ...@@ -102,15 +104,17 @@ class Groups::LabelsController < Groups::ApplicationController
session[:previous_labels_path] = URI(request.referer || '').path session[:previous_labels_path] = URI(request.referer || '').path
end end
def available_labels def available_labels(options = params)
@available_labels ||= @available_labels ||=
LabelsFinder.new( LabelsFinder.new(
current_user, current_user,
group_id: @group.id, group_id: @group.id,
only_group_labels: params[:only_group_labels], only_group_labels: options[:only_group_labels],
include_ancestor_groups: params[:include_ancestor_groups], include_ancestor_groups: show_inherited_labels?(params[:include_ancestor_groups]),
include_descendant_groups: params[:include_descendant_groups], sort: sort,
search: params[:search]).execute subscribed: options[:subscribed],
include_descendant_groups: options[:include_descendant_groups],
search: options[:search]).execute
end end
def sort def sort
......
...@@ -26,8 +26,7 @@ class Import::ManifestController < Import::BaseController ...@@ -26,8 +26,7 @@ class Import::ManifestController < Import::BaseController
manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile) manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile)
if manifest.valid? if manifest.valid?
session[:manifest_import_repositories] = manifest.projects manifest_import_metadata.save(manifest.projects, group.id)
session[:manifest_import_group_id] = group.id
redirect_to status_import_manifest_path redirect_to status_import_manifest_path
else else
...@@ -96,12 +95,16 @@ class Import::ManifestController < Import::BaseController ...@@ -96,12 +95,16 @@ class Import::ManifestController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def group def group
@group ||= Group.find_by(id: session[:manifest_import_group_id]) @group ||= Group.find_by(id: manifest_import_metadata.group_id)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def manifest_import_metadata
@manifest_import_status ||= Gitlab::ManifestImport::Metadata.new(current_user, fallback: session)
end
def repositories def repositories
@repositories ||= session[:manifest_import_repositories] @repositories ||= manifest_import_metadata.repositories
end end
def find_jobs def find_jobs
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
class Projects::LabelsController < Projects::ApplicationController class Projects::LabelsController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include ShowInheritedLabelsChecker
before_action :check_issuables_available! before_action :check_issuables_available!
before_action :label, only: [:edit, :update, :destroy, :promote] before_action :label, only: [:edit, :update, :destroy, :promote]
...@@ -161,7 +162,7 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -161,7 +162,7 @@ class Projects::LabelsController < Projects::ApplicationController
@available_labels ||= @available_labels ||=
LabelsFinder.new(current_user, LabelsFinder.new(current_user,
project_id: @project.id, project_id: @project.id,
include_ancestor_groups: params[:include_ancestor_groups], include_ancestor_groups: show_inherited_labels?(params[:include_ancestor_groups]),
search: params[:search], search: params[:search],
subscribed: params[:subscribed], subscribed: params[:subscribed],
sort: sort).execute sort: sort).execute
......
# frozen_string_literal: true
class GroupLabelsFinder
attr_reader :current_user, :group, :params
def initialize(current_user, group, params = {})
@current_user = current_user
@group = group
@params = params
end
def execute
group.labels
.optionally_subscribed_by(subscriber_id)
.optionally_search(params[:search])
.order_by(params[:sort])
.page(params[:page])
end
private
def subscriber_id
current_user&.id if subscribed?
end
def subscribed?
params[:subscribed] == 'true'
end
end
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
- auto_devops - auto_devops
- backup_restore - backup_restore
- behavior_analytics - behavior_analytics
- billing
- chatops - chatops
- cloud_native_installation - cloud_native_installation
- cluster_cost_optimization - cluster_cost_optimization
...@@ -87,6 +86,8 @@ ...@@ -87,6 +86,8 @@
- planning_analytics - planning_analytics
- product_analytics - product_analytics
- projects - projects
- provision
- purchase
- quality_management - quality_management
- release_evidence - release_evidence
- release_orchestration - release_orchestration
......
---
name: show_inherited_labels
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42960
rollout_issue_url:
group: group::project management
type: development
default_enabled: false
...@@ -67,3 +67,7 @@ That's totally fine. We use HTTP(s) to fetch repository changes from the **prima ...@@ -67,3 +67,7 @@ That's totally fine. We use HTTP(s) to fetch repository changes from the **prima
## Is this possible to set up a Docker Registry for a **secondary** node that mirrors the one on the **primary** node? ## Is this possible to set up a Docker Registry for a **secondary** node that mirrors the one on the **primary** node?
Yes. See [Docker Registry for a **secondary** node](docker_registry.md). Yes. See [Docker Registry for a **secondary** node](docker_registry.md).
## Can I login to a secondary node?
Yes, but secondary nodes receive all authentication data (like user accounts and logins) from the primary instance. This means you will be re-directed to the primary for authentication and routed back afterwards.
...@@ -117,9 +117,9 @@ For source installations the following settings are nested under `artifacts:` an ...@@ -117,9 +117,9 @@ For source installations the following settings are nested under `artifacts:` an
|---------|-------------|---------| |---------|-------------|---------|
| `enabled` | Enable/disable object storage | `false` | | `enabled` | Enable/disable object storage | `false` |
| `remote_directory` | The bucket name where Artifacts will be stored| | | `remote_directory` | The bucket name where Artifacts will be stored| |
| `direct_upload` | Set to true to enable direct upload of Artifacts without the need of local shared storage. Option may be removed once we decide to support only single storage for all files. | `false` | | `direct_upload` | Set to `true` to enable direct upload of Artifacts without the need of local shared storage. Option may be removed once we decide to support only single storage for all files. | `false` |
| `background_upload` | Set to false to disable automatic upload. Option may be removed once upload is direct to S3 | `true` | | `background_upload` | Set to `false` to disable automatic upload. Option may be removed once upload is direct to S3 | `true` |
| `proxy_download` | Set to true to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` | | `proxy_download` | Set to `true` to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` |
| `connection` | Various connection options described below | | | `connection` | Various connection options described below | |
#### Connection settings #### Connection settings
...@@ -318,7 +318,7 @@ _The uploads are stored by default in ...@@ -318,7 +318,7 @@ _The uploads are stored by default in
In order to migrate back to local storage: In order to migrate back to local storage:
1. Set both `direct_upload` and `background_upload` to false in `gitlab.rb`, under the artifacts object storage settings. 1. Set both `direct_upload` and `background_upload` to `false` in `gitlab.rb`, under the artifacts object storage settings.
1. [Reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure). 1. [Reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure).
1. Run `gitlab-rake gitlab:artifacts:migrate_to_local`. 1. Run `gitlab-rake gitlab:artifacts:migrate_to_local`.
1. Disable object_storage for artifacts in `gitlab.rb`: 1. Disable object_storage for artifacts in `gitlab.rb`:
......
...@@ -92,9 +92,9 @@ then `object_store:`. On Omnibus installations, they are prefixed by ...@@ -92,9 +92,9 @@ then `object_store:`. On Omnibus installations, they are prefixed by
|---------|-------------|---------| |---------|-------------|---------|
| `enabled` | Enable/disable object storage | `false` | | `enabled` | Enable/disable object storage | `false` |
| `remote_directory` | The bucket name where external diffs will be stored| | | `remote_directory` | The bucket name where external diffs will be stored| |
| `direct_upload` | Set to true to enable direct upload of external diffs without the need of local shared storage. Option may be removed once we decide to support only single storage for all files. | `false` | | `direct_upload` | Set to `true` to enable direct upload of external diffs without the need of local shared storage. Option may be removed once we decide to support only single storage for all files. | `false` |
| `background_upload` | Set to false to disable automatic upload. Option may be removed once upload is direct to S3 | `true` | | `background_upload` | Set to `false` to disable automatic upload. Option may be removed once upload is direct to S3 | `true` |
| `proxy_download` | Set to true to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` | | `proxy_download` | Set to `true` to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` |
| `connection` | Various connection options described below | | | `connection` | Various connection options described below | |
### S3 compatible connection settings ### S3 compatible connection settings
......
...@@ -68,9 +68,9 @@ For source installations the following settings are nested under `uploads:` and ...@@ -68,9 +68,9 @@ For source installations the following settings are nested under `uploads:` and
|---------|-------------|---------| |---------|-------------|---------|
| `enabled` | Enable/disable object storage | `false` | | `enabled` | Enable/disable object storage | `false` |
| `remote_directory` | The bucket name where Uploads will be stored| | | `remote_directory` | The bucket name where Uploads will be stored| |
| `direct_upload` | Set to true to remove Puma from the Upload path. Workhorse handles the actual Artifact Upload to Object Storage while Puma does minimal processing to keep track of the upload. There is no need for local shared storage. The option may be removed if support for a single storage type for all files is introduced. Read more on [direct upload](../development/uploads.md#direct-upload). | `false` | | `direct_upload` | Set to `true` to remove Puma from the Upload path. Workhorse handles the actual Artifact Upload to Object Storage while Puma does minimal processing to keep track of the upload. There is no need for local shared storage. The option may be removed if support for a single storage type for all files is introduced. Read more on [direct upload](../development/uploads.md#direct-upload). | `false` |
| `background_upload` | Set to false to disable automatic upload. Option may be removed once upload is direct to S3 (if `direct_upload` is set to `true` it will override `background_upload`) | `true` | | `background_upload` | Set to `false` to disable automatic upload. Option may be removed once upload is direct to S3 (if `direct_upload` is set to `true` it will override `background_upload`) | `true` |
| `proxy_download` | Set to true to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` | | `proxy_download` | Set to `true` to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` |
| `connection` | Various connection options described below | | | `connection` | Various connection options described below | |
### Connection settings ### Connection settings
......
...@@ -172,7 +172,7 @@ ...@@ -172,7 +172,7 @@
:idempotent: :idempotent:
:tags: [] :tags: []
- :name: cronjob:historical_data - :name: cronjob:historical_data
:feature_category: :billing :feature_category: :provision
:has_external_dependencies: :has_external_dependencies:
:urgency: :low :urgency: :low
:resource_boundary: :unknown :resource_boundary: :unknown
...@@ -236,7 +236,7 @@ ...@@ -236,7 +236,7 @@
:idempotent: :idempotent:
:tags: [] :tags: []
- :name: cronjob:sync_seat_link - :name: cronjob:sync_seat_link
:feature_category: :billing :feature_category: :provision
:has_external_dependencies: :has_external_dependencies:
:urgency: :low :urgency: :low
:resource_boundary: :unknown :resource_boundary: :unknown
...@@ -700,7 +700,7 @@ ...@@ -700,7 +700,7 @@
:idempotent: true :idempotent: true
:tags: [] :tags: []
- :name: sync_seat_link_request - :name: sync_seat_link_request
:feature_category: :billing :feature_category: :provision
:has_external_dependencies: true :has_external_dependencies: true
:urgency: :low :urgency: :low
:resource_boundary: :unknown :resource_boundary: :unknown
......
...@@ -7,7 +7,7 @@ class HistoricalDataWorker # rubocop:disable Scalability/IdempotentWorker ...@@ -7,7 +7,7 @@ class HistoricalDataWorker # rubocop:disable Scalability/IdempotentWorker
include CronjobQueue include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext # rubocop:enable Scalability/CronWorkerContext
feature_category :billing feature_category :provision
def perform def perform
return if License.current.nil? || License.current&.trial? return if License.current.nil? || License.current&.trial?
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class SyncSeatLinkRequestWorker class SyncSeatLinkRequestWorker
include ApplicationWorker include ApplicationWorker
feature_category :billing feature_category :provision
# Retry for up to approximately 6 days # Retry for up to approximately 6 days
sidekiq_options retry: 20 sidekiq_options retry: 20
......
...@@ -7,7 +7,7 @@ class SyncSeatLinkWorker # rubocop:disable Scalability/IdempotentWorker ...@@ -7,7 +7,7 @@ class SyncSeatLinkWorker # rubocop:disable Scalability/IdempotentWorker
include CronjobQueue include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext # rubocop:enable Scalability/CronWorkerContext
feature_category :billing feature_category :provision
# Retry for up to approximately 17 hours # Retry for up to approximately 17 hours
sidekiq_options retry: 12, dead: false sidekiq_options retry: 12, dead: false
......
--- ---
name: codeowners_match_ancestor_groups name: codeowners_match_ancestor_groups
introduced_by_url: introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31887
rollout_issue_url: rollout_issue_url:
group: group: group::source code
type: development type: development
default_enabled: true default_enabled: true
# frozen_string_literal: true
module Gitlab
module ManifestImport
class Metadata
EXPIRY_TIME = 1.week
attr_reader :user, :fallback
def initialize(user, fallback: {})
@user = user
@fallback = fallback
end
def save(repositories, group_id)
Gitlab::Redis::SharedState.with do |redis|
redis.multi do
redis.set(key_for('repositories'), Gitlab::Json.dump(repositories), ex: EXPIRY_TIME)
redis.set(key_for('group_id'), group_id, ex: EXPIRY_TIME)
end
end
end
def repositories
redis_get('repositories').then do |repositories|
next unless repositories
Gitlab::Json.parse(repositories).map(&:symbolize_keys)
end || fallback[:manifest_import_repositories]
end
def group_id
redis_get('group_id')&.to_i || fallback[:manifest_import_group_id]
end
private
def key_for(field)
"manifest_import:metadata:user:#{user.id}:#{field}"
end
def redis_get(field)
Gitlab::Redis::SharedState.with do |redis|
redis.get(key_for(field))
end
end
end
end
end
...@@ -9,6 +9,8 @@ RSpec.describe Groups::LabelsController do ...@@ -9,6 +9,8 @@ RSpec.describe Groups::LabelsController do
before do before do
group.add_owner(user) group.add_owner(user)
# by default FFs are enabled in specs so we turn it off
stub_feature_flags(show_inherited_labels: false)
sign_in(user) sign_in(user)
end end
...@@ -32,11 +34,41 @@ RSpec.describe Groups::LabelsController do ...@@ -32,11 +34,41 @@ RSpec.describe Groups::LabelsController do
subgroup.add_owner(user) subgroup.add_owner(user)
end end
it 'returns ancestor group labels' do RSpec.shared_examples 'returns ancestor group labels' do
get :index, params: { group_id: subgroup, include_ancestor_groups: true, only_group_labels: true }, format: :json it 'returns ancestor group labels' do
get :index, params: params, format: :json
label_ids = json_response.map {|label| label['title']} label_ids = json_response.map {|label| label['title']}
expect(label_ids).to match_array([group_label_1.title, subgroup_label_1.title]) expect(label_ids).to match_array([group_label_1.title, subgroup_label_1.title])
end
end
context 'when include_ancestor_groups true' do
let(:params) { { group_id: subgroup, include_ancestor_groups: true, only_group_labels: true } }
it_behaves_like 'returns ancestor group labels'
end
context 'when include_ancestor_groups false' do
let(:params) { { group_id: subgroup, only_group_labels: true } }
it 'does not return ancestor group labels', :aggregate_failures do
get :index, params: params, format: :json
label_ids = json_response.map {|label| label['title']}
expect(label_ids).to match_array([subgroup_label_1.title])
expect(label_ids).not_to include([group_label_1.title])
end
end
context 'when show_inherited_labels enabled' do
let(:params) { { group_id: subgroup } }
before do
stub_feature_flags(show_inherited_labels: true)
end
it_behaves_like 'returns ancestor group labels'
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Import::ManifestController do RSpec.describe Import::ManifestController, :clean_gitlab_redis_shared_state do
include ImportSpecHelper include ImportSpecHelper
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
...@@ -16,42 +16,93 @@ RSpec.describe Import::ManifestController do ...@@ -16,42 +16,93 @@ RSpec.describe Import::ManifestController do
sign_in(user) sign_in(user)
end end
def assign_session_group describe 'POST upload' do
session[:manifest_import_repositories] = [] context 'with a valid manifest' do
session[:manifest_import_group_id] = group.id it 'saves the manifest and redirects to the status page', :aggregate_failures do
post :upload, params: {
group_id: group.id,
manifest: fixture_file_upload('spec/fixtures/aosp_manifest.xml')
}
metadata = Gitlab::ManifestImport::Metadata.new(user)
expect(metadata.group_id).to eq(group.id)
expect(metadata.repositories.size).to eq(660)
expect(metadata.repositories.first).to include(name: 'platform/build', path: 'build/make')
expect(response).to redirect_to(status_import_manifest_path)
end
end
context 'with an invalid manifest' do
it 'displays an error' do
post :upload, params: {
group_id: group.id,
manifest: fixture_file_upload('spec/fixtures/invalid_manifest.xml')
}
expect(assigns(:errors)).to be_present
end
end
context 'when the user cannot create projects in the group' do
it 'displays an error' do
sign_in(create(:user))
post :upload, params: {
group_id: group.id,
manifest: fixture_file_upload('spec/fixtures/aosp_manifest.xml')
}
expect(assigns(:errors)).to be_present
end
end
end end
describe 'GET status' do describe 'GET status' do
let(:repo1) { OpenStruct.new(id: 'test1', url: 'http://demo.host/test1') } let(:repo1) { { id: 'test1', url: 'http://demo.host/test1' } }
let(:repo2) { OpenStruct.new(id: 'test2', url: 'http://demo.host/test2') } let(:repo2) { { id: 'test2', url: 'http://demo.host/test2' } }
let(:repos) { [repo1, repo2] } let(:repos) { [repo1, repo2] }
before do shared_examples 'status action' do
assign_session_group it "returns variables for json request" do
project = create(:project, import_type: 'manifest', creator_id: user.id)
session[:manifest_import_repositories] = repos get :status, format: :json
end
it "returns variables for json request" do expect(response).to have_gitlab_http_status(:ok)
project = create(:project, import_type: 'manifest', creator_id: user.id) expect(json_response.dig("imported_projects", 0, "id")).to eq(project.id)
expect(json_response.dig("provider_repos", 0, "id")).to eq(repo1[:id])
expect(json_response.dig("provider_repos", 1, "id")).to eq(repo2[:id])
expect(json_response.dig("namespaces", 0, "id")).to eq(group.id)
end
get :status, format: :json it "does not show already added project" do
project = create(:project, import_type: 'manifest', namespace: user.namespace, import_status: :finished, import_url: repo1[:url])
expect(response).to have_gitlab_http_status(:ok) get :status, format: :json
expect(json_response.dig("imported_projects", 0, "id")).to eq(project.id)
expect(json_response.dig("provider_repos", 0, "id")).to eq(repo1.id) expect(json_response.dig("imported_projects", 0, "id")).to eq(project.id)
expect(json_response.dig("provider_repos", 1, "id")).to eq(repo2.id) expect(json_response.dig("provider_repos").length).to eq(1)
expect(json_response.dig("namespaces", 0, "id")).to eq(group.id) expect(json_response.dig("provider_repos", 0, "id")).not_to eq(repo1[:id])
end
end end
it "does not show already added project" do context 'when the data is stored via Gitlab::ManifestImport::Metadata' do
project = create(:project, import_type: 'manifest', namespace: user.namespace, import_status: :finished, import_url: repo1.url) before do
Gitlab::ManifestImport::Metadata.new(user).save(repos, group.id)
end
include_examples 'status action'
end
get :status, format: :json context 'when the data is stored in the user session' do
before do
session[:manifest_import_repositories] = repos
session[:manifest_import_group_id] = group.id
end
expect(json_response.dig("imported_projects", 0, "id")).to eq(project.id) include_examples 'status action'
expect(json_response.dig("provider_repos").length).to eq(1)
expect(json_response.dig("provider_repos", 0, "id")).not_to eq(repo1.id)
end end
end end
end end
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Projects::LabelsController do RSpec.describe Projects::LabelsController do
let(:group) { create(:group) } let_it_be(:group) { create(:group) }
let(:project) { create(:project, namespace: group) } let_it_be(:project, reload: true) { create(:project, namespace: group) }
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
...@@ -14,16 +14,21 @@ RSpec.describe Projects::LabelsController do ...@@ -14,16 +14,21 @@ RSpec.describe Projects::LabelsController do
end end
describe 'GET #index' do describe 'GET #index' do
let!(:label_1) { create(:label, project: project, priority: 1, title: 'Label 1') } let_it_be(:label_1) { create(:label, project: project, priority: 1, title: 'Label 1') }
let!(:label_2) { create(:label, project: project, priority: 3, title: 'Label 2') } let_it_be(:label_2) { create(:label, project: project, priority: 3, title: 'Label 2') }
let!(:label_3) { create(:label, project: project, priority: 1, title: 'Label 3') } let_it_be(:label_3) { create(:label, project: project, priority: 1, title: 'Label 3') }
let!(:label_4) { create(:label, project: project, title: 'Label 4') } let_it_be(:label_4) { create(:label, project: project, title: 'Label 4') }
let!(:label_5) { create(:label, project: project, title: 'Label 5') } let_it_be(:label_5) { create(:label, project: project, title: 'Label 5') }
let!(:group_label_1) { create(:group_label, group: group, title: 'Group Label 1') } let_it_be(:group_label_1) { create(:group_label, group: group, title: 'Group Label 1') }
let!(:group_label_2) { create(:group_label, group: group, title: 'Group Label 2') } let_it_be(:group_label_2) { create(:group_label, group: group, title: 'Group Label 2') }
let!(:group_label_3) { create(:group_label, group: group, title: 'Group Label 3') } let_it_be(:group_label_3) { create(:group_label, group: group, title: 'Group Label 3') }
let!(:group_label_4) { create(:group_label, group: group, title: 'Group Label 4') } let_it_be(:group_label_4) { create(:group_label, group: group, title: 'Group Label 4') }
let_it_be(:group_labels) { [group_label_3, group_label_4]}
let_it_be(:project_labels) { [label_4, label_5]}
let_it_be(:group_priority_labels) { [group_label_1, group_label_2]}
let_it_be(:project_priority_labels) { [label_1, label_2, label_3]}
before do before do
create(:label_priority, project: project, label: group_label_1, priority: 3) create(:label_priority, project: project, label: group_label_1, priority: 3)
...@@ -68,6 +73,60 @@ RSpec.describe Projects::LabelsController do ...@@ -68,6 +73,60 @@ RSpec.describe Projects::LabelsController do
end end
end end
context 'with subgroups' do
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:subgroup_label_1) { create(:group_label, group: subgroup, title: 'subgroup_label_1') }
let_it_be(:subgroup_label_2) { create(:group_label, group: subgroup, title: 'subgroup_label_2') }
before do
project.update!(namespace: subgroup)
subgroup.add_owner(user)
create(:label_priority, project: project, label: subgroup_label_2, priority: 1)
end
RSpec.shared_examples 'returns ancestor group labels' do
it 'returns ancestor group labels', :aggregate_failures do
get :index, params: params
expect(assigns(:labels)).to match_array([subgroup_label_1] + group_labels + project_labels)
expect(assigns(:prioritized_labels)).to match_array([subgroup_label_2] + group_priority_labels + project_priority_labels)
end
end
context 'when show_inherited_labels disabled' do
before do
stub_feature_flags(show_inherited_labels: false)
end
context 'when include_ancestor_groups false' do
let(:params) { { namespace_id: project.namespace.to_param, project_id: project } }
it 'does not return ancestor group labels', :aggregate_failures do
get :index, params: params
expect(assigns(:labels)).to match_array([subgroup_label_1] + project_labels)
expect(assigns(:prioritized_labels)).to match_array([subgroup_label_2] + project_priority_labels)
end
end
context 'when include_ancestor_groups true' do
let(:params) { { namespace_id: project.namespace.to_param, project_id: project, include_ancestor_groups: true } }
it_behaves_like 'returns ancestor group labels'
end
end
context 'when show_inherited_labels enabled' do
let(:params) { { namespace_id: project.namespace.to_param, project_id: project } }
before do
stub_feature_flags(show_inherited_labels: true)
end
it_behaves_like 'returns ancestor group labels'
end
end
def list_labels def list_labels
get :index, params: { namespace_id: project.namespace.to_param, project_id: project } get :index, params: { namespace_id: project.namespace.to_param, project_id: project }
end end
...@@ -75,7 +134,7 @@ RSpec.describe Projects::LabelsController do ...@@ -75,7 +134,7 @@ RSpec.describe Projects::LabelsController do
describe 'POST #generate' do describe 'POST #generate' do
context 'personal project' do context 'personal project' do
let(:personal_project) { create(:project, namespace: user.namespace) } let_it_be(:personal_project) { create(:project, namespace: user.namespace) }
it 'creates labels' do it 'creates labels' do
post :generate, params: { namespace_id: personal_project.namespace.to_param, project_id: personal_project } post :generate, params: { namespace_id: personal_project.namespace.to_param, project_id: personal_project }
...@@ -116,8 +175,8 @@ RSpec.describe Projects::LabelsController do ...@@ -116,8 +175,8 @@ RSpec.describe Projects::LabelsController do
end end
describe 'POST #promote' do describe 'POST #promote' do
let!(:promoted_label_name) { "Promoted Label" } let_it_be(:promoted_label_name) { "Promoted Label" }
let!(:label_1) { create(:label, title: promoted_label_name, project: project) } let_it_be(:label_1) { create(:label, title: promoted_label_name, project: project) }
context 'not group reporters' do context 'not group reporters' do
it 'denies access' do it 'denies access' do
...@@ -196,7 +255,7 @@ RSpec.describe Projects::LabelsController do ...@@ -196,7 +255,7 @@ RSpec.describe Projects::LabelsController do
end end
context 'when requesting a redirected path' do context 'when requesting a redirected path' do
let!(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') } let_it_be(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
it 'redirects to the canonical path' do it 'redirects to the canonical path' do
get :index, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' } get :index, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' }
...@@ -242,7 +301,7 @@ RSpec.describe Projects::LabelsController do ...@@ -242,7 +301,7 @@ RSpec.describe Projects::LabelsController do
end end
context 'when requesting a redirected path' do context 'when requesting a redirected path' do
let!(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') } let_it_be(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
it 'returns not found' do it 'returns not found' do
post :generate, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' } post :generate, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GroupLabelsFinder, '#execute' do
let!(:group) { create(:group) }
let!(:user) { create(:user) }
let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) }
let!(:label2) { create(:group_label, title: 'Bar', description: 'Fusce consequat', group: group) }
it 'returns all group labels sorted by name if no params' do
result = described_class.new(user, group).execute
expect(result.to_a).to match_array([label2, label1])
end
it 'returns all group labels sorted by name desc' do
result = described_class.new(user, group, sort: 'name_desc').execute
expect(result.to_a).to match_array([label2, label1])
end
it 'returns group labels that match search' do
result = described_class.new(user, group, search: 'Foo').execute
expect(result.to_a).to match_array([label1])
end
it 'returns group labels user subscribed to' do
label2.subscribe(user)
result = described_class.new(user, group, subscribed: 'true').execute
expect(result.to_a).to match_array([label2])
end
it 'returns second page of labels' do
result = described_class.new(user, group, page: '2').execute
expect(result.to_a).to match_array([])
end
end
<manifest>
<remote review="invalid-url" />
<project name="platform/build"/>
</manifest>
...@@ -12,19 +12,7 @@ RSpec.describe Gitlab::ManifestImport::Manifest do ...@@ -12,19 +12,7 @@ RSpec.describe Gitlab::ManifestImport::Manifest do
end end
context 'missing or invalid attributes' do context 'missing or invalid attributes' do
let(:file) { Tempfile.new('foo') } let(:file) { File.open(Rails.root.join('spec/fixtures/invalid_manifest.xml')) }
before do
content = <<~EOS
<manifest>
<remote review="invalid-url" />
<project name="platform/build"/>
</manifest>
EOS
file.write(content)
file.rewind
end
it { expect(manifest.valid?).to be false } it { expect(manifest.valid?).to be false }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::ManifestImport::Metadata, :clean_gitlab_redis_shared_state do
let(:user) { double(id: 1) }
let(:repositories) do
[
{ id: 'test1', url: 'http://demo.host/test1' },
{ id: 'test2', url: 'http://demo.host/test2' }
]
end
describe '#save' do
it 'stores data in Redis with an expiry of EXPIRY_TIME' do
status = described_class.new(user)
repositories_key = 'manifest_import:metadata:user:1:repositories'
group_id_key = 'manifest_import:metadata:user:1:group_id'
status.save(repositories, 2)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.ttl(repositories_key)).to be_within(5).of(described_class::EXPIRY_TIME)
expect(redis.ttl(group_id_key)).to be_within(5).of(described_class::EXPIRY_TIME)
end
end
end
describe '#repositories' do
it 'allows repositories to round-trip with symbol keys' do
status = described_class.new(user)
status.save(repositories, 2)
expect(status.repositories).to eq(repositories)
end
it 'uses the fallback when there is nothing in Redis' do
fallback = { manifest_import_repositories: repositories }
status = described_class.new(user, fallback: fallback)
expect(status.repositories).to eq(repositories)
end
end
describe '#group_id' do
it 'returns the group ID as an integer' do
status = described_class.new(user)
status.save(repositories, 2)
expect(status.group_id).to eq(2)
end
it 'uses the fallback when there is nothing in Redis' do
fallback = { manifest_import_group_id: 3 }
status = described_class.new(user, fallback: fallback)
expect(status.group_id).to eq(3)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment