From cc9d987321e12aee3882805c402a59f355421296 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Tue, 24 Sep 2019 12:41:45 +0200 Subject: [PATCH] Add update result(file) and create result(file) API endpoint --- app/controllers/api/v1/results_controller.rb | 72 ++++++++-- app/serializers/api/v1/asset_serializer.rb | 3 - config/routes.rb | 2 +- .../requests/api/v1/assets_controller_spec.rb | 2 +- .../api/v1/results_controller_spec.rb | 132 +++++++++++++++++- 5 files changed, 188 insertions(+), 23 deletions(-) diff --git a/app/controllers/api/v1/results_controller.rb b/app/controllers/api/v1/results_controller.rb index b4b12cc4b..12aa92e20 100644 --- a/app/controllers/api/v1/results_controller.rb +++ b/app/controllers/api/v1/results_controller.rb @@ -1,15 +1,11 @@ # frozen_string_literal: true -# rubocop:disable Metrics/LineLength module Api module V1 class ResultsController < BaseController - before_action :load_team - before_action :load_project - before_action :load_experiment - before_action :load_task - before_action :load_result, only: %i(show) - before_action :check_manage_permissions, only: %i(create) + before_action :load_team, :load_project, :load_experiment, :load_task + before_action :load_result, only: %i(show update) + before_action :check_manage_permissions, only: %i(create update) def index results = @task.results @@ -21,12 +17,31 @@ module Api def create create_text_result if result_text_params.present? + + create_file_result if !@result && result_file_params.present? + render jsonapi: @result, serializer: ResultSerializer, include: %i(text table file), status: :created end + def update + @result.attributes = result_params + + update_file_result if result_file_params.present? && @result.is_asset + update_text_result if result_text_params.present? && @result.is_text + + if (@result.changed? && @result.save!) || @asset_result_updated + render jsonapi: @result, + serializer: ResultSerializer, + include: %i(text table file), + status: :ok + else + render body: nil, status: :no_content + end + end + def show render jsonapi: @result, serializer: ResultSerializer, include: %i(text table file) @@ -39,12 +54,11 @@ module Api end def check_manage_permissions - unless can_manage_module?(@task) - raise PermissionError.new(MyModule, :manage) - end + raise PermissionError.new(MyModule, :manage) unless can_manage_module?(@task) end def create_text_result + # rubocop:disable Metrics/BlockLength: Result.transaction do @result = Result.create!( user: current_user, @@ -80,6 +94,30 @@ module Api result_text.save! end end + # rubocop:enable Metrics/BlockLength: + end + + def update_text_result + raise NotImplementedError, 'update_text_result should be implemented!' + end + + def create_file_result + Result.transaction do + @result = @task.results.create!(result_params.merge(user_id: current_user.id)) + asset = Asset.create!(result_file_params) + ResultAsset.create!(asset: asset, result: @result) + end + end + + def update_file_result + old_checksum, new_checksum = nil + Result.transaction do + old_checksum = @result.asset.file.blob.checksum + @result.asset.file.purge + @result.asset.file.attach(result_file_params[:file]) + new_checksum = @result.asset.file.blob.checksum + end + @asset_result_updated = old_checksum != new_checksum end def result_params @@ -92,10 +130,15 @@ module Api # Partially implement sideposting draft # https://github.com/json-api/json-api/pull/1197 def result_text_params - prms = - params[:included]&.select { |el| el[:type] == 'result_texts' }&.first - prms.require(:attributes).require(:text) - prms[:attributes] + prms = params[:included]&.select { |el| el[:type] == 'result_texts' }&.first + prms&.require(:attributes)&.require(:text) + prms&.dig(:attributes)&.permit(:text) + end + + def result_file_params + prms = params[:included]&.select { |el| el[:type] == 'result_files' }&.first + prms&.require(:attributes)&.require(:file) + prms&.dig(:attributes)&.permit(:file) end def tiny_mce_asset_params @@ -126,4 +169,3 @@ module Api end end end -# rubocop:enable Metrics/LineLength diff --git a/app/serializers/api/v1/asset_serializer.rb b/app/serializers/api/v1/asset_serializer.rb index c603ef0f9..ffa66a937 100644 --- a/app/serializers/api/v1/asset_serializer.rb +++ b/app/serializers/api/v1/asset_serializer.rb @@ -9,9 +9,6 @@ module Api 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 diff --git a/config/routes.rb b/config/routes.rb index 682def9db..596e21cea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -670,7 +670,7 @@ Rails.application.routes.draw do resources :assets, only: %i(index show create), path: 'attachments' end end - resources :results, only: %i(index create show) + resources :results, only: %i(index create show update) get 'activities', to: 'tasks#activities' end end diff --git a/spec/requests/api/v1/assets_controller_spec.rb b/spec/requests/api/v1/assets_controller_spec.rb index 2642ed17f..8369648ae 100644 --- a/spec/requests/api/v1/assets_controller_spec.rb +++ b/spec/requests/api/v1/assets_controller_spec.rb @@ -126,7 +126,7 @@ RSpec.describe 'Api::V1::AssetsController', type: :request do protocol_id: @protocol.id, step_id: @step.id ), - params: request_body.to_json, + params: request_body, headers: @valid_headers) end diff --git a/spec/requests/api/v1/results_controller_spec.rb b/spec/requests/api/v1/results_controller_spec.rb index 195274568..573ef2ab9 100644 --- a/spec/requests/api/v1/results_controller_spec.rb +++ b/spec/requests/api/v1/results_controller_spec.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -# rubocop:disable Metrics/LineLength require 'rails_helper' RSpec.describe 'Api::V1::ResultsController', type: :request do @@ -47,7 +46,7 @@ RSpec.describe 'Api::V1::ResultsController', type: :request do attributes: { name: Faker::Name.unique.name } }, - included: [ + included: [ { type: 'result_texts', attributes: { text: Faker::Lorem.sentence(word_count: 25) @@ -267,6 +266,44 @@ RSpec.describe 'Api::V1::ResultsController', type: :request do expect { hash_body = json }.not_to raise_exception expect(hash_body['errors'][0]).to include('status': 404) end + + context 'when resultType is File' do + let(:file) { fixture_file_upload('files/test.jpg', 'image/jpg') } + let(:request_body) do + { + data: { + type: 'results', + attributes: { + name: 'my result' + } + }, + included: [ + { type: 'result_files', + attributes: { + file: file + } } + ] + } + end + let(:action) do + post(api_v1_team_project_experiment_task_results_path( + team_id: @teams.first.id, + project_id: @valid_project, + experiment_id: @valid_experiment, + task_id: @valid_task + ), params: request_body, headers: @valid_headers) + end + + it 'creates new asset' do + expect { action }.to change { ResultAsset.count }.by(1) + end + + it 'returns status 201' do + action + + expect(response).to have_http_status 201 + end + end end describe 'GET result, #show' do @@ -329,5 +366,94 @@ RSpec.describe 'Api::V1::ResultsController', type: :request do expect(hash_body['errors'][0]).to include('status': 404) end end + + describe 'PUT result, #update' do + context 'when resultType is file' do + let(:result_file) { @valid_task.results.last } + let(:file) { fixture_file_upload('files/test.jpg', 'image/jpg') } + let(:request_body) do + { + data: { + type: 'results', + attributes: { + name: 'my result' + } + }, + included: [ + { type: 'result_files', + attributes: { + file: file + } } + ] + } + end + let(:action) do + put(api_v1_team_project_experiment_task_result_path( + team_id: @teams.first.id, + project_id: @valid_project, + experiment_id: @valid_experiment, + task_id: @valid_task, + id: result_file.id + ), params: request_body, headers: @valid_headers) + end + + context 'when has attributes for update' do + it 'updates tasks name' do + action + + expect(result_file.reload.name).to eq('my result') + end + + it 'returns status 200' do + action + + expect(response).to have_http_status 200 + end + end + + context 'when there is nothing to update' do + let(:request_body_with_same_name) do + { + data: { + type: 'results', + attributes: { + name: result_file.reload.name + } + } + } + end + + it 'returns 204' do + put(api_v1_team_project_experiment_task_result_path( + team_id: @teams.first.id, + project_id: @valid_project, + experiment_id: @valid_experiment, + task_id: @valid_task, + id: result_file.id + ), params: request_body_with_same_name.to_json, headers: @valid_headers) + + expect(response).to have_http_status 204 + end + end + end + + context 'when resultType is text' do + let(:result_text) { @valid_task.results.first } + let(:action) do + put(api_v1_team_project_experiment_task_result_path( + team_id: @teams.first.id, + project_id: @valid_project, + experiment_id: @valid_experiment, + task_id: @valid_task, + id: result_text.id + ), params: @valid_text_hash_body.to_json, headers: @valid_headers) + end + + it 'returns status 500' do + action + + expect(response).to have_http_status 500 + end + end + end end -# rubocop:enable Metrics/LineLength