Skip to content

Commit 3c12216

Browse files
authored
Discovery: store and return Scratch data (#720)
## Status - Closes https://github.com/orgs/RaspberryPiFoundation/projects/51/views/11?pane=issue&itemId=154102194&issue=RaspberryPiFoundation%7Cdigital-editor-issues%7C1130 - Related to RaspberryPiFoundation/editor-standalone#749 ## Points for consideration: See [the ADR](https://github.com/RaspberryPiFoundation/documentation/blob/e1d78cf16af5bd97fe46194750b48c89806eec0c/docs/technology/codebases-and-products/editor/adrs/011-scratch-project-storage.md) for performance and security implications. ## What's changed? - Enables creation, saving and loading of Scratch project files (`project.json`). Does not yet enable these for assets.
1 parent 726c3fa commit 3c12216

13 files changed

Lines changed: 77 additions & 8 deletions

File tree

app/controllers/api/lessons_controller.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ def base_params
114114
:name,
115115
:project_type,
116116
:locale,
117-
{ components: %i[id name extension content index default] }
117+
{ components: %i[id name extension content index default] },
118+
{ scratch_component: {} }
118119
]
119120
}
120121
)

app/controllers/api/projects_controller.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def base_params
117117
{
118118
components: %i[id name extension content index default]
119119
},
120+
scratch_component: {},
120121
parent: {},
121122
image_list: []
122123
)

app/controllers/api/scratch/projects_controller.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ module Scratch
55
class ProjectsController < ScratchController
66
skip_before_action :authorize_user, only: [:show]
77
skip_before_action :check_scratch_feature, only: [:show]
8+
before_action :load_project, only: %i[show update]
89

910
def show
10-
render :show, formats: [:json]
11+
render json: @project.scratch_component.content
1112
end
1213

1314
def update
15+
scratch_content = params.permit!.slice(:meta, :targets, :monitors, :extensions)
16+
@project.scratch_component&.content = scratch_content.to_unsafe_h
17+
@project.save!
1418
render json: { status: 'ok' }, status: :ok
1519
end
1620
end

app/controllers/api/scratch/scratch_controller.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ def check_scratch_feature
1616

1717
raise ActiveRecord::RecordNotFound, 'Not Found'
1818
end
19+
20+
def load_project
21+
project_loader = ProjectLoader.new(params[:id], [params[:locale]])
22+
@project = project_loader.load
23+
end
1924
end
2025
end
2126
end

app/graphql/mutations/create_project.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ class CreateProject < BaseMutation
1010
def resolve(**input)
1111
project_hash = input.merge(
1212
user_id: context[:current_user]&.id,
13-
components: input[:components]&.map(&:to_h)
13+
components: input[:components]&.map(&:to_h),
14+
scratch_component: input[:scratch_component]&.to_h
1415
)
1516

1617
response = Project::Create.call(project_hash:, current_user: context[:current_user])

app/models/project.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ module Types
1313
belongs_to :parent, optional: true, class_name: :Project, foreign_key: :remixed_from_id, inverse_of: :remixes
1414
has_many :remixes, dependent: :nullify, class_name: :Project, foreign_key: :remixed_from_id, inverse_of: :parent
1515
has_many :components, -> { order(default: :desc, name: :asc) }, dependent: :destroy, inverse_of: :project
16+
has_one :scratch_component, dependent: :destroy, inverse_of: :project, required: false
1617
has_many :project_errors, dependent: :nullify
1718
has_many_attached :images
1819
has_many_attached :videos
1920
has_many_attached :audio
2021
has_one :school_project, dependent: :destroy
2122

2223
accepts_nested_attributes_for :components
24+
accepts_nested_attributes_for :scratch_component
2325

2426
before_validation :check_unique_not_null, on: :create
2527
before_validation :create_school_project_if_needed
@@ -67,6 +69,10 @@ def components=(array)
6769
super(array.map { |o| o.is_a?(Hash) ? Component.new(o) : o })
6870
end
6971

72+
def scratch_component=(value)
73+
super(value.is_a?(Hash) ? ScratchComponent.new(value) : value)
74+
end
75+
7076
def last_edited_at
7177
# datetime that the project or one of its components was last updated
7278
[updated_at, components.maximum(:updated_at)].compact.max

app/models/scratch_component.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# frozen_string_literal: true
2+
3+
class ScratchComponent < ApplicationRecord
4+
belongs_to :project
5+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class CreateScratchComponents < ActiveRecord::Migration[7.2]
2+
def change
3+
create_table :scratch_components, id: :uuid do |t|
4+
t.jsonb :content
5+
t.references :project, null: false, foreign_key: true, type: :uuid, index: { unique: true }
6+
7+
t.timestamps
8+
end
9+
end
10+
end

db/schema.rb

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/concepts/project/operations/create.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ def call(project_hash:, current_user:)
1818

1919
def build_project(project_hash, current_user)
2020
project_hash[:identifier] = PhraseIdentifier.generate unless current_user&.experience_cs_admin?
21-
new_project = Project.new(project_hash.except(:components))
21+
new_project = Project.new(project_hash.except(:components, :scratch_component))
2222
new_project.components.build(project_hash[:components])
23+
new_project.build_scratch_component(project_hash[:scratch_component]) if project_hash[:scratch_component].present?
2324
new_project
2425
end
2526
end

0 commit comments

Comments
 (0)