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
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def migrate_project!
authorize! :migrate_from_experience_cs, @project
@project.update!(
attributes.slice(:name, :instructions).merge(
project_type: Project::Types::CODE_EDITOR_SCRATCH
project_type: Project::Types::CODE_EDITOR_SCRATCH,
origin: Project::Origins::EXPERIENCE_CS
)
)
scratch_component = @project.scratch_component || @project.build_scratch_component
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 @@ -43,7 +43,7 @@ def create
end

def update
result = Project::Update.call(project: @project, update_hash: project_params)
result = Project::Update.call(project: @project, update_hash: project_params, current_user:)

if result.success?
track_project_event('Project - Saved', @project)
Expand Down
10 changes: 6 additions & 4 deletions lib/concepts/project/operations/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
class Project
class Update
class << self
def call(project:, update_hash:)
def call(project:, update_hash:, current_user: nil)
response = setup_response(project)

setup_deletions(response, update_hash)
update_project_attributes(response, update_hash)
update_project_attributes(response, update_hash, current_user)
update_component_attributes(response, update_hash)
update_scratch_component_attributes(response, update_hash)
persist_changes(response)
Expand Down Expand Up @@ -43,10 +43,12 @@ def validate_deletions(response)
response[:error] = I18n.t 'errors.project.editing.delete_default_component'
end

def update_project_attributes(response, update_hash)
def update_project_attributes(response, update_hash, current_user)
return if response.failure?

response[:project].assign_attributes(update_hash.slice(:name, :instructions, :project_type))
attributes = update_hash.slice(:name, :instructions, :project_type)
attributes[:origin] = Project::Origins::EXPERIENCE_CS if current_user&.experience_cs_admin?
response[:project].assign_attributes(attributes)
end

def update_component_attributes(response, update_hash)
Expand Down
26 changes: 26 additions & 0 deletions spec/concepts/project/update_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,32 @@
expect { update }.to change { project.reload.instructions }.to('new instructions')
end
end

context 'when the current user is an Experience CS admin' do
subject(:update_as_admin) { described_class.call(project:, update_hash:, current_user:) }

let(:current_user) { create(:experience_cs_admin_user) }
let(:update_hash) do
{
name: 'updated project name',
components: component_hash,
instructions:
}
end

it 'returns success? true' do
expect(update_as_admin.success?).to be(true)
end

it 'sets the project origin to experience_cs' do
expect(update_as_admin[:project].origin).to eq(Project::Origins::EXPERIENCE_CS)
end

it 'succeeds when the origin is already experience_cs' do
project.update(origin: Project::Origins::EXPERIENCE_CS)
expect(update_as_admin.success?).to be(true)
end
end
end

def component_properties_hash(component)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
school_id: school.id,
name: 'Migrated project',
instructions: [{ 'markdown_content' => 'Make the sprite move.' }],
project_type: Project::Types::CODE_EDITOR_SCRATCH
project_type: Project::Types::CODE_EDITOR_SCRATCH,
origin: Project::Origins::EXPERIENCE_CS
)
expect(project.scratch_component.content.to_h).to eq(scratch_data.deep_stringify_keys)
end
Expand Down
39 changes: 37 additions & 2 deletions spec/requests/projects/update_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,19 @@
expect(response.body).to include('updated component content')
end

it 'calls update operation' do
it 'calls update operation with the current user' do
mock_response = instance_double(OperationResponse)
allow(mock_response).to receive(:success?).and_return(true)
allow(Project::Update).to receive(:call).and_return(mock_response)
put("/api/projects/#{project.identifier}", params:, headers:)
expect(Project::Update).to have_received(:call)
expect(Project::Update).to have_received(:call).with(
a_hash_including(project:, current_user: owner)
)
end

it 'does not set the project origin' do
put("/api/projects/#{project.identifier}", params:, headers:)
expect(project.reload.origin).to be_nil
end

context 'when no components specified' do
Expand Down Expand Up @@ -181,6 +188,28 @@
expect(project.scratch_component.reload.content.to_h).to eq(scratch_data.deep_stringify_keys)
end

it 'sets the project origin to experience_cs' do
put('/api/projects/experience-cs-project?locale=fr', params:, headers:, as: :json)

expect(project.reload.origin).to eq(Project::Origins::EXPERIENCE_CS)
end

it 'sets the origin when overwriting an existing template project in the en locale' do
project.update!(locale: 'en', origin: nil)

put('/api/projects/experience-cs-project?locale=en', params:, headers:, as: :json)

expect(project.reload.origin).to eq(Project::Origins::EXPERIENCE_CS)
end

it 'leaves the origin as experience_cs when re-syncing a project' do
project.update!(origin: Project::Origins::EXPERIENCE_CS)

put('/api/projects/experience-cs-project?locale=fr', params:, headers:, as: :json)

expect(project.reload.origin).to eq(Project::Origins::EXPERIENCE_CS)
end

context 'when authenticated with the Experience CS service API key' do
let(:headers) { { ExperienceCsServiceAuthenticator::HEADER => 'service-api-key' } }

Expand Down Expand Up @@ -242,6 +271,12 @@

expect(response).to have_http_status(:forbidden)
end

it 'sets the project origin to experience_cs' do
put('/api/projects/experience-cs-project?locale=fr', params:, headers:, as: :json)

expect(project.reload.origin).to eq(Project::Origins::EXPERIENCE_CS)
end
end
end

Expand Down
Loading