From 4da9a12bf82b8743b739fd31c88ca768fad481a3 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 3 Jul 2022 16:59:12 +0100 Subject: [PATCH 1/4] Add test for friendica featured collection --- .../friendica_featured_collection.json | 29 ++++++++++++++ .../friendica_featured_collection_first.json | 30 ++++++++++++++ .../web/activity_pub/activity_pub_test.exs | 40 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 test/fixtures/friendica/friendica_featured_collection.json create mode 100644 test/fixtures/friendica/friendica_featured_collection_first.json diff --git a/test/fixtures/friendica/friendica_featured_collection.json b/test/fixtures/friendica/friendica_featured_collection.json new file mode 100644 index 000000000..f640975f3 --- /dev/null +++ b/test/fixtures/friendica/friendica_featured_collection.json @@ -0,0 +1,29 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "vcard": "http://www.w3.org/2006/vcard/ns#", + "dfrn": "http://purl.org/macgirvin/dfrn/1.0/", + "diaspora": "https://diasporafoundation.org/ns/", + "litepub": "http://litepub.social/ns#", + "toot": "http://joinmastodon.org/ns#", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "schema": "http://schema.org#", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "Hashtag": "as:Hashtag", + "directMessage": "litepub:directMessage", + "discoverable": "toot:discoverable", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value" + } + ], + "id": "https://friendica.example.com/featured/raha", + "type": "OrderedCollection", + "totalItems": 0, + "first": "https://friendica.example.com/featured/raha?page=1" +} diff --git a/test/fixtures/friendica/friendica_featured_collection_first.json b/test/fixtures/friendica/friendica_featured_collection_first.json new file mode 100644 index 000000000..7d450e42f --- /dev/null +++ b/test/fixtures/friendica/friendica_featured_collection_first.json @@ -0,0 +1,30 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "vcard": "http://www.w3.org/2006/vcard/ns#", + "dfrn": "http://purl.org/macgirvin/dfrn/1.0/", + "diaspora": "https://diasporafoundation.org/ns/", + "litepub": "http://litepub.social/ns#", + "toot": "http://joinmastodon.org/ns#", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "schema": "http://schema.org#", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "Hashtag": "as:Hashtag", + "directMessage": "litepub:directMessage", + "discoverable": "toot:discoverable", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value" + } + ], + "id": "https://friendica.example.com/featured/raha?page=1", + "type": "OrderedCollectionPage", + "totalItems": 0, + "partOf": "https://friendica.example.com/featured/raha", + "orderedItems": [] +} diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 574ef0d71..b21b9ee28 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -314,6 +314,46 @@ test "fetches user featured collection" do end end + test "fetches user featured collection using the first property" do + ap_id = "https://friendica.example.com/raha" + featured_url = "https://friendica.example.com/raha/collections/featured" + first_url = "https://friendica.mnementh.co.uk/featured/spyro?page=1" + + featured_data = + "test/fixtures/friendica/friendica_featured_collection.json" + |> File.read!() + + page_data = + "test/fixtures/friendica/friendica_featured_collection.json" + |> File.read!() + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^featured_url + } -> + %Tesla.Env{ + status: 200, + body: featured_data, + headers: [{"content-type", "application/activity+json"}] + } + end) + + Tesla.Mock.mock_global(fn + %{ + method: :get, + url: ^first_url + } -> + %Tesla.Env{ + status: 200, + body: page_data, + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, data} = ActivityPub.fetch_and_prepare_featured_from_ap_id(featured_url) + end + test "it fetches the appropriate tag-restricted posts" do user = insert(:user) From 0a3a552696949b754248d1987a953a5c54e16605 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 3 Jul 2022 17:25:20 +0100 Subject: [PATCH 2/4] Add support for a `first` reference in pinned objects --- lib/pleroma/web/activity_pub/activity_pub.ex | 30 +++++++++++++++++++ .../friendica_featured_collection_first.json | 6 +++- .../web/activity_pub/activity_pub_test.exs | 8 ++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index e6548a818..bf766699d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1662,6 +1662,36 @@ def maybe_handle_clashing_nickname(data) do end end + def pin_data_from_featured_collection(%{ + "type" => "OrderedCollection", + "first" => first + }) do + with {:ok, page} <- Fetcher.fetch_and_contain_remote_object_from_id(first) do + page + |> Map.get("orderedItems") + |> Map.new(fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end) + else + e -> + Logger.error("Could not decode featured collection at fetch #{first}, #{inspect(e)}") + {:ok, %{}} + end + end + + def pin_data_from_featured_collection(%{ + "type" => "Collection", + "first" => first + }) do + with {:ok, page} <- Fetcher.fetch_and_contain_remote_object_from_id(first) do + page + |> Map.get("items") + |> Map.new(fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end) + else + e -> + Logger.error("Could not decode featured collection at fetch #{first}, #{inspect(e)}") + {:ok, %{}} + end + end + def pin_data_from_featured_collection(%{ "type" => type, "orderedItems" => objects diff --git a/test/fixtures/friendica/friendica_featured_collection_first.json b/test/fixtures/friendica/friendica_featured_collection_first.json index 7d450e42f..1f9dce420 100644 --- a/test/fixtures/friendica/friendica_featured_collection_first.json +++ b/test/fixtures/friendica/friendica_featured_collection_first.json @@ -26,5 +26,9 @@ "type": "OrderedCollectionPage", "totalItems": 0, "partOf": "https://friendica.example.com/featured/raha", - "orderedItems": [] + "orderedItems": [ + { + "id": "http://inserted" + } + ] } diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index b21b9ee28..2b65f59e0 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -315,16 +315,15 @@ test "fetches user featured collection" do end test "fetches user featured collection using the first property" do - ap_id = "https://friendica.example.com/raha" featured_url = "https://friendica.example.com/raha/collections/featured" - first_url = "https://friendica.mnementh.co.uk/featured/spyro?page=1" + first_url = "https://friendica.example.com/featured/raha?page=1" featured_data = "test/fixtures/friendica/friendica_featured_collection.json" |> File.read!() page_data = - "test/fixtures/friendica/friendica_featured_collection.json" + "test/fixtures/friendica/friendica_featured_collection_first.json" |> File.read!() Tesla.Mock.mock(fn @@ -337,9 +336,7 @@ test "fetches user featured collection using the first property" do body: featured_data, headers: [{"content-type", "application/activity+json"}] } - end) - Tesla.Mock.mock_global(fn %{ method: :get, url: ^first_url @@ -352,6 +349,7 @@ test "fetches user featured collection using the first property" do end) {:ok, data} = ActivityPub.fetch_and_prepare_featured_from_ap_id(featured_url) + assert Map.has_key?(data, "http://inserted") end test "it fetches the appropriate tag-restricted posts" do From 05081cd81be675db4825335181182dd110c4b834 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 3 Jul 2022 19:20:59 +0100 Subject: [PATCH 3/4] Add collection fetching module --- config/config.exs | 3 +- lib/pleroma/collections/fetcher.ex | 68 +++++++ test/fixtures/collections/ordered_array.json | 19 ++ .../fixtures/collections/unordered_array.json | 19 ++ .../collections/unordered_page_embedded.json | 20 +++ .../collections/unordered_page_first.json | 13 ++ .../collections/unordered_page_reference.json | 8 + .../collections/unordered_page_second.json | 12 ++ .../collections/collections_fetcher_test.exs | 167 ++++++++++++++++++ 9 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/collections/fetcher.ex create mode 100644 test/fixtures/collections/ordered_array.json create mode 100644 test/fixtures/collections/unordered_array.json create mode 100644 test/fixtures/collections/unordered_page_embedded.json create mode 100644 test/fixtures/collections/unordered_page_first.json create mode 100644 test/fixtures/collections/unordered_page_reference.json create mode 100644 test/fixtures/collections/unordered_page_second.json create mode 100644 test/pleroma/collections/collections_fetcher_test.exs diff --git a/config/config.exs b/config/config.exs index eb39155df..914b5db61 100644 --- a/config/config.exs +++ b/config/config.exs @@ -363,7 +363,8 @@ follow_handshake_timeout: 500, note_replies_output_limit: 5, sign_object_fetches: true, - authorized_fetch_mode: false + authorized_fetch_mode: false, + max_collection_objects: 50 config :pleroma, :streamer, workers: 3, diff --git a/lib/pleroma/collections/fetcher.ex b/lib/pleroma/collections/fetcher.ex new file mode 100644 index 000000000..205c62b4e --- /dev/null +++ b/lib/pleroma/collections/fetcher.ex @@ -0,0 +1,68 @@ +# Akkoma: The cooler fediverse server +# Copyright © 2022- Akkoma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Akkoma.Collections.Fetcher do + @moduledoc """ + Activitypub Collections fetching functions + see: https://www.w3.org/TR/activitystreams-core/#paging + """ + alias Pleroma.Object.Fetcher + alias Pleroma.Config + + def fetch_collection_by_ap_id(ap_id) when is_binary(ap_id) do + fetch_collection(ap_id) + end + + defp fetch_collection(ap_id) do + with {:ok, page} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do + {:ok, objects_from_collection(page)} + end + end + + defp items_in_page(%{"type" => type, "orderedItems" => items}) + when is_list(items) and type in ["OrderedCollection", "OrderedCollectionPage"], + do: items + + defp items_in_page(%{"type" => type, "items" => items}) + when is_list(items) and type in ["Collection", "CollectionPage"], + do: items + + defp objects_from_collection(%{"type" => "OrderedCollection", "orderedItems" => items}) + when is_list(items), + do: items + + defp objects_from_collection(%{"type" => "Collection", "items" => items}) when is_list(items), + do: items + + defp objects_from_collection(%{"type" => type, "first" => first}) + when is_binary(first) and type in ["Collection", "OrderedCollection"] do + fetch_page_items(first) + end + + defp objects_from_collection(%{"type" => type, "first" => %{"id" => id}}) + when is_binary(id) and type in ["Collection", "OrderedCollection"] do + fetch_page_items(id) + end + + defp fetch_page_items(id, items \\ []) do + if Enum.count(items) >= Config.get([:activitypub, :max_collection_objects]) do + items + else + {:ok, page} = Fetcher.fetch_and_contain_remote_object_from_id(id) + objects = items_in_page(page) + + if Enum.count(objects) > 0 do + maybe_next_page(page, items ++ objects) + else + items + end + end + end + + defp maybe_next_page(%{"next" => id}, items) when is_binary(id) do + fetch_page_items(id, items) + end + + defp maybe_next_page(_, items), do: items +end diff --git a/test/fixtures/collections/ordered_array.json b/test/fixtures/collections/ordered_array.json new file mode 100644 index 000000000..1f196c6d3 --- /dev/null +++ b/test/fixtures/collections/ordered_array.json @@ -0,0 +1,19 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://example.com/collection/ordered_array", + "summary": "Object history", + "type": "OrderedCollection", + "totalItems": 2, + "orderedItems": [ + { + "type": "Create", + "actor": "http://www.test.example/sally", + "object": "http://example.org/foo" + }, + { + "type": "Like", + "actor": "http://www.test.example/joe", + "object": "http://example.org/foo" + } + ] +} diff --git a/test/fixtures/collections/unordered_array.json b/test/fixtures/collections/unordered_array.json new file mode 100644 index 000000000..05d9f8175 --- /dev/null +++ b/test/fixtures/collections/unordered_array.json @@ -0,0 +1,19 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://example.com/collection/unordered_array", + "summary": "Object history", + "type": "Collection", + "totalItems": 2, + "items": [ + { + "type": "Create", + "actor": "http://www.test.example/sally", + "object": "http://example.org/foo" + }, + { + "type": "Like", + "actor": "http://www.test.example/joe", + "object": "http://example.org/foo" + } + ] +} diff --git a/test/fixtures/collections/unordered_page_embedded.json b/test/fixtures/collections/unordered_page_embedded.json new file mode 100644 index 000000000..01f9230a4 --- /dev/null +++ b/test/fixtures/collections/unordered_page_embedded.json @@ -0,0 +1,20 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "summary": "Sally's recent activities", + "type": "Collection", + "id": "http://example.org/foo", + "totalItems": 10, + "first": { + "type": "CollectionPage", + "id": "http://example.org/foo?page=1", + "partOf": "http://example.org/foo", + "next": "http://example.org/foo?page=2", + "items": [ + { + "type": "Create", + "actor": "http://www.test.example/sally", + "object": "http://example.org/foo" + } + ] + } +} diff --git a/test/fixtures/collections/unordered_page_first.json b/test/fixtures/collections/unordered_page_first.json new file mode 100644 index 000000000..f6d54f352 --- /dev/null +++ b/test/fixtures/collections/unordered_page_first.json @@ -0,0 +1,13 @@ +{ + "type": "CollectionPage", + "id": "https://example.com/collection/unordered_page_reference?page=1", + "partOf": "https://example.com/collection/unordered_page_reference", + "next": "https://example.com/collection/unordered_page_reference?page=2", + "items": [ + { + "type": "Create", + "actor": "http://www.test.example/sally", + "object": "http://example.org/foo" + } + ] +} diff --git a/test/fixtures/collections/unordered_page_reference.json b/test/fixtures/collections/unordered_page_reference.json new file mode 100644 index 000000000..7376e4f22 --- /dev/null +++ b/test/fixtures/collections/unordered_page_reference.json @@ -0,0 +1,8 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "summary": "Sally's recent activities", + "type": "Collection", + "id": "https://example.com/collection/unordered_page_reference", + "totalItems": 10, + "first": "https://example.com/collection/unordered_page_reference?page=1" +} diff --git a/test/fixtures/collections/unordered_page_second.json b/test/fixtures/collections/unordered_page_second.json new file mode 100644 index 000000000..ee557cb56 --- /dev/null +++ b/test/fixtures/collections/unordered_page_second.json @@ -0,0 +1,12 @@ +{ + "type": "CollectionPage", + "id": "https://example.com/collection/unordered_page_reference?page=2", + "partOf": "https://example.com/collection/unordered_page_reference", + "items": [ + { + "type": "Like", + "actor": "http://www.test.example/sally", + "object": "http://example.org/foo" + } + ] +} diff --git a/test/pleroma/collections/collections_fetcher_test.exs b/test/pleroma/collections/collections_fetcher_test.exs new file mode 100644 index 000000000..b9f84f5c4 --- /dev/null +++ b/test/pleroma/collections/collections_fetcher_test.exs @@ -0,0 +1,167 @@ +defmodule Akkoma.Collections.FetcherTest do + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + + alias Akkoma.Collections.Fetcher + + import Tesla.Mock + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "it should extract items from an embedded array in a Collection" do + unordered_collection = + "test/fixtures/collections/unordered_array.json" + |> File.read!() + + ap_id = "https://example.com/collection/ordered_array" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^ap_id + } -> + %Tesla.Env{ + status: 200, + body: unordered_collection, + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, objects} = Fetcher.fetch_collection_by_ap_id(ap_id) + assert [%{"type" => "Create"}, %{"type" => "Like"}] = objects + end + + test "it should extract items from an embedded array in an OrderedCollection" do + ordered_collection = + "test/fixtures/collections/ordered_array.json" + |> File.read!() + + ap_id = "https://example.com/collection/ordered_array" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^ap_id + } -> + %Tesla.Env{ + status: 200, + body: ordered_collection, + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, objects} = Fetcher.fetch_collection_by_ap_id(ap_id) + assert [%{"type" => "Create"}, %{"type" => "Like"}] = objects + end + + test "it should extract items from an referenced first page in a Collection" do + unordered_collection = + "test/fixtures/collections/unordered_page_reference.json" + |> File.read!() + + first_page = + "test/fixtures/collections/unordered_page_first.json" + |> File.read!() + + second_page = + "test/fixtures/collections/unordered_page_second.json" + |> File.read!() + + ap_id = "https://example.com/collection/unordered_page_reference" + first_page_id = "https://example.com/collection/unordered_page_reference?page=1" + second_page_id = "https://example.com/collection/unordered_page_reference?page=2" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^ap_id + } -> + %Tesla.Env{ + status: 200, + body: unordered_collection, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^first_page_id + } -> + %Tesla.Env{ + status: 200, + body: first_page, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^second_page_id + } -> + %Tesla.Env{ + status: 200, + body: second_page, + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, objects} = Fetcher.fetch_collection_by_ap_id(ap_id) + assert [%{"type" => "Create"}, %{"type" => "Like"}] = objects + end + + test "it should stop fetching when we hit :max_collection_objects" do + clear_config([:activitypub, :max_collection_objects], 1) + + unordered_collection = + "test/fixtures/collections/unordered_page_reference.json" + |> File.read!() + + first_page = + "test/fixtures/collections/unordered_page_first.json" + |> File.read!() + + second_page = + "test/fixtures/collections/unordered_page_second.json" + |> File.read!() + + ap_id = "https://example.com/collection/unordered_page_reference" + first_page_id = "https://example.com/collection/unordered_page_reference?page=1" + second_page_id = "https://example.com/collection/unordered_page_reference?page=2" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^ap_id + } -> + %Tesla.Env{ + status: 200, + body: unordered_collection, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^first_page_id + } -> + %Tesla.Env{ + status: 200, + body: first_page, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^second_page_id + } -> + %Tesla.Env{ + status: 200, + body: second_page, + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, objects} = Fetcher.fetch_collection_by_ap_id(ap_id) + assert [%{"type" => "Create"}] = objects + end +end From 95ef3a8b1ea11567f09221b023f8d24a7e750d09 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 3 Jul 2022 19:36:30 +0100 Subject: [PATCH 4/4] Use Akkoma modification for collections --- config/description.exs | 7 ++++++ docs/configuration/cheatsheet.md | 1 + lib/pleroma/collections/fetcher.ex | 11 ++++++++- lib/pleroma/web/activity_pub/activity_pub.ex | 26 ++++++-------------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/config/description.exs b/config/description.exs index 9401bed5c..3777905a3 100644 --- a/config/description.exs +++ b/config/description.exs @@ -1689,6 +1689,13 @@ type: :integer, description: "Following handshake timeout", suggestions: [500] + }, + %{ + key: :max_collection_objects, + type: :integer, + description: + "The maximum number of items to fetch from a remote collections. Setting this too low can lead to only getting partial collections, but too high and you can end up fetching far too many objects.", + suggestions: [50] } ] }, diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 3097f1190..11083e831 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -236,6 +236,7 @@ Notes: * `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question * `sign_object_fetches`: Sign object fetches with HTTP signatures * `authorized_fetch_mode`: Require HTTP signatures for AP fetches +* `max_collection_objects`: The maximum number of objects to fetch from a remote AP collection. ## Pleroma.User diff --git a/lib/pleroma/collections/fetcher.ex b/lib/pleroma/collections/fetcher.ex index 205c62b4e..382defff4 100644 --- a/lib/pleroma/collections/fetcher.ex +++ b/lib/pleroma/collections/fetcher.ex @@ -14,12 +14,21 @@ def fetch_collection_by_ap_id(ap_id) when is_binary(ap_id) do fetch_collection(ap_id) end - defp fetch_collection(ap_id) do + def fetch_collection(ap_id) when is_binary(ap_id) do with {:ok, page} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do {:ok, objects_from_collection(page)} + else + e -> + Logger.error("Could not fetch collection #{ap_id} - #{inspect(e)}") + e end end + def fetch_collection(%{"type" => type} = page) + when type in ["Collection", "OrderedCollection"] do + {:ok, objects_from_collection(page)} + end + defp items_in_page(%{"type" => type, "orderedItems" => items}) when is_list(items) and type in ["OrderedCollection", "OrderedCollectionPage"], do: items diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index bf766699d..77f38f9f1 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPub do + alias Akkoma.Collections alias Pleroma.Activity alias Pleroma.Activity.Ir.Topics alias Pleroma.Config @@ -1677,26 +1678,13 @@ def pin_data_from_featured_collection(%{ end end - def pin_data_from_featured_collection(%{ - "type" => "Collection", - "first" => first - }) do - with {:ok, page} <- Fetcher.fetch_and_contain_remote_object_from_id(first) do - page - |> Map.get("items") - |> Map.new(fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end) - else - e -> - Logger.error("Could not decode featured collection at fetch #{first}, #{inspect(e)}") - {:ok, %{}} - end - end - - def pin_data_from_featured_collection(%{ - "type" => type, - "orderedItems" => objects - }) + def pin_data_from_featured_collection( + %{ + "type" => type + } = collection + ) when type in ["OrderedCollection", "Collection"] do + {:ok, objects} = Collections.Fetcher.fetch_collection(collection) Map.new(objects, fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end) end