diff --git a/app/services/repositories/multiple_share_update_service.rb b/app/services/repositories/multiple_share_update_service.rb new file mode 100644 index 000000000..067bd5dbe --- /dev/null +++ b/app/services/repositories/multiple_share_update_service.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +module Repositories + class MultipleShareUpdateService + extend Service + include Canaid::Helpers::PermissionsHelper + + attr_reader :repository, :user, :errors + + def initialize(repository_id:, + user_id:, + team_id:, + team_ids_for_share: [], + team_ids_for_unshare: [], + team_ids_for_update: []) + @repository = Repository.find_by_id repository_id + @user = User.find_by_id user_id + @team = Team.find_by_id team_id + @team_ids_for_share = team_ids_for_share + @team_ids_for_unshare = team_ids_for_unshare + @team_ids_for_update = team_ids_for_update + @errors = { shares: [] } + end + + def call + return self unless valid? + + @team_ids_for_share.each do |share| + team_repository = TeamRepository.new(repository: @repository, + team_id: share[:id], + permission_level: share[:permission_level]) + + if team_repository.save + log_activity(:share_inventory, team_repository) + else + @errors[:shares] << I18n.t('repositories.multiple_share_service.unable_to_share', + repository: @repository.name, team: share[:id]) + end + end + + @team_ids_for_unshare.each do |unshare| + team_repository = TeamRepository.where(repository: @repository, team_id: unshare[:id]).first + + if team_repository + log_activity(:unshare_inventory, team_repository) + team_repository.destroy + else + @errors[:shares] << I18n.t('repositories.multiple_share_service.unable_to_unshare', + repository: @repository.name, team: unshare[:id]) + end + end + + @team_ids_for_update.each do |update| + team_repository = TeamRepository.where(repository: @repository, team_id: update[:id]).first + team_repository.permission_level = update[:permission_level] if team_repository + + if team_repository&.save + log_activity(:update_share_inventory, team_repository) + else + @errors[:shares] << I18n.t('repositories.multiple_share_service.unable_to_update', + repository: @repository.name, team: update[:id]) + end + end + + self + end + + def succeed? + # Shares are not errors but more like warnings. + @errors.except(:shares).none? + end + + private + + def valid? + unless @user && @repository + @errors[:invalid_arguments] = + { 'repository': @repository, + 'user': @user, + 'team': @team } + .map do |key, value| + "Can't find #{key.capitalize}" if value.nil? + end.compact + return false + end + + if can_manage_repository?(@user, @repository) + true + else + @errors[:user_without_permissions] = + ['You are not allowed to share this repository'] + false + end + end + + def log_activity(type_of, team_repository) + Activities::CreateActivityService + .call(activity_type: type_of, + owner: @user, + subject: team_repository.repository, + team: @team, + message_items: { repository: team_repository.repository.id, + team: team_repository.team.id, + permission_level: team_repository.permission_level }) + end + end +end diff --git a/spec/services/repositories/multiple_share_update_service_spec.rb b/spec/services/repositories/multiple_share_update_service_spec.rb new file mode 100644 index 000000000..23c193757 --- /dev/null +++ b/spec/services/repositories/multiple_share_update_service_spec.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Repositories::MultipleShareUpdateService do + let(:user) { create :user } + let!(:user_team) { create :user_team, :admin, user: user, team: team } + let(:team) { create :team } + let(:team2) { create :team } + let(:team3) { create :team } + let(:repository) { create :repository, team: team } + + context 'when user do not have permissions' do + it 'returns error about permissions' do + new_repo = create :repository + service_call = Repositories::MultipleShareUpdateService.call(repository_id: new_repo.id, + user_id: user.id, + team_id: team.id) + + expect(service_call.errors).to have_key(:user_without_permissions) + end + end + + context 'when repository not found' do + it 'returns error about repository' do + service_call = Repositories::MultipleShareUpdateService.call(repository_id: -1, + user_id: user.id, + team_id: team.id) + + expect(service_call.errors).to have_key(:invalid_arguments) + end + end + + context 'when share' do + let(:service_call) do + Repositories::MultipleShareUpdateService.call(repository_id: repository.id, + user_id: user.id, + team_id: team.id, + team_ids_for_share: [{ id: team2.id, permission_level: 'read' }]) + end + + it 'adds TeamRepository record' do + expect { service_call }.to change { TeamRepository.count }.by(1) + end + + it 'adds Activity record' do + expect { service_call }.to(change { Activity.all.count }.by(1)) + end + + context 'when cannot generate share' do + let(:service_call) do + Repositories::MultipleShareUpdateService.call(repository_id: repository.id, + user_id: user.id, + team_id: team.id, + team_ids_for_share: [{ id: -1, permission_level: 'read' }]) + end + + it 'returns error' do + expect(service_call.errors[:shares].count).to eq(1) + end + end + end + + context 'when unshare' do + let(:service_call) do + Repositories::MultipleShareUpdateService.call(repository_id: repository.id, + user_id: user.id, + team_id: team.id, + team_ids_for_unshare: [{ id: team2.id }]) + end + + it 'removes TeamRepository record' do + create :team_repository, :write, team: team2, repository: repository + + expect { service_call }.to change { TeamRepository.count }.by(-1) + end + + it 'adds Activity record' do + create :team_repository, :write, team: team2, repository: repository + + expect { service_call }.to(change { Activity.all.count }.by(1)) + end + + context 'when cannot delete share' do + let(:service_call) do + Repositories::MultipleShareUpdateService.call(repository_id: repository.id, + user_id: user.id, + team_id: team.id, + team_ids_for_unshare: [{ id: -1 }]) + end + + it 'returns error' do + expect(service_call.errors[:shares].count).to eq(1) + end + end + end + + context 'when updates share permissions' do + let(:service_call) do + Repositories::MultipleShareUpdateService.call(repository_id: repository.id, + user_id: user.id, + team_id: team.id, + team_ids_for_update: [{ id: team2.id, permission_level: 'read' }]) + end + + it 'updates permission for share record' do + tr = create :team_repository, :write, team: team2, repository: repository + + expect { service_call }.to(change { tr.reload.permission_level }) + end + + it 'adds Activity record' do + create :team_repository, :write, team: team2, repository: repository + + expect { service_call }.to(change { Activity.all.count }.by(1)) + end + + context 'when cannot update share' do + let(:service_call) do + Repositories::MultipleShareUpdateService.call(repository_id: repository.id, + user_id: user.id, + team_id: team.id, + team_ids_for_update: [{ id: -1, permission_level: 'read' }]) + end + + it 'returns error' do + expect(service_call.errors[:shares].count).to eq(1) + end + end + end +end