mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-14 08:19:05 +00:00
First draft of API to get a single collection (#37053)
This commit is contained in:
@@ -9,7 +9,14 @@ class Api::V1Alpha::CollectionsController < Api::BaseController
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:collections' }, only: [:create]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :require_user!, only: [:create]
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
@collection = Collection.find(params[:id])
|
||||
|
||||
render json: @collection, serializer: REST::CollectionSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@collection = CreateCollectionService.new.call(collection_params, current_user.account)
|
||||
|
||||
@@ -38,10 +38,18 @@ class Collection < ApplicationRecord
|
||||
validate :tag_is_usable
|
||||
validate :items_do_not_exceed_limit
|
||||
|
||||
scope :with_items, -> { includes(:collection_items).merge(CollectionItem.with_accounts) }
|
||||
|
||||
def remote?
|
||||
!local?
|
||||
end
|
||||
|
||||
def items_for(account = nil)
|
||||
result = collection_items.with_accounts
|
||||
result = result.not_blocked_by(account) unless account.nil?
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_is_usable
|
||||
|
||||
@@ -33,6 +33,8 @@ class CollectionItem < ApplicationRecord
|
||||
validates :object_uri, presence: true, if: -> { account.nil? }
|
||||
|
||||
scope :ordered, -> { order(position: :asc) }
|
||||
scope :with_accounts, -> { includes(account: [:account_stat, :user]) }
|
||||
scope :not_blocked_by, ->(account) { where.not(accounts: { id: account.blocking }) }
|
||||
|
||||
def local_item_with_remote_account?
|
||||
local? && account&.remote?
|
||||
|
||||
9
app/serializers/rest/collection_item_serializer.rb
Normal file
9
app/serializers/rest/collection_item_serializer.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class REST::CollectionItemSerializer < ActiveModel::Serializer
|
||||
delegate :accepted?, to: :object
|
||||
|
||||
attributes :position, :state
|
||||
|
||||
belongs_to :account, serializer: REST::AccountSerializer, if: :accepted?
|
||||
end
|
||||
@@ -5,4 +5,11 @@ class REST::CollectionSerializer < ActiveModel::Serializer
|
||||
:created_at, :updated_at
|
||||
|
||||
belongs_to :account, serializer: REST::AccountSerializer
|
||||
belongs_to :tag, serializer: REST::StatusSerializer::TagSerializer
|
||||
|
||||
has_many :items, serializer: REST::CollectionItemSerializer
|
||||
|
||||
def items
|
||||
object.items_for(current_user&.account)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace :api, format: false do
|
||||
namespace :v1_alpha do
|
||||
resources :async_refreshes, only: :show
|
||||
|
||||
resources :collections, only: [:create]
|
||||
resources :collections, only: [:show, :create]
|
||||
end
|
||||
|
||||
# JSON / REST API
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Fabricator(:collection_item) do
|
||||
collection { Fabricate.build(:collection) }
|
||||
account { Fabricate.build(:account) }
|
||||
position 1
|
||||
position { sequence(:position, 1) }
|
||||
state :accepted
|
||||
end
|
||||
|
||||
|
||||
@@ -48,4 +48,28 @@ RSpec.describe Collection do
|
||||
it { is_expected.to_not be_valid }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#item_for' do
|
||||
subject { Fabricate(:collection) }
|
||||
|
||||
let!(:items) { Fabricate.times(2, :collection_item, collection: subject) }
|
||||
|
||||
context 'when given no account' do
|
||||
it 'returns all items' do
|
||||
expect(subject.items_for).to match_array(items)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given an account' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
account.block!(items.first.account)
|
||||
end
|
||||
|
||||
it 'does not return items blocked by this account' do
|
||||
expect(subject.items_for(account)).to contain_exactly(items.last)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,6 +5,50 @@ require 'rails_helper'
|
||||
RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do
|
||||
include_context 'with API authentication', oauth_scopes: 'read:collections write:collections'
|
||||
|
||||
describe 'GET /api/v1_alpha/collections/:id' do
|
||||
subject do
|
||||
get "/api/v1_alpha/collections/#{collection.id}", headers: headers
|
||||
end
|
||||
|
||||
let(:collection) { Fabricate(:collection) }
|
||||
let!(:items) { Fabricate.times(2, :collection_item, collection:) }
|
||||
|
||||
shared_examples 'unfiltered, successful request' do
|
||||
it 'includes all items in the response' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.parsed_body[:items].size).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not signed in' do
|
||||
let(:headers) { {} }
|
||||
|
||||
it_behaves_like 'unfiltered, successful request'
|
||||
end
|
||||
|
||||
context 'when user is signed in' do
|
||||
context 'when the user has not blocked or muted anyone' do
|
||||
it_behaves_like 'unfiltered, successful request'
|
||||
end
|
||||
|
||||
context 'when the user has blocked an account' do
|
||||
before do
|
||||
user.account.block!(items.first.account)
|
||||
end
|
||||
|
||||
it 'only includes the non-blocked account in the response' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.parsed_body[:items].size).to eq 1
|
||||
expect(response.parsed_body[:items][0]['position']).to eq items.last.position
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1_alpha/collections' do
|
||||
subject do
|
||||
post '/api/v1_alpha/collections', headers: headers, params: params
|
||||
|
||||
36
spec/serializers/rest/collection_item_serializer_spec.rb
Normal file
36
spec/serializers/rest/collection_item_serializer_spec.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe REST::CollectionItemSerializer do
|
||||
subject { serialized_record_json(collection_item, described_class) }
|
||||
|
||||
let(:collection_item) do
|
||||
Fabricate(:collection_item,
|
||||
state:,
|
||||
position: 4)
|
||||
end
|
||||
|
||||
context 'when state is `accepted`' do
|
||||
let(:state) { :accepted }
|
||||
|
||||
it 'includes the relevant attributes including the account' do
|
||||
expect(subject)
|
||||
.to include(
|
||||
'account' => an_instance_of(Hash),
|
||||
'state' => 'accepted',
|
||||
'position' => 4
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
%i(pending rejected revoked).each do |unaccepted_state|
|
||||
context "when state is `#{unaccepted_state}`" do
|
||||
let(:state) { unaccepted_state }
|
||||
|
||||
it 'does not include an account' do
|
||||
expect(subject.keys).to_not include('account')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,15 +3,24 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe REST::CollectionSerializer do
|
||||
subject { serialized_record_json(collection, described_class) }
|
||||
subject do
|
||||
serialized_record_json(collection, described_class, options: {
|
||||
scope: current_user,
|
||||
scope_name: :current_user,
|
||||
})
|
||||
end
|
||||
|
||||
let(:current_user) { nil }
|
||||
|
||||
let(:tag) { Fabricate(:tag, name: 'discovery') }
|
||||
let(:collection) do
|
||||
Fabricate(:collection,
|
||||
name: 'Exquisite follows',
|
||||
description: 'Always worth a follow',
|
||||
local: true,
|
||||
sensitive: true,
|
||||
discoverable: false)
|
||||
discoverable: false,
|
||||
tag:)
|
||||
end
|
||||
|
||||
it 'includes the relevant attributes' do
|
||||
@@ -23,6 +32,7 @@ RSpec.describe REST::CollectionSerializer do
|
||||
'local' => true,
|
||||
'sensitive' => true,
|
||||
'discoverable' => false,
|
||||
'tag' => a_hash_including('name' => 'discovery'),
|
||||
'created_at' => match_api_datetime_format,
|
||||
'updated_at' => match_api_datetime_format
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user