-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Project copying #2861
Project copying #2861
Conversation
app/workers/project_copy_worker.rb
Outdated
def perform(project_id, user_id) | ||
ProjectCopyWorker.copy(project_id, user_id) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/TrailingBlankLines: Final newline missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These newlines exist locally and git thinks the files are synced. That is, I can't commit or push them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a linter for your editor? Something like https://github.com/ngmy/vim-rubocop might help to run it locally as well.
lib/project_copier.rb
Outdated
copied_project.save! | ||
copied_project | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/TrailingBlankLines: Final newline missing.
lib/project_copier.rb
Outdated
:avatar, | ||
:background, | ||
{ active_workflows: [ :tutorials, :attached_images, :workflow_contents ] } | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/MultilineArrayBraceLayout: Closing array brace must be on the same line as the last array element when opening brace is on the same line as the first array element.
lib/project_copier.rb
Outdated
:tagged_resources, | ||
:avatar, | ||
:background, | ||
{ active_workflows: [ :tutorials, :attached_images, :workflow_contents ] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style/SymbolArray: Use %i or %I for an array of symbols.
lib/project_copier.rb
Outdated
@@ -0,0 +1,30 @@ | |||
class ProjectCopier | |||
EXCLUDE_ATTRIBUTES = [ :classifications_count, :launched_row_order, :beta_row_order ] | |||
INCLUDE_ATTRIBUTES = [ :project_contents, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style/MutableConstant: Freeze mutable objects assigned to constants.
lib/project_copier.rb
Outdated
@@ -0,0 +1,30 @@ | |||
class ProjectCopier | |||
EXCLUDE_ATTRIBUTES = [ :classifications_count, :launched_row_order, :beta_row_order ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style/MutableConstant: Freeze mutable objects assigned to constants.
Style/SymbolArray: Use %i or %I for an array of symbols.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work this looks pretty good, i left some minor comments on your code.
My main interest is in the gem dependency code, i.e. How does the deep clone work under the hood?
A quick read of the source shows that it dups a record, excludes attributes, selects specific attributes and then builds up a chain of associations to link before assigning them to the dup'ed new record.
https://github.com/moiristo/deep_cloneable/blob/1d67dd44031b1df7ae77c8a33b4afd39ade7706f/lib/deep_cloneable.rb#L85
https://github.com/moiristo/deep_cloneable/blob/1d67dd44031b1df7ae77c8a33b4afd39ade7706f/lib/deep_cloneable.rb#L68
Also that looks interesting and the copy is always assumed to be valid. Does this validate: false
persist to later saves? Could this cause issues?
Some AR associations auto save the new record (has_many, etc to link the FKs), does this mean that for a small period of time the owner of the template project now owns the new copy as well?
Could it leave the copied template project owned by the non-requesting user? Is this an issue or just something to be aware of (i.e. what happens if the copy fails after the association auto save?
app/policies/project_policy.rb
Outdated
@@ -21,7 +21,7 @@ class TranslateScope < Scope | |||
:create_subjects_export, | |||
:create_workflows_export, | |||
:create_workflow_contents_export, | |||
:retire_subjects, with: WriteScope | |||
:retire_subjects, :copy, with: WriteScope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will users copy template projects they don't collab / own? Should this be ReadScope
i.e. public projects or ones i can see in my lab?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depends. Write makes sense to me to roll out this feature on a small number of hand selected projects (via the manually setting the configuration) and the button in PFE doesn't exist yet. For those same reasons, though, I can see not mattering very much and maybe it's better to put it in Read now and not have to change it later.
app/workers/project_copy_worker.rb
Outdated
class ProjectCopyWorker | ||
include Sidekiq::Worker | ||
|
||
sidekiq_options queue: :data_high |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to look at locking on unique args to ensure users don't trigger multiple copies by double clicking form submission?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
app/workers/project_copy_worker.rb
Outdated
def perform(project_id, user_id) | ||
ProjectCopyWorker.copy(project_id, user_id) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a linter for your editor? Something like https://github.com/ngmy/vim-rubocop might help to run it locally as well.
lib/project_copier.rb
Outdated
].freeze | ||
|
||
def self.copy(project_id, user_id) | ||
project = Project.find(project_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens if i can't find the project / user by ID?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️ Good call, added a rescue to the worker.
lib/project_copier.rb
Outdated
@@ -0,0 +1,30 @@ | |||
class ProjectCopier | |||
EXCLUDE_ATTRIBUTES = [ :classifications_count, :launched_row_order, :beta_row_order ].freeze | |||
INCLUDE_ATTRIBUTES = [ :project_contents, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren't really attributes but associations. Should the variable name reflect that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
lib/project_copier.rb
Outdated
user = User.find(user_id) | ||
|
||
copied_project = project.deep_clone include: INCLUDE_ATTRIBUTES, except: EXCLUDE_ATTRIBUTES | ||
copied_project.owner = user |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you look at the block syntax for updating the clone? https://github.com/moiristo/deep_cloneable#optional-block
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not doing anything with the copy that needs any pre-exclude original attributes or anything else I can think of that requires that block. It's all happening pre-save!
, so it's not hitting the db yet.
|
||
it "has matching attributes" do | ||
expect(copied_project.display_name).to eq(project.display_name) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to test the other changes to the copied instance as well, e.g. copied_project.update_attributes(launch_approved: false, live: false)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
Thanks, updated with suggested changes. Also, somehow we both managed to miss that I was calling the ProjectCopyWorker from itself instead of the ProjectCopier. There are specs for that, now, too. :3
First, that would only happen using the
I didn't see that happening. Running a deep clone on a project, it's workflows are unsaved until you save the project. As a matter of fact, a |
}.not_to raise_error | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/TrailingBlankLines: Final newline missing.
@@ -1,4 +1,4 @@ | |||
class SubjectWorkflowStatusCreateWorker | |||
class SubjectWorkflowStatusCreateWorker |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Layout/InitialIndentation: Indentation of first line in file detected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aw c'mon, The Hound, this wasn't even--aw, forget it. Fine, I'll fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice - my original comments were misfounded and your interpretation is correct. This gem doesn't use the validate option unless specified and doesn't cause auto save on certain associations to fire (still not 100% sure why though).
I was thinking should we store the originating copy ID in the project configuration for data provenance? That way we can see which project it was copied from if we have to debug it.
A couple of minor comments on this PR, nice work 👍
lib/project_copier.rb
Outdated
@@ -0,0 +1,29 @@ | |||
class ProjectCopier | |||
EXCLUDE_ASSOCIATIONS = [ :classifications_count, :launched_row_order, :beta_row_order ].freeze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren't associations, rather attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doh! shouldn't have changed both.
lib/project_copier.rb
Outdated
copied_project.display_name += " (copy)" | ||
end | ||
|
||
copied_project.update_attributes(launch_approved: false, live: false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will run a save after assigning the attributes, i think you want assign_attributes
instead to avoid the double save.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absolutely, ✔️
spec/lib/project_copier_spec.rb
Outdated
describe '#copy' do | ||
let(:project) { create(:full_project, build_extra_contents: true)} | ||
let(:copyist) { create(:user) } | ||
let(:tags) { create(:tag, resource: project) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
none of these associated records exist as they are never called before the copy. Might want to add a let!(:tags)
or call them before you use them. Also you should probably test these associations exist on the copied project, i wouldn't validate the copy just that the included associations are copied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup. bang!
:avatar, | ||
:background, | ||
:translations, | ||
{ active_workflows: [ :tutorials, :attached_images, :workflow_contents] }].freeze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style/SymbolArray: Use %i or %I for an array of symbols.
Adds project copying via a
POST /projects/:project_id/copy
. Requires that a) the project is not live, b) the project's configuration object includestemplate: true
, and c) the user has write privileges on the project.Utilizes the deep_cloneable gem to keep the logic clean. This gem just provides a new
#dup
that takes a list of associations to call the same on. List of associations included (as well as excluded) associations is in the ProjectCopier lib.Review checklist
apiary.apib
file?