diff --git a/lib/diaspora_federation/entities.rb b/lib/diaspora_federation/entities.rb index 66bb1aa..a7689d0 100644 --- a/lib/diaspora_federation/entities.rb +++ b/lib/diaspora_federation/entities.rb @@ -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" diff --git a/lib/diaspora_federation/entities/embed.rb b/lib/diaspora_federation/entities/embed.rb new file mode 100644 index 0000000..166b99a --- /dev/null +++ b/lib/diaspora_federation/entities/embed.rb @@ -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 diff --git a/lib/diaspora_federation/entities/status_message.rb b/lib/diaspora_federation/entities/status_message.rb index c2eda40..6a50b61 100644 --- a/lib/diaspora_federation/entities/status_message.rb +++ b/lib/diaspora_federation/entities/status_message.rb @@ -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 diff --git a/lib/diaspora_federation/schemas/federation_entities.json b/lib/diaspora_federation/schemas/federation_entities.json index 7196fdd..42ecf23 100644 --- a/lib/diaspora_federation/schemas/federation_entities.json +++ b/lib/diaspora_federation/schemas/federation_entities.json @@ -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" } + } + } + } } } } diff --git a/lib/diaspora_federation/test/factories.rb b/lib/diaspora_federation/test/factories.rb index 40744fb..38e8d99 100644 --- a/lib/diaspora_federation/test/factories.rb +++ b/lib/diaspora_federation/test/factories.rb @@ -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 diff --git a/lib/diaspora_federation/validators.rb b/lib/diaspora_federation/validators.rb index 864ab8e..986daef 100644 --- a/lib/diaspora_federation/validators.rb +++ b/lib/diaspora_federation/validators.rb @@ -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" diff --git a/lib/diaspora_federation/validators/embed_validator.rb b/lib/diaspora_federation/validators/embed_validator.rb new file mode 100644 index 0000000..de766cc --- /dev/null +++ b/lib/diaspora_federation/validators/embed_validator.rb @@ -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 diff --git a/spec/lib/diaspora_federation/entities/embed_spec.rb b/spec/lib/diaspora_federation/entities/embed_spec.rb new file mode 100644 index 0000000..7b857e4 --- /dev/null +++ b/spec/lib/diaspora_federation/entities/embed_spec.rb @@ -0,0 +1,56 @@ +module DiasporaFederation + describe Entities::Embed do + let(:data) { Fabricate.attributes_for(:embed_entity) } + + let(:xml) { <<-XML } + + #{data[:url]} + #{data[:title]} + #{data[:description]} + #{data[:image]} + +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 diff --git a/spec/lib/diaspora_federation/entities/status_message_spec.rb b/spec/lib/diaspora_federation/entities/status_message_spec.rb index 52d550a..4fe600a 100644 --- a/spec/lib/diaspora_federation/entities/status_message_spec.rb +++ b/spec/lib/diaspora_federation/entities/status_message_spec.rb @@ -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 diff --git a/spec/lib/diaspora_federation/validators/embed_validator_spec.rb b/spec/lib/diaspora_federation/validators/embed_validator_spec.rb new file mode 100644 index 0000000..d6045b3 --- /dev/null +++ b/spec/lib/diaspora_federation/validators/embed_validator_spec.rb @@ -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 diff --git a/spec/lib/diaspora_federation/validators/web_finger_validator_spec.rb b/spec/lib/diaspora_federation/validators/web_finger_validator_spec.rb index bad3334..8aaf77b 100644 --- a/spec/lib/diaspora_federation/validators/web_finger_validator_spec.rb +++ b/spec/lib/diaspora_federation/validators/web_finger_validator_spec.rb @@ -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 diff --git a/spec/support/shared_validator_specs.rb b/spec/support/shared_validator_specs.rb index 97f46ee..862148f 100644 --- a/spec/support/shared_validator_specs.rb +++ b/spec/support/shared_validator_specs.rb @@ -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"))