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
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.0.3'

gem 'bootsnap', require: false
gem 'cancancan', '~> 3.3'
gem 'faraday'
gem 'importmap-rails'
gem 'jbuilder'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ GEM
msgpack (~> 1.0)
builder (3.2.4)
byebug (11.1.3)
cancancan (3.3.0)
coderay (1.1.3)
concurrent-ruby (1.1.9)
crack (0.4.5)
Expand Down Expand Up @@ -250,6 +251,7 @@ PLATFORMS

DEPENDENCIES
bootsnap
cancancan (~> 3.3)
dotenv-rails
factory_bot_rails
faker
Expand Down
4 changes: 2 additions & 2 deletions app/concepts/project/operation/create_remix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ class << self
private

def validate_params(response, params, user_id)
valid = params[:phrase_id].present? && user_id.present?
valid = params[:project_id].present? && user_id.present?
response[:error] = 'Invalid parameters' unless valid
end

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

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

Expand Down
36 changes: 0 additions & 36 deletions app/controllers/api/projects/phrases_controller.rb

This file was deleted.

2 changes: 1 addition & 1 deletion app/controllers/api/projects/remixes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def create
private

def remix_params
params.permit(:phrase_id)
params.permit(:project_id)
end
end
end
Expand Down
35 changes: 35 additions & 0 deletions app/controllers/api/projects_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Api
class ProjectsController < ApiController
require 'phrase_identifier'

before_action :require_oauth_user, only: %i[update]

def show
@project = Project.find_by!(identifier: params[:id])
render :show, formats: [:json]
end

def update
@project = Project.find_by!(identifier: params[:id])
authorize! :update, @project

components = project_params[:components]
components.each do |comp_params|
component = Component.find(comp_params[:id])
component.update(comp_params)
end
head :ok
end

private

def project_params
params.require(:project)
.permit(:identifier,
:type,
components: %i[id name extension content])
end
end
end
12 changes: 11 additions & 1 deletion app/controllers/api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class ApiController < ActionController::API

unless Rails.application.config.consider_all_requests_local
rescue_from ActiveRecord::RecordNotFound, with: -> { return404 }
rescue_from CanCan::AccessDenied, with: -> { return401 }
end

private
Expand All @@ -13,7 +14,16 @@ def require_oauth_user
head :unauthorized unless oauth_user_id
end

def current_user
# current_user is required by CanCanCan
oauth_user_id
end

def return404
render json: { error: '404 Not found' }, status: :not_found
head :not_found
end

def return401
head :unauthorized
end
end
11 changes: 11 additions & 0 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class Ability
include CanCan::Ability

def initialize(user)
return if user.blank?

can :update, Project, user_id: user
end
end
6 changes: 2 additions & 4 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@
get '/python', to: 'default_projects#python'
end

namespace :projects do
resources :phrases, only: %i[show update] do
resource :remix, only: %i[create]
end
resources :projects, only: %i[show update] do
resource :remix, only: %i[create], controller: 'projects/remixes'
end
end
end
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ services:
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3009 -b '0.0.0.0'"
volumes:
- .:/app
- bundle:/usr/local/bundle
ports:
- "3009:3009"
depends_on:
Expand All @@ -22,4 +23,5 @@ services:
tty: true

volumes:
pg-data:
pg-data: null
bundle: null
4 changes: 2 additions & 2 deletions spec/concepts/project/operation/create_remix_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

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

it 'returns success' do
result = create_remix
Expand Down Expand Up @@ -54,7 +54,7 @@

context 'when user_id is not present' do
let(:user_id) { nil }
let(:params) { { phrase_id: original_project.identifier } }
let(:params) { { project_id: original_project.identifier } }

it 'returns failure' do
result = create_remix
Expand Down
6 changes: 3 additions & 3 deletions spec/request/projects/remix_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@
end

it 'returns success response' do
post "/api/projects/phrases/#{original_project.identifier}/remix"
post "/api/projects/#{original_project.identifier}/remix"

expect(response.status).to eq(200)
end

it 'returns 404 response if invalid project' do
post '/api/projects/phrases/no-such-project/remix'
post '/api/projects/no-such-project/remix'

expect(response.status).to eq(404)
end
end

context 'when auth is invalid' do
it 'returns unauthorized' do
post "/api/projects/phrases/#{original_project.identifier}/remix"
post "/api/projects/#{original_project.identifier}/remix"

expect(response.status).to eq(401)
end
Expand Down
38 changes: 38 additions & 0 deletions spec/request/projects/show_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe 'Project show requests', type: :request do
let!(:project) { create(:project) }
let(:expected_json) do
{
identifier: project.identifier,
project_type: 'python',
name: project.name,
user_id: project.user_id,
components: []
}.to_json
end

it 'returns success response' do
get "/api/projects/#{project.identifier}"

expect(response.status).to eq(200)
end

it 'returns json' do
get "/api/projects/#{project.identifier}"
expect(response.content_type).to eq('application/json; charset=utf-8')
end

it 'returns the project json' do
get "/api/projects/#{project.identifier}"
expect(response.body).to eq(expected_json)
end

it 'returns 404 response if invalid project' do
get '/api/projects/no-such-project'

expect(response.status).to eq(404)
end
end
52 changes: 52 additions & 0 deletions spec/request/projects/update_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe 'Project update requests', type: :request do
let(:user_id) { 'e0675b6c-dc48-4cd6-8c04-0f7ac05af51a' }
let(:project) { create(:project, user_id: user_id) }

context 'when authed user is project creator' do
let(:project) { create(:project, user_id: user_id) }
let!(:component) { create(:component, project: project) }
let(:changes) { { name: 'updated', extension: 'py', content: 'updated content' } }
let(:params) { { project: { components: [{ id: component.id, **changes }] } } }

before do
mock_oauth_user(user_id)
end

it 'returns success response' do
put "/api/projects/#{project.identifier}", params: params
expect(response.status).to eq(200)
end

it 'updates component' do
put "/api/projects/#{project.identifier}", params: params
updated = component.reload.attributes.symbolize_keys.slice(:name, :content, :extension)
expect(updated).to eq(changes)
end
end

context 'when authed user is not creator' do
let(:project) { create(:project) }
let(:params) { { project: { components: [] } } }

before do
mock_oauth_user(user_id)
end

it 'returns unauthorized response' do
put "/api/projects/#{project.identifier}", params: params
expect(response.status).to eq(401)
end
end

context 'when auth is invalid' do
it 'returns unauthorized' do
put "/api/projects/#{project.identifier}"

expect(response.status).to eq(401)
end
end
end