app/services/feed_serializer_service.rb
class FeedSerializerService
class FeedSerializer < JSONAPI::ResourceSerializer
def links_hash(source)
if source.is_a? ActivityGroupResource
{}
else
super
end
end
def foreign_key_value(source, relationship)
related_resource_id = if source.preloaded_fragments.has_key?(format_key(relationship.name))
source.preloaded_fragments[format_key(relationship.name)].values.first.try(:id)
elsif source._model.respond_to?("#{relationship.name}_id")
# If you have direct access to the underlying id, you don't have to load the relationship
# which can save quite a lot of time when loading a lot of data.
# This does not apply to e.g. has_one :through relationships.
source._model.public_send("#{relationship.name}_id")
else
source.public_send(relationship.name).try(:id)
end
return nil unless related_resource_id
@id_formatter.format(related_resource_id)
end
def link_object_to_many(*)
super.reject { |k, _v| k == :links }.to_h
end
def link_object_to_one(*)
super.reject { |k, _v| k == :links }.to_h
end
def relationships_hash(*)
super.select { |_k, v| v.present? }.to_h
end
end
attr_reader :activity_list, :including, :fields, :context, :stream_feed,
:base_url
def initialize(list, including: nil, fields: nil, context: nil, feed:, base_url:)
@including = including || []
@fields = fields || {}
@context = context || {}
@base_url = base_url
@feed = feed
@activity_list = list.includes(stream_enrichment_fields)
end
def as_json(*)
serializer.serialize_to_hash(resources).merge(meta: meta, links: links)
end
def resources
activities.map { |activity| resource_class.new(activity, context) }
end
def activities
@activities ||= activity_list.to_a
end
def including
if aggregated?
@including.map { |inc| "activities.#{inc}" } + ['activities']
else
@including
end
end
def meta
{
feed: {
group: feed.read_target[0],
id: feed.read_target[1],
token: feed.readonly_token
},
readonlyToken: activity_list.feed.readonly_token,
unseenCount: activity_list.unseen_count,
unreadCount: activity_list.unread_count
}.compact
end
def links
if activity_list.more? && activities.count != 0
{ next: url_for_params('page[cursor]' => activities.last.id) }
else
{}
end
end
def stream_enrichment_fields
@including.each_with_object([]) do |inc, includes|
field, reference = inc.split('.')
if (field == 'subject' || field == 'target') && !reference.nil?
includes.push(*non_polymorphic_references(reference))
else
includes << field
end
end
end
def serializer
@serializer ||= FeedSerializer.new(resource_class, include: including, fields: fields)
end
def resource_class
if aggregated?
ActivityGroupResource
else
ActivityResource
end
end
def feed
activity_list.feed
end
private
def aggregated?
activities.first&.[]('activities')
end
def url_for_params(params)
uri = URI.parse(base_url)
query = URI.decode_www_form(uri.query || '').to_h.merge(params)
uri.query = URI.encode_www_form(query.to_a)
uri.to_s
end
def non_polymorphic_references(reference, models: nil)
models ||= [Post, Comment, LibraryEntry]
models.each_with_object([]) do |model, references|
if model.reflections.keys.include?(reference)
references << "#{model.name.underscore}.#{reference}"
end
end
end
end