From 6f7fcb0831255955068bc521992fe822377ccf2c Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Mon, 21 Jan 2019 16:08:42 +0100 Subject: [PATCH] Refactor method `clone_experiment_as_template` into service object [SCI-2925] --- app/controllers/experiments_controller.rb | 34 +----- app/models/experiment.rb | 30 ----- .../copy_experiment_as_template_service.rb | 103 ++++++++++++++++++ ...opy_experiment_as_template_service_sepc.rb | 60 ++++++++++ 4 files changed, 169 insertions(+), 58 deletions(-) create mode 100644 app/services/experiments/copy_experiment_as_template_service.rb create mode 100644 spec/services/experiments/copy_experiment_as_template_service_sepc.rb diff --git a/app/controllers/experiments_controller.rb b/app/controllers/experiments_controller.rb index 1259c893f..e3b4a4b00 100644 --- a/app/controllers/experiments_controller.rb +++ b/app/controllers/experiments_controller.rb @@ -185,37 +185,15 @@ class ExperimentsController < ApplicationController # POST: clone_experiment(id) def clone - project = Project.find_by_id(params[:experiment].try(:[], :project_id)) - - # Try to clone the experiment - success = true - if @experiment.projects_with_role_above_user(current_user).include?(project) - cloned_experiment = @experiment.deep_clone_to_project(current_user, - project) - success = cloned_experiment.valid? - # Create workflow image - cloned_experiment.delay.generate_workflow_img if success - else - success = false - end - - if success - Activity.create( - type_of: :clone_experiment, - project: project, - experiment: @experiment, - user: current_user, - message: I18n.t( - 'activities.clone_experiment', - user: current_user.full_name, - experiment_new: cloned_experiment.name, - experiment_original: @experiment.name - ) - ) + service = Experiments::CopyExperimentAsTemplateService + .call(experiment_id: @experiment.id, + project_id: move_experiment_param, + user_id: current_user.id) + if service.succeed? flash[:success] = t('experiments.clone.success_flash', experiment: @experiment.name) - redirect_to canvas_experiment_path(cloned_experiment) + redirect_to canvas_experiment_path(service.cloned_experiment) else flash[:error] = t('experiments.clone.error_flash', experiment: @experiment.name) diff --git a/app/models/experiment.rb b/app/models/experiment.rb index ee99a7bb5..2a38bb42a 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -225,36 +225,6 @@ class Experiment < ApplicationRecord Experiments::GenerateWorkflowImageService.call(experiment_id: id) end - # Clone this experiment to given project - def deep_clone_to_project(current_user, project) - # First we have to find unique name for our little experiment - experiment_names = project.experiments.map(&:name) - format = 'Clone %d - %s' - - i = 1 - i += 1 while experiment_names.include?(format(format, i, name)) - - clone = Experiment.new( - name: format(format, i, name).truncate(Constants::NAME_MAX_LENGTH), - description: description, - created_by: current_user, - last_modified_by: current_user, - project: project - ) - - # Copy all workflows - my_module_groups.each do |g| - clone.my_module_groups << g.deep_clone_to_experiment(current_user, clone) - end - - # Copy modules without group - clone.my_modules << my_modules.without_group.map do |m| - m.deep_clone_to_experiment(current_user, clone) - end - clone.save - clone - end - # Get projects where user is either owner or user in the same team # as this experiment def projects_with_role_above_user(current_user) diff --git a/app/services/experiments/copy_experiment_as_template_service.rb b/app/services/experiments/copy_experiment_as_template_service.rb new file mode 100644 index 000000000..5655d84c2 --- /dev/null +++ b/app/services/experiments/copy_experiment_as_template_service.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module Experiments + class CopyExperimentAsTemplateService + extend Service + + attr_reader :errors, :c_exp + alias cloned_experiment c_exp + + def initialize(experiment_id:, project_id:, user_id:) + @exp = Experiment.find experiment_id + @project = Project.find project_id + @user = User.find user_id + @original_project = @exp&.project + @c_exp = nil + @errors = {} + end + + def call + return self unless valid? + + ActiveRecord::Base.transaction do + @c_exp = Experiment.new( + name: find_uniq_name, + description: @exp.description, + created_by: @user, + last_modified_by: @user, + project: @project + ) + + # Copy all signle taskas + @c_exp.my_modules << @exp.my_modules.without_group.map do |m| + m.deep_clone_to_experiment(@user, @c_exp) + end + + # Copy all grouped tasks + @exp.my_module_groups.each do |g| + @c_exp.my_module_groups << g.deep_clone_to_experiment(@user, @c_exp) + end + + raise ActiveRecord::Rollback unless @c_exp.save + end + @errors.merge!(@c_exp.errors.to_hash) unless @c_exp.valid? + + @c_exp = nil unless succeed? + @c_exp.delay.generate_workflow_img if succeed? + track_activity if succeed? + + self + end + + def succeed? + @errors.none? + end + + private + + def find_uniq_name + experiment_names = @project.experiments.map(&:name) + format = 'Clone %d - %s' + free_index = 1 + free_index += 1 while experiment_names + .include?(format(format, free_index, @exp.name)) + format(format, free_index, @exp.name).truncate(Constants::NAME_MAX_LENGTH) + end + + def valid? + unless @exp && @project && @user + @errors[:invalid_arguments] = + { 'experiment': @exp, + 'project': @project, + 'user': @user } + .map do |key, value| + "Can't find #{key.capitalize}" if value.nil? + end.compact + return false + end + + if @exp.projects_with_role_above_user(@user).include?(@project) + true + else + @errors[:user_without_permissions] = + ['You are not allowed to copy this experiment to this project'] + false + end + end + + def track_activity + Activity.create( + type_of: :clone_experiment, + project: @project, + experiment: @exp, + user: @user, + message: I18n.t( + 'activities.clone_experiment', + user: @user, + experiment_new: @c_exp.name, + experiment_original: @exp.name + ) + ) + end + end +end diff --git a/spec/services/experiments/copy_experiment_as_template_service_sepc.rb b/spec/services/experiments/copy_experiment_as_template_service_sepc.rb new file mode 100644 index 000000000..b17874250 --- /dev/null +++ b/spec/services/experiments/copy_experiment_as_template_service_sepc.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Experiments::CopyExperimentAsTemplateService do + let(:team) { create :team, :with_members } + let(:user_project) { create :user_project, :normal_user, user: user } + + let(:project) do + create :project, team: team + end + let(:new_project) do + create :project, team: team, user_projects: [user_project] + end + let(:experiment) do + create :experiment_with_tasks, name: 'MyExp', project: project + end + let(:user) { create :user } + let(:service_call) do + Experiments::CopyExperimentAsTemplateService + .call(experiment_id: experiment.id, + project_id: new_project.id, + user_id: user.id) + end + + context 'when service call is successful' do + it 'adds new experiment to target project' do + expect { service_call }.to(change { new_project.experiments.count }) + end + + it 'adds Activity record' do + expect { service_call }.to(change { Activity.all.count }) + end + + it 'copies all tasks to new experiment' do + expect(service_call.cloned_experiment.my_modules.count) + .to be == experiment.my_modules.count + end + + it 'copies all task groups to new experiment' do + expect(service_call.cloned_experiment.my_module_groups.count) + .to be == experiment.my_module_groups.count + end + end + + context 'when service call is not successful' do + it 'returns an error when can\'t find experiment' do + allow(Experiment).to receive(:find).and_return(nil) + + expect(service_call.errors).to have_key(:invalid_arguments) + end + + it 'returns error when user don\'t have permissions' do + expect_any_instance_of(Experiment) + .to receive(:projects_with_role_above_user).and_return([]) + + expect(service_call.errors).not_to be_empty + end + end +end