Merge branch 'next-minor' into develop

This commit is contained in:
Benjamin Neff 2017-02-18 23:00:59 +01:00
commit ec2c4a8564
No known key found for this signature in database
GPG key ID: 971464C3F1A90194
12 changed files with 415 additions and 191 deletions

View file

@ -21,6 +21,7 @@
* Increase time to wait before showing the hovercard [#7319](https://github.com/diaspora/diaspora/pull/7319) * Increase time to wait before showing the hovercard [#7319](https://github.com/diaspora/diaspora/pull/7319)
* Remove some unused color-theme overrides [#7325](https://github.com/diaspora/diaspora/pull/7325) * Remove some unused color-theme overrides [#7325](https://github.com/diaspora/diaspora/pull/7325)
* Change color of author-name on hover [#7326](https://github.com/diaspora/diaspora/pull/7326) * Change color of author-name on hover [#7326](https://github.com/diaspora/diaspora/pull/7326)
* Add like and reshare services [#7337](https://github.com/diaspora/diaspora/pull/7337)
## Bug fixes ## Bug fixes
* Fix path to `bundle` in `script/server` [#7281](https://github.com/diaspora/diaspora/pull/7281) * Fix path to `bundle` in `script/server` [#7281](https://github.com/diaspora/diaspora/pull/7281)

View file

@ -11,59 +11,34 @@ class LikesController < ApplicationController
:json :json
def create def create
begin like = like_service.create(params[:post_id])
@like = if target rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
current_user.like!(target)
end
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid => e
# do nothing
end
if @like
respond_to do |format|
format.html { render :nothing => true, :status => 201 }
format.mobile { redirect_to post_path(@like.post_id) }
format.json { render :json => @like.as_api_response(:backbone), :status => 201 }
end
else
render text: I18n.t("likes.create.error"), status: 422 render text: I18n.t("likes.create.error"), status: 422
else
respond_to do |format|
format.html { render nothing: true, status: 201 }
format.mobile { redirect_to post_path(like.post_id) }
format.json { render json: like.as_api_response(:backbone), status: 201 }
end end
end end
def destroy def destroy
begin if like_service.destroy(params[:id])
@like = Like.find_by_id_and_author_id!(params[:id], current_user.person.id) render nothing: true, status: 204
rescue ActiveRecord::RecordNotFound else
render text: I18n.t("likes.destroy.error"), status: 404 render text: I18n.t("likes.destroy.error"), status: 404
return
end
current_user.retract(@like)
respond_to do |format|
format.json { render :nothing => true, :status => 204 }
end end
end end
#I can go when the old stream goes.
def index def index
@likes = target.likes.includes(:author => :profile) render json: like_service.find_for_post(params[:post_id])
@people = @likes.map(&:author) .includes(author: :profile)
.as_api_response(:backbone)
respond_to do |format|
format.all { render :layout => false }
format.json { render :json => @likes.as_api_response(:backbone) }
end
end end
private private
def target def like_service
@target ||= if params[:post_id] @like_service ||= LikeService.new(current_user)
current_user.find_visible_shareable_by_id(Post, params[:post_id]) || raise(ActiveRecord::RecordNotFound.new)
else
Comment.find(params[:comment_id]).tap do |comment|
raise(ActiveRecord::RecordNotFound.new) unless current_user.find_visible_shareable_by_id(Post, comment.commentable_id)
end
end
end end
end end

View file

@ -3,30 +3,22 @@ class ResharesController < ApplicationController
respond_to :json respond_to :json
def create def create
post = Post.where(:guid => params[:root_guid]).first reshare = reshare_service.create(params[:root_guid])
if post.is_a? Reshare rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
@reshare = current_user.build_post(:reshare, :root_guid => post.absolute_root.guid)
else
@reshare = current_user.build_post(:reshare, :root_guid => params[:root_guid])
end
if @reshare.save
current_user.dispatch_post(@reshare)
render :json => ExtremePostPresenter.new(@reshare, current_user), :status => 201
else
render text: I18n.t("reshares.create.error"), status: 422 render text: I18n.t("reshares.create.error"), status: 422
end else
render json: ExtremePostPresenter.new(reshare, current_user), status: 201
end end
def index def index
@reshares = target.reshares.includes(author: :profile) render json: reshare_service.find_for_post(params[:post_id])
render json: @reshares.as_api_response(:backbone) .includes(author: :profile)
.as_api_response(:backbone)
end end
private private
def target def reshare_service
@target ||= current_user.find_visible_shareable_by_id(Post, params[:post_id]) || @reshare_service ||= ReshareService.new(current_user)
raise(ActiveRecord::RecordNotFound.new)
end end
end end

View file

@ -0,0 +1,33 @@
class LikeService
def initialize(user=nil)
@user = user
end
def create(post_id)
post = post_service.find!(post_id)
user.like!(post)
end
def destroy(like_id)
like = Like.find(like_id)
if user.owns?(like)
user.retract(like)
true
else
false
end
end
def find_for_post(post_id)
likes = post_service.find!(post_id).likes
user ? likes.order("author_id = #{user.person.id} DESC") : likes
end
private
attr_reader :user
def post_service
@post_service ||= PostService.new(user)
end
end

View file

@ -0,0 +1,24 @@
class ReshareService
def initialize(user=nil)
@user = user
end
def create(post_id)
post = post_service.find!(post_id)
post = post.absolute_root if post.is_a? Reshare
user.reshare!(post)
end
def find_for_post(post_id)
reshares = post_service.find!(post_id).reshares
user ? reshares.order("author_id = #{user.person.id} DESC") : reshares
end
private
attr_reader :user
def post_service
@post_service ||= PostService.new(user)
end
end

View file

@ -1,6 +0,0 @@
-# Copyright (c) 2010-2011, Diaspora Inc. This file is
-# licensed under the Affero General Public License version 3 or later. See
-# the COPYRIGHT file.
- @people[0..17].each do |person|
= person_image_link(person, size: :thumb_small)

View file

@ -1 +0,0 @@
= render 'likes', :likes => @likes

View file

@ -1,8 +1,8 @@
# Copyright (c) 2010-2011, Diaspora Inc. This file is # Copyright (c) 2010-2011, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See # licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file. # the COPYRIGHT file.
describe LikesController, :type => :controller do describe LikesController, type: :controller do
before do before do
@alices_aspect = alice.aspects.where(:name => "generic").first @alices_aspect = alice.aspects.where(:name => "generic").first
@bobs_aspect = bob.aspects.where(:name => "generic").first @bobs_aspect = bob.aspects.where(:name => "generic").first
@ -10,137 +10,114 @@ describe LikesController, :type => :controller do
sign_in(alice, scope: :user) sign_in(alice, scope: :user)
end end
[Comment, Post].each do |class_const| describe "#create" do
context class_const.to_s do
let(:id_field){
"#{class_const.to_s.underscore}_id"
}
describe '#create' do
let(:like_hash) { let(:like_hash) {
{:positive => 1, {post_id: @target.id}
id_field => "#{@target.id}"}
}
let(:dislike_hash) {
{:positive => 0,
id_field => "#{@target.id}"}
} }
context "on my own post" do context "on my own post" do
it 'succeeds' do it "succeeds" do
@target = alice.post :status_message, :text => "AWESOME", :to => @alices_aspect.id @target = alice.post :status_message, text: "AWESOME", to: @alices_aspect.id
@target = alice.comment!(@target, "hey") if class_const == Comment post :create, like_hash.merge(format: :json)
post :create, like_hash.merge(:format => :json) expect(response.code).to eq("201")
expect(response.code).to eq('201')
end end
end end
context "on a post from a contact" do context "on a post from a contact" do
before do before do
@target = bob.post(:status_message, :text => "AWESOME", :to => @bobs_aspect.id) @target = bob.post(:status_message, text: "AWESOME", to: @bobs_aspect.id)
@target = bob.comment!(@target, "hey") if class_const == Comment
end end
it 'likes' do it "likes" do
post :create, like_hash post :create, like_hash
expect(response.code).to eq('201') expect(response.code).to eq("201")
end
it 'dislikes' do
post :create, dislike_hash
expect(response.code).to eq('201')
end end
it "doesn't post multiple times" do it "doesn't post multiple times" do
alice.like!(@target) alice.like!(@target)
post :create, dislike_hash post :create, like_hash
expect(response.code).to eq('422') expect(response.code).to eq("422")
end end
end end
context "on a post from a stranger" do context "on a post from a stranger" do
before do before do
@target = eve.post :status_message, :text => "AWESOME", :to => eve.aspects.first.id @target = eve.post :status_message, text: "AWESOME", to: eve.aspects.first.id
@target = eve.comment!(@target, "hey") if class_const == Comment
end end
it "doesn't post" do it "doesn't post" do
expect(alice).not_to receive(:like!) expect(alice).not_to receive(:like!)
post :create, like_hash post :create, like_hash
expect(response.code).to eq('422') expect(response.code).to eq("422")
end end
end end
context "when an the exception is raised" do context "when an the exception is raised" do
before do before do
@target = alice.post :status_message, :text => "AWESOME", :to => @alices_aspect.id @target = alice.post :status_message, text: "AWESOME", to: @alices_aspect.id
@target = alice.comment!(@target, "hey") if class_const == Comment
end end
it "should be catched when it means that the target is not found" do it "should be catched when it means that the target is not found" do
params = like_hash.merge(format: :json, id_field => -1) params = like_hash.merge(format: :json, post_id: -1)
post :create, params post :create, params
expect(response.code).to eq('422') expect(response.code).to eq("422")
end end
it "should not be catched when it is unexpected" do it "should not be catched when it is unexpected" do
@target = alice.post :status_message, :text => "AWESOME", :to => @alices_aspect.id @target = alice.post :status_message, text: "AWESOME", to: @alices_aspect.id
@target = alice.comment!(@target, "hey") if class_const == Comment
allow(alice).to receive(:like!).and_raise("something") allow(alice).to receive(:like!).and_raise("something")
allow(@controller).to receive(:current_user).and_return(alice) allow(@controller).to receive(:current_user).and_return(alice)
expect { post :create, like_hash.merge(:format => :json) }.to raise_error("something") expect { post :create, like_hash.merge(format: :json) }.to raise_error("something")
end end
end end
end end
describe '#index' do describe "#index" do
before do before do
@message = alice.post(:status_message, :text => "hey", :to => @alices_aspect.id) @message = alice.post(:status_message, text: "hey", to: @alices_aspect.id)
@message = alice.comment!(@message, "hey") if class_const == Comment
end end
it 'returns a 404 for a post not visible to the user' do it "returns a 404 for a post not visible to the user" do
sign_in eve sign_in eve
expect{get :index, id_field => @message.id}.to raise_error(ActiveRecord::RecordNotFound) expect {
get :index, post_id: @message.id
}.to raise_error(ActiveRecord::RecordNotFound)
end end
it 'returns an array of likes for a post' do it "returns an array of likes for a post" do
like = bob.like!(@message) bob.like!(@message)
get :index, id_field => @message.id get :index, post_id: @message.id
expect(assigns[:likes].map(&:id)).to eq(@message.likes.map(&:id)) expect(JSON.parse(response.body).map {|h| h["id"] }).to match_array(@message.likes.map(&:id))
end end
it 'returns an empty array for a post with no likes' do it "returns an empty array for a post with no likes" do
get :index, id_field => @message.id get :index, post_id: @message.id
expect(assigns[:likes]).to eq([]) expect(JSON.parse(response.body).map(&:id)).to eq([])
end end
end end
describe '#destroy' do describe "#destroy" do
before do before do
@message = bob.post(:status_message, :text => "hey", :to => @alices_aspect.id) @message = bob.post(:status_message, text: "hey", to: @alices_aspect.id)
@message = bob.comment!(@message, "hey") if class_const == Comment
@like = alice.like!(@message) @like = alice.like!(@message)
end end
it 'lets a user destroy their like' do it "lets a user destroy their like" do
current_user = controller.send(:current_user) current_user = controller.send(:current_user)
expect(current_user).to receive(:retract).with(@like) expect(current_user).to receive(:retract).with(@like)
delete :destroy, :format => :json, id_field => @like.target_id, :id => @like.id delete :destroy, format: :json, post_id: @message.id, id: @like.id
expect(response.status).to eq(204) expect(response.status).to eq(204)
end end
it 'does not let a user destroy other likes' do it "does not let a user destroy other likes" do
like2 = eve.like!(@message) like2 = eve.like!(@message)
like_count = Like.count like_count = Like.count
delete :destroy, :format => :json, id_field => like2.target_id, :id => like2.id delete :destroy, format: :json, post_id: @message.id, id: like2.id
expect(response.status).to eq(404) expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("likes.destroy.error")) expect(response.body).to eq(I18n.t("likes.destroy.error"))
expect(Like.count).to eq(like_count) expect(Like.count).to eq(like_count)
end end
end end
end
end
end end

View file

@ -31,8 +31,9 @@ describe ResharesController, :type => :controller do
}.to change(Reshare, :count).by(1) }.to change(Reshare, :count).by(1)
end end
it 'calls dispatch' do it "federates" do
expect(bob).to receive(:dispatch_post) allow_any_instance_of(Participation::Generator).to receive(:create!)
expect(Diaspora::Federation::Dispatcher).to receive(:defer_dispatch)
post_request! post_request!
end end

View file

@ -26,7 +26,7 @@ describe CommentService do
it "fail if the user can not see the post" do it "fail if the user can not see the post" do
expect { expect {
CommentService.new(eve).create("unknown id", "hi") CommentService.new(eve).create(post.id, "hi")
}.to raise_error ActiveRecord::RecordNotFound }.to raise_error ActiveRecord::RecordNotFound
end end
end end

View file

@ -0,0 +1,121 @@
describe LikeService do
let(:post) { alice.post(:status_message, text: "hello", to: alice.aspects.first) }
describe "#create" do
it "creates a like on my own post" do
expect {
LikeService.new(alice).create(post.id)
}.not_to raise_error
end
it "creates a like on a post of a contact" do
expect {
LikeService.new(bob).create(post.id)
}.not_to raise_error
end
it "attaches the like to the post" do
like = LikeService.new(alice).create(post.id)
expect(post.likes.first.id).to eq(like.id)
end
it "fails if the post does not exist" do
expect {
LikeService.new(bob).create("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
end
it "fails if the user can't see the post" do
expect {
LikeService.new(eve).create(post.id)
}.to raise_error ActiveRecord::RecordNotFound
end
it "fails if the user already liked the post" do
LikeService.new(alice).create(post.id)
expect {
LikeService.new(alice).create(post.id)
}.to raise_error ActiveRecord::RecordInvalid
end
end
describe "#destroy" do
let(:like) { LikeService.new(bob).create(post.id) }
it "lets the user destroy their own like" do
result = LikeService.new(bob).destroy(like.id)
expect(result).to be_truthy
end
it "doesn't let the parent author destroy others likes" do
result = LikeService.new(alice).destroy(like.id)
expect(result).to be_falsey
end
it "doesn't let someone destroy others likes" do
result = LikeService.new(eve).destroy(like.id)
expect(result).to be_falsey
end
it "fails if the like doesn't exist" do
expect {
LikeService.new(bob).destroy("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
end
end
describe "#find_for_post" do
context "with user" do
it "returns likes for a public post" do
post = alice.post(:status_message, text: "hello", public: true)
like = LikeService.new(alice).create(post.id)
expect(LikeService.new(eve).find_for_post(post.id)).to include(like)
end
it "returns likes for a visible private post" do
like = LikeService.new(alice).create(post.id)
expect(LikeService.new(bob).find_for_post(post.id)).to include(like)
end
it "doesn't return likes for a private post the user can not see" do
LikeService.new(alice).create(post.id)
expect {
LikeService.new(eve).find_for_post(post.id)
}.to raise_error ActiveRecord::RecordNotFound
end
it "returns the user's like first" do
post = alice.post(:status_message, text: "hello", public: true)
[alice, bob, eve].map {|user| LikeService.new(user).create(post.id) }
[alice, bob, eve].each do |user|
expect(
LikeService.new(user).find_for_post(post.id).first.author.id
).to be user.person.id
end
end
end
context "without user" do
it "returns likes for a public post" do
post = alice.post(:status_message, text: "hello", public: true)
like = LikeService.new(alice).create(post.id)
expect(LikeService.new.find_for_post(post.id)).to include(like)
end
it "doesn't return likes a for private post" do
LikeService.new(alice).create(post.id)
expect {
LikeService.new.find_for_post(post.id)
}.to raise_error Diaspora::NonPublic
end
end
it "returns all likes of a post" do
post = alice.post(:status_message, text: "hello", public: true)
likes = [alice, bob, eve].map {|user| LikeService.new(user).create(post.id) }
expect(LikeService.new.find_for_post(post.id)).to match_array(likes)
end
end
end

View file

@ -0,0 +1,107 @@
describe ReshareService do
let(:post) { alice.post(:status_message, text: "hello", public: true) }
describe "#create" do
it "doesn't create a reshare of my own post" do
expect {
ReshareService.new(alice).create(post.id)
}.not_to raise_error
end
it "creates a reshare of a post of a contact" do
expect {
ReshareService.new(bob).create(post.id)
}.not_to raise_error
end
it "attaches the reshare to the post" do
reshare = ReshareService.new(bob).create(post.id)
expect(post.reshares.first.id).to eq(reshare.id)
end
it "reshares the original post when called with a reshare" do
reshare = ReshareService.new(bob).create(post.id)
reshare2 = ReshareService.new(eve).create(reshare.id)
expect(post.reshares.map(&:id)).to include(reshare2.id)
end
it "fails if the post does not exist" do
expect {
ReshareService.new(bob).create("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
end
it "fails if the post is not public" do
post = alice.post(:status_message, text: "hello", to: alice.aspects.first)
expect {
ReshareService.new(bob).create(post.id)
}.to raise_error ActiveRecord::RecordInvalid
end
it "fails if the user already reshared the post" do
ReshareService.new(bob).create(post.id)
expect {
ReshareService.new(bob).create(post.id)
}.to raise_error ActiveRecord::RecordInvalid
end
it "fails if the user already reshared the original post" do
reshare = ReshareService.new(bob).create(post.id)
expect {
ReshareService.new(bob).create(reshare.id)
}.to raise_error ActiveRecord::RecordInvalid
end
end
describe "#find_for_post" do
context "with user" do
it "returns reshares for a public post" do
reshare = ReshareService.new(bob).create(post.id)
expect(ReshareService.new(eve).find_for_post(post.id)).to include(reshare)
end
it "returns reshares for a visible private post" do
post = alice.post(:status_message, text: "hello", to: alice.aspects.first)
expect(ReshareService.new(bob).find_for_post(post.id)).to be_empty
end
it "doesn't return reshares for a private post the user can not see" do
post = alice.post(:status_message, text: "hello", to: alice.aspects.first)
expect {
ReshareService.new(eve).find_for_post(post.id)
}.to raise_error ActiveRecord::RecordNotFound
end
it "returns the user's reshare first" do
[alice, bob, eve].map {|user| ReshareService.new(user).create(post.id) }
[alice, bob, eve].each do |user|
expect(
ReshareService.new(user).find_for_post(post.id).first.author.id
).to be user.person.id
end
end
end
context "without user" do
it "returns reshares for a public post" do
reshare = ReshareService.new(alice).create(post.id)
expect(ReshareService.new.find_for_post(post.id)).to include(reshare)
end
it "doesn't return reshares a for private post" do
post = alice.post(:status_message, text: "hello", to: alice.aspects.first)
expect {
ReshareService.new.find_for_post(post.id)
}.to raise_error Diaspora::NonPublic
end
end
it "returns all reshares of a post" do
reshares = [alice, bob, eve].map {|user| ReshareService.new(user).create(post.id) }
expect(ReshareService.new.find_for_post(post.id)).to match_array(reshares)
end
end
end