Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions app/concepts/project/operation/create_remix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,41 @@ module Operation
class CreateRemix
require 'operation_response'

def self.call(params, user_id)
response = OperationResponse.new

validate_params(response, params, user_id)
return response if response.failure?
class << self
def call(params:, user_id:, original_project:)
response = OperationResponse.new

remix_project(response, params, user_id)
response
end
validate_params(response, params, user_id, original_project)
remix_project(response, params, user_id, original_project)
response
end

class << self
private

def validate_params(response, params, user_id)
valid = params[:project_id].present? && user_id.present?
response[:error] = 'Invalid parameters' unless valid
def validate_params(response, params, user_id, original_project)
valid = params[:identifier].present? && user_id.present? && original_project.present?
response[:error] = I18n.t('errors.project.remixing.invalid_params') unless valid
end

def remix_project(response, params, user_id)
original_project = Project.find_by!(identifier: params[:project_id])
def remix_project(response, params, user_id, original_project)
return if response[:error]

response[:project] = create_remix(original_project, user_id)
response[:project] = create_remix(original_project, params, user_id)

response[:error] = 'Unable to create project' unless response[:project].save
response[:error] = I18n.t('errors.project.remixing.cannot_save') unless response[:project].save
response
end

def create_remix(original_project, user_id)
original_project.dup.tap do |proj|
def create_remix(original_project, params, user_id)
remix = original_project.dup.tap do |proj|
proj.user_id = user_id
proj.components = original_project.components.map(&:dup)
proj.remixed_from_id = original_project.id
end

params[:components].each do |x|
remix.components.build(x.slice(:name, :extension, :content, :index))
end
remix
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/concepts/project/operation/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Operation
class Update
require 'operation_response'

def self.call(params, project)
def self.call(params:, project:)
response = setup_response(project)

setup_deletions(response, params)
Expand Down
13 changes: 11 additions & 2 deletions app/controllers/api/projects/remixes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ class RemixesController < ApiController
before_action :require_oauth_user

def create
result = Project::Operation::CreateRemix.call(remix_params, oauth_user_id)
result = Project::Operation::CreateRemix.call(params: remix_params,
user_id: oauth_user_id,
original_project: project)

if result.success?
@project = result[:project]
Expand All @@ -18,8 +20,15 @@ def create

private

def project
@project ||= Project.find_by!(identifier: params[:project_id])
end

def remix_params
params.permit(:project_id)
params.require(:project)
.permit(:name,
:identifier,
components: %i[id name extension content index])
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/projects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def show
end

def update
result = Project::Operation::Update.call(project_params, @project)
result = Project::Operation::Update.call(params: project_params, project: @project)

if result.success?
render :show, formats: [:json]
Expand Down
3 changes: 3 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ en:
delete_default_component: 'Cannot delete default file'
change_default_name: 'Cannot amend default file name'
change_default_extension: 'Cannot amend default file extension'
remixing:
invalid_params: 'Invalid parameters'
cannot_save: 'Cannot create project remix'
142 changes: 106 additions & 36 deletions spec/concepts/project/operation/create_remix_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,148 @@
require 'rails_helper'

RSpec.describe Project::Operation::CreateRemix, type: :unit do
subject(:create_remix) { described_class.call(params, user_id) }
subject(:create_remix) { described_class.call(params: remix_params, user_id: user_id, original_project: original_project) }

let(:user_id) { 'e0675b6c-dc48-4cd6-8c04-0f7ac05af51a' }
let!(:original_project) { create(:project, :with_components) }
let(:remix_params) do
component = original_project.components.first
{
name: original_project.name,
identifier: original_project.identifier,
components: [
{
id: component.id,
name: component.name,
extension: component.extension,
content: 'some updated component content',
index: component.index
}
]
}
end

before do
mock_phrase_generation
end

describe '.call' do
context 'when all params valid' do
let(:params) { { project_id: original_project.identifier } }
let(:params) { { project_id: original_project.identifier } }

it 'returns success' do
result = create_remix
expect(result.success?).to eq(true)
end
it 'returns success' do
result = create_remix
expect(result.success?).to eq(true)
end

it 'creates new project' do
expect { create_remix }.to change(Project, :count).by(1)
end

it 'assigns a new identifer to new project' do
result = create_remix
remixed_project = result[:project]
expect(remixed_project.identifier).not_to eq(original_project.identifier)
end

it 'assigns user_id to new project' do
remixed_project = create_remix[:project]
expect(remixed_project.user_id).to eq(user_id)
end

it 'duplicates properties on new project' do
remixed_project = create_remix[:project]

