diff --git a/app/controllers/api/v2_alpha/notifications/accounts_controller.rb b/app/controllers/api/v2_alpha/notifications/accounts_controller.rb new file mode 100644 index 000000000..9933b6337 --- /dev/null +++ b/app/controllers/api/v2_alpha/notifications/accounts_controller.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class Api::V2Alpha::Notifications::AccountsController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:notifications' } + before_action :require_user! + before_action :set_notifications! + after_action :insert_pagination_headers, only: :index + + def index + @accounts = load_accounts + render json: @accounts, each_serializer: REST::AccountSerializer + end + + private + + def load_accounts + @paginated_notifications.map(&:from_account) + end + + def set_notifications! + @paginated_notifications = begin + current_account + .notifications + .without_suspended + .where(group_key: params[:notification_group_key]) + .includes(from_account: [:account_stat, :user]) + .paginate_by_max_id( + limit_param(DEFAULT_ACCOUNTS_LIMIT), + params[:max_id], + params[:since_id] + ) + end + end + + def next_path + api_v2_alpha_notification_accounts_url pagination_params(max_id: pagination_max_id) if records_continue? + end + + def prev_path + api_v2_alpha_notification_accounts_url pagination_params(min_id: pagination_since_id) unless @paginated_notifications.empty? + end + + def pagination_collection + @paginated_notifications + end + + def records_continue? + @paginated_notifications.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) + end +end diff --git a/app/controllers/api/v2_alpha/notifications_controller.rb b/app/controllers/api/v2_alpha/notifications_controller.rb index 13a016aeb..bd6979955 100644 --- a/app/controllers/api/v2_alpha/notifications_controller.rb +++ b/app/controllers/api/v2_alpha/notifications_controller.rb @@ -46,7 +46,7 @@ class Api::V2Alpha::NotificationsController < Api::BaseController end def show - @notification = current_account.notifications.without_suspended.find_by!(group_key: params[:id]) + @notification = current_account.notifications.without_suspended.find_by!(group_key: params[:group_key]) presenter = GroupedNotificationsPresenter.new(NotificationGroup.from_notifications([@notification])) render json: presenter, serializer: REST::DedupNotificationGroupSerializer end @@ -57,7 +57,7 @@ class Api::V2Alpha::NotificationsController < Api::BaseController end def dismiss - current_account.notifications.where(group_key: params[:id]).destroy_all + current_account.notifications.where(group_key: params[:group_key]).destroy_all render_empty end diff --git a/config/routes/api.rb b/config/routes/api.rb index c5addd338..df975065b 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -344,7 +344,7 @@ namespace :api, format: false do end namespace :v2_alpha do - resources :notifications, only: [:index, :show] do + resources :notifications, param: :group_key, only: [:index, :show] do collection do post :clear get :unread_count @@ -353,6 +353,8 @@ namespace :api, format: false do member do post :dismiss end + + resources :accounts, only: [:index], module: :notifications end end diff --git a/spec/requests/api/v2_alpha/notifications/accounts_spec.rb b/spec/requests/api/v2_alpha/notifications/accounts_spec.rb new file mode 100644 index 000000000..6a6ce043d --- /dev/null +++ b/spec/requests/api/v2_alpha/notifications/accounts_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Accounts in grouped notifications' do + let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:notifications write:notifications' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v2_alpha/notifications/:group_key/accounts', :inline_jobs do + subject do + get "/api/v2_alpha/notifications/#{user.account.notifications.first.group_key}/accounts", headers: headers, params: params + end + + let(:params) { {} } + + before do + first_status = PostStatusService.new.call(user.account, text: 'Test') + FavouriteService.new.call(Fabricate(:account), first_status) + FavouriteService.new.call(Fabricate(:account), first_status) + ReblogService.new.call(Fabricate(:account), first_status) + FollowService.new.call(Fabricate(:account), user.account) + FavouriteService.new.call(Fabricate(:account), first_status) + end + + it_behaves_like 'forbidden for wrong scope', 'write write:notifications' + + it 'returns a list of accounts' do + subject + + expect(response).to have_http_status(200) + + # The group we are interested in is only favorites + notifications = user.account.notifications.where(type: 'favourite').reorder(id: :desc) + expect(body_as_json).to match( + [ + a_hash_including( + id: notifications.first.from_account_id.to_s + ), + a_hash_including( + id: notifications.second.from_account_id.to_s + ), + a_hash_including( + id: notifications.third.from_account_id.to_s + ), + ] + ) + end + + context 'with limit param' do + let(:params) { { limit: 2 } } + + it 'returns the requested number of accounts, with pagination headers' do + subject + + expect(response).to have_http_status(200) + + # The group we are interested in is only favorites + notifications = user.account.notifications.where(type: 'favourite').reorder(id: :desc) + expect(body_as_json).to match( + [ + a_hash_including( + id: notifications.first.from_account_id.to_s + ), + a_hash_including( + id: notifications.second.from_account_id.to_s + ), + ] + ) + + expect(response) + .to include_pagination_headers( + prev: api_v2_alpha_notification_accounts_url(limit: params[:limit], min_id: notifications.first.id), + next: api_v2_alpha_notification_accounts_url(limit: params[:limit], max_id: notifications.second.id) + ) + end + end + end +end