From baaf1f510f51015e5f07ad0f5ff93558e2f86eff Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Mon, 18 Dec 2023 13:38:01 +0200 Subject: [PATCH] generate unique uuids for cloned templates --- .../api/templates_clone_controller.rb | 23 +++---- app/controllers/templates_controller.rb | 13 ++-- lib/templates/clone.rb | 42 ++++++++++++ lib/templates/clone_attachments.rb | 45 ++++++++++--- spec/factories/template_folders.rb | 16 +++++ spec/system/template_spec.rb | 65 +++++++++++++++++++ 6 files changed, 176 insertions(+), 28 deletions(-) create mode 100644 lib/templates/clone.rb create mode 100644 spec/factories/template_folders.rb diff --git a/app/controllers/api/templates_clone_controller.rb b/app/controllers/api/templates_clone_controller.rb index 45c76076..7537274d 100644 --- a/app/controllers/api/templates_clone_controller.rb +++ b/app/controllers/api/templates_clone_controller.rb @@ -7,23 +7,18 @@ module Api def create authorize!(:manage, @template) - template = current_account.templates.new(source: :api) + cloned_template = Templates::Clone.call(@template, + author: current_user, + name: params[:name], + application_key: params[:application_key], + folder_name: params[:folder_name]) - template.application_key = params[:application_key] - template.name = params[:name] || "#{@template.name} (Clone)" - template.account = @template.account - template.author = current_user - template.assign_attributes(@template.slice(:folder_id, :fields, :schema, :submitters)) + cloned_template.source = :api + cloned_template.save! - if params[:folder_name].present? - template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:folder_name]) - end + Templates::CloneAttachments.call(template: cloned_template, original_template: @template) - template.save! - - Templates::CloneAttachments.call(template:, original_template: @template) - - render json: template.as_json(serialize_params) + render json: cloned_template.as_json(serialize_params) end private diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index e8861725..9650c809 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -42,10 +42,15 @@ class TemplatesController < ApplicationController end def create - @template.account = current_account - @template.author = current_user - @template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:folder_name]) - @template.assign_attributes(@base_template.slice(:fields, :schema, :submitters)) if @base_template + if @base_template + @template = Templates::Clone.call(@base_template, author: current_user, + name: params.dig(:template, :name), + folder_name: params[:folder_name]) + else + @template.account = current_account + @template.author = current_user + @template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:folder_name]) + end if @template.save Templates::CloneAttachments.call(template: @template, original_template: @base_template) if @base_template diff --git a/lib/templates/clone.rb b/lib/templates/clone.rb new file mode 100644 index 00000000..278e2247 --- /dev/null +++ b/lib/templates/clone.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Templates + module Clone + module_function + + def call(original_template, author:, application_key: nil, name: nil, folder_name: nil) + original_template_account = original_template.account + + template = original_template_account.templates.new + + template.application_key = application_key + template.author = author + template.name = name || "#{original_template.name} (Clone)" + + template.assign_attributes(original_template.slice(:folder_id, :schema)) + + template.folder = TemplateFolders.find_or_create_by_name(author, folder_name) if folder_name.present? + + submitter_uuids_replacements = {} + + cloned_submitters = original_template['submitters'].deep_dup + cloned_fields = original_template['fields'].deep_dup + + cloned_submitters.each do |submitter| + new_submitter_uuid = SecureRandom.uuid + + submitter_uuids_replacements[submitter['uuid']] = new_submitter_uuid + submitter['uuid'] = new_submitter_uuid + end + + cloned_fields.each do |field| + field['uuid'] = SecureRandom.uuid + field['submitter_uuid'] = submitter_uuids_replacements[field['submitter_uuid']] + end + + template.assign_attributes(fields: cloned_fields, submitters: cloned_submitters) + + template + end + end +end diff --git a/lib/templates/clone_attachments.rb b/lib/templates/clone_attachments.rb index 8c797bbc..2228cc16 100644 --- a/lib/templates/clone_attachments.rb +++ b/lib/templates/clone_attachments.rb @@ -5,23 +5,48 @@ module Templates module_function def call(template:, original_template:) + schema_uuids_replacements = {} + + cloned_schema = original_template['schema'].deep_dup + cloned_fields = template['fields'].deep_dup + + cloned_schema.each do |schema_item| + new_schema_item_uuid = SecureRandom.uuid + + schema_uuids_replacements[schema_item['attachment_uuid']] = new_schema_item_uuid + schema_item['attachment_uuid'] = new_schema_item_uuid + end + + cloned_fields.each do |field| + next if field['areas'].blank? + + field['areas'].each do |area| + area['attachment_uuid'] = schema_uuids_replacements[area['attachment_uuid']] + end + end + + template.update!(schema: cloned_schema, fields: cloned_fields) + original_template.documents.preload(:preview_images_attachments).each do |document| new_document = ActiveStorage::Attachment.create!( - uuid: document.uuid, + uuid: schema_uuids_replacements[document.uuid], blob_id: document.blob_id, name: 'documents', record: template ) - ApplicationRecord.no_touching do - document.preview_images_attachments.each do |preview_image| - ActiveStorage::Attachment.create!( - uuid: preview_image.uuid, - blob_id: preview_image.blob_id, - name: 'preview_images', - record: new_document - ) - end + clone_document_preview_images_attachments(document:, new_document:) + end + end + + def clone_document_preview_images_attachments(document:, new_document:) + ApplicationRecord.no_touching do + document.preview_images_attachments.each do |preview_image| + ActiveStorage::Attachment.create!( + blob_id: preview_image.blob_id, + name: 'preview_images', + record: new_document + ) end end end diff --git a/spec/factories/template_folders.rb b/spec/factories/template_folders.rb new file mode 100644 index 00000000..b7f084d0 --- /dev/null +++ b/spec/factories/template_folders.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :template_folder do + account + + author factory: %i[user] + name { Faker::Book.title } + + trait :with_templates do + after(:create) do |template_folder| + create_list(:template, 2, folder: template_folder, account: template_folder.account) + end + end + end +end diff --git a/spec/system/template_spec.rb b/spec/system/template_spec.rb index 35cd4bad..76f4a42d 100644 --- a/spec/system/template_spec.rb +++ b/spec/system/template_spec.rb @@ -74,5 +74,70 @@ RSpec.describe 'Template' do expect(page).to have_current_path(edit_template_path(template), ignore_query: true) end end + + it 'clone a template and move it to a new folder' do + click_link 'Clone' + + within '#modal' do + fill_in 'template[name]', with: 'New Template Name' + click_link 'Change Folder' + fill_in 'folder_name', with: 'New Folder Name' + + expect do + click_button 'Submit' + end.to change { Template.active.count }.by(1).and change { TemplateFolder.active.count }.by(1) + + template = Template.last + + expect(template.name).to eq('New Template Name') + expect(template.folder.name).to eq('New Folder Name') + expect(page).to have_current_path(edit_template_path(template), ignore_query: true) + end + end + + it 'clones a template and moves it to an existing folder' do + template_folder = create(:template_folder, :with_templates, account:, author: user) + + click_link 'Clone' + + within '#modal' do + template_folder.reload + fill_in 'template[name]', with: 'New Template Name' + click_link 'Change Folder' + end + + within '.autocomplete' do + find('div', text: template_folder.name).click + end + + within '#modal' do + expect do + click_button 'Submit' + end.not_to(change { TemplateFolder.active.count }) + end + + template = Template.last + expect(template.name).to eq('New Template Name') + expect(template.folder.name).to eq(template_folder.name) + expect(page).to have_current_path(edit_template_path(template), ignore_query: true) + end + + it 'moves a template' do + find('[data-tip="Move"]', visible: false).hover + find('[data-tip="Move"]').click + + within '#modal' do + fill_in 'name', with: 'New Folder Name' + + expect do + click_button 'Move' + end.to change { TemplateFolder.active.count }.by(1) + + template_folder = TemplateFolder.last + + expect(template_folder.name).to eq('New Folder Name') + expect(page).to have_current_path(template_path(template), ignore_query: true) + end + end end end