From 5fa8d5457a106a1c6a6e6f9b891b13a3a6085d9b Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:57:35 +0000 Subject: [PATCH] Scratch projects create action for remixing This is based on the similar endpoint in Experience CS. Having this will allowed me to test the behaviour of Scratch remixing locally and it will allow us to start work implementing remixing on the frontend before the API is fully implemented. I considered re-using our existing endpoint for remixing, but this is not possible because Scratch does not support updating an unsaved project's id (see [1] for more). [1] - https://github.com/RaspberryPiFoundation/digital-editor-issues/issues/1189#issuecomment-4047292796 --- .../api/scratch/projects_controller.rb | 14 +++++ config/routes.rb | 2 +- .../creating_a_scratch_project_spec.rb | 51 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 spec/features/scratch/creating_a_scratch_project_spec.rb diff --git a/app/controllers/api/scratch/projects_controller.rb b/app/controllers/api/scratch/projects_controller.rb index 4904724d3..dcae49e89 100644 --- a/app/controllers/api/scratch/projects_controller.rb +++ b/app/controllers/api/scratch/projects_controller.rb @@ -6,13 +6,27 @@ class ProjectsController < ScratchController skip_before_action :authorize_user, only: [:show] skip_before_action :check_scratch_feature, only: [:show] + before_action :ensure_create_is_a_remix, only: %i[create] + def show render :show, formats: [:json] end + def create + render json: { status: 'ok', 'content-name': 'new-project-id' }, status: :ok + end + def update render json: { status: 'ok' }, status: :ok end + + private + + def ensure_create_is_a_remix + return if params[:is_remix] == '1' + + render json: { error: 'Only remixing existing projects is allowed' }, status: :forbidden + end end end end diff --git a/config/routes.rb b/config/routes.rb index 6dfc482c3..9aefae218 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -34,7 +34,7 @@ namespace :api do namespace :scratch do - resources :projects, only: %i[show update] + resources :projects, only: %i[show update create] get '/assets/internalapi/asset/:id(.:format)/get/' => 'assets#show' post '/assets/:id' => 'assets#create' end diff --git a/spec/features/scratch/creating_a_scratch_project_spec.rb b/spec/features/scratch/creating_a_scratch_project_spec.rb new file mode 100644 index 000000000..a06446997 --- /dev/null +++ b/spec/features/scratch/creating_a_scratch_project_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Creating a Scratch project (remixing)', type: :request do + let(:school) { create(:school) } + let(:teacher) { create(:teacher, school:) } + let(:cookie_headers) { { 'Cookie' => "scratch_auth=#{UserProfileMock::TOKEN}" } } + let(:params) { { original_id: 'original-project-id', project: { targets: [] }, is_remix: '1' } } + + before do + Flipper.disable :cat_mode + Flipper.disable_actor :cat_mode, school + end + + it 'responds 401 Unauthorized when no cookie is provided' do + post '/api/scratch/projects', params: params + + expect(response).to have_http_status(:unauthorized) + end + + it 'responds 404 Not Found when cat_mode is not enabled' do + authenticated_in_hydra_as(teacher) + + post '/api/scratch/projects', params: params, headers: cookie_headers + + expect(response).to have_http_status(:not_found) + end + + it 'responds 403 Forbidden when not remixing' do + authenticated_in_hydra_as(teacher) + Flipper.enable_actor :cat_mode, school + + post '/api/scratch/projects', params: params.merge(is_remix: '0'), headers: cookie_headers + + expect(response).to have_http_status(:forbidden) + end + + it 'return new project id when cat_mode is enabled and a cookie is provided' do + authenticated_in_hydra_as(teacher) + Flipper.enable_actor :cat_mode, school + + post '/api/scratch/projects', params: params, headers: cookie_headers + + expect(response).to have_http_status(:ok) + + data = JSON.parse(response.body, symbolize_names: true) + expect(data[:status]).to eq('ok') + expect(data[:'content-name']).to eq('new-project-id') + end +end