Merge branch 'ur-SCI-2925-copy-experiment-refactor'

This commit is contained in:
Urban Rotnik 2019-01-31 07:23:06 +01:00
commit 293eef01c1
4 changed files with 169 additions and 58 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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