Skip to content

Commit e43a93c

Browse files
committed
Add ActivityPub Link object type handling
1 parent 48baa26 commit e43a93c

File tree

4 files changed

+107
-8
lines changed

4 files changed

+107
-8
lines changed

lib/discourse_activity_pub/ap/link.rb

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
module DiscourseActivityPub
3+
module AP
4+
class Link
5+
attr_accessor :value
6+
7+
def initialize(value = nil)
8+
@value = value
9+
end
10+
11+
def type
12+
'Link'
13+
end
14+
15+
def href
16+
return value if value.is_a?(String)
17+
return value[:href] if value.is_a?(Hash)
18+
end
19+
20+
def media_type
21+
return value[:mediaType] if value.is_a?(Hash)
22+
end
23+
24+
def name
25+
return value[:name] if value.is_a?(Hash)
26+
end
27+
end
28+
end
29+
end

lib/discourse_activity_pub/ap/object.rb

+23-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ def actor?
5151
end
5252

5353
def url
54-
stored.respond_to?(:url) && stored&.url
54+
if stored
55+
stored.respond_to?(:url) && stored&.url
56+
elsif json.present?
57+
Link.new(json[:url])
58+
end
5559
end
5660

5761
def audience
@@ -95,7 +99,11 @@ def summary
9599
end
96100

97101
def name
98-
stored.respond_to?(:name) && stored&.name
102+
if stored.present?
103+
stored.respond_to?(:name) && stored&.name
104+
elsif json.present?
105+
json[:name]
106+
end
99107
end
100108

101109
def context
@@ -110,6 +118,19 @@ def delivered_to
110118
@delivered_to ||= []
111119
end
112120

121+
def media_type
122+
json[:mediaType] if json.present?
123+
end
124+
125+
def attachment
126+
if json.present? && json[:attachment].present?
127+
json[:attachment].each_with_object([]) do |attachment_json, result|
128+
obj = AP::Object.factory(attachment_json)
129+
result << obj if obj.present?
130+
end
131+
end
132+
end
133+
113134
def cache
114135
@cache ||= {}
115136
end

plugin.rb

+13-6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
require_relative "lib/discourse_activity_pub/context_resolver"
4848
require_relative "lib/discourse_activity_pub/ap"
4949
require_relative "lib/discourse_activity_pub/ap/handlers"
50+
require_relative "lib/discourse_activity_pub/ap/link"
5051
require_relative "lib/discourse_activity_pub/ap/object"
5152
require_relative "lib/discourse_activity_pub/ap/actor"
5253
require_relative "lib/discourse_activity_pub/ap/actor/group"
@@ -1164,16 +1165,22 @@
11641165
)
11651166
end
11661167

1167-
if object.json[:attachment].present?
1168-
object.json[:attachment].each do |attachment|
1168+
if object.attachment.present?
1169+
object.attachment.each do |attachment|
1170+
# Some platforms (e.g. Mastodon) put attachment url media types on the attachment itself,
1171+
# instead of on a Link object in the url attribute. Technically this violates the specification,
1172+
# but we need to support it nevertheless. See further https://www.w3.org/TR/activitystreams-vocabulary/#dfn-mediatype
1173+
media_type = attachment.url.media_type || attachment.media_type
1174+
name = attachment.url.name || attachment.name
1175+
11691176
begin
11701177
DiscourseActivityPubAttachment.create(
11711178
object_id: object.stored.id,
11721179
object_type: "DiscourseActivityPubObject",
1173-
ap_type: attachment[:type],
1174-
url: attachment[:url],
1175-
name: attachment[:name],
1176-
media_type: attachment[:mediaType],
1180+
ap_type: attachment.type,
1181+
url: attachment.url.href,
1182+
name: name,
1183+
media_type: media_type,
11771184
)
11781185
rescue ActiveRecord::RecordInvalid => error
11791186
# fail silently if an attachment does not validate

spec/lib/discourse_activity_pub/ap/activity/create_spec.rb

+42
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,20 @@
477477
{ type: "Image", mediaType: "invalid-type", url: attachment_url_3 },
478478
]
479479
end
480+
let!(:attachments_with_link_objects) do
481+
[
482+
{
483+
type: "Document",
484+
url: {
485+
type: "Link",
486+
href: attachment_url_1,
487+
mediaType: "image/png",
488+
name: attachment_name_1,
489+
},
490+
},
491+
{ type: "Image", mediaType: "image/jpeg", url: attachment_url_2 },
492+
]
493+
end
480494

481495
context "with supported media types" do
482496
let!(:new_post_json) do
@@ -588,6 +602,34 @@
588602
expect(post.present?).to be(true)
589603
end
590604
end
605+
606+
context "with Link objects" do
607+
let!(:new_post_json) do
608+
build_activity_json(
609+
actor: actor,
610+
object:
611+
build_object_json(
612+
name: "My cool topic title",
613+
attributed_to: actor,
614+
attachments: attachments_with_link_objects,
615+
),
616+
type: "Create",
617+
)
618+
end
619+
620+
it "creates records for all attachments" do
621+
perform_process(new_post_json, delivered_to)
622+
object =
623+
DiscourseActivityPubObject.find_by(ap_type: "Note", attributed_to_id: actor.ap_id)
624+
expect(object.attachments.size).to eq(2)
625+
expect(object.attachments.first.ap_type).to eq("Document")
626+
expect(object.attachments.first.url).to eq(attachment_url_1)
627+
expect(object.attachments.first.media_type).to eq("image/png")
628+
expect(object.attachments.second.ap_type).to eq("Image")
629+
expect(object.attachments.second.url).to eq(attachment_url_2)
630+
expect(object.attachments.second.media_type).to eq("image/jpeg")
631+
end
632+
end
591633
end
592634

593635
context "with unsupported attachment types" do

0 commit comments

Comments
 (0)