diff --git a/app/controllers/api/v1/assets_controller.rb b/app/controllers/api/v1/assets_controller.rb new file mode 100644 index 000000000..c2354715a --- /dev/null +++ b/app/controllers/api/v1/assets_controller.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Api + module V1 + class AssetsController < BaseController + before_action :load_team, :load_project, :load_experiment, :load_task, :load_protocol, :load_step + before_action :load_asset, only: :show + + def index + attachments = @step.assets + .page(params.dig(:page, :number)) + .per(params.dig(:page, :size)) + + render jsonapi: attachments, each_serializer: AssetSerializer + end + + def show + render jsonapi: @asset, serializer: AssetSerializer + end + + def create + raise PermissionError.new(Asset, :create) unless can_manage_protocol_in_module?(@protocol) + + asset = @step.assets.create!(asset_params) + + asset.reload + + render jsonapi: asset, + serializer: AssetSerializer, + status: :created + end + + private + + def asset_params + raise TypeError unless params.require(:data).require(:type) == 'attachments' + + attr_list = %i(file) + params.require(:data).require(:attributes).require(attr_list) + params.require(:data).require(:attributes).permit(attr_list) + end + + def load_asset + @asset = @step.assets.find(params.require(:id)) + end + end + end +end diff --git a/app/serializers/api/v1/asset_serializer.rb b/app/serializers/api/v1/asset_serializer.rb new file mode 100644 index 000000000..c603ef0f9 --- /dev/null +++ b/app/serializers/api/v1/asset_serializer.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Api + module V1 + class AssetSerializer < ActiveModel::Serializer + include Rails.application.routes.url_helpers + + type :attachments + attributes :id, :file_name, :file_size, :file_type, :file_url + belongs_to :step, serializer: StepSerializer + + delegate :file_name, to: :object + delegate :file_size, to: :object + + def file_type + object.content_type + end + + def file_url + rails_blob_url(object.file, disposition: 'attachment') if object.file.attached? + end + end + end +end diff --git a/app/serializers/api/v1/step_serializer.rb b/app/serializers/api/v1/step_serializer.rb index 1a4ff7553..9a6b3c285 100644 --- a/app/serializers/api/v1/step_serializer.rb +++ b/app/serializers/api/v1/step_serializer.rb @@ -7,6 +7,7 @@ module Api attributes :id, :name, :description, :position, :completed attribute :completed_on, if: :completed? belongs_to :protocol, serializer: ProtocolSerializer + has_many :assets, serializer: AssetSerializer def completed? object.completed diff --git a/config/routes.rb b/config/routes.rb index 7076531c8..682def9db 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -666,7 +666,9 @@ Rails.application.routes.draw do path: 'tags', as: :tags resources :protocols, only: %i(index) do - resources :steps, only: %i(index show create) + resources :steps, only: %i(index show create) do + resources :assets, only: %i(index show create), path: 'attachments' + end end resources :results, only: %i(index create show) get 'activities', to: 'tasks#activities' diff --git a/spec/requests/api/v1/assets_controller_spec.rb b/spec/requests/api/v1/assets_controller_spec.rb new file mode 100644 index 000000000..bc2403ba4 --- /dev/null +++ b/spec/requests/api/v1/assets_controller_spec.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Api::V1::AssetsController', type: :request do + before :all do + @user = create(:user) + @team = create(:team, created_by: @user) + @project = create(:project, team: @team) + @experiment = create(:experiment, :with_tasks, project: @project) + @task = @experiment.my_modules.first + @protocol = create(:protocol, my_module: @task) + @step = create(:step, protocol: @protocol) + + create(:user_team, user: @user, team: @team) + create(:user_project, :normal_user, user: @user, project: @project) + + @valid_headers = + { 'Authorization': 'Bearer ' + generate_token(@user.id) } + end + + let(:asset) { create :asset, step: @step } + + describe 'GET steps, #index' do + context 'when has valid params' do + it 'renders 200' do + create(:step_asset, step: @step, asset: asset) + + get(api_v1_team_project_experiment_task_protocol_step_assets_path( + team_id: @team.id, + project_id: @project.id, + experiment_id: @experiment.id, + task_id: @task.id, + protocol_id: @protocol.id, + step_id: @step.id + ), headers: @valid_headers) + + expect(response).to have_http_status(200) + end + end + + context 'when protocol is not found' do + it 'renders 404' do + get(api_v1_team_project_experiment_task_protocol_step_assets_path( + team_id: @team.id, + project_id: @project.id, + experiment_id: @experiment.id, + task_id: @task.id, + protocol_id: -1, + step_id: @step.id + ), headers: @valid_headers) + + expect(response).to have_http_status(404) + end + end + end + + describe 'GET step, #show' do + context 'when has valid params' do + it 'renders 200' do + create(:step_asset, step: @step, asset: asset) + + get(api_v1_team_project_experiment_task_protocol_step_asset_path( + team_id: @team.id, + project_id: @project.id, + experiment_id: @experiment.id, + task_id: @task.id, + protocol_id: @protocol.id, + step_id: @step.id, + id: asset.id + ), headers: @valid_headers) + + expect(response).to have_http_status(200) + end + end + + context 'when experiment is not found' do + it 'renders 404' do + get(api_v1_team_project_experiment_task_protocol_step_asset_path( + team_id: @team.id, + project_id: @project.id, + experiment_id: -1, + task_id: @task.id, + protocol_id: @protocol.id, + step_id: @step.id, + id: asset.id + ), headers: @valid_headers) + + expect(response).to have_http_status(404) + end + end + end + + describe 'POST step, #create' do + before :all do + @valid_headers['Content-Type'] = 'application/json' + end + + before :each do + @file = fixture_file_upload('files/test.jpg', 'image/jpg') + end + + let(:action) do + post(api_v1_team_project_experiment_task_protocol_step_assets_path( + team_id: @team.id, + project_id: @project.id, + experiment_id: @experiment.id, + task_id: @task.id, + protocol_id: @protocol.id, + step_id: @step.id + ), + params: request_body.to_json, + headers: @valid_headers) + end + + context 'when has valid params' do + let(:request_body) do + { + data: { + type: 'attachments', + attributes: { + file: @file + } + } + } + end + + it 'creates new asset' do + expect { action }.to change { Asset.count }.by(1) + end + + it 'returns status 201' do + action + + expect(response).to have_http_status 201 + end + end + + context 'when has missing param' do + let(:request_body) do + { + data: { + type: 'attachments', + attributes: { + } + } + } + end + + it 'renders 400' do + action + + expect(response).to have_http_status(400) + end + end + end +end