From 49875a863d4dfa9a2574d1f7dfd598f6e25049b9 Mon Sep 17 00:00:00 2001 From: Karel Picman Date: Fri, 27 Apr 2018 09:04:02 +0200 Subject: [PATCH] REST API and delete Folder/document #847 --- app/controllers/dmsf_controller.rb | 18 ++++--- app/controllers/dmsf_files_controller.rb | 18 ++++--- app/views/dmsf/show.html.erb | 2 +- extra/api/api_client.sh | 26 ++++++--- .../rest_api/dmsf_file_api_test.rb | 51 +++++++++++++++++- .../rest_api/dmsf_folder_api_test.rb | 54 ++++++++++++++++++- 6 files changed, 148 insertions(+), 21 deletions(-) diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb index e039712d..86061405 100644 --- a/app/controllers/dmsf_controller.rb +++ b/app/controllers/dmsf_controller.rb @@ -30,7 +30,7 @@ class DmsfController < ApplicationController before_action :tree_view, :only => [:delete, :show] before_action :permissions - accept_api_auth :show, :create, :save + accept_api_auth :show, :create, :save, :delete skip_before_action :verify_authenticity_token, if: -> { request.headers['HTTP_X_REDMINE_API_KEY'].present? } @@ -251,15 +251,21 @@ class DmsfController < ApplicationController def delete commit = params[:commit] == 'yes' - if @folder.delete(commit) + result = @folder.delete(commit) + if result flash[:notice] = l(:notice_folder_deleted) else flash[:error] = @folder.errors.full_messages.to_sentence end - if commit || @tree_view - redirect_to :back - else - redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder.dmsf_folder) + respond_to do |format| + format.html { + if commit || @tree_view + redirect_to :back + else + redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder.dmsf_folder) + end + } + format.api { result ? render_api_ok : render_validation_errors(@folder) } end end diff --git a/app/controllers/dmsf_files_controller.rb b/app/controllers/dmsf_files_controller.rb index caaa2292..f48050bf 100644 --- a/app/controllers/dmsf_files_controller.rb +++ b/app/controllers/dmsf_files_controller.rb @@ -29,7 +29,7 @@ class DmsfFilesController < ApplicationController before_action :tree_view, :only => [:delete] before_action :permissions - accept_api_auth :show, :view + accept_api_auth :show, :view, :delete helper :all helper :dmsf_workflows @@ -191,7 +191,8 @@ class DmsfFilesController < ApplicationController def delete if @file commit = params[:commit] == 'yes' - if @file.delete(commit) + result = @file.delete(commit) + if result flash[:notice] = l(:notice_file_deleted) unless commit begin @@ -216,10 +217,15 @@ class DmsfFilesController < ApplicationController Rails.logger.error msg end end - if commit || (@tree_view && params[:details].blank?) - redirect_to :back - else - redirect_to dmsf_folder_path(:id => @project, :folder_id => @file.dmsf_folder) + respond_to do |format| + format.html { + if commit || (@tree_view && params[:details].blank?) + redirect_to :back + else + redirect_to dmsf_folder_path(:id => @project, :folder_id => @file.dmsf_folder) + end + } + format.api { result ? render_api_ok : render_validation_errors(@file) } end end diff --git a/app/views/dmsf/show.html.erb b/app/views/dmsf/show.html.erb index c2ce2188..094eee2f 100644 --- a/app/views/dmsf/show.html.erb +++ b/app/views/dmsf/show.html.erb @@ -221,7 +221,7 @@ <% unless @system_folder %> <% other_formats_links do |f| %> - <%= f.link_to 'CSV', :url => params, :onclick => "showModal('csv-export-options', '350px'); return false;" %> + <%= f.link_to 'CSV', :onclick => "showModal('csv-export-options', '350px'); return false;" %> <% end %> <% end %> diff --git a/extra/api/api_client.sh b/extra/api/api_client.sh index 9abbbbf6..be4eb78d 100644 --- a/extra/api/api_client.sh +++ b/extra/api/api_client.sh @@ -26,9 +26,11 @@ # BOTH XML and JSON formats are supported. # Just replace .xml with .json +# +# Uncomment a corresponding line to the case you would like to test # 1. List of documents in a given folder or the root folder -curl -v -H "Content-Type: application/xml" -X GET -u ${1}:${2} http://localhost:3000/projects/12/dmsf.xml +#curl -v -H "Content-Type: application/xml" -X GET -u ${1}:${2} http://localhost:3000/projects/12/dmsf.xml #curl -v -H "Content-Type: application/xml" -X GET -u ${1}:${2} http://localhost:3000/projects/12/dmsf.xml?folder_id=5155 # 2. Get a document @@ -42,11 +44,11 @@ curl -v -H "Content-Type: application/xml" -X GET -u ${1}:${2} http://localhost: #curl --data-binary "@cat.gif" -H "Content-Type: application/octet-stream" -X POST -u ${1}:${2} http://localhost:3000/projects/12/dmsf/upload.xml?filename=cat.gif #curl -v -H "Content-Type: application/xml" -X POST --data "@file.xml" -u ${1}:${2} http://localhost:3000/projects/12/dmsf/commit.xml -# 5. list folder contents & check folder existence (by folder title) +# 5. List folder content & check folder existence (by folder title) # curl -v -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/projects/1/dmsf.json?folder_title=Updated%20title -# 6. list folder contents & check folder existence (by folder id) -# curl -v -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: USERS_API_KE" http://localhost:3000/projects/1/dmsf.json?folder_id=3 +# 6. List folder content & check folder existence (by folder id) +# curl -v -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/projects/1/dmsf.json?folder_id=3 # both returns 404 not found, or json with following structure: # { # "dmsf":{ @@ -67,7 +69,7 @@ curl -v -H "Content-Type: application/xml" -X GET -u ${1}:${2} http://localhost: # } #} -# 7. update folder +# 7. Update a folder # curl -v -H "Content-Type: application/json" -X POST --data "@update-folder-payload.json" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000//projects/#{project_id}/dmsf/save.json?folder_id=#{folder_id} # update-folder-payload.json @@ -76,4 +78,16 @@ curl -v -H "Content-Type: application/xml" -X GET -u ${1}:${2} http://localhost: # "title": title, # "description": description # }, -# } \ No newline at end of file +# } + +# 8. Delete a folder +# a) Move to trash only +# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/projects/2387/dmsf/delete.xml?folder_id=#{folder_id} +# b) Delete permanently +# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} "http://localhost:3000/projects/2387/dmsf/delete.xml?folder_id=#{folder_id}&commit=yes" + +# 8. Delete a file +# a) Move to trash only +# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml +# b) Delete permanently +# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml?commit=yes" \ No newline at end of file diff --git a/test/integration/rest_api/dmsf_file_api_test.rb b/test/integration/rest_api/dmsf_file_api_test.rb index 719e8d2e..dd4cd11c 100644 --- a/test/integration/rest_api/dmsf_file_api_test.rb +++ b/test/integration/rest_api/dmsf_file_api_test.rb @@ -21,10 +21,12 @@ require File.expand_path('../../../test_helper', __FILE__) class DmsfFileApiTest < RedmineDmsf::Test::IntegrationTest + include Redmine::I18n fixtures :projects, :users, :dmsf_files, :dmsf_file_revisions, :members, :roles, :member_roles def setup + @admin = User.find_by_id 1 @jsmith = User.find_by_id 2 @file1 = DmsfFile.find_by_id 1 Setting.rest_api_enabled = '1' @@ -34,6 +36,7 @@ class DmsfFileApiTest < RedmineDmsf::Test::IntegrationTest end def test_truth + assert_kind_of User, @admin assert_kind_of User, @jsmith assert_kind_of DmsfFile, @file1 assert_kind_of Role, @role @@ -109,7 +112,7 @@ class DmsfFileApiTest < RedmineDmsf::Test::IntegrationTest } assert_difference 'DmsfFileRevision.count', +1 do - post "/projects/#{@project1.id}/dmsf/commit.xml?&key=#{token.value}", payload, {"CONTENT_TYPE" => 'application/xml'} + post "/projects/#{@project1.id}/dmsf/commit.xml?key=#{token.value}", payload, {"CONTENT_TYPE" => 'application/xml'} end # # @@ -128,4 +131,50 @@ class DmsfFileApiTest < RedmineDmsf::Test::IntegrationTest error e.message end end + + def test_delete_file + @role.add_permission! :file_delete + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml + delete "/dmsf/files/#{@file1.id}.xml?key=#{token.value}", {'CONTENT_TYPE' => 'application/xml'} + assert_response :success + @file1.reload + assert_equal DmsfFile::STATUS_DELETED, @file1.deleted + assert_equal User.current, @file1.deleted_by_user + end + + def test_delete_file_no_permissions + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml + delete "/dmsf/files/#{@file1.id}.xml?key=#{token.value}", {'CONTENT_TYPE' => 'application/xml'} + assert_response :forbidden + end + + def test_delete_folder_commit_yes + @role.add_permission! :file_delete + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml&commit=yes + delete "/dmsf/files/#{@file1.id}.xml?key=#{token.value}&commit=yes", {'CONTENT_TYPE' => 'application/xml'} + assert_response :success + assert_nil DmsfFile.find_by_id(@file1.id) + end + + def test_delete_file_locked + @role.add_permission! :file_delete + User.current = @admin + @file1.lock! + User.current = @jsmith + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml + delete "/dmsf/files/#{@file1.id}.xml?key=#{token.value}", {'CONTENT_TYPE' => 'application/xml'} + assert_response 422 + # + # + # Locked by Admin + # + assert_select 'errors > error', :text => l(:title_locked_by_user, user: @admin.name) + @file1.reload + assert_equal DmsfFile::STATUS_ACTIVE, @file1.deleted + end + end \ No newline at end of file diff --git a/test/integration/rest_api/dmsf_folder_api_test.rb b/test/integration/rest_api/dmsf_folder_api_test.rb index c1b98d8c..cb901f52 100644 --- a/test/integration/rest_api/dmsf_folder_api_test.rb +++ b/test/integration/rest_api/dmsf_folder_api_test.rb @@ -21,12 +21,14 @@ require File.expand_path('../../../test_helper', __FILE__) class DmsfFolderApiTest < RedmineDmsf::Test::IntegrationTest + include Redmine::I18n fixtures :dmsf_folders, :dmsf_files, :dmsf_file_revisions, :projects, :users, :members, :roles, :member_roles def setup Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.expand_path '../../../fixtures/files', __FILE__ + @admin = User.find_by_id 1 @jsmith = User.find_by_id 2 @file1 = DmsfFile.find_by_id 1 @folder1 = DmsfFolder.find_by_id 1 @@ -37,6 +39,7 @@ class DmsfFolderApiTest < RedmineDmsf::Test::IntegrationTest end def test_truth + assert_kind_of User, @admin assert_kind_of User, @jsmith assert_kind_of DmsfFolder, @folder1 assert_kind_of DmsfFile, @file1 @@ -91,7 +94,7 @@ class DmsfFolderApiTest < RedmineDmsf::Test::IntegrationTest } - post "/projects/#{@project1.id}/dmsf/create.xml?&key=#{token.value}", payload, {'CONTENT_TYPE' => 'application/xml'} + post "/projects/#{@project1.id}/dmsf/create.xml?key=#{token.value}", payload, {'CONTENT_TYPE' => 'application/xml'} assert_response :success # # @@ -195,4 +198,53 @@ class DmsfFolderApiTest < RedmineDmsf::Test::IntegrationTest assert_select 'dmsf_folder > title', :text => 'rest_api' end + def test_delete_folder + @role.add_permission! :folder_manipulation + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/projects/1/dmsf/delete.xml?folder_id=3 + delete "/projects/#{@project1.id}/dmsf/delete.xml?key=#{token.value}&folder_id=#{@folder1.id}", + {'CONTENT_TYPE' => 'application/xml'} + assert_response :success + @folder1.reload + assert_equal DmsfFolder::STATUS_DELETED, @folder1.deleted + assert_equal User.current, @folder1.deleted_by_user + end + + def test_delete_folder_no_permission + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/projects/1/dmsf/delete.xml?folder_id=3 + delete "/projects/#{@project1.id}/dmsf/delete.xml?key=#{token.value}&folder_id=#{@folder1.id}", + {'CONTENT_TYPE' => 'application/xml'} + assert_response :forbidden + end + + def test_delete_folder_commit_yes + @role.add_permission! :folder_manipulation + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/projects/1/dmsf/delete.xml?folder_id=3 + delete "/projects/#{@project1.id}/dmsf/delete.xml?key=#{token.value}&folder_id=#{@folder1.id}&commit=yes", + {'CONTENT_TYPE' => 'application/xml'} + assert_response :success + assert_nil DmsfFolder.find_by_id(@folder1.id) + end + + def test_delete_folder_locked + @role.add_permission! :folder_manipulation + User.current = @admin + @folder1.lock! + User.current = @jsmith + token = Token.create!(:user => @jsmith, :action => 'api') + # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/projects/1/dmsf/delete.xml?folder_id=3 + delete "/projects/#{@project1.id}/dmsf/delete.xml?key=#{token.value}&folder_id=#{@folder1.id}", + {'CONTENT_TYPE' => 'application/xml'} + assert_response 422 + # + # + # Folder is locked + # + assert_select 'errors > error', :text => l(:error_folder_is_locked) + @folder1.reload + assert_equal DmsfFolder::STATUS_ACTIVE, @folder1.deleted + end + end \ No newline at end of file