Skip to content

Commit e8049ce

Browse files
committed
WIP: Add Api::PublicProjectsController#create
1 parent f348dfc commit e8049ce

File tree

6 files changed

+208
-0
lines changed

6 files changed

+208
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module Api
4+
class PublicProjectsController < ApiController
5+
before_action :authorize_user
6+
7+
def create
8+
result = PublicProject::Create.call(project_hash: project_params)
9+
10+
if result.success?
11+
@project = result[:project]
12+
render 'api/projects/show', formats: [:json], status: :created
13+
else
14+
render json: { error: result[:error] }, status: :unprocessable_entity
15+
end
16+
end
17+
18+
private
19+
20+
def project_params
21+
params.fetch(:project, {}).permit(:identifier, :locale, :project_type, :name)
22+
end
23+
end
24+
end

config/routes.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
resource :images, only: %i[show create], controller: 'projects/images'
4242
end
4343

44+
resources :public_projects, only: %i[create]
45+
4446
resource :project_errors, only: %i[create]
4547

4648
resource :school, only: [:show], controller: 'my_school'
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
class PublicProject
4+
class Create
5+
class << self
6+
def call(project_hash:)
7+
response = OperationResponse.new
8+
response[:project] = build_project(project_hash)
9+
response[:project].save!
10+
response
11+
rescue StandardError => e
12+
Sentry.capture_exception(e)
13+
response[:error] = "Error creating project: #{e}"
14+
response
15+
end
16+
17+
private
18+
19+
def build_project(project_hash)
20+
Project.new(project_hash)
21+
end
22+
end
23+
end
24+
end
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe PublicProject::Create, type: :unit do
6+
subject(:create_project) { described_class.call(project_hash:) }
7+
8+
describe '.call' do
9+
let(:project_hash) { ActionController::Parameters.new({}) }
10+
11+
context 'with valid content' do
12+
subject(:create_project_with_content) { described_class.call(project_hash:) }
13+
14+
let(:project_hash) do
15+
{
16+
identifier: 'foo-bar-baz',
17+
locale: 'en',
18+
project_type: Project::Types::SCRATCH,
19+
name: 'Foo bar baz'
20+
}
21+
end
22+
23+
it 'returns success' do
24+
expect(create_project_with_content.success?).to be(true)
25+
end
26+
27+
it 'returns project with identifier' do
28+
new_project = create_project_with_content[:project]
29+
expect(new_project.identifier).to eq('foo-bar-baz')
30+
end
31+
end
32+
33+
context 'when creation fails' do
34+
before do
35+
mock_project = instance_double(Project)
36+
allow(mock_project).to receive(:save!).and_raise('Some error')
37+
allow(Project).to receive(:new).and_return(mock_project)
38+
allow(Sentry).to receive(:capture_exception)
39+
end
40+
41+
it 'returns failure' do
42+
expect(create_project.failure?).to be(true)
43+
end
44+
45+
it 'returns error message' do
46+
expect(create_project[:error]).to eq('Error creating project: Some error')
47+
end
48+
49+
it 'sent the exception to Sentry' do
50+
create_project
51+
expect(Sentry).to have_received(:capture_exception).with(kind_of(StandardError))
52+
end
53+
end
54+
end
55+
end
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe 'Creating a public project', type: :request do
6+
let(:creator) { build(:user) }
7+
let(:headers) { { Authorization: UserProfileMock::TOKEN } }
8+
let(:params) do
9+
{
10+
project: {
11+
identifier: 'test-project',
12+
locale: 'en',
13+
project_type: Project::Types::SCRATCH,
14+
name: 'Test Project'
15+
}
16+
}
17+
end
18+
19+
before do
20+
authenticated_in_hydra_as(creator)
21+
end
22+
23+
it 'responds 201 Created' do
24+
post('/api/public_projects', headers:, params:)
25+
expect(response).to have_http_status(:created)
26+
end
27+
28+
it 'responds with the project JSON' do
29+
post('/api/public_projects', headers:, params:)
30+
data = JSON.parse(response.body, symbolize_names: true)
31+
32+
expect(data).to include(
33+
{
34+
identifier: 'test-project',
35+
locale: 'en',
36+
project_type: Project::Types::SCRATCH,
37+
name: 'Test Project'
38+
}
39+
)
40+
end
41+
42+
it 'responds 422 Unprocessable Entity when params are invalid' do
43+
post('/api/public_projects', headers:, params: { project: {} })
44+
expect(response).to have_http_status(:unprocessable_entity)
45+
end
46+
47+
it 'responds 401 Unauthorized when no token is given' do
48+
post('/api/public_projects', params:)
49+
expect(response).to have_http_status(:unauthorized)
50+
end
51+
end
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe 'Create public project requests' do
6+
let(:project) { create(:project) }
7+
let(:creator) { build(:user) }
8+
9+
context 'when auth is correct' do
10+
let(:headers) { { Authorization: UserProfileMock::TOKEN } }
11+
12+
context 'when creating project is successful' do
13+
before do
14+
authenticated_in_hydra_as(creator)
15+
16+
response = OperationResponse.new
17+
response[:project] = project
18+
allow(PublicProject::Create).to receive(:call).and_return(response)
19+
end
20+
21+
it 'returns success' do
22+
post('/api/public_projects', headers:)
23+
24+
expect(response).to have_http_status(:created)
25+
end
26+
end
27+
28+
context 'when creating project fails' do
29+
before do
30+
authenticated_in_hydra_as(creator)
31+
32+
response = OperationResponse.new
33+
response[:error] = 'Error creating project'
34+
allow(PublicProject::Create).to receive(:call).and_return(response)
35+
end
36+
37+
it 'returns error' do
38+
post('/api/public_projects', headers:)
39+
40+
expect(response).to have_http_status(:unprocessable_entity)
41+
end
42+
end
43+
end
44+
45+
context 'when no token is given' do
46+
it 'returns unauthorized' do
47+
post('/api/public_projects')
48+
49+
expect(response).to have_http_status(:unauthorized)
50+
end
51+
end
52+
end

0 commit comments

Comments
 (0)