about summary refs log tree commit diff
path: root/app/services/activitypub/fetch_collection_items_service.rb
blob: 5b93ea221ddbaa7f1586a404d03b8ecfa63ccf0c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# frozen_string_literal: true

class ActivityPub::FetchCollectionItemsService < BaseService
  include JsonLdHelper

  def call(collection_or_uri, account, page_limit: 10, item_limit: 100, allow_synchronous_requests: true)
    @account = account
    @allow_synchronous_requests = allow_synchronous_requests

    return [] if ActivityPub::TagManager.instance.local_uri?(collection_or_uri)

    collection_items(collection_or_uri, page_limit, item_limit)
  end

  private

  def collection_items(collection_or_uri, page_limit, item_limit)
    collection = fetch_collection(collection_or_uri)
    return [] unless collection.is_a?(Hash)

    collection = fetch_collection(collection['first']) if collection['first'].present?
    page_count = 0
    item_count = 0
    items = []

    while collection.present? && collection.is_a?(Hash) && collection['type'].present?
      batch = case collection['type']
              when 'Collection', 'CollectionPage'
                collection['items'].each
              when 'OrderedCollection', 'OrderedCollectionPage'
                collection['orderedItems']
              end

      batch_size = [batch.count, item_limit - item_count].min
      items.push(
        batch.take(batch_size)
             .map { |item| value_or_id(item) }
             .reject { |uri| unsupported_uri_scheme?(uri) || ActivityPub::TagManager.instance.local_uri?(uri) }
      )

      item_count += batch_size
      page_count += 1

      break unless item_count < item_limit && page_count < page_limit && collection['next'].present?

      collection = fetch_collection(collection['next'])
    end

    items.uniq
  end

  def fetch_collection(collection_or_uri)
    return collection_or_uri if collection_or_uri.is_a?(Hash)
    return unless @allow_synchronous_requests
    return if invalid_origin?(collection_or_uri)

    on_behalf_of = @account.present? ? @account.followers.local.first : nil
    fetch_resource_without_id_validation(collection_or_uri, on_behalf_of, true)
  rescue Mastodon::UnexpectedResponseError
    nil
  end
end