Merge pull request #101 from SuperTux88/add-embed-entity
Add embed entity
This commit is contained in:
commit
8d6006ce73
14 changed files with 298 additions and 4 deletions
69
docs/_entities/embed.md
Normal file
69
docs/_entities/embed.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
title: Embed
|
||||
---
|
||||
|
||||
This entity represents the embed information about an URL that should be
|
||||
embedded, it is nested in a [StatusMessage][status_message]. To embed a URL
|
||||
means to keep an embedded representation or a preview of a third party
|
||||
resource referenced by the URL inside the status message.
|
||||
|
||||
* If this entity is present, the receiving server should only embed the included
|
||||
`url` and not search for other URLs to embed.
|
||||
* If the included `url` is a
|
||||
trusted oEmbed provider, the server should query the oEmbed data.
|
||||
* If `title`, `description` or `image` are missing, the server should query the
|
||||
information from the URL (oEmbed or OpenGraph).
|
||||
* If `nothing` is `true` the server should not embed any URLs.
|
||||
|
||||
A link to the embedded resource should also be included in the `text` of the
|
||||
[StatusMessage][status_message] for accessibility reasons, otherwise it could
|
||||
happen that some people don't see the link, for example when this entity isn't
|
||||
implemented or where no embeds are supported at all. However, it is possible
|
||||
that the link in the `text` and the `url` here are different, because some sites
|
||||
have different URLs in `og:url` as requested.
|
||||
|
||||
## Optional Properties
|
||||
|
||||
All properties are optional, but either `url` is required or `nothing` must be `true`.
|
||||
|
||||
| Property | Type (Length) | Description |
|
||||
| ------------- | ------------------------ | ------------------------------------- |
|
||||
| `url` | [URL][url] (65535) | The URL that should be embedded. |
|
||||
| `title` | [String][string] (255) | The title of the embedded URL. |
|
||||
| `description` | [String][string] (65535) | The description of the embedded URL. |
|
||||
| `image` | [URL][url] (65535) | The image of the embedded URL. |
|
||||
| `nothing` | [Boolean][boolean] | `true` if nothing should be embedded. |
|
||||
|
||||
## Example
|
||||
|
||||
### Only `url`
|
||||
|
||||
~~~xml
|
||||
<embed>
|
||||
<url>https://example.org/</url>
|
||||
</embed>
|
||||
~~~
|
||||
|
||||
### With metadata
|
||||
|
||||
~~~xml
|
||||
<embed>
|
||||
<url>https://example.org/</url>
|
||||
<title>Example Website</title>
|
||||
<description>This is an example!</description>
|
||||
<image>https://example.org/example.png</image>
|
||||
</embed>
|
||||
~~~
|
||||
|
||||
### With `nothing`
|
||||
|
||||
~~~xml
|
||||
<embed>
|
||||
<nothing>true</nothing>
|
||||
</embed>
|
||||
~~~
|
||||
|
||||
[string]: {{ site.baseurl }}/federation/types.html#string
|
||||
[url]: {{ site.baseurl }}/federation/types.html#url
|
||||
[boolean]: {{ site.baseurl }}/federation/types.html#url
|
||||
[status_message]: {{ site.baseurl }}/entities/status_message.html
|
||||
|
|
@ -24,6 +24,7 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
| `photo` | [Photo][photo]s | ✔ | The attached Photos of the status message, the `status_message_guid` and the `author` need to match the status message. |
|
||||
| `poll` | [Poll][poll] | ✘ | The attached Poll of the status message. |
|
||||
| `event` | [Event][event] | ✘ | The attached Event of the status message. |
|
||||
| `embed` | [Embed][embed] | ✔ | The Embed information of an URL that should be embedded in the status message. |
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
@ -47,12 +48,12 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
<guid>c3893bf029e7013487753131731751e9</guid>
|
||||
<created_at>2016-07-11T22:50:18Z</created_at>
|
||||
<text>I am a very interesting status update</text>
|
||||
<public>true</public>
|
||||
<location>
|
||||
<address>Vienna, Austria</address>
|
||||
<lat>48.208174</lat>
|
||||
<lng>16.373819</lng>
|
||||
</location>
|
||||
<public>true</public>
|
||||
</status_message>
|
||||
~~~
|
||||
|
||||
|
|
@ -64,6 +65,7 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
<guid>e05828d029e7013487753131731751e9</guid>
|
||||
<created_at>2016-07-11T22:52:56Z</created_at>
|
||||
<text>I am a very interesting status update</text>
|
||||
<public>true</public>
|
||||
<photo>
|
||||
<guid>0788070029e8013487753131731751e9</guid>
|
||||
<author>alice@example.org</author>
|
||||
|
|
@ -86,7 +88,6 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
<height>480</height>
|
||||
<width>800</width>
|
||||
</photo>
|
||||
<public>true</public>
|
||||
</status_message>
|
||||
|
||||
~~~
|
||||
|
|
@ -99,6 +100,7 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
<guid>378473f029e9013487753131731751e9</guid>
|
||||
<created_at>2016-07-11T23:00:42Z</created_at>
|
||||
<text>I am a very interesting status update</text>
|
||||
<public>true</public>
|
||||
<poll>
|
||||
<guid>2a22d6c029e9013487753131731751e9</guid>
|
||||
<question>Select an answer</question>
|
||||
|
|
@ -115,7 +117,24 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
<answer>Maybe</answer>
|
||||
</poll_answer>
|
||||
</poll>
|
||||
</status_message>
|
||||
~~~
|
||||
|
||||
### With [Embed][embed]
|
||||
|
||||
~~~xml
|
||||
<status_message>
|
||||
<author>alice@example.org</author>
|
||||
<guid>378473f029e9013487753131731751e9</guid>
|
||||
<created_at>2016-07-11T23:00:42Z</created_at>
|
||||
<text>I am a very interesting status update</text>
|
||||
<public>true</public>
|
||||
<embed>
|
||||
<url>https://example.org/</url>
|
||||
<title>Example Website</title>
|
||||
<description>This is an example!</description>
|
||||
<image>https://example.org/example.png</image>
|
||||
</embed>
|
||||
</status_message>
|
||||
~~~
|
||||
|
||||
|
|
@ -128,6 +147,7 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
<created_at>2016-07-11T23:02:24Z</created_at>
|
||||
<provider_display_name>mobile</provider_display_name>
|
||||
<text>i am a very interesting status update</text>
|
||||
<public>true</public>
|
||||
<photo>
|
||||
<guid>0788070029e8013487753131731751e9</guid>
|
||||
<author>alice@example.org</author>
|
||||
|
|
@ -171,7 +191,12 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
<answer>Maybe</answer>
|
||||
</poll_answer>
|
||||
</poll>
|
||||
<public>true</public>
|
||||
<embed>
|
||||
<url>https://example.org/</url>
|
||||
<title>Example Website</title>
|
||||
<description>This is an example!</description>
|
||||
<image>https://example.org/example.png</image>
|
||||
</embed>
|
||||
</status_message>
|
||||
~~~
|
||||
|
||||
|
|
@ -186,3 +211,4 @@ This entity represents a reshare of a status message. It inherits from [Post][po
|
|||
[photo]: {{ site.baseurl }}/entities/photo.html
|
||||
[poll]: {{ site.baseurl }}/entities/poll.html
|
||||
[event]: {{ site.baseurl }}/entities/event.html
|
||||
[embed]: {{ site.baseurl }}/entities/embed.html
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ require "diaspora_federation/entities/poll_answer"
|
|||
require "diaspora_federation/entities/poll"
|
||||
require "diaspora_federation/entities/poll_participation"
|
||||
|
||||
require "diaspora_federation/entities/embed"
|
||||
require "diaspora_federation/entities/location"
|
||||
|
||||
require "diaspora_federation/entities/event"
|
||||
|
|
|
|||
44
lib/diaspora_federation/entities/embed.rb
Normal file
44
lib/diaspora_federation/entities/embed.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
module DiasporaFederation
|
||||
module Entities
|
||||
# This entity is used to specify embed information about an URL that should be embedded.
|
||||
#
|
||||
# @see Validators::EmbedValidator
|
||||
class Embed < Entity
|
||||
# @!attribute [r] url
|
||||
# URL that should be embedded.
|
||||
# @return [String] url
|
||||
property :url, :string, optional: true
|
||||
|
||||
# @!attribute [r] title
|
||||
# The title of the embedded URL.
|
||||
# @return [String] title
|
||||
property :title, :string, optional: true
|
||||
|
||||
# @!attribute [r] description
|
||||
# The description of the embedded URL.
|
||||
# @return [String] description
|
||||
property :description, :string, optional: true
|
||||
|
||||
# @!attribute [r] image
|
||||
# The image of the embedded URL.
|
||||
# @return [String] image
|
||||
property :image, :string, optional: true
|
||||
|
||||
# @!attribute [r] nothing
|
||||
# True, if nothing should be embedded.
|
||||
# @return [String] nothing
|
||||
property :nothing, :boolean, optional: true
|
||||
|
||||
# @return [String] string representation of this object
|
||||
def to_s
|
||||
"Embed#{":#{url}" if url}"
|
||||
end
|
||||
|
||||
def validate
|
||||
super
|
||||
|
||||
raise ValidationError, "Either 'url' must be set or 'nothing' must be 'true'" unless nothing ^ url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -36,6 +36,11 @@ module DiasporaFederation
|
|||
# @return [Entities::Event] event
|
||||
entity :event, Entities::Event, optional: true
|
||||
|
||||
# @!attribute [r] embed
|
||||
# Optional embed information of an URL that should be embedded in the status message
|
||||
# @return [Entities::Embed] embed
|
||||
entity :embed, Entities::Embed, optional: true
|
||||
|
||||
private
|
||||
|
||||
def validate
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
{"$ref": "#/definitions/reshare"},
|
||||
{"$ref": "#/definitions/profile"},
|
||||
{"$ref": "#/definitions/location"},
|
||||
{"$ref": "#/definitions/embed"},
|
||||
{"$ref": "#/definitions/photo"},
|
||||
{"$ref": "#/definitions/poll"},
|
||||
{"$ref": "#/definitions/poll_answer"}
|
||||
|
|
@ -373,6 +374,26 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"embed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity_type": {
|
||||
"type": "string",
|
||||
"pattern": "^embed$"
|
||||
},
|
||||
"entity_data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": { "type": ["string", "null"] },
|
||||
"title": { "type": ["string", "null"] },
|
||||
"description": { "type": ["string", "null"] },
|
||||
"image": { "type": ["string", "null"] },
|
||||
"nothing": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,6 +207,14 @@ module DiasporaFederation
|
|||
edited_at { Time.now.utc }
|
||||
end
|
||||
|
||||
Fabricator(:embed_entity, class_name: DiasporaFederation::Entities::Embed) do
|
||||
url "https://example.org/"
|
||||
title "Example Website"
|
||||
description "This is an example!"
|
||||
image "https://example.org/example.png"
|
||||
nothing nil
|
||||
end
|
||||
|
||||
Fabricator(:related_entity, class_name: DiasporaFederation::Entities::RelatedEntity) do
|
||||
author { Fabricate.sequence(:diaspora_id) }
|
||||
local true
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ require "diaspora_federation/validators/account_migration_validator"
|
|||
require "diaspora_federation/validators/comment_validator"
|
||||
require "diaspora_federation/validators/contact_validator"
|
||||
require "diaspora_federation/validators/conversation_validator"
|
||||
require "diaspora_federation/validators/embed_validator"
|
||||
require "diaspora_federation/validators/event_participation_validator"
|
||||
require "diaspora_federation/validators/event_validator"
|
||||
require "diaspora_federation/validators/h_card_validator"
|
||||
|
|
|
|||
13
lib/diaspora_federation/validators/embed_validator.rb
Normal file
13
lib/diaspora_federation/validators/embed_validator.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module DiasporaFederation
|
||||
module Validators
|
||||
# This validates a {Entities::Embed}.
|
||||
class EmbedValidator < OptionalAwareValidator
|
||||
include Validation
|
||||
|
||||
rule :url, :URI
|
||||
rule :title, length: {maximum: 255}
|
||||
rule :description, length: {maximum: 65_535}
|
||||
rule :image, URI: %i[host path]
|
||||
end
|
||||
end
|
||||
end
|
||||
56
spec/lib/diaspora_federation/entities/embed_spec.rb
Normal file
56
spec/lib/diaspora_federation/entities/embed_spec.rb
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
module DiasporaFederation
|
||||
describe Entities::Embed do
|
||||
let(:data) { Fabricate.attributes_for(:embed_entity) }
|
||||
|
||||
let(:xml) { <<-XML }
|
||||
<embed>
|
||||
<url>#{data[:url]}</url>
|
||||
<title>#{data[:title]}</title>
|
||||
<description>#{data[:description]}</description>
|
||||
<image>#{data[:image]}</image>
|
||||
</embed>
|
||||
XML
|
||||
|
||||
let(:json) { <<-JSON }
|
||||
{
|
||||
"entity_type": "embed",
|
||||
"entity_data": {
|
||||
"url": "#{data[:url]}",
|
||||
"title": "#{data[:title]}",
|
||||
"description": "#{data[:description]}",
|
||||
"image": "#{data[:image]}"
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
let(:string) { "Embed:#{data[:url]}" }
|
||||
|
||||
it_behaves_like "an Entity subclass"
|
||||
|
||||
it_behaves_like "an XML Entity"
|
||||
|
||||
it_behaves_like "a JSON Entity"
|
||||
|
||||
describe "#validate" do
|
||||
it "allows 'url' to be set if 'nothing' is not true" do
|
||||
expect { Entities::Embed.new(data) }.not_to raise_error
|
||||
end
|
||||
|
||||
it "allows 'url' to be missing if 'nothing' is true" do
|
||||
expect { Entities::Embed.new(nothing: true) }.not_to raise_error
|
||||
end
|
||||
|
||||
it "doesn't allow 'url' to be set if 'nothing' is true" do
|
||||
expect {
|
||||
Entities::Embed.new(data.merge(nothing: true))
|
||||
}.to raise_error Entity::ValidationError, "Either 'url' must be set or 'nothing' must be 'true'"
|
||||
end
|
||||
|
||||
it "doesn't allow 'url' to be missing if 'nothing' is not true" do
|
||||
expect {
|
||||
Entities::Embed.new({})
|
||||
}.to raise_error Entity::ValidationError, "Either 'url' must be set or 'nothing' must be 'true'"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -10,6 +10,7 @@ module DiasporaFederation
|
|||
location: location,
|
||||
poll: nil,
|
||||
event: nil,
|
||||
embed: nil,
|
||||
provider_display_name: "something"
|
||||
)
|
||||
}
|
||||
|
|
@ -137,6 +138,8 @@ XML
|
|||
expect(parsed_instance.photos).to eq([])
|
||||
expect(parsed_instance.location).to be_nil
|
||||
expect(parsed_instance.poll).to be_nil
|
||||
expect(parsed_instance.event).to be_nil
|
||||
expect(parsed_instance.embed).to be_nil
|
||||
expect(parsed_instance.public).to be_falsey
|
||||
expect(parsed_instance.provider_display_name).to be_nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
module DiasporaFederation
|
||||
describe Validators::EmbedValidator do
|
||||
let(:entity) { :embed_entity }
|
||||
it_behaves_like "a common validator"
|
||||
|
||||
describe "#url" do
|
||||
it_behaves_like "a property with a value validation/restriction" do
|
||||
let(:property) { :url }
|
||||
let(:wrong_values) { %w[https://asdf$%.com example.com] }
|
||||
let(:correct_values) { [nil, "https://example.org", "https://example.org/index.html"] }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#title" do
|
||||
it_behaves_like "a length validator" do
|
||||
let(:property) { :title }
|
||||
let(:length) { 255 }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#description" do
|
||||
it_behaves_like "a length validator" do
|
||||
let(:property) { :description }
|
||||
let(:length) { 65_535 }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#image" do
|
||||
it_behaves_like "a property with a value validation/restriction" do
|
||||
let(:property) { :image }
|
||||
let(:wrong_values) { %w[https://asdf$%.com example.com] }
|
||||
let(:correct_values) { [nil] }
|
||||
end
|
||||
|
||||
it_behaves_like "a url path validator" do
|
||||
let(:property) { :image }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -27,7 +27,7 @@ module DiasporaFederation
|
|||
describe "##{prop}" do
|
||||
it_behaves_like "a property with a value validation/restriction" do
|
||||
let(:property) { prop }
|
||||
let(:wrong_values) { %w[https://asdf$.com example.com] }
|
||||
let(:wrong_values) { %w[https://asdf$%.com example.com] }
|
||||
let(:correct_values) { [nil] }
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -277,6 +277,13 @@ shared_examples "a url validator without path" do
|
|||
end
|
||||
|
||||
shared_examples "a url path validator" do
|
||||
it "validates url with a path" do
|
||||
validator = described_class.new(entity_stub(entity, property => "https://example.com/some/path"))
|
||||
|
||||
expect(validator).to be_valid
|
||||
expect(validator.errors).to be_empty
|
||||
end
|
||||
|
||||
it "fails for url with special chars" do
|
||||
validator = described_class.new(entity_stub(entity, property => "https://asdf$%.com/some/path"))
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue