diff --git a/app/serializers/remote_mirror_entity.rb b/app/serializers/remote_mirror_entity.rb index 440e427466822c7481d645a864eb27fe744968c8..7eddb3fef4a43c2f4e2408c623c94b3da97fd5d8 100644 --- a/app/serializers/remote_mirror_entity.rb +++ b/app/serializers/remote_mirror_entity.rb @@ -13,3 +13,5 @@ class RemoteMirrorEntity < Grape::Entity remote_mirror.ssh_known_hosts_fingerprints.as_json end end + +RemoteMirrorEntity.prepend_mod diff --git a/ee/app/serializers/ee/project_mirror_entity.rb b/ee/app/serializers/ee/project_mirror_entity.rb index 3cd3e75cfe66bb1c27c5f5828dbae0876973bc2c..5287ef7ad2217ede6816ed02f5787801a46eb0e3 100644 --- a/ee/app/serializers/ee/project_mirror_entity.rb +++ b/ee/app/serializers/ee/project_mirror_entity.rb @@ -32,6 +32,8 @@ module ProjectMirrorEntity data end + + expose :mirror_branch_regex, if: proc { |project| ::Feature.enabled?(:mirror_only_branches_match_regex, project) } end end end diff --git a/ee/app/serializers/ee/remote_mirror_entity.rb b/ee/app/serializers/ee/remote_mirror_entity.rb new file mode 100644 index 0000000000000000000000000000000000000000..ef6ecebb22f95b1ab9d7efa8bd88facebc514e56 --- /dev/null +++ b/ee/app/serializers/ee/remote_mirror_entity.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module EE + module RemoteMirrorEntity + extend ActiveSupport::Concern + + prepended do + expose :only_protected_branches + + expose :mirror_branch_regex, if: proc { |mirror| + ::Feature.enabled?(:mirror_only_branches_match_regex, mirror.project) + } + end + end +end diff --git a/ee/lib/ee/api/entities/remote_mirror.rb b/ee/lib/ee/api/entities/remote_mirror.rb new file mode 100644 index 0000000000000000000000000000000000000000..2734baed47d1e2160a57f624d0eabaf9ef9489a8 --- /dev/null +++ b/ee/lib/ee/api/entities/remote_mirror.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module EE + module API + module Entities + module RemoteMirror + extend ActiveSupport::Concern + + prepended do + expose :mirror_branch_regex, if: proc { |mirror| + ::Feature.enabled?(:mirror_only_branches_match_regex, mirror.project) + } + end + end + end + end +end diff --git a/ee/lib/ee/api/helpers/projects_helpers.rb b/ee/lib/ee/api/helpers/projects_helpers.rb index 125da31d1b4035f7cfb504d74235a21b4da4710b..9d8eca9f342a5259b74f545696daa66e5569401e 100644 --- a/ee/lib/ee/api/helpers/projects_helpers.rb +++ b/ee/lib/ee/api/helpers/projects_helpers.rb @@ -32,6 +32,8 @@ module ProjectsHelpers params :optional_update_params_ee do optional :mirror_user_id, type: Integer, desc: 'User responsible for all the activity surrounding a pull mirror event. Can only be set by admins' optional :only_mirror_protected_branches, type: Grape::API::Boolean, desc: 'Only mirror protected branches' + optional :mirror_branch_regex, type: String, desc: 'Only mirror branches match regex' + mutually_exclusive :only_mirror_protected_branches, :mirror_branch_regex optional :mirror_overwrites_diverged_branches, type: Grape::API::Boolean, desc: 'Pull mirror overwrites diverged branches' optional :import_url, type: String, desc: 'URL from which the project is imported' optional :fallback_approvals_required, type: Integer, desc: 'Overall approvals required when no rule is present' @@ -74,6 +76,13 @@ def filter_attributes_using_license!(attrs) attrs.delete(:external_authorization_classification_label) end end + + override :filter_attributes_under_feature_flag! + def filter_attributes_under_feature_flag!(attrs, project) + super + + attrs.delete(:mirror_branch_regex) unless ::Feature.enabled?(:mirror_only_branches_match_regex, project) + end end end end diff --git a/ee/lib/ee/api/helpers/remote_mirrors_helpers.rb b/ee/lib/ee/api/helpers/remote_mirrors_helpers.rb new file mode 100644 index 0000000000000000000000000000000000000000..a58ce2f6d96ad39dca93a850020ae580a7f9ce2f --- /dev/null +++ b/ee/lib/ee/api/helpers/remote_mirrors_helpers.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module EE + module API + module Helpers + module RemoteMirrorsHelpers + extend ActiveSupport::Concern + extend ::Gitlab::Utils::Override + + prepended do + params :mirror_branches_setting_ee do + optional :mirror_branch_regex, type: String, desc: 'Determines if only matched branches are mirrored' + mutually_exclusive :only_protected_branches, :mirror_branch_regex + end + end + + override :verify_mirror_branches_setting + def verify_mirror_branches_setting(attrs, project) + attrs[:mirror_branch_regex] = nil unless ::Feature.enabled?(:mirror_only_branches_match_regex, project) + + if attrs[:only_protected_branches] + attrs[:mirror_branch_regex] = nil + elsif attrs[:mirror_branch_regex].present? + attrs[:only_protected_branches] = false + end + end + end + end + end +end diff --git a/ee/spec/models/ee/project_setting_spec.rb b/ee/spec/models/ee/project_setting_spec.rb index 4acf0fd5396184a96d90a7e52c3f703d8a2390dc..bd95609229dda9750892b1702fb5022f51a6e620 100644 --- a/ee/spec/models/ee/project_setting_spec.rb +++ b/ee/spec/models/ee/project_setting_spec.rb @@ -15,7 +15,7 @@ end describe 'validations' do - context 'when enable only_mirror_protected_branches and mirror_branch_regex' do + context 'when enabling only_mirror_protected_branches and mirror_branch_regex' do it 'is invalid' do project = build(:project, only_mirror_protected_branches: true ) setting = build(:project_setting, project: project, mirror_branch_regex: 'text') diff --git a/ee/spec/models/remote_mirror_spec.rb b/ee/spec/models/remote_mirror_spec.rb index 22945152a62aa3763e3e97c76eecf7301b0fb0c9..8ddfae22286a36d2efdb4c7369e2d03757c7a814 100644 --- a/ee/spec/models/remote_mirror_spec.rb +++ b/ee/spec/models/remote_mirror_spec.rb @@ -6,7 +6,7 @@ let(:project) { create(:project, :repository, :remote_mirror) } describe 'validations' do - context 'when enable only_protected_branches and mirror_branch_regex' do + context 'when enabling only_protected_branches and mirror_branch_regex' do it 'is invalid' do remote_mirror = build(:remote_mirror, only_protected_branches: true, mirror_branch_regex: 'text') diff --git a/ee/spec/requests/api/projects_spec.rb b/ee/spec/requests/api/projects_spec.rb index 9b0d5a65c3e2c8988f5ba66298f0750dfeded947..4f8db9786af399d02ccbd1d61ad3571514ad4b19 100644 --- a/ee/spec/requests/api/projects_spec.rb +++ b/ee/spec/requests/api/projects_spec.rb @@ -1313,6 +1313,169 @@ expect(response).to have_gitlab_http_status(:forbidden) end + + context 'with mirror_branch_regex and only_mirror_protected_branches' do + let(:project_params) do + { + mirror: true, + import_url: import_url, + only_mirror_protected_branches: false, + mirror_branch_regex: 'text' + } + end + + it 'fails' do + subject + + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'with only_mirror_protected_branches' do + context 'when enabling only_mirror_protected_branches' do + let(:project_params) do + { + mirror: true, + import_url: import_url, + only_mirror_protected_branches: true + } + end + + before do + project.update!(mirror_branch_regex: 'text') + end + + it 'removes mirror_branch_regex' do + expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once + + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(project.reload).to have_attributes( + only_mirror_protected_branches: true, + mirror_branch_regex: nil + ) + end + end + + context 'when disabling only_mirror_protected_branches' do + let(:project_params) do + { + mirror: true, + import_url: import_url, + only_mirror_protected_branches: false + } + end + + before do + project.update!(mirror_branch_regex: 'text') + end + + it 'keeps mirror_branch_regex' do + expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once + + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(project.reload).to have_attributes( + only_mirror_protected_branches: false, + mirror_branch_regex: 'text' + ) + end + end + end + + context 'when removing mirror_branch_regex' do + let(:project_params) do + { mirror: true, + import_url: import_url, + mirror_branch_regex: nil } + end + + context 'with mirror_branch_regex present' do + before do + project.update!(mirror_branch_regex: 'text') + end + + it 'removes mirror_branch_regex' do + expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once + + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(project.reload.mirror_branch_regex).to be_nil + end + end + + context 'with mirror_branch_regex nil and only_mirror_protected_branches is truthy' do + before do + project.update!(mirror_branch_regex: nil, only_mirror_protected_branches: true) + end + + it 'does not change only_mirror_protected_branches value' do + expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once + + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(project.reload.mirror_branch_regex).to be_nil + expect(project.reload.only_mirror_protected_branches).to be_truthy + end + end + + context 'with mirror_branch_regex nil and only_mirror_protected_branches is false' do + before do + project.update!(mirror_branch_regex: nil, only_mirror_protected_branches: false) + end + + it 'does not change only_mirror_protected_branches value' do + expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once + + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(project.reload.mirror_branch_regex).to be_nil + expect(project.reload.only_mirror_protected_branches).to be_falsey + end + end + end + + context 'with mirror_branch_regex' do + let(:project_params) do + { mirror: true, + import_url: import_url, + mirror_branch_regex: 'text' } + end + + before do + project.update!(only_mirror_protected_branches: true) + end + + it 'succeeds' do + expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once + + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(project.reload).to have_attributes( + only_mirror_protected_branches: false, + mirror_branch_regex: 'text' + ) + end + + it 'does nothing when feature flag is disabled' do + stub_feature_flags(mirror_only_branches_match_regex: false) + expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once + + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(project.reload).to have_attributes( + only_mirror_protected_branches: true, + mirror_branch_regex: nil + ) + end + end end describe 'updating approvals_before_merge attribute' do diff --git a/ee/spec/requests/api/remote_mirrors_spec.rb b/ee/spec/requests/api/remote_mirrors_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..486963b650080b36c2b68fabc34896c22d0ad669 --- /dev/null +++ b/ee/spec/requests/api/remote_mirrors_spec.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::RemoteMirrors, feature_category: :source_code_management do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository, :remote_mirror) } + let_it_be(:project_setting) { create(:project_setting, project: project) } + + before do + project.add_maintainer(user) + end + + describe 'POST /projects/:id/remote_mirrors' do + let(:route) { "/projects/#{project.id}/remote_mirrors" } + + subject { post api(route, user), params: params } + + context 'when creating a remote mirror' do + context 'with only_protected_branches and mirror_branch_regex' do + let(:params) { { url: 'https://foo:bar@test.com', only_protected_branches: true, mirror_branch_regex: 'test' } } + + it 'returns 400 error' do + subject + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'with mirror_branch_regex' do + let(:params) { { url: 'https://foo:bar@test.com', mirror_branch_regex: 'test' } } + + it 'succeeds' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(response).to match_response_schema('remote_mirror') + expect(json_response['mirror_branch_regex']).to eq('test') + end + end + + context 'when feature flag is disabled' do + let(:params) { { url: 'https://foo:bar@test.com', mirror_branch_regex: 'test' } } + + before do + stub_feature_flags(mirror_only_branches_match_regex: false) + end + + it 'succeeds' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(response).to match_response_schema('remote_mirror') + mirror = RemoteMirror.find(json_response['id']) + expect(mirror.mirror_branch_regex).to be_nil + end + end + end + end + + describe 'PUT /projects/:id/remote_mirrors/:mirror_id' do + let(:route) { "/projects/#{project.id}/remote_mirrors/#{mirror.id}" } + let(:mirror) { project.remote_mirrors.first } + + subject { put api(route, user), params: params } + + context 'when enabling only_protected_branches' do + let(:params) { { only_protected_branches: true } } + + before do + mirror.update!(mirror_branch_regex: 'test') + end + + it 'removes mirror_branch_regex' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(json_response['only_protected_branches']).to eq(true) + expect(json_response['mirror_branch_regex']).to be_nil + end + end + + context 'when disabling only_protected_branches' do + let(:params) { { only_protected_branches: false } } + + context 'with only_protected_branches enabled' do + before do + mirror.update!(only_protected_branches: true, mirror_branch_regex: nil) + end + + it 'disables protected branches mirroring' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(json_response['only_protected_branches']).to be_falsey + expect(json_response['mirror_branch_regex']).to be_nil + end + end + + context 'with only_protected_branches disabled' do + before do + mirror.update!(only_protected_branches: false, mirror_branch_regex: 'text') + end + + it 'does not remove mirror_branch_regex' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(json_response['only_protected_branches']).to be_falsey + expect(json_response['mirror_branch_regex']).to eq 'text' + end + end + end + + context 'when setting mirror_branch_regex' do + let(:params) { { mirror_branch_regex: 'test' } } + + before do + mirror.update!(only_protected_branches: true, mirror_branch_regex: nil) + end + + it 'disables protected branches mirroring' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(json_response['only_protected_branches']).to be_falsey + expect(json_response['mirror_branch_regex']).to eq('test') + end + end + + context 'when removing mirror_branch_regex' do + let(:params) { { mirror_branch_regex: nil } } + + before do + mirror.update!(only_protected_branches: false, mirror_branch_regex: 'text') + end + + it 'succeeds' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(json_response['only_protected_branches']).to be_falsey + expect(json_response['mirror_branch_regex']).to be_nil + end + end + + context 'when feature flag is disabled' do + let(:params) { { mirror_branch_regex: 'text1' } } + + before do + mirror.update!(only_protected_branches: false, mirror_branch_regex: 'text2') + stub_feature_flags(mirror_only_branches_match_regex: false) + end + + it 'removes mirror_branch_regex' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(response).to match_response_schema('remote_mirror') + mirror = RemoteMirror.find(json_response['id']) + expect(mirror.mirror_branch_regex).to be_nil + end + end + end +end diff --git a/ee/spec/serializers/project_mirror_entity_spec.rb b/ee/spec/serializers/project_mirror_entity_spec.rb index 9b948ee867185320c239857aa5f280e785309650..279bbcbad36099483a96e26a244232949d09c79d 100644 --- a/ee/spec/serializers/project_mirror_entity_spec.rb +++ b/ee/spec/serializers/project_mirror_entity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ProjectMirrorEntity do +RSpec.describe ProjectMirrorEntity, feature_category: :source_code_management do subject(:entity) { described_class.new(project).as_json.deep_symbolize_keys } describe 'pull mirror' do @@ -33,7 +33,8 @@ ssh_known_hosts_verified_by_id: nil, ssh_public_key: nil }, - remote_mirrors_attributes: [] + remote_mirrors_attributes: [], + mirror_branch_regex: project.mirror_branch_regex ) end @@ -67,9 +68,20 @@ ssh_known_hosts_verified_by_id: import_data.ssh_known_hosts_verified_by_id, ssh_public_key: import_data.ssh_public_key }, - remote_mirrors_attributes: [] + remote_mirrors_attributes: [], + mirror_branch_regex: project.mirror_branch_regex ) end end + + context 'when mirror_only_branches_match_regex is disabled' do + before do + stub_feature_flags(mirror_only_branches_match_regex: false) + end + + it 'exclude mirror_branch_regex' do + expect(subject).not_to include(:mirror_branch_regex) + end + end end end diff --git a/ee/spec/serializers/remote_mirror_entity_spec.rb b/ee/spec/serializers/remote_mirror_entity_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..ff7c8b4ac81adad3943646b2d4f3f8bee5935439 --- /dev/null +++ b/ee/spec/serializers/remote_mirror_entity_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RemoteMirrorEntity, feature_category: :source_code_management do + let(:remote_mirror) { build(:remote_mirror) } + let(:entity) { described_class.new(remote_mirror) } + + subject { entity.as_json } + + it 'exposes mirror_branch_regex' do + is_expected.to include( + :only_protected_branches, :mirror_branch_regex + ) + end + + context 'when mirror_only_branches_match_regex disabled' do + before do + stub_feature_flags(mirror_only_branches_match_regex: false) + end + + it 'does not expose mirror_branch_regex' do + is_expected.not_to include(:mirror_branch_regex) + end + end +end diff --git a/lib/api/entities/remote_mirror.rb b/lib/api/entities/remote_mirror.rb index 9fb5b2697bcd338a0aa5863524d391815e1bb62f..f64ec71bffbfd5faa7a38cb97087060dd1a4cc09 100644 --- a/lib/api/entities/remote_mirror.rb +++ b/lib/api/entities/remote_mirror.rb @@ -16,3 +16,5 @@ class RemoteMirror < Grape::Entity end end end + +API::Entities::RemoteMirror.prepend_mod diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index 9d370176e62ec00c78bd03f73b5b5907dceeaa7b..c5636fa06de1699cfe7cfa2df9dc72a124ff5293 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -205,6 +205,9 @@ def self.update_params_at_least_one_of def filter_attributes_using_license!(attrs) end + def filter_attributes_under_feature_flag!(attrs, project) + end + def validate_git_import_url!(import_url) return if import_url.blank? diff --git a/lib/api/helpers/remote_mirrors_helpers.rb b/lib/api/helpers/remote_mirrors_helpers.rb new file mode 100644 index 0000000000000000000000000000000000000000..efd81a5ac5aba7b893ac2942cf3de1ef30e61f64 --- /dev/null +++ b/lib/api/helpers/remote_mirrors_helpers.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module API + module Helpers + module RemoteMirrorsHelpers + extend ActiveSupport::Concern + extend Grape::API::Helpers + + params :mirror_branches_setting_ce do + optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored' + end + + params :mirror_branches_setting_ee do + end + + params :mirror_branches_setting do + use :mirror_branches_setting_ce + use :mirror_branches_setting_ee + end + + def verify_mirror_branches_setting(attrs, project); end + end + end +end + +API::Helpers::RemoteMirrorsHelpers.prepend_mod diff --git a/lib/api/projects.rb b/lib/api/projects.rb index de39419b70b5e31d4bf52c9d8081315d5cccbfb2..5077f02fcc1f479ef7c8965a5d5ee8d714f4f07a 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -490,6 +490,7 @@ def add_import_params(params) attrs = translate_params_for_compatibility(attrs) attrs = add_import_params(attrs) filter_attributes_using_license!(attrs) + filter_attributes_under_feature_flag!(attrs, user_project) verify_update_project_attrs!(user_project, attrs) user_project.remove_avatar! if attrs.key?(:avatar) && attrs[:avatar].nil? diff --git a/lib/api/remote_mirrors.rb b/lib/api/remote_mirrors.rb index f7ea5a6ad2b533e63cefdc614e3603783dbfa7bf..c3c7d9370e001af3003cc21012dae4530907506c 100644 --- a/lib/api/remote_mirrors.rb +++ b/lib/api/remote_mirrors.rb @@ -3,6 +3,7 @@ module API class RemoteMirrors < ::API::Base include PaginationParams + helpers Helpers::RemoteMirrorsHelpers feature_category :source_code_management @@ -60,14 +61,13 @@ class RemoteMirrors < ::API::Base params do requires :url, type: String, desc: 'The URL for a remote mirror', documentation: { example: 'https://*****:*****@example.com/gitlab/example.git' } optional :enabled, type: Boolean, desc: 'Determines if the mirror is enabled', documentation: { example: false } - optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored', - documentation: { example: false } optional :keep_divergent_refs, type: Boolean, desc: 'Determines if divergent refs are kept on the target', documentation: { example: false } + use :mirror_branches_setting end post ':id/remote_mirrors' do create_params = declared_params(include_missing: false) - + verify_mirror_branches_setting(create_params, user_project) new_mirror = user_project.remote_mirrors.create(create_params) if new_mirror.persisted? @@ -89,10 +89,9 @@ class RemoteMirrors < ::API::Base params do requires :mirror_id, type: String, desc: 'The ID of a remote mirror' optional :enabled, type: Boolean, desc: 'Determines if the mirror is enabled', documentation: { example: true } - optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored', - documentation: { example: false } optional :keep_divergent_refs, type: Boolean, desc: 'Determines if divergent refs are kept on the target', documentation: { example: false } + use :mirror_branches_setting end put ':id/remote_mirrors/:mirror_id' do mirror = user_project.remote_mirrors.find(params[:mirror_id]) @@ -100,6 +99,7 @@ class RemoteMirrors < ::API::Base mirror_params = declared_params(include_missing: false) mirror_params[:id] = mirror_params.delete(:mirror_id) + verify_mirror_branches_setting(mirror_params, user_project) update_params = { remote_mirrors_attributes: mirror_params } result = ::Projects::UpdateService diff --git a/spec/fixtures/api/schemas/remote_mirror.json b/spec/fixtures/api/schemas/remote_mirror.json index 87bde189db52f59846f665a97a308eecc1fbc15c..a4e886a8c7cc9bf5b969a5e096d8dd6e860ade51 100644 --- a/spec/fixtures/api/schemas/remote_mirror.json +++ b/spec/fixtures/api/schemas/remote_mirror.json @@ -12,16 +12,57 @@ "only_protected_branches" ], "properties": { - "id": { "type": "integer" }, - "enabled": { "type": "boolean" }, - "url": { "type": "string" }, - "update_status": { "type": "string" }, - "last_update_at": { "type": ["string", "null"] }, - "last_update_started_at": { "type": ["string", "null"] }, - "last_successful_update_at": { "type": ["string", "null"] }, - "last_error": { "type": ["string", "null"] }, - "only_protected_branches": { "type": "boolean" }, - "keep_divergent_refs": { "type": ["boolean", "null"] } + "id": { + "type": "integer" + }, + "enabled": { + "type": "boolean" + }, + "url": { + "type": "string" + }, + "update_status": { + "type": "string" + }, + "last_update_at": { + "type": [ + "string", + "null" + ] + }, + "last_update_started_at": { + "type": [ + "string", + "null" + ] + }, + "last_successful_update_at": { + "type": [ + "string", + "null" + ] + }, + "last_error": { + "type": [ + "string", + "null" + ] + }, + "only_protected_branches": { + "type": "boolean" + }, + "mirror_branch_regex": { + "type": [ + "string", + "null" + ] + }, + "keep_divergent_refs": { + "type": [ + "boolean", + "null" + ] + } }, "additionalProperties": false } diff --git a/spec/serializers/project_mirror_entity_spec.rb b/spec/serializers/project_mirror_entity_spec.rb index 7ed530ed9e89c6cdbe1023f21967af1b61305517..88531b3c3d3e8460499aeba387d69ab8821eb431 100644 --- a/spec/serializers/project_mirror_entity_spec.rb +++ b/spec/serializers/project_mirror_entity_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' -RSpec.describe ProjectMirrorEntity do - let(:project) { create(:project, :repository, :remote_mirror) } +RSpec.describe ProjectMirrorEntity, feature_category: :source_code_management do + let(:project) { build(:project, :repository, :remote_mirror) } let(:entity) { described_class.new(project) } subject { entity.as_json }