diff --git a/app/controllers/access_permissions/experiments_controller.rb b/app/controllers/access_permissions/experiments_controller.rb index 604eff459..9c57e8dfc 100644 --- a/app/controllers/access_permissions/experiments_controller.rb +++ b/app/controllers/access_permissions/experiments_controller.rb @@ -21,7 +21,7 @@ module AccessPermissions def update @experiment_member = ExperimentMember.new(current_user, @experiment, @project) - @experiment_member.update(permitted_update_params) + @experiment_member.handle_change(permitted_update_params) respond_to do |format| format.json do diff --git a/app/controllers/access_permissions/my_modules_controller.rb b/app/controllers/access_permissions/my_modules_controller.rb index 314c7f14f..43d3dfe63 100644 --- a/app/controllers/access_permissions/my_modules_controller.rb +++ b/app/controllers/access_permissions/my_modules_controller.rb @@ -22,7 +22,7 @@ module AccessPermissions def update @my_module_member = MyModuleMember.new(current_user, @my_module, @experiment, @project) - @my_module_member.update(permitted_update_params) + @my_module_member.handle_change(permitted_update_params) respond_to do |format| format.json do diff --git a/app/controllers/api/v1/experiment_user_assignments_controller.rb b/app/controllers/api/v1/experiment_user_assignments_controller.rb new file mode 100644 index 000000000..8f6f233f0 --- /dev/null +++ b/app/controllers/api/v1/experiment_user_assignments_controller.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module Api + module V1 + class ExperimentUserAssignmentsController < BaseController + before_action :load_team + before_action :load_project + before_action :load_experiment + before_action :load_user_assignment, only: :show + before_action :load_user_assignment_for_managing, only: %i(update destroy) + + def index + user_assignments = @experiment.user_assignments + .page(params.dig(:page, :number)) + .per(params.dig(:page, :size)) + + render jsonapi: user_assignments, + each_serializer: ExperimentUserAssignmentSerializer, + include: :user + end + + def show + render jsonapi: @user_assignment, + serializer: ExperimentUserAssignmentSerializer, + include: :user + end + + def create + raise PermissionError.new(Experiment, :manage) unless can_manage_experiment?(@experiment) + + user_role = UserRole.find_by_name user_assignment_params[:role] + experiment_member = ExperimentMember.new(current_user, @experiment, @project) + experiment_member.create( + user_role_id: user_role.id, + user_id: user_assignment_params[:user_id] + ) + render jsonapi: experiment_member.user_project, serializer: ExperimentUserAssignmentSerializer, status: :created + end + + def update + user_role = UserRole.find_by_name user_assignment_params[:role] + user = @user_assignment.user + experiment_member = ExperimentMember.new( + current_user, + @experiment, + @project, + user, + @user_assignment + ) + + return render body: nil, status: :no_content if @user_assignment.user_role == user_role + + experiment_member.update(user_role_id: user_role.id, user_id: user.id) + render jsonapi: experiment_member.user_project, serializer: ExperimentUserAssignmentSerializer, status: :ok + end + + def destroy + experiment_member = ExperimentMember.new( + current_user, + @experiment, + @project, + @user_assignment.user, + @user_assignment + ) + experiment_member.destroy + render body: nil + end + + private + + def load_user_assignment + @user_assignment = @experiment.user_assignments.find(params.require(:id)) + end + + def load_user_assignment_for_managing + @user_assignment = @experiment.user_assignments.find(params.require(:id)) + raise PermissionError.new(Experiment, :manage) unless can_manage_experiment?(@experiment) + end + + def user_assignment_params + raise TypeError unless params.require(:data).require(:type) == 'experiment_user_assignments' + + params.require(:data).require(:attributes).permit(:user_id, :role) + end + end + end +end diff --git a/app/controllers/api/v1/task_user_assignments_controller.rb b/app/controllers/api/v1/task_user_assignments_controller.rb new file mode 100644 index 000000000..8038b7d6f --- /dev/null +++ b/app/controllers/api/v1/task_user_assignments_controller.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +module Api + module V1 + class TaskUserAssignmentsController < BaseController + before_action :load_team + before_action :load_project + before_action :load_experiment + before_action :load_task + before_action :load_user_assignment, only: :show + before_action :load_user_assignment_for_managing, only: %i(update destroy) + + def index + user_assignments = @task.user_assignments + .page(params.dig(:page, :number)) + .per(params.dig(:page, :size)) + + render jsonapi: user_assignments, + each_serializer: TaskUserAssignmentSerializer, + include: :user + end + + def show + render jsonapi: @user_assignment, + serializer: TaskUserAssignmentSerializer, + include: :user + end + + def create + raise PermissionError.new(MyModule, :read) unless can_manage_module?(@task) + + user_role = UserRole.find_by_name user_assignment_params[:role] + my_module_member = MyModuleMember.new(current_user, @task, @experiment, @project) + my_module_member.create( + user_role_id: user_role.id, + user_id: user_assignment_params[:user_id] + ) + render jsonapi: my_module_member.user_project, serializer: TaskUserAssignmentSerializer, status: :created + end + + def update + user_role = UserRole.find_by_name user_assignment_params[:role] + user = @user_assignment.user + my_module_member = MyModuleMember.new( + current_user, + @task, + @experiment, + @project, + user, + @user_assignment + ) + + return render body: nil, status: :no_content if @user_assignment.user_role == user_role + + my_module_member.update(user_role_id: user_role.id, user_id: user.id) + render jsonapi: my_module_member.user_project, serializer: TaskUserAssignmentSerializer, status: :ok + end + + def destroy + my_module_member = MyModuleMember.new( + current_user, + @task, + @experiment, + @project, + @user_assignment.user, + @user_assignment + ) + my_module_member.destroy + render body: nil + end + + private + + def load_user_assignment + @user_assignment = @task.user_assignments.find(params.require(:id)) + end + + def load_user_assignment_for_managing + @user_assignment = @task.user_assignments.find(params.require(:id)) + raise PermissionError.new(MyModule, :manage) unless can_manage_module?(@task) + end + + def user_assignment_params + raise TypeError unless params.require(:data).require(:type) == 'task_user_assignments' + + params.require(:data).require(:attributes).permit(:user_id, :role) + end + end + end +end diff --git a/app/controllers/api/v1/user_projects_controller.rb b/app/controllers/api/v1/user_projects_controller.rb index 1e10d47ca..c7c2fc7c6 100644 --- a/app/controllers/api/v1/user_projects_controller.rb +++ b/app/controllers/api/v1/user_projects_controller.rb @@ -27,22 +27,34 @@ module Api def create raise PermissionError.new(Project, :manage) unless can_manage_project?(@project) - user_project = @project.user_projects.create!(user_project_params.merge!(assigned_by: current_user)) + # internally we reuse the same logic as for user project assignment + user_role = UserRole.find_by_name user_project_params[:role] + user = @team.users.find(user_project_params[:user_id]) - render jsonapi: user_project, serializer: UserProjectSerializer, status: :created + project_member = ProjectMember.new(user, @project, current_user) + project_member.assign = true + project_member.user_role_id = user_role.id + project_member.create + + render jsonapi: project_member.user_project, serializer: UserProjectSerializer, status: :created end def update - @user_project.role = user_project_params[:role] - return render body: nil, status: :no_content unless @user_project.changed? + user_role = UserRole.find_by_name user_project_params[:role] + project_member = ProjectMember.new(@user_project.user, @project, current_user) - @user_project.assigned_by = current_user - @user_project.save! + if project_member.user_assignment&.user_role == user_role + return render body: nil, status: :no_content + end + + project_member.user_role_id = user_role.id + project_member.update render jsonapi: @user_project, serializer: UserProjectSerializer, status: :ok end def destroy - @user_project.destroy! + project_member = ProjectMember.new(@user_project.user, @project, current_user) + project_member.destroy render body: nil end diff --git a/app/models/experiment_member.rb b/app/models/experiment_member.rb index fd7d179c0..b69b4ceac 100644 --- a/app/models/experiment_member.rb +++ b/app/models/experiment_member.rb @@ -8,10 +8,11 @@ class ExperimentMember :user, :project, :user_role, :user_assignment - def initialize(current_user, experiment, project, user = nil) + def initialize(current_user, experiment, project, user = nil, user_assignment = nil) @experiment = experiment @current_user = current_user @project = project + @user_assignment = user_assignment if user @user = user @@ -19,15 +20,9 @@ class ExperimentMember end end - def update(params) - self.user_role_id = params[:user_role_id] - self.user_id = params[:user_id] - + def handle_change(params) + prepare_data(params) ActiveRecord::Base.transaction do - @user = @project.users.find(user_id) - @user_role = UserRole.find_by(id: user_role_id) - @user_assignment = UserAssignment.find_by(assignable: experiment, user: user) - if destroy_role? user_assignment.destroy elsif user_assignment.present? @@ -44,8 +39,47 @@ class ExperimentMember end end + def update(params) + prepare_data(params) + + ActiveRecord::Base.transaction do + user_assignment.update!(user_role: user_role) + log_change_activity + end + end + + def create(params) + prepare_data(params) + + ActiveRecord::Base.transaction do + @user_assignment = UserAssignment.create!( + assignable: experiment, + user: user, + user_role: user_role, + assigned_by: current_user + ) + log_change_activity + end + end + + def destroy + ActiveRecord::Base.transaction do + user_assignment.destroy + log_change_activity + end + end + private + def prepare_data(params) + self.user_role_id = params[:user_role_id] + self.user_id = params[:user_id] + + @user = @project.users.find(user_id) + @user_role = UserRole.find_by(id: user_role_id) + @user_assignment ||= UserAssignment.find_by(assignable: experiment, user: user) + end + def log_change_activity Activities::CreateActivityService.call( activity_type: :change_user_role_on_experiment, diff --git a/app/models/my_module_member.rb b/app/models/my_module_member.rb index ed17511d4..e85c73e1e 100644 --- a/app/models/my_module_member.rb +++ b/app/models/my_module_member.rb @@ -8,11 +8,12 @@ class MyModuleMember :experiment, :user, :project, :user_role, :user_assignment - def initialize(current_user, my_module, experiment, project, user = nil) + def initialize(current_user, my_module, experiment, project, user = nil , user_assignment = nil) @experiment = experiment @current_user = current_user @project = project @my_module = my_module + @user_assignment = user_assignment if user @user = user @@ -20,15 +21,9 @@ class MyModuleMember end end - def update(params) - self.user_role_id = params[:user_role_id] - self.user_id = params[:user_id] - + def handle_change(params) + prepare_data(params) ActiveRecord::Base.transaction do - @user = @project.users.find(user_id) - @user_role = UserRole.find_by(id: user_role_id) - @user_assignment = UserAssignment.find_by(assignable: my_module, user: user) - if destroy_role? user_assignment.destroy elsif user_assignment.present? @@ -45,8 +40,47 @@ class MyModuleMember end end + def update(params) + prepare_data(params) + + ActiveRecord::Base.transaction do + user_assignment.update!(user_role: user_role) + log_change_activity + end + end + + def create(params) + prepare_data(params) + + ActiveRecord::Base.transaction do + @user_assignment = UserAssignment.create!( + assignable: my_module, + user: user, + user_role: user_role, + assigned_by: current_user + ) + log_change_activity + end + end + + def destroy + ActiveRecord::Base.transaction do + user_assignment.destroy + log_change_activity + end + end + private + def prepare_data(params) + self.user_role_id = params[:user_role_id] + self.user_id = params[:user_id] + + @user = @project.users.find(user_id) + @user_role = UserRole.find_by(id: user_role_id) + @user_assignment ||= UserAssignment.find_by(assignable: my_module, user: user) + end + def log_change_activity Activities::CreateActivityService.call( activity_type: :change_user_role_on_my_module, diff --git a/app/models/project_member.rb b/app/models/project_member.rb index b748d1255..5698a8c70 100644 --- a/app/models/project_member.rb +++ b/app/models/project_member.rb @@ -4,7 +4,7 @@ class ProjectMember include ActiveModel::Model attr_accessor :user, :project, :assign, :user_role_id, :user_id - attr_reader :current_user, :user_assignment, :user_role + attr_reader :current_user, :user_assignment, :user_role, :user_project delegate :user_role, to: :user_assignment, allow_nil: true @@ -25,7 +25,7 @@ class ProjectMember return unless assign ActiveRecord::Base.transaction do - UserProject.create!(project: @project, user: @user) + @user_project = UserProject.create!(project: @project, user: @user) @user_assignment = UserAssignment.create!( assignable: @project, user: @user, diff --git a/app/serializers/api/v1/experiment_user_assignment_serializer.rb b/app/serializers/api/v1/experiment_user_assignment_serializer.rb new file mode 100644 index 000000000..4c2d88625 --- /dev/null +++ b/app/serializers/api/v1/experiment_user_assignment_serializer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Api + module V1 + class ExperimentUserAssignmentSerializer < ActiveModel::Serializer + type :experiment_user_assignments + attributes :id, :role + + belongs_to :user, serializer: UserSerializer + belongs_to :experiment, serializer: ExperimentSerializer + end + end +end diff --git a/app/serializers/api/v1/task_user_assigment_serializer.rb b/app/serializers/api/v1/task_user_assigment_serializer.rb new file mode 100644 index 000000000..37f9ae66c --- /dev/null +++ b/app/serializers/api/v1/task_user_assigment_serializer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Api + module V1 + class TaskUserAssignmentSerializer < ActiveModel::Serializer + type :task_user_assignments + attributes :id, :role + + belongs_to :user, serializer: UserSerializer + belongs_to :task, serializer: TaskSerializer + end + end +end diff --git a/config/routes.rb b/config/routes.rb index c63ece63f..5a70116d0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -697,9 +697,11 @@ Rails.application.routes.draw do get 'activities', to: 'projects#activities' resources :reports, only: %i(index show), path: 'reports', as: :reports resources :experiments, only: %i(index show create update) do + resources :experiment_user_assignments, only: %i(index show create update) resources :task_groups, only: %i(index show) resources :connections, only: %i(index show) resources :tasks, only: %i(index show create update) do + resources :tasks_user_assignments, only: %i(index show create update) resources :task_inventory_items, only: %i(index show), path: 'items', as: :items diff --git a/spec/models/experiment_member_spec.rb b/spec/models/experiment_member_spec.rb index 5a580759d..976e2aac4 100644 --- a/spec/models/experiment_member_spec.rb +++ b/spec/models/experiment_member_spec.rb @@ -19,7 +19,7 @@ describe ExperimentMember, type: :model do let(:subject) { described_class.new(user, experiment, project) } - describe 'update' do + describe '#handle_change' do let!(:valid_params) { { user_id: user.id, @@ -29,7 +29,7 @@ describe ExperimentMember, type: :model do it 'creates a new user assigment when no assigment present' do expect { - subject.update(valid_params) + subject.handle_change(valid_params) }.to change(UserAssignment, :count).by(1) end @@ -38,21 +38,101 @@ describe ExperimentMember, type: :model do create :user_assignment, assignable: project, user: user, user_role: normal_user_role, assigned_by: user expect { - subject.update(valid_params) + subject.handle_change(valid_params) }.to change(UserAssignment, :count).by(-1) end it 'updates the assigment user role' do assigment = create :user_assignment, assignable: experiment, user: user, user_role: owner_role, assigned_by: user - subject.update(valid_params) + subject.handle_change(valid_params) expect(assigment.reload.user_role).to eq normal_user_role end it 'logs a change_user_role_on_experiment activity' do + expect { + subject.handle_change(valid_params) + }.to change(Activity, :count).by(1) + expect(Activity.last.type_of).to eq 'change_user_role_on_experiment' + end + end + + describe '#update' do + let!(:experiment_user_assignment) { + create :user_assignment, + assignable: experiment, + user: user, + user_role: owner_role, + assigned_by: user + } + + let!(:valid_params) { + { + user_id: user.id, + user_role_id: normal_user_role.id + } + } + + let!(:subject) { described_class.new(user, experiment, project, user, experiment_user_assignment) } + + it 'updates the assigment user role' do + subject.update(valid_params) + expect(experiment_user_assignment.reload.user_role).to eq normal_user_role + end + + it 'logs a change_user_role_on_my_module activity' do expect { subject.update(valid_params) }.to change(Activity, :count).by(1) expect(Activity.last.type_of).to eq 'change_user_role_on_experiment' end end + + describe '#destroy' do + let!(:experiment_user_assignment) { + create :user_assignment, + assignable: experiment, + user: user, + user_role: owner_role, + assigned_by: user + } + + let!(:subject) { described_class.new(user, experiment, project, user, experiment_user_assignment) } + + it 'destroys the assigment user role' do + expect { + subject.destroy + }.to change(UserAssignment, :count).by(-1) + end + + it 'logs a change_user_role_on_my_module activity' do + expect { + subject.destroy + }.to change(Activity, :count).by(1) + expect(Activity.last.type_of).to eq 'change_user_role_on_experiment' + end + end + + describe '#create' do + let!(:valid_params) { + { + user_id: user.id, + user_role_id: normal_user_role.id + } + } + + let(:subject) { described_class.new(user, experiment, project) } + + it 'creates a new user assigment when no assigment present' do + expect { + subject.create(valid_params) + }.to change(UserAssignment, :count).by(1) + end + + it 'logs a change_user_role_on_my_module activity' do + expect { + subject.create(valid_params) + }.to change(Activity, :count).by(1) + expect(Activity.last.type_of).to eq 'change_user_role_on_experiment' + end + end end diff --git a/spec/models/my_module_member_spec.rb b/spec/models/my_module_member_spec.rb index fd6e25b04..cc9ce96c5 100644 --- a/spec/models/my_module_member_spec.rb +++ b/spec/models/my_module_member_spec.rb @@ -7,14 +7,18 @@ describe MyModuleMember, type: :model do let!(:user) { create :user } let!(:project) { create :project } let!(:user_project) { create :user_project, user: user, project: project } - let!(:user_assignment) { create :user_assignment, assignable: project, user: user, user_role: owner_role, assigned_by: user } + let!(:user_assignment) { + create :user_assignment, + assignable: project, + user: user, + user_role: owner_role, + assigned_by: user + } let!(:experiment) { create :experiment, project: project } let!(:my_module) { create :my_module, experiment: experiment } let(:normal_user_role) { create :normal_user_role } - let(:subject) { described_class.new(user, my_module, experiment, project) } - - describe 'update' do + describe '#handle_change' do let!(:valid_params) { { user_id: user.id, @@ -22,9 +26,11 @@ describe MyModuleMember, type: :model do } } + let(:subject) { described_class.new(user, my_module, experiment, project) } + it 'creates a new user assigment when no assigment present' do expect { - subject.update(valid_params) + subject.handle_change(valid_params) }.to change(UserAssignment, :count).by(1) end @@ -33,7 +39,7 @@ describe MyModuleMember, type: :model do create :user_assignment, assignable: experiment, user: user, user_role: normal_user_role, assigned_by: user expect { - subject.update(valid_params) + subject.handle_change(valid_params) }.to change(UserAssignment, :count).by(-1) end @@ -42,16 +48,47 @@ describe MyModuleMember, type: :model do create :user_assignment, assignable: project, user: user, user_role: normal_user_role, assigned_by: user expect { - subject.update(valid_params) + subject.handle_change(valid_params) }.to change(UserAssignment, :count).by(-1) end it 'updates the assigment user role' do assigment = create :user_assignment, assignable: my_module, user: user, user_role: owner_role, assigned_by: user - subject.update(valid_params) + subject.handle_change(valid_params) expect(assigment.reload.user_role).to eq normal_user_role end + it 'logs a change_user_role_on_my_module activity' do + expect { + subject.handle_change(valid_params) + }.to change(Activity, :count).by(1) + expect(Activity.last.type_of).to eq 'change_user_role_on_my_module' + end + end + + describe '#update' do + let!(:my_module_user_assignment) { + create :user_assignment, + assignable: my_module, + user: user, + user_role: owner_role, + assigned_by: user + } + + let!(:valid_params) { + { + user_id: user.id, + user_role_id: normal_user_role.id + } + } + + let!(:subject) { described_class.new(user, my_module, experiment, project, user, my_module_user_assignment) } + + it 'updates the assigment user role' do + subject.update(valid_params) + expect(my_module_user_assignment.reload.user_role).to eq normal_user_role + end + it 'logs a change_user_role_on_my_module activity' do expect { subject.update(valid_params) @@ -59,4 +96,53 @@ describe MyModuleMember, type: :model do expect(Activity.last.type_of).to eq 'change_user_role_on_my_module' end end + + describe '#destroy' do + let!(:user_assignment) { + create :user_assignment, + assignable: my_module, + user: user, + user_role: owner_role, + assigned_by: user + } + + let!(:subject) { described_class.new(user, my_module, experiment, project, user, user_assignment) } + + it 'destroys the assigment user role' do + expect { + subject.destroy + }.to change(UserAssignment, :count).by(-1) + end + + it 'logs a change_user_role_on_my_module activity' do + expect { + subject.destroy + }.to change(Activity, :count).by(1) + expect(Activity.last.type_of).to eq 'change_user_role_on_my_module' + end + end + + describe '#create' do + let!(:valid_params) { + { + user_id: user.id, + user_role_id: normal_user_role.id + } + } + + let(:subject) { described_class.new(user, my_module, experiment, project) } + + it 'creates a new user assigment when no assigment present' do + expect { + subject.create(valid_params) + }.to change(UserAssignment, :count).by(1) + end + + it 'logs a change_user_role_on_my_module activity' do + expect { + subject.create(valid_params) + }.to change(Activity, :count).by(1) + expect(Activity.last.type_of).to eq 'change_user_role_on_my_module' + end + end end diff --git a/spec/models/project_member_spec.rb b/spec/models/project_member_spec.rb index 4b170062e..219acd92e 100644 --- a/spec/models/project_member_spec.rb +++ b/spec/models/project_member_spec.rb @@ -10,7 +10,7 @@ describe ProjectMember, type: :model do let(:subject) { described_class.new(user, project, user) } - describe 'create' do + describe '#create' do it 'create a user_assignment and user_project records' do subject.assign = true subject.user_role_id = owner_role.id @@ -30,7 +30,7 @@ describe ProjectMember, type: :model do end end - describe 'update' do + describe '#update' do let!(:user_project) { create :user_project, user: user, project: project } let!(:user_assignment) { create :user_assignment, @@ -55,7 +55,7 @@ describe ProjectMember, type: :model do end end - describe 'destroy' do + describe '#destroy' do let!(:user_two) { create :user } let!(:user_project_two) { create :user_project, user: user_two, project: project } let!(:user_assignment_two) {