diff --git a/app/services/namespaces/projects/create_service.rb b/app/services/namespaces/projects/create_service.rb index 015e72508..d82892a94 100644 --- a/app/services/namespaces/projects/create_service.rb +++ b/app/services/namespaces/projects/create_service.rb @@ -51,9 +51,30 @@ def execute } ) + assign_runtime_if_unambiguous(project) + ServiceResponse.success(message: 'Created new project', payload: project) end end + + private + + def assign_runtime_if_unambiguous(project) + runtime = runtime_to_assign + return if runtime.nil? + + Namespaces::Projects::AssignRuntimesService.new(current_authentication, project, [runtime]).execute + end + + def runtime_to_assign + global_runtimes = Runtime.where(namespace: nil) + namespace_runtimes = namespace.runtimes + + return nil if global_runtimes.exists? && namespace_runtimes.exists? + return global_runtimes.first if global_runtimes.one? + + namespace_runtimes.first if namespace_runtimes.one? + end end end end diff --git a/app/services/runtimes/create_service.rb b/app/services/runtimes/create_service.rb index 74a75bfc5..f98953cde 100644 --- a/app/services/runtimes/create_service.rb +++ b/app/services/runtimes/create_service.rb @@ -33,22 +33,8 @@ def execute target: namespace || AuditEvent::GLOBAL_TARGET ) - assign_as_primary_if_only_runtime(runtime) - ServiceResponse.success(payload: runtime) end end - - private - - def assign_as_primary_if_only_runtime(runtime) - return if namespace.nil? - return unless namespace.runtimes.one? - - namespace.projects.where(primary_runtime_id: nil).find_each do |project| - project.runtimes << runtime unless project.runtimes.include?(runtime) - project.update!(primary_runtime: runtime) - end - end end end diff --git a/spec/services/namespaces/projects/create_service_spec.rb b/spec/services/namespaces/projects/create_service_spec.rb index d9ed155a3..d40e85bb0 100644 --- a/spec/services/namespaces/projects/create_service_spec.rb +++ b/spec/services/namespaces/projects/create_service_spec.rb @@ -100,5 +100,96 @@ end end end + + context 'when only one global runtime exists' do + let!(:global_runtime) { create(:runtime, namespace: nil) } + + context 'when user has permission to assign runtimes' do + before do + stub_allowed_ability( + NamespaceProjectPolicy, :assign_project_runtimes, + user: current_user, subject: an_instance_of(NamespaceProject) + ) + end + + it 'assigns the global runtime as the primary runtime' do + service_response + expect(service_response.payload.primary_runtime).to eq(global_runtime) + end + + it 'assigns the global runtime to the project' do + service_response + expect(service_response.payload.runtimes).to contain_exactly(global_runtime) + end + end + + context 'when user does not have permission to assign runtimes' do + it 'does not assign a primary runtime' do + service_response + expect(service_response.payload.primary_runtime).to be_nil + end + end + end + + context 'when multiple global runtimes exist' do + before do + create(:runtime, namespace: nil) + create(:runtime, namespace: nil) + end + + it 'does not assign a primary runtime' do + service_response + expect(service_response.payload.primary_runtime).to be_nil + end + end + + context 'when no global runtime exists and namespace has exactly one runtime' do + let!(:namespace_runtime) { create(:runtime, namespace: namespace) } + + before do + stub_allowed_ability( + NamespaceProjectPolicy, :assign_project_runtimes, + user: current_user, subject: an_instance_of(NamespaceProject) + ) + end + + it 'assigns the namespace runtime as the primary runtime' do + service_response + expect(service_response.payload.primary_runtime).to eq(namespace_runtime) + end + + it 'assigns the namespace runtime to the project' do + service_response + expect(service_response.payload.runtimes).to contain_exactly(namespace_runtime) + end + end + + context 'when no global runtime exists and namespace has multiple runtimes' do + before do + create(:runtime, namespace: namespace) + create(:runtime, namespace: namespace) + end + + it 'does not assign a primary runtime' do + service_response + expect(service_response.payload.primary_runtime).to be_nil + end + end + + context 'when a global runtime exists and the namespace also has a runtime' do + before do + create(:runtime, namespace: nil) + create(:runtime, namespace: namespace) + stub_allowed_ability( + NamespaceProjectPolicy, :assign_project_runtimes, + user: current_user, subject: an_instance_of(NamespaceProject) + ) + end + + it 'does not assign a primary runtime' do + service_response + expect(service_response.payload.primary_runtime).to be_nil + end + end end end diff --git a/spec/services/runtimes/create_service_spec.rb b/spec/services/runtimes/create_service_spec.rb index 10fcdcdb5..220475c60 100644 --- a/spec/services/runtimes/create_service_spec.rb +++ b/spec/services/runtimes/create_service_spec.rb @@ -71,65 +71,6 @@ end end - context 'when namespace has a project without a primary runtime' do - let(:namespace) { create(:namespace) } - let(:current_user) { create(:user) } - let(:params) do - { name: generate(:runtime_name) } - end - let!(:project) { create(:namespace_project, namespace: namespace) } - - before do - stub_allowed_ability(NamespacePolicy, :create_runtime, user: current_user, subject: namespace) - end - - it 'assigns the created runtime as the primary runtime' do - service_response - expect(project.reload.primary_runtime).to eq(service_response.payload) - end - - it 'assigns the created runtime to the project' do - service_response - expect(project.reload.runtimes).to contain_exactly(service_response.payload) - end - end - - context 'when namespace already has a runtime' do - let(:namespace) { create(:namespace) } - let(:current_user) { create(:user) } - let(:params) do - { name: generate(:runtime_name) } - end - let!(:existing_runtime) { create(:runtime, namespace: namespace) } - let!(:project) { create(:namespace_project, namespace: namespace, primary_runtime: existing_runtime) } - - before do - stub_allowed_ability(NamespacePolicy, :create_runtime, user: current_user, subject: namespace) - end - - it 'does not change the primary runtime of existing projects' do - expect { service_response }.not_to(change { project.reload.primary_runtime }) - end - end - - context 'when project in namespace already has a primary runtime' do - let(:namespace) { create(:namespace) } - let(:current_user) { create(:user) } - let(:params) do - { name: generate(:runtime_name) } - end - let!(:other_runtime) { create(:runtime) } - let!(:project) { create(:namespace_project, namespace: namespace, primary_runtime: other_runtime) } - - before do - stub_allowed_ability(NamespacePolicy, :create_runtime, user: current_user, subject: namespace) - end - - it 'does not change the primary runtime' do - expect { service_response }.not_to(change { project.reload.primary_runtime }) - end - end - context 'when user and params are valid and user is admin' do let(:current_user) { create(:user, :admin) } let(:params) do