it 'creates new project' do
expect { create_remix }.to change(Project, :count).by(1)
remixed_attrs = remixed_project.attributes.symbolize_keys.slice(:name, :project_type)
original_attrs = original_project.attributes.symbolize_keys.slice(:name, :project_type)
expect(remixed_attrs).to eq(original_attrs)
end

it 'creates new components' do
expect { create_remix }.to change(Component, :count).by(1)
end

it 'persists changes made to submitted components' do
remixed_project = create_remix[:project]
expect(remixed_project.components.first.content).to eq('some updated component content')
end

context 'when a new component has been added before remixing' do
let(:new_component_params) { { name: 'added_component', extension: 'py', content: 'some added component content', index: 9999 } }

before do
remix_params[:components] << new_component_params
end

it 'assigns a new identifer to new project' do
result = create_remix
remixed_project = result[:project]
expect(remixed_project.identifier).not_to eq(original_project.identifier)
it 'creates all components' do
expect { create_remix }.to change(Component, :count).by(2)
end

it 'assigns user_id to new project' do
it 'persists the new component' do
remixed_project = create_remix[:project]
expect(remixed_project.user_id).to eq(user_id)
expect(remixed_project.components.last.attributes.symbolize_keys).to include(new_component_params)
end
end

it 'duplicates properties on new project' do
remixed_project = create_remix[:project]
context 'when user_id is not present' do
let(:user_id) { nil }
let(:params) { { project_id: original_project.identifier } }

remixed_attrs = remixed_project.attributes.symbolize_keys.slice(:name, :project_type)
original_attrs = original_project.attributes.symbolize_keys.slice(:name, :project_type)
expect(remixed_attrs).to eq(original_attrs)
it 'returns failure' do
result = create_remix
expect(result.failure?).to eq(true)
end

it 'duplicates project components' do
remixed_props_array = component_array_props(create_remix[:project].components)
original_props_array = component_array_props(original_project.components)
it 'returns error message' do
result = create_remix
expect(result[:error]).to eq(I18n.t('errors.project.remixing.invalid_params'))
end

expect(remixed_props_array).to match_array(original_props_array)
it 'does not create new project' do
expect { create_remix }.not_to change(Project, :count)
end
end

context 'when user_id is not present' do
let(:user_id) { nil }
let(:params) { { project_id: original_project.identifier } }
context 'when original project is not present' do
subject(:create_remix) { described_class.call(params: remix_params, user_id: user_id, original_project: nil) }

it 'returns failure' do
result = create_remix
expect(result.failure?).to eq(true)
end

it 'returns error message' do
result = create_remix
expect(result[:error]).to eq(I18n.t('errors.project.remixing.invalid_params'))
end

it 'does not create new project' do
expect { create_remix }.not_to change(Project, :count)
end
end
end

def component_array_props(components)
components.map do |x|
{
name: x.name,
content: x.content,
extension: x.extension,
index: x.index
}
context 'when project components are invalid' do
let(:invalid_component_params) { { name: 'added_component', extension: 'py', content: '' } }

before do
remix_params[:components] << invalid_component_params
end

it 'returns failure' do
expect(create_remix.failure?).to eq(true)
end

it 'sets error message' do
expect(create_remix[:error]).to eq(I18n.t('errors.project.remixing.cannot_save'))
end
end
end

def component_props(component)
{
name: component.name,
content: component.content,
extension: component.extension,
index: component.index
}
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require 'rails_helper'

RSpec.describe Project::Operation::Update, type: :unit do
subject(:update) { described_class.call(project_params, project) }
subject(:update) { described_class.call(params: project_params, project: project) }

let!(:project) { create(:project, :with_default_component) }
let(:default_component) { project.components.first }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require 'rails_helper'

RSpec.describe Project::Operation::Update, type: :unit do
subject(:update) { described_class.call(project_params, project) }
subject(:update) { described_class.call(params: project_params, project: project) }

let!(:project) { create(:project, :with_default_component, :with_components) }
let(:component_to_delete) { project.components.last }
Expand Down
2 changes: 1 addition & 1 deletion spec/concepts/project/operation/update_invalid_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
name: 'updated project name',
components: [default_component_params, edited_component_params, new_component_params]
}
described_class.call(params, project)
described_class.call(params: params, project: project)
end

let!(:project) { create(:project, :with_default_component, :with_components, component_count: 2) }
Expand Down
2 changes: 1 addition & 1 deletion spec/concepts/project/operation/update_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
name: 'updated project name',
components: component_params
}
described_class.call(params, project)
described_class.call(params: params, project: project)
end

let!(:project) { create(:project, :with_default_component, :with_components) }
Expand Down
1 change: 1 addition & 0 deletions spec/factories/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
extension { 'py' }
sequence(:index) { |n| n }
default { false }
content { Faker::Lorem.paragraph }
project

factory :default_python_component do
Expand Down
Loading