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 -> { 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
|
def create
|
||||||
@collection = CreateCollectionService.new.call(collection_params, current_user.account)
|
@collection = CreateCollectionService.new.call(collection_params, current_user.account)
|
||||||
|
|||||||
@@ -38,10 +38,18 @@ class Collection < ApplicationRecord
|
|||||||
validate :tag_is_usable
|
validate :tag_is_usable
|
||||||
validate :items_do_not_exceed_limit
|
validate :items_do_not_exceed_limit
|
||||||
|
|
||||||
|
scope :with_items, -> { includes(:collection_items).merge(CollectionItem.with_accounts) }
|
||||||
|
|
||||||
def remote?
|
def remote?
|
||||||
!local?
|
!local?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def items_for(account = nil)
|
||||||
|
result = collection_items.with_accounts
|
||||||
|
result = result.not_blocked_by(account) unless account.nil?
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def tag_is_usable
|
def tag_is_usable
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ class CollectionItem < ApplicationRecord
|
|||||||
validates :object_uri, presence: true, if: -> { account.nil? }
|
validates :object_uri, presence: true, if: -> { account.nil? }
|
||||||
|
|
||||||
scope :ordered, -> { order(position: :asc) }
|
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?
|
def local_item_with_remote_account?
|
||||||
local? && account&.remote?
|
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
|
:created_at, :updated_at
|
||||||
|
|
||||||
belongs_to :account, serializer: REST::AccountSerializer
|
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
|
end
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace :api, format: false do
|
|||||||
namespace :v1_alpha do
|
namespace :v1_alpha do
|
||||||
resources :async_refreshes, only: :show
|
resources :async_refreshes, only: :show
|
||||||
|
|
||||||
resources :collections, only: [:create]
|
resources :collections, only: [:show, :create]
|
||||||
end
|
end
|
||||||
|
|
||||||
# JSON / REST API
|
# JSON / REST API
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Fabricator(:collection_item) do
|
Fabricator(:collection_item) do
|
||||||
collection { Fabricate.build(:collection) }
|
collection { Fabricate.build(:collection) }
|
||||||
account { Fabricate.build(:account) }
|
account { Fabricate.build(:account) }
|
||||||
position 1
|
position { sequence(:position, 1) }
|
||||||
state :accepted
|
state :accepted
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -48,4 +48,28 @@ RSpec.describe Collection do
|
|||||||
it { is_expected.to_not be_valid }
|
it { is_expected.to_not be_valid }
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|||||||
@@ -5,6 +5,50 @@ require 'rails_helper'
|
|||||||
RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do
|
RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do
|
||||||
include_context 'with API authentication', oauth_scopes: 'read:collections write:collections'
|
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
|
describe 'POST /api/v1_alpha/collections' do
|
||||||
subject do
|
subject do
|
||||||
post '/api/v1_alpha/collections', headers: headers, params: params
|
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'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe REST::CollectionSerializer do
|
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
|
let(:collection) do
|
||||||
Fabricate(:collection,
|
Fabricate(:collection,
|
||||||
name: 'Exquisite follows',
|
name: 'Exquisite follows',
|
||||||
description: 'Always worth a follow',
|
description: 'Always worth a follow',
|
||||||
local: true,
|
local: true,
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
discoverable: false)
|
discoverable: false,
|
||||||
|
tag:)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes the relevant attributes' do
|
it 'includes the relevant attributes' do
|
||||||
@@ -23,6 +32,7 @@ RSpec.describe REST::CollectionSerializer do
|
|||||||
'local' => true,
|
'local' => true,
|
||||||
'sensitive' => true,
|
'sensitive' => true,
|
||||||
'discoverable' => false,
|
'discoverable' => false,
|
||||||
|
'tag' => a_hash_including('name' => 'discovery'),
|
||||||
'created_at' => match_api_datetime_format,
|
'created_at' => match_api_datetime_format,
|
||||||
'updated_at' => match_api_datetime_format
|
'updated_at' => match_api_datetime_format
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user