diff --git a/app/abilities/api_capability.rb b/app/abilities/api_capability.rb index d8be136438..0e953d50b1 100644 --- a/app/abilities/api_capability.rb +++ b/app/abilities/api_capability.rb @@ -9,6 +9,7 @@ def initialize(token) if user&.active? can [:create, :comment, :close, :reopen], Note if scope?(token, :write_notes) + can [:create, :destroy], NoteSubscription if scope?(token, :write_notes) can [:show, :data], Trace if scope?(token, :read_gpx) can [:create, :update, :destroy], Trace if scope?(token, :write_gpx) can [:details], User if scope?(token, :read_prefs) diff --git a/app/controllers/api/note_subscriptions_controller.rb b/app/controllers/api/note_subscriptions_controller.rb new file mode 100644 index 0000000000..c416dd8036 --- /dev/null +++ b/app/controllers/api/note_subscriptions_controller.rb @@ -0,0 +1,27 @@ +module Api + class NoteSubscriptionsController < ApiController + before_action :check_api_writable + before_action :authorize + + authorize_resource + + def create + note_id = params[:note_id].to_i + note = Note.find(note_id) + note.subscribers << current_user + rescue ActiveRecord::RecordNotFound + report_error "Note #{note_id} not found.", :not_found + rescue ActiveRecord::RecordNotUnique + report_error "You are already subscribed to note #{note_id}.", :conflict + end + + def destroy + note_id = params[:note_id].to_i + note = Note.find(note_id) + count = note.subscriptions.where(:user => current_user).delete_all + report_error "You are not subscribed to note #{note_id}.", :not_found if count.zero? + rescue ActiveRecord::RecordNotFound + report_error "Note #{note_id} not found.", :not_found + end + end +end diff --git a/config/routes.rb b/config/routes.rb index af72c457d2..f65042dd7d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -108,6 +108,8 @@ post "close" post "reopen" end + + resource :subscription, :only => [:create, :destroy], :controller => "note_subscriptions" end resources :user_blocks, :only => :show, :id => /\d+/, :controller => "user_blocks" diff --git a/test/controllers/api/note_subscriptions_controller_test.rb b/test/controllers/api/note_subscriptions_controller_test.rb new file mode 100644 index 0000000000..0e388697cc --- /dev/null +++ b/test/controllers/api/note_subscriptions_controller_test.rb @@ -0,0 +1,142 @@ +require "test_helper" + +module Api + class NoteSubscriptionsControllerTest < ActionDispatch::IntegrationTest + def test_routes + assert_routing( + { :path => "/api/0.6/notes/1/subscription", :method => :post }, + { :controller => "api/note_subscriptions", :action => "create", :note_id => "1" } + ) + assert_routing( + { :path => "/api/0.6/notes/1/subscription", :method => :delete }, + { :controller => "api/note_subscriptions", :action => "destroy", :note_id => "1" } + ) + end + + def test_create + user = create(:user) + auth_header = bearer_authorization_header user + note = create(:note_with_comments) + assert_empty note.subscribers + + assert_difference "NoteSubscription.count", 1 do + assert_difference "note.subscribers.count", 1 do + post api_note_subscription_path(note), :headers => auth_header + assert_response :success + end + end + assert_equal user, note.subscribers.last + end + + def test_create_fail_anonymous + note = create(:note_with_comments) + + assert_no_difference "NoteSubscription.count" do + assert_no_difference "note.subscribers.count" do + post api_note_subscription_path(note) + assert_response :unauthorized + end + end + end + + def test_create_fail_no_scope + user = create(:user) + auth_header = bearer_authorization_header user, :scopes => %w[read_prefs] + note = create(:note_with_comments) + + assert_no_difference "NoteSubscription.count" do + assert_no_difference "note.subscribers.count" do + post api_note_subscription_path(note), :headers => auth_header + assert_response :forbidden + end + end + end + + def test_create_fail_note_not_found + user = create(:user) + auth_header = bearer_authorization_header user + + assert_no_difference "NoteSubscription.count" do + post api_note_subscription_path(999111), :headers => auth_header + assert_response :not_found + end + assert_match "not found", @response.body + end + + def test_create_fail_already_subscribed + user = create(:user) + auth_header = bearer_authorization_header user + note = create(:note_with_comments) + create(:note_subscription, :user => user, :note => note) + + assert_no_difference "NoteSubscription.count" do + assert_no_difference "note.subscribers.count" do + post api_note_subscription_path(note), :headers => auth_header + assert_response :conflict + end + end + assert_match "already subscribed", @response.body + end + + def test_destroy + user = create(:user) + auth_header = bearer_authorization_header user + other_user = create(:user) + note = create(:note_with_comments) + other_note = create(:note_with_comments) + create(:note_subscription, :user => user, :note => note) + create(:note_subscription, :user => other_user, :note => note) + create(:note_subscription, :user => user, :note => other_note) + + assert_difference "NoteSubscription.count", -1 do + assert_difference "note.subscribers.count", -1 do + delete api_note_subscription_path(note), :headers => auth_header + assert_response :success + end + end + note.reload + assert_equal [other_user], note.subscribers + assert_equal [user], other_note.subscribers + end + + def test_destroy_fail_anonymous + note = create(:note_with_comments) + + delete api_note_subscription_path(note) + assert_response :unauthorized + end + + def test_destroy_fail_no_scope + user = create(:user) + auth_header = bearer_authorization_header user, :scopes => %w[read_prefs] + note = create(:note_with_comments) + create(:note_subscription, :user => user, :note => note) + + assert_no_difference "NoteSubscription.count" do + assert_no_difference "note.subscribers.count" do + delete api_note_subscription_path(note), :headers => auth_header + assert_response :forbidden + end + end + end + + def test_destroy_fail_note_not_found + user = create(:user) + auth_header = bearer_authorization_header user + + delete api_note_subscription_path(999111), :headers => auth_header + assert_response :not_found + assert_match "not found", @response.body + end + + def test_destroy_fail_not_subscribed + user = create(:user) + auth_header = bearer_authorization_header user + note = create(:note_with_comments) + + delete api_note_subscription_path(note), :headers => auth_header + assert_response :not_found + assert_match "not subscribed", @response.body + end + end +end