Commit 6d6a429a authored by Eugen Rochko's avatar Eugen Rochko Committed by GitHub

Rewrite Atom generation from stream entries to use Ox instead of Nokogiri (#1124)

* Rewrite Atom generation from stream entries to use Ox instead of Nokogiri::Builder

StreamEntry is now limited to only statuses, which allows some optimization. Removed
extra queries on AccountsController#show. AtomSerializer instead of AtomBuilderHelper
used in AccountsController#show, StreamEntriesController#show, StreamEntryRenderer
and PubSubHubbub::DistributionWorker

PubSubHubbub::DistributionWorker moves n+1 DomainBlock query to PubSubHubbub::DeliveryWorker
instead.

All Salmon slaps that aren't based on StreamEntry still use AtomBuilderHelper and Nokogiri

* All Salmon slaps now use Ox instead of Nokogiri. No touch from status on account
parent 31597fd3
......@@ -34,6 +34,7 @@ gem 'doorkeeper'
gem 'rabl'
gem 'rqrcode'
gem 'twitter-text'
gem 'ox'
gem 'oj'
gem 'hiredis'
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
......
......@@ -240,6 +240,7 @@ GEM
addressable (~> 2.4)
http (~> 2.0)
nokogiri (~> 1.6)
ox (2.4.11)
paperclip (5.1.0)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
......@@ -482,6 +483,7 @@ DEPENDENCIES
nokogiri
oj
ostatus2
ox
paperclip (~> 5.1)
paperclip-av-transcoder
pg
......
......@@ -16,7 +16,8 @@ class AccountsController < ApplicationController
end
format.atom do
@entries = @account.stream_entries.order('id desc').where(activity_type: 'Status').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
@entries = @account.stream_entries.order('id desc').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
end
format.activitystreams2
......
......@@ -19,7 +19,9 @@ class StreamEntriesController < ApplicationController
end
end
format.atom
format.atom do
render xml: AtomSerializer.render(AtomSerializer.new.entry(@stream_entry, true))
end
end
end
......
This diff is collapsed.
......@@ -78,6 +78,8 @@ class TagManager
case target.object_type
when :person
account_url(target)
when :note, :comment, :activity
unique_tag(target.created_at, target.id, 'Status')
else
unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
end
......
......@@ -5,25 +5,21 @@ class StreamEntry < ApplicationRecord
belongs_to :account, inverse_of: :stream_entries
belongs_to :activity, polymorphic: true
belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id', inverse_of: :stream_entry
validates :account, :activity, presence: true
STATUS_INCLUDES = [:account, :stream_entry, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, mentions: :account], thread: [:stream_entry, :account]].freeze
STATUS_INCLUDES = [:account, :stream_entry, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :media_attachments, :tags, mentions: :account], thread: [:stream_entry, :account]].freeze
default_scope { where(activity_type: 'Status') }
scope :with_includes, -> { includes(:account, status: STATUS_INCLUDES) }
def object_type
if orphaned?
:activity
else
targeted? ? :activity : activity.object_type
end
orphaned? || targeted? ? :activity : status.object_type
end
def verb
orphaned? ? :delete : activity.verb
orphaned? ? :delete : status.verb
end
def targeted?
......@@ -31,15 +27,15 @@ class StreamEntry < ApplicationRecord
end
def target
orphaned? ? nil : activity.target
orphaned? ? nil : status.target
end
def title
orphaned? ? nil : activity.title
orphaned? ? nil : status.title
end
def content
orphaned? ? nil : activity.content
orphaned? ? nil : status.content
end
def threaded?
......@@ -47,20 +43,16 @@ class StreamEntry < ApplicationRecord
end
def thread
orphaned? ? nil : activity.thread
orphaned? ? nil : status.thread
end
def mentions
activity.respond_to?(:mentions) ? activity.mentions.map(&:account) : []
end
def activity
!new_record? ? send(activity_type.underscore) || super : super
orphaned? ? [] : status.mentions.map(&:account)
end
private
def orphaned?
activity.nil?
status.nil?
end
end
......@@ -9,20 +9,20 @@ class AfterBlockService < BaseService
private
def clear_timelines(account, target_account)
mentions_key = FeedManager.instance.key(:mentions, account.id)
home_key = FeedManager.instance.key(:home, account.id)
redis.pipelined do
target_account.statuses.select('id').find_each do |status|
redis.zrem(mentions_key, status.id)
redis.zrem(home_key, status.id)
end
end
end
def clear_notifications(account, target_account)
Notification.where(account: account).joins(:follow).where(activity_type: 'Follow', follows: { account_id: target_account.id }).destroy_all
Notification.where(account: account).joins(mention: :status).where(activity_type: 'Mention', statuses: { account_id: target_account.id }).destroy_all
Notification.where(account: account).joins(:favourite).where(activity_type: 'Favourite', favourites: { account_id: target_account.id }).destroy_all
Notification.where(account: account).joins(:status).where(activity_type: 'Status', statuses: { account_id: target_account.id }).destroy_all
Notification.where(account: account).joins(:follow).where(activity_type: 'Follow', follows: { account_id: target_account.id }).delete_all
Notification.where(account: account).joins(mention: :status).where(activity_type: 'Mention', statuses: { account_id: target_account.id }).delete_all
Notification.where(account: account).joins(:favourite).where(activity_type: 'Favourite', favourites: { account_id: target_account.id }).delete_all
Notification.where(account: account).joins(:status).where(activity_type: 'Status', statuses: { account_id: target_account.id }).delete_all
end
def redis
......
......@@ -10,31 +10,6 @@ class AuthorizeFollowService < BaseService
private
def build_xml(follow_request)
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, Time.now.utc, follow_request.id, 'FollowRequest'
title xml, "#{follow_request.target_account.acct} authorizes follow request by #{follow_request.account.acct}"
author(xml) do
include_author xml, follow_request.target_account
end
object_type xml, :activity
verb xml, :authorize
target(xml) do
author(xml) do
include_author xml, follow_request.account
end
object_type xml, :activity
verb xml, :request_friend
target(xml) do
include_author xml, follow_request.target_account
end
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.authorize_follow_request_salmon(follow_request))
end
end
......@@ -18,22 +18,6 @@ class BlockService < BaseService
private
def build_xml(block)
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, block.created_at, block.id, 'Block'
title xml, "#{block.account.acct} no longer wishes to interact with #{block.target_account.acct}"
author(xml) do
include_author xml, block.account
end
object_type xml, :activity
verb xml, :block
target(xml) do
include_author xml, block.target_account
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.block_salmon(block))
end
end
......@@ -2,7 +2,6 @@
module StreamEntryRenderer
def stream_entry_to_xml(stream_entry)
renderer = StreamEntriesController.renderer.new(method: 'get', http_host: Rails.configuration.x.local_domain, https: Rails.configuration.x.use_https)
renderer.render(:show, assigns: { stream_entry: stream_entry }, formats: [:atom])
AtomSerializer.render(AtomSerializer.new.entry(stream_entry, true))
end
end
......@@ -22,26 +22,6 @@ class FavouriteService < BaseService
private
def build_xml(favourite)
description = "#{favourite.account.acct} favourited a status by #{favourite.status.account.acct}"
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, favourite.created_at, favourite.id, 'Favourite'
title xml, description
content xml, description
author(xml) do
include_author xml, favourite.account
end
object_type xml, :activity
verb xml, :favorite
in_reply_to xml, TagManager.instance.uri_for(favourite.status), TagManager.instance.url_for(favourite.status)
target(xml) do
include_target xml, favourite.status
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.favourite_salmon(favourite))
end
end
......@@ -55,48 +55,10 @@ class FollowService < BaseService
end
def build_follow_request_xml(follow_request)
description = "#{follow_request.account.acct} requested to follow #{follow_request.target_account.acct}"
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, follow_request.created_at, follow_request.id, 'FollowRequest'
title xml, description
content xml, description
author(xml) do
include_author xml, follow_request.account
end
object_type xml, :activity
verb xml, :request_friend
target(xml) do
include_author xml, follow_request.target_account
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.follow_request_salmon(follow_request))
end
def build_follow_xml(follow)
description = "#{follow.account.acct} started following #{follow.target_account.acct}"
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, follow.created_at, follow.id, 'Follow'
title xml, description
content xml, description
author(xml) do
include_author xml, follow.account
end
object_type xml, :activity
verb xml, :follow
target(xml) do
include_author xml, follow.target_account
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.follow_salmon(follow))
end
end
......@@ -10,31 +10,6 @@ class RejectFollowService < BaseService
private
def build_xml(follow_request)
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, Time.now.utc, follow_request.id, 'FollowRequest'
title xml, "#{follow_request.target_account.acct} rejects follow request by #{follow_request.account.acct}"
author(xml) do
include_author xml, follow_request.target_account
end
object_type xml, :activity
verb xml, :reject
target(xml) do
author(xml) do
include_author xml, follow_request.account
end
object_type xml, :activity
verb xml, :request_friend
target(xml) do
include_author xml, follow_request.target_account
end
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.reject_follow_request_salmon(follow_request))
end
end
......@@ -11,22 +11,6 @@ class UnblockService < BaseService
private
def build_xml(block)
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, Time.now.utc, block.id, 'Block'
title xml, "#{block.account.acct} no longer blocks #{block.target_account.acct}"
author(xml) do
include_author xml, block.account
end
object_type xml, :activity
verb xml, :unblock
target(xml) do
include_author xml, block.target_account
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.unblock_salmon(block))
end
end
......@@ -13,26 +13,6 @@ class UnfavouriteService < BaseService
private
def build_xml(favourite)
description = "#{favourite.account.acct} no longer favourites a status by #{favourite.status.account.acct}"
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, Time.now.utc, favourite.id, 'Favourite'
title xml, description
content xml, description
author(xml) do
include_author xml, favourite.account
end
object_type xml, :activity
verb xml, :unfavorite
in_reply_to xml, TagManager.instance.uri_for(favourite.status), TagManager.instance.url_for(favourite.status)
target(xml) do
include_target xml, favourite.status
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.unfavourite_salmon(favourite))
end
end
......@@ -13,25 +13,6 @@ class UnfollowService < BaseService
private
def build_xml(follow)
description = "#{follow.account.acct} is no longer following #{follow.target_account.acct}"
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
unique_id xml, Time.now.utc, follow.id, 'Follow'
title xml, description
content xml, description
author(xml) do
include_author xml, follow.account
end
object_type xml, :activity
verb xml, :unfollow
target(xml) do
include_author xml, follow.target_account
end
end
end.to_xml
AtomSerializer.render(AtomSerializer.new.unfollow_salmon(follow))
end
end
# frozen_string_literal: true
Nokogiri::XML::Builder.new do |xml|
feed(xml) do
simple_id xml, account_url(@account, format: 'atom')
title xml, @account.display_name
subtitle xml, @account.note
updated_at xml, stream_updated_at
logo xml, full_asset_url(@account.avatar.url(:original))
author(xml) do
include_author xml, @account
end
link_alternate xml, TagManager.instance.url_for(@account)
link_self xml, account_url(@account, format: 'atom')
link_next xml, account_url(@account, format: 'atom', max_id: @entries.last.id) if @entries.size == 20
link_hub xml, api_push_url
link_salmon xml, api_salmon_url(@account.id)
@entries.each do |stream_entry|
entry(xml, false) do
include_entry xml, stream_entry
end
end
end
end.to_xml
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
author(xml) do
include_author xml, @stream_entry.account
end
include_entry xml, @stream_entry
end
end.to_xml
......@@ -13,6 +13,9 @@ class Pubsubhubbub::DeliveryWorker
def perform(subscription_id, payload)
subscription = Subscription.find(subscription_id)
headers = {}
host = Addressable::URI.parse(subscription.callback_url).host
return if DomainBlock.blocked?(host)
headers['User-Agent'] = 'Mastodon/PubSubHubbub'
headers['Link'] = LinkHeader.new([[api_push_url, [%w(rel hub)]], [account_url(subscription.account, format: :atom), [%w(rel self)]]]).to_s
......
......@@ -11,13 +11,9 @@ class Pubsubhubbub::DistributionWorker
return if stream_entry.hidden?
account = stream_entry.account
renderer = AccountsController.renderer.new(method: 'get', http_host: Rails.configuration.x.local_domain, https: Rails.configuration.x.use_https)
payload = renderer.render(:show, assigns: { account: account, entries: [stream_entry] }, formats: [:atom])
# domains = account.followers_domains
payload = AtomSerializer.render(AtomSerializer.new.feed(account, [stream_entry]))
Subscription.where(account: account).active.select('id, callback_url').find_each do |subscription|
host = Addressable::URI.parse(subscription.callback_url).host
next if DomainBlock.blocked?(host) # || !domains.include?(host)
Pubsubhubbub::DeliveryWorker.perform_async(subscription.id, payload)
end
rescue ActiveRecord::RecordNotFound
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment