add types for string properties

This commit is contained in:
Benjamin Neff 2016-12-28 00:59:34 +01:00
parent a91e3c2c97
commit dd1c16ce91
43 changed files with 311 additions and 196 deletions

View file

@ -43,16 +43,16 @@ module DiasporaFederation
# @!attribute [r] guid
# @see Entities::Person#guid
# @return [String] guid
property :guid
property :guid, :string
# @!attribute [r] nickname
# The first part of the diaspora* ID
# @return [String] nickname
property :nickname, default: nil
property :nickname, :string, default: nil
# @!attribute [r] full_name
# @return [String] display name of the user
property :full_name
property :full_name, :string
# @!attribute [r] url
# @deprecated should be changed to the profile url. The pod url is in
@ -60,7 +60,7 @@ module DiasporaFederation
# installations).
#
# @return [String] link to the pod
property :url, default: nil
property :url, :string, default: nil
# @!attribute [r] public_key
# When a user is created on the pod, the pod MUST generate a pgp keypair
@ -69,17 +69,17 @@ module DiasporaFederation
# "-----BEGIN PUBLIC KEY-----" and ending with "-----END PUBLIC KEY-----".
#
# @return [String] public key
property :public_key
property :public_key, :string
# @!attribute [r] photo_large_url
# @return [String] url to the big avatar (300x300)
property :photo_large_url
property :photo_large_url, :string
# @!attribute [r] photo_medium_url
# @return [String] url to the medium avatar (100x100)
property :photo_medium_url
property :photo_medium_url, :string
# @!attribute [r] photo_small_url
# @return [String] url to the small avatar (50x50)
property :photo_small_url
property :photo_small_url, :string
# @!attribute [r] first_name
# @deprecated We decided to only use one name field, these should be removed
@ -87,7 +87,7 @@ module DiasporaFederation
#
# @see #full_name
# @return [String] first name
property :first_name
property :first_name, :string
# @!attribute [r] last_name
# @deprecated We decided to only use one name field, these should be removed
@ -95,7 +95,7 @@ module DiasporaFederation
#
# @see #full_name
# @return [String] last name
property :last_name
property :last_name, :string
# @!attribute [r] searchable
# @deprecated As this is a simple property, consider move to WebFinger instead
@ -105,7 +105,7 @@ module DiasporaFederation
#
# flag if a user is searchable by name
# @return [Boolean] searchable flag
property :searchable
property :searchable, :boolean
# CSS selectors for finding all the hCard fields
SELECTORS = {

View file

@ -35,24 +35,24 @@ module DiasporaFederation
# The Subject element should contain the webfinger address that was asked
# for. If it does not, then this webfinger profile MUST be ignored.
# @return [String]
property :acct_uri
property :acct_uri, :string
# @!attribute [r] alias_url
# @note could be nil
# @return [String] link to the users profile
property :alias_url
property :alias_url, :string
# @!attribute [r] hcard_url
# @return [String] link to the +hCard+
property :hcard_url
property :hcard_url, :string
# @!attribute [r] seed_url
# @return [String] link to the pod
property :seed_url
property :seed_url, :string
# @!attribute [r] profile_url
# @return [String] link to the users profile
property :profile_url
property :profile_url, :string
# @!attribute [r] atom_url
# This atom feed is an Activity Stream of the user's public posts. diaspora*
@ -63,18 +63,18 @@ module DiasporaFederation
# Note that this feed MAY also be made available through the PubSubHubbub
# mechanism by supplying a <link rel="hub"> in the atom feed itself.
# @return [String] atom feed url
property :atom_url
property :atom_url, :string
# @!attribute [r] salmon_url
# @note could be nil
# @return [String] salmon endpoint url
# @see https://cdn.rawgit.com/salmon-protocol/salmon-protocol/master/draft-panzer-salmon-00.html#SMLR
# Panzer draft for Salmon, paragraph 3.3
property :salmon_url
property :salmon_url, :string
# @!attribute [r] subscribe_url
# This url is used to find another user on the home-pod of the user in the webfinger.
property :subscribe_url
property :subscribe_url, :string
# @!attribute [r] guid
# @deprecated Either convert these to +Property+ elements or move to the
@ -84,7 +84,7 @@ module DiasporaFederation
# @see HCard#guid
# @see Entities::Person#guid
# @return [String] guid
property :guid
property :guid, :string
# @!attribute [r] public_key
# @deprecated Either convert these to +Property+ elements or move to the
@ -98,7 +98,7 @@ module DiasporaFederation
# DER-encoded PKCS#1 key beginning with the text
# "-----BEGIN PUBLIC KEY-----" and ending with "-----END PUBLIC KEY-----".
# @return [String] public key
property :public_key
property :public_key, :string
# +hcard_url+ link relation
REL_HCARD = "http://microformats.org/profile/hcard".freeze

View file

@ -12,7 +12,7 @@ module DiasporaFederation
# Alias for author
# @see AccountDeletion#author
# @return [String] diaspora* ID
property :author, alias: :diaspora_id, xml_name: :diaspora_handle
property :author, :string, alias: :diaspora_id, xml_name: :diaspora_handle
# @return [String] string representation of this object
def to_s

View file

@ -15,12 +15,12 @@ module DiasporaFederation
# @!attribute [r] text
# @return [String] the comment text
property :text
property :text, :string
# @!attribute [r] created_at
# Comment entity creation time
# @return [Time] creation time
property :created_at, default: -> { Time.now.utc }
property :created_at, :timestamp, default: -> { Time.now.utc }
end
end
end

View file

@ -9,21 +9,21 @@ module DiasporaFederation
# The diaspora* ID of the person who shares their profile
# @see Person#author
# @return [String] sender ID
property :author
property :author, :string
# @!attribute [r] recipient
# The diaspora* ID of the person who will be shared with
# @see Validation::Rule::DiasporaId
# @return [String] recipient ID
property :recipient
property :recipient, :string
# @!attribute [r] following
# @return [Boolean] if the author is following the person
property :following, default: true
property :following, :boolean, default: true
# @!attribute [r] sharing
# @return [Boolean] if the author is sharing with the person
property :sharing, default: true
property :sharing, :boolean, default: true
# @return [String] string representation of this object
def to_s

View file

@ -8,26 +8,26 @@ module DiasporaFederation
# The diaspora* ID of the person initiated the conversation
# @see Person#author
# @return [String] diaspora* ID
property :author, xml_name: :diaspora_handle
property :author, :string, xml_name: :diaspora_handle
# @!attribute [r] guid
# A random string of at least 16 chars
# @see Validation::Rule::Guid
# @return [String] conversation guid
property :guid
property :guid, :string
# @!attribute [r] subject
# @return [String] the conversation subject
property :subject
property :subject, :string
# @!attribute [r] created_at
# @return [Time] Conversation creation time
property :created_at, default: -> { Time.now.utc }
property :created_at, :timestamp, default: -> { Time.now.utc }
# @!attribute [r] participants
# The diaspora* IDs of the persons participating the conversation separated by ";"
# @return [String] participants diaspora* IDs
property :participants, xml_name: :participant_handles
property :participants, :string, xml_name: :participant_handles
# @!attribute [r] messages
# @return [[Entities::Message]] Messages of this conversation

View file

@ -14,14 +14,14 @@ module DiasporaFederation
# If +true+ set a like, if +false+, set a dislike (dislikes are currently not
# implemented in the diaspora* frontend).
# @return [Boolean] is it a like or a dislike
property :positive
property :positive, :boolean
# @!attribute [r] parent_type
# A string describing the type of the parent
# Can be "Post" or "Comment" (Comments are currently not implemented in the
# diaspora* frontend).
# @return [String] parent type
property :parent_type, xml_name: :target_type
property :parent_type, :string, xml_name: :target_type
end
end
end

View file

@ -7,17 +7,17 @@ module DiasporaFederation
# @!attribute [r] address
# A string describing your location, e.g. a city name, a street name, etc
# @return [String] address
property :address
property :address, :string
# @!attribute [r] lat
# Geographical latitude of your location
# @return [String] latitude
property :lat
property :lat, :string
# @!attribute [r] lng
# Geographical longitude of your location
# @return [String] longitude
property :lng
property :lng, :string
end
end
end

View file

@ -13,18 +13,18 @@ module DiasporaFederation
# @!attribute [r] text
# Text of the message composed by a user
# @return [String] text
property :text
property :text, :string
# @!attribute [r] created_at
# Message creation time
# @return [Time] creation time
property :created_at, default: -> { Time.now.utc }
property :created_at, :timestamp, default: -> { Time.now.utc }
# @!attribute [r] conversation_guid
# Guid of a conversation this message belongs to
# @see Conversation#guid
# @return [String] conversation guid
property :conversation_guid
property :conversation_guid, :string
# It is only valid to receive a {Message} from the author itself,
# or from the author of the parent {Conversation} if the author signature is valid.
@ -48,6 +48,16 @@ module DiasporaFederation
parent.author
end
# old timestamp format, because this signature is only used from old pods which also relay with old format
# @deprecated remove after {Message} doesn't include {Relayable} anymore
def normalize_property(name, value)
if name == :created_at
value.to_s
else
super
end
end
# Default implementation, don't verify signatures for a {Message}.
# @see Entity.populate_entity
# @deprecated remove after {Message} doesn't include {Relayable} anymore

View file

@ -14,7 +14,7 @@ module DiasporaFederation
# A string describing a type of the target to subscribe on
# Currently only "Post" is supported.
# @return [String] parent type
property :parent_type, xml_name: :target_type
property :parent_type, :string, xml_name: :target_type
# It is only valid to receive a {Participation} from the author themself.
# @deprecated remove after {Participation} doesn't include {Relayable} anymore

View file

@ -9,7 +9,7 @@ module DiasporaFederation
# MUST assign them a guid - a random string of at least 16 chars.
# @see Validation::Rule::Guid
# @return [String] guid
property :guid
property :guid, :string
# @!attribute [r] author
# The diaspora* ID of the person
@ -19,12 +19,12 @@ module DiasporaFederation
# alias for author
# @see Person#author
# @return [String] diaspora* ID
property :author, alias: :diaspora_id, xml_name: :diaspora_handle
property :author, :string, alias: :diaspora_id, xml_name: :diaspora_handle
# @!attribute [r] url
# @see Discovery::WebFinger#seed_url
# @return [String] link to the pod
property :url
property :url, :string
# @!attribute [r] profile
# All profile data of the person
@ -34,7 +34,7 @@ module DiasporaFederation
# @!attribute [r] exported_key
# @see Discovery::HCard#public_key
# @return [String] public key
property :exported_key
property :exported_key, :string
end
end
end

View file

@ -8,52 +8,52 @@ module DiasporaFederation
# A random string of at least 16 chars
# @see Validation::Rule::Guid
# @return [String] guid
property :guid
property :guid, :string
# @!attribute [r] author
# The diaspora* ID of the person who uploaded the photo
# @see Person#author
# @return [String] author diaspora* ID
property :author, xml_name: :diaspora_handle
property :author, :string, xml_name: :diaspora_handle
# @!attribute [r] public
# Points if the photo is visible to everyone or only to some aspects
# @return [Boolean] is it public
property :public, default: false
property :public, :boolean, default: false
# @!attribute [r] created_at
# Photo entity creation time
# @return [Time] creation time
property :created_at, default: -> { Time.now.utc }
property :created_at, :timestamp, default: -> { Time.now.utc }
# @!attribute [r] remote_photo_path
# An url of the photo on a remote server
# @return [String] remote photo url
property :remote_photo_path
property :remote_photo_path, :string
# @!attribute [r] remote_photo_name
# @return [String] remote photo name
property :remote_photo_name
property :remote_photo_name, :string
# @!attribute [r] text
# @return [String] text
property :text, default: nil
property :text, :string, default: nil
# @!attribute [r] status_message_guid
# Guid of a status message this photo belongs to
# @see StatusMessage#guid
# @return [String] guid
property :status_message_guid, default: nil
property :status_message_guid, :string, default: nil
# @!attribute [r] height
# Photo height
# @return [String] height
property :height
# @return [Integer] height
property :height, :integer
# @!attribute [r] width
# Photo width
# @return [String] width
property :width
# @return [Integer] width
property :width, :integer
end
end
end

View file

@ -8,12 +8,12 @@ module DiasporaFederation
# A random string of at least 16 chars
# @see Validation::Rule::Guid
# @return [String] poll guid
property :guid
property :guid, :string
# @!attribute [r] question
# Text of the question posed by a user
# @return [String] question
property :question
property :question, :string
# @!attribute [r] poll_answers
# Array of possible answers for the poll

View file

@ -8,12 +8,12 @@ module DiasporaFederation
# A random string of at least 16 chars
# @see Validation::Rule::Guid
# @return [String] guid
property :guid
property :guid, :string
# @!attribute [r] answer
# Text of the answer
# @return [String] answer
property :answer
property :answer, :string
end
end
end

View file

@ -17,7 +17,7 @@ module DiasporaFederation
# Guid of the answer selected by the user
# @see PollAnswer#guid
# @return [String] poll answer guid
property :poll_answer_guid
property :poll_answer_guid, :string
end
end
end

View file

@ -26,10 +26,10 @@ module DiasporaFederation
# @param [Entity] entity the entity in which it is included
def self.included(entity)
entity.class_eval do
property :author, xml_name: :diaspora_handle
property :guid
property :created_at, default: -> { Time.now.utc }
property :provider_display_name, default: nil
property :author, :string, xml_name: :diaspora_handle
property :guid, :string
property :created_at, :timestamp, default: -> { Time.now.utc }
property :provider_display_name, :string, default: nil
end
end
end

View file

@ -12,7 +12,7 @@ module DiasporaFederation
# Alias for author
# @see Profile#author
# @return [String] diaspora* ID
property :author, alias: :diaspora_id, xml_name: :diaspora_handle
property :author, :string, alias: :diaspora_id, xml_name: :diaspora_handle
# @!attribute [r] first_name
# @deprecated We decided to only use one name field, these should be removed
@ -20,7 +20,7 @@ module DiasporaFederation
# @see #full_name
# @see Discovery::HCard#first_name
# @return [String] first name
property :first_name, default: nil
property :first_name, :string, default: nil
# @!attribute [r] last_name
# @deprecated We decided to only use one name field, these should be removed
@ -28,33 +28,33 @@ module DiasporaFederation
# @see #full_name
# @see Discovery::HCard#last_name
# @return [String] last name
property :last_name, default: nil
property :last_name, :string, default: nil
# @!attribute [r] image_url
# @see Discovery::HCard#photo_large_url
# @return [String] url to the big avatar (300x300)
property :image_url, default: nil
property :image_url, :string, default: nil
# @!attribute [r] image_url_medium
# @see Discovery::HCard#photo_medium_url
# @return [String] url to the medium avatar (100x100)
property :image_url_medium, default: nil
property :image_url_medium, :string, default: nil
# @!attribute [r] image_url_small
# @see Discovery::HCard#photo_small_url
# @return [String] url to the small avatar (50x50)
property :image_url_small, default: nil
property :image_url_small, :string, default: nil
property :birthday, default: nil
property :gender, default: nil
property :bio, default: nil
property :location, default: nil
property :birthday, :string, default: nil
property :gender, :string, default: nil
property :bio, :string, default: nil
property :location, :string, default: nil
# @!attribute [r] searchable
# @see Discovery::HCard#searchable
# @return [Boolean] searchable flag
property :searchable, default: true
property :searchable, :boolean, default: true
property :nsfw, default: false
property :tag_string, default: nil
property :nsfw, :boolean, default: false
property :tag_string, :string, default: nil
# @return [String] string representation of this object
def to_s

View file

@ -7,17 +7,17 @@ module DiasporaFederation
# The diaspora* ID of the author
# @see Person#author
# @return [String] diaspora* ID
property :author
property :author, :string
# @!attribute [r] local
# +true+ if the owner of the entity is local on the pod
# @return [Boolean] is it a like or a dislike
property :local
property :local, :boolean
# @!attribute [r] public
# Shows whether the entity is visible to everyone or only to some aspects
# @return [Boolean] is it public
property :public, default: false
property :public, :boolean, default: false
# @!attribute [r] parent
# Parent if the entity also has a parent (Comment or Like) or +nil+ if it has no parent

View file

@ -54,11 +54,11 @@ module DiasporaFederation
# @param [Entity] klass the entity in which it is included
def self.included(klass)
klass.class_eval do
property :author, xml_name: :diaspora_handle
property :guid
property :parent_guid
property :author_signature, default: nil
property :parent_author_signature, default: nil
property :author, :string, xml_name: :diaspora_handle
property :guid, :string
property :parent_guid, :string
property :author_signature, :string, default: nil
property :parent_author_signature, :string, default: nil
entity :parent, Entities::RelatedEntity
end

View file

@ -24,25 +24,25 @@ module DiasporaFederation
# This signature is mandatory only when federating from an upstream author to the subscribers.
# @see Relayable#parent_author_signature
# @return [String] parent author signature
property :parent_author_signature, default: nil
property :parent_author_signature, :string, default: nil
# @!attribute [r] target_guid
# Guid of a relayable to be deleted
# @see Comment#guid
# @return [String] target guid
property :target_guid
property :target_guid, :string
# @!attribute [r] target_type
# A string describing a type of the target
# @see Retraction#target_type
# @return [String] target type
property :target_type
property :target_type, :string
# @!attribute [r] author
# The diaspora* ID of the person who deletes a relayable
# @see Person#author
# @return [String] diaspora* ID
property :author, xml_name: :sender_handle
property :author, :string, xml_name: :sender_handle
# @!attribute [r] target_author_signature
# Contains a signature of the entity using the private key of the
@ -51,7 +51,7 @@ module DiasporaFederation
# author is done.
# @see Relayable#author_signature
# @return [String] target author signature
property :target_author_signature, default: nil
property :target_author_signature, :string, default: nil
# @!attribute [r] target
# Target entity

View file

@ -10,13 +10,13 @@ module DiasporaFederation
# The diaspora* ID of the person who share their profile
# @see Person#author
# @return [String] sender ID
property :author, xml_name: :sender_handle
property :author, :string, xml_name: :sender_handle
# @!attribute [r] recipient
# The diaspora* ID of the person who will be shared with
# @see Validation::Rule::DiasporaId
# @return [String] recipient ID
property :recipient, xml_name: :recipient_handle
property :recipient, :string, xml_name: :recipient_handle
# Use only {Contact} for receive
# @return [Contact] instance as contact

View file

@ -10,18 +10,18 @@ module DiasporaFederation
# The diaspora* ID of the person who posted the original post
# @see Person#author
# @return [String] diaspora* ID
property :root_author, xml_name: :root_diaspora_id
property :root_author, :string, xml_name: :root_diaspora_id
# @!attribute [r] root_guid
# Guid of the original post
# @see StatusMessage#guid
# @return [String] root guid
property :root_guid
property :root_guid, :string
# @!attribute [r] public
# Has no meaning at the moment
# @return [Boolean] public
property :public, default: true # always true? (we only reshare public posts)
property :public, :boolean, default: true # always true? (we only reshare public posts)
# @return [String] string representation of this object
def to_s

View file

@ -8,17 +8,17 @@ module DiasporaFederation
# The diaspora* ID of the person who deletes the entity
# @see Person#author
# @return [String] diaspora* ID
property :author, xml_name: :diaspora_handle
property :author, :string, xml_name: :diaspora_handle
# @!attribute [r] target_guid
# Guid of the entity to be deleted
# @return [String] target guid
property :target_guid, xml_name: :post_guid
property :target_guid, :string, xml_name: :post_guid
# @!attribute [r] target_type
# A string describing the type of the target
# @return [String] target type
property :target_type, xml_name: :type
property :target_type, :string, xml_name: :type
# @!attribute [r] target
# Target entity

View file

@ -10,25 +10,25 @@ module DiasporaFederation
# Guid of a post to be deleted
# @see Retraction#target_guid
# @return [String] target guid
property :target_guid
property :target_guid, :string
# @!attribute [r] target_type
# A string describing the type of the target
# @see Retraction#target_type
# @return [String] target type
property :target_type
property :target_type, :string
# @!attribute [r] author
# The diaspora* ID of the person who deletes a post
# @see Person#author
# @return [String] diaspora* ID
property :author, xml_name: :sender_handle
property :author, :string, xml_name: :sender_handle
# @!attribute [r] author_signature
# Contains a signature of the entity using the private key of the author of a post
# This signature is mandatory.
# @return [String] author signature
property :target_author_signature, default: nil
property :target_author_signature, :string, default: nil
# @!attribute [r] target
# Target entity

View file

@ -9,7 +9,7 @@ module DiasporaFederation
# @!attribute [r] text
# Text of the status message composed by the user
# @return [String] text of the status message
property :text, xml_name: :raw_message
property :text, :string, xml_name: :raw_message
# @!attribute [r] photos
# Optional photos attached to the status message
@ -29,7 +29,7 @@ module DiasporaFederation
# @!attribute [r] public
# Shows whether the status message is visible to everyone or only to some aspects
# @return [Boolean] is it public
property :public, default: false
property :public, :boolean, default: false
private

View file

@ -76,7 +76,7 @@ module DiasporaFederation
properties.map {|key, value|
type = self.class.class_props[key]
if type == String || value.nil?
if type.instance_of?(Symbol) || value.nil?
[key, value]
elsif type.instance_of?(Class)
[key, value.to_h]
@ -160,11 +160,15 @@ module DiasporaFederation
type = self.class.class_props[name]
return false if type.nil? # property undefined
setable_string?(type, val) || setable_nested?(type, val) || setable_multi?(type, val)
setable_property?(type, val) || setable_nested?(type, val) || setable_multi?(type, val)
end
def setable_property?(type, val)
setable_string?(type, val) || type == :timestamp && val.is_a?(Time)
end
def setable_string?(type, val)
type == String && val.respond_to?(:to_s)
%i(string integer boolean).include?(type) && val.respond_to?(:to_s)
end
def setable_nested?(type, val)
@ -216,7 +220,18 @@ module DiasporaFederation
end
def normalized_properties
properties.map {|name, value| [name, self.class.class_props[name] == String ? value.to_s : value] }.to_h
properties.map {|name, value| [name, normalize_property(name, value)] }.to_h
end
def normalize_property(name, value)
case self.class.class_props[name]
when :string, :integer, :boolean
value.to_s
when :timestamp
value.nil? ? "" : value.utc.iso8601
else
value
end
end
# default: nothing to enrich
@ -265,12 +280,12 @@ module DiasporaFederation
end
# @param [String] name property name to parse
# @param [Class] type target type to parse
# @param [Class, Symbol] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML node to parse
# @return [Object] parsed data
private_class_method def self.parse_element_from_node(name, type, root_node)
if type == String
parse_string_from_node(name, root_node)
if type.instance_of?(Symbol)
parse_string_from_node(name, type, root_node)
elsif type.instance_of?(Array)
parse_array_from_node(type.first, root_node)
elsif type.ancestors.include?(Entity)
@ -281,12 +296,34 @@ module DiasporaFederation
# Create simple entry in data hash
#
# @param [String] name xml tag to parse
# @param [Class, Symbol] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML root_node to parse
# @return [String] data
private_class_method def self.parse_string_from_node(name, root_node)
private_class_method def self.parse_string_from_node(name, type, root_node)
node = root_node.xpath(name.to_s)
node = root_node.xpath(xml_names[name].to_s) if node.empty?
node.first.text if node.any?
parse_property(type, node.first.text) if node.any?
end
# @param [Symbol] type target type to parse
# @param [String] text data as string
# @return [String, Boolean, Integer, Time] data
private_class_method def self.parse_property(type, text)
case type
when :timestamp
begin
Time.parse(text).utc
rescue
nil
end
when :integer
text.to_i if text =~ /^\d+$/
when :boolean
return true if text =~ /(true|t|yes|y|1)$/i
false if text =~ /(false|f|no|n|0)$/i
else
text
end
end
# Create an entry in the data hash for the nested entity

View file

@ -18,12 +18,15 @@ module DiasporaFederation
# Define a generic (string-type) property
# @param [Symbol] name property name
# @param [Symbol] type property type
# @param [Hash] opts further options
# @option opts [Object, #call] :default a default value, making the
# property optional
# @option opts [Symbol] :xml_name another name used for xml generation
def property(name, opts={})
define_property name, String, opts
def property(name, type, opts={})
raise InvalidType unless property_type_valid?(type)
define_property name, type, opts
end
# Define a property that should contain another Entity or an array of
@ -35,7 +38,7 @@ module DiasporaFederation
# @option opts [Object, #call] :default a default value, making the
# property optional
def entity(name, type, opts={})
raise InvalidType unless type_valid?(type)
raise InvalidType unless entity_type_valid?(type)
define_property name, type, opts
end
@ -86,9 +89,11 @@ module DiasporaFederation
# @deprecated
def determine_xml_name(name, type, opts={})
raise ArgumentError, "xml_name is not supported for nested entities" if type != String && opts.has_key?(:xml_name)
if !type.instance_of?(Symbol) && opts.has_key?(:xml_name)
raise ArgumentError, "xml_name is not supported for nested entities"
end
if type == String
if type.instance_of?(Symbol)
if opts.has_key? :xml_name
raise InvalidName, "invalid xml_name" unless name_valid?(opts[:xml_name])
opts[:xml_name]
@ -121,10 +126,14 @@ module DiasporaFederation
name.instance_of?(Symbol)
end
def property_type_valid?(type)
%i(string integer boolean timestamp).include?(type)
end
# Checks if the type extends {Entity}
# @param [Class] type the type to check
# @return [Boolean]
def type_valid?(type)
def entity_type_valid?(type)
[type].flatten.all? {|type|
type.respond_to?(:ancestors) && type.ancestors.include?(Entity)
}

View file

@ -1,38 +1,38 @@
module DiasporaFederation
module Entities
class TestEntity < DiasporaFederation::Entity
property :test
property :test, :string
end
class TestDefaultEntity < DiasporaFederation::Entity
property :test1
property :test2
property :test3, default: true
property :test4, default: -> { true }
property :test1, :string
property :test2, :string
property :test3, :boolean, default: true
property :test4, :boolean, default: -> { true }
end
class OtherEntity < DiasporaFederation::Entity
property :asdf
property :asdf, :string
end
class TestNestedEntity < DiasporaFederation::Entity
property :asdf
property :asdf, :string
entity :test, TestEntity, default: nil
entity :multi, [OtherEntity]
end
class TestEntityWithXmlName < DiasporaFederation::Entity
property :test
property :qwer, xml_name: :asdf
property :test, :string
property :qwer, :string, xml_name: :asdf
end
class TestEntityWithRelatedEntity < DiasporaFederation::Entity
property :test
property :test, :string
entity :parent, RelatedEntity
end
class Entity < DiasporaFederation::Entity
property :test
property :test, :string
end
end

View file

@ -3,8 +3,9 @@ module DiasporaFederation
let(:parent) { FactoryGirl.create(:post, author: bob) }
let(:parent_entity) { FactoryGirl.build(:related_entity, author: bob.diaspora_id) }
let(:data) {
add_signatures(
FactoryGirl.build(:comment_entity, author: alice.diaspora_id, parent_guid: parent.guid, parent: parent_entity)
.send(:enriched_properties).merge(created_at: Time.now.utc, parent: parent_entity)
)
}
let(:xml) { <<-XML }
@ -33,14 +34,14 @@ XML
end
it "parses the created_at from the xml if it is included and correctly signed" do
created_at = Time.now.utc - 1.minute
created_at = Time.now.utc.change(usec: 0) - 1.minute
comment_data = FactoryGirl.build(:comment_entity, author: alice.diaspora_id, parent_guid: parent.guid).to_h
comment_data[:created_at] = created_at
comment_data[:parent] = parent_entity
comment = described_class.new(comment_data, %i(author guid parent_guid text created_at))
parsed_comment = described_class.from_xml(comment.to_xml)
expect(parsed_comment.created_at).to eq(created_at.to_s)
expect(parsed_comment.created_at).to eq(created_at)
end
end
end

View file

@ -3,12 +3,14 @@ module DiasporaFederation
let(:parent) { FactoryGirl.create(:conversation, author: bob) }
let(:parent_entity) { FactoryGirl.build(:related_entity, author: bob.diaspora_id) }
let(:signed_msg1) {
add_signatures(
FactoryGirl.build(:message_entity, author: bob.diaspora_id, parent_guid: parent.guid, parent: parent_entity)
.send(:enriched_properties).merge(parent: parent_entity)
)
}
let(:signed_msg2) {
add_signatures(
FactoryGirl.build(:message_entity, author: bob.diaspora_id, parent_guid: parent.guid, parent: parent_entity)
.send(:enriched_properties).merge(parent: parent_entity)
)
}
let(:data) {
FactoryGirl.attributes_for(:conversation_entity).merge!(
@ -24,7 +26,7 @@ module DiasporaFederation
<diaspora_handle>#{data[:author]}</diaspora_handle>
<guid>#{parent.guid}</guid>
<subject>#{data[:subject]}</subject>
<created_at>#{data[:created_at]}</created_at>
<created_at>#{data[:created_at].utc.iso8601}</created_at>
<participant_handles>#{data[:participants]}</participant_handles>
#{data[:messages].map {|a| a.to_xml.to_s.indent(2) }.join("\n")}
</conversation>

View file

@ -3,13 +3,15 @@ module DiasporaFederation
let(:parent) { FactoryGirl.create(:post, author: bob) }
let(:parent_entity) { FactoryGirl.build(:related_entity, author: bob.diaspora_id) }
let(:data) {
add_signatures(
FactoryGirl.build(
:like_entity,
author: alice.diaspora_id,
parent_guid: parent.guid,
parent_type: parent.entity_type,
parent: parent_entity
).send(:enriched_properties).merge(parent: parent_entity)
)
)
}
let(:xml) { <<-XML }

View file

@ -3,8 +3,9 @@ module DiasporaFederation
let(:parent) { FactoryGirl.create(:conversation, author: bob) }
let(:parent_entity) { FactoryGirl.build(:related_entity, author: bob.diaspora_id) }
let(:data) {
add_signatures(
FactoryGirl.build(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid, parent: parent_entity)
.send(:enriched_properties).merge(parent: parent_entity)
)
}
let(:xml) { <<-XML }
@ -36,19 +37,19 @@ XML
end
it "allows parent author if the signature is valid" do
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(data[:parent])
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(parent_entity)
expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.private_key)
expect(entity.sender_valid?(bob.diaspora_id)).to be_truthy
end
it "does not allow any other person" do
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(data[:parent])
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(parent_entity)
invalid_sender = FactoryGirl.generate(:diaspora_id)
expect(entity.sender_valid?(invalid_sender)).to be_falsey
end
it "does not allow the parent author if the signature is invalid" do
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(data[:parent])
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(parent_entity)
expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.private_key)
invalid_msg = Entities::Message.new(data.merge(author_signature: "aa"))
expect {

View file

@ -3,13 +3,15 @@ module DiasporaFederation
let(:parent) { FactoryGirl.create(:post, author: bob) }
let(:parent_entity) { FactoryGirl.build(:related_entity, author: bob.diaspora_id) }
let(:data) {
add_signatures(
FactoryGirl.build(
:participation_entity,
author: alice.diaspora_id,
parent_guid: parent.guid,
parent_type: parent.entity_type,
parent: parent_entity
).send(:enriched_properties).merge(parent: parent_entity)
)
)
}
let(:xml) { <<-XML }

View file

@ -7,7 +7,7 @@ module DiasporaFederation
<guid>#{data[:guid]}</guid>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<public>#{data[:public]}</public>
<created_at>#{data[:created_at]}</created_at>
<created_at>#{data[:created_at].utc.iso8601}</created_at>
<remote_photo_path>#{data[:remote_photo_path]}</remote_photo_path>
<remote_photo_name>#{data[:remote_photo_name]}</remote_photo_name>
<text>#{data[:text]}</text>

View file

@ -3,12 +3,14 @@ module DiasporaFederation
let(:parent) { FactoryGirl.create(:poll, author: bob) }
let(:parent_entity) { FactoryGirl.build(:related_entity, author: bob.diaspora_id) }
let(:data) {
add_signatures(
FactoryGirl.build(
:poll_participation_entity,
author: alice.diaspora_id,
parent_guid: parent.guid,
parent: parent_entity
).send(:enriched_properties).merge(parent: parent_entity)
)
)
}
let(:xml) { <<-XML }

View file

@ -21,7 +21,7 @@ module DiasporaFederation
include Entities::Relayable
property :property
property :property, :string
def parent_type
PARENT_TYPE

View file

@ -7,7 +7,7 @@ module DiasporaFederation
<reshare>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<guid>#{data[:guid]}</guid>
<created_at>#{data[:created_at]}</created_at>
<created_at>#{data[:created_at].utc.iso8601}</created_at>
<provider_display_name>#{data[:provider_display_name]}</provider_display_name>
<root_diaspora_id>#{data[:root_author]}</root_diaspora_id>
<root_guid>#{data[:root_guid]}</root_guid>

View file

@ -17,14 +17,14 @@ module DiasporaFederation
<status_message>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<guid>#{data[:guid]}</guid>
<created_at>#{data[:created_at]}</created_at>
<created_at>#{data[:created_at].utc.iso8601}</created_at>
<provider_display_name>#{data[:provider_display_name]}</provider_display_name>
<raw_message>#{data[:text]}</raw_message>
<photo>
<guid>#{photo1.guid}</guid>
<diaspora_handle>#{photo1.author}</diaspora_handle>
<public>#{photo1.public}</public>
<created_at>#{photo1.created_at}</created_at>
<created_at>#{photo1.created_at.utc.iso8601}</created_at>
<remote_photo_path>#{photo1.remote_photo_path}</remote_photo_path>
<remote_photo_name>#{photo1.remote_photo_name}</remote_photo_name>
<text>#{photo1.text}</text>
@ -36,7 +36,7 @@ module DiasporaFederation
<guid>#{photo2.guid}</guid>
<diaspora_handle>#{photo2.author}</diaspora_handle>
<public>#{photo2.public}</public>
<created_at>#{photo2.created_at}</created_at>
<created_at>#{photo2.created_at.utc.iso8601}</created_at>
<remote_photo_path>#{photo2.remote_photo_path}</remote_photo_path>
<remote_photo_name>#{photo2.remote_photo_name}</remote_photo_name>
<text>#{photo2.text}</text>

View file

@ -175,6 +175,23 @@ XML
expect(entity.test).to eq("asdf")
expect(entity.qwer).to eq("qwer")
end
it "parses the string to the correct type" do
xml = <<-XML.strip
<test_default_entity>
<test1>asdf</test1>
<test2>qwer</qwer2>
<test3>true</qwer3>
</test_default_entity>
XML
entity = Entities::TestDefaultEntity.from_xml(Nokogiri::XML::Document.parse(xml).root)
expect(entity).to be_an_instance_of Entities::TestDefaultEntity
expect(entity.test1).to eq("asdf")
expect(entity.test2).to eq("qwer")
expect(entity.test3).to eq(true)
end
end
context "nested entities" do

View file

@ -19,7 +19,7 @@ module DiasporaFederation
expect(magic_env.payload.guid).to eq(post.guid)
expect(magic_env.payload.author).to eq(post.author)
expect(magic_env.payload.text).to eq(post.text)
expect(magic_env.payload.public).to eq("true")
expect(magic_env.payload.public).to eq(post.public)
receiver
end
expect(receiver).to receive(:receive)
@ -42,7 +42,7 @@ module DiasporaFederation
expect(magic_env.payload.guid).to eq(post.guid)
expect(magic_env.payload.author).to eq(post.author)
expect(magic_env.payload.text).to eq(post.text)
expect(magic_env.payload.public).to eq("true")
expect(magic_env.payload.public).to eq(post.public)
receiver
end
expect(receiver).to receive(:receive)

View file

@ -4,43 +4,59 @@ module DiasporaFederation
context "simple properties" do
it "can name simple properties by symbol" do
dsl.property :test
dsl.property :test, :string
properties = dsl.class_props
expect(properties).to have(1).item
expect(properties[:test]).to eq(String)
expect(properties[:test]).to eq(:string)
expect(dsl.xml_names[:test]).to eq(:test)
end
it "will not accept other types for names" do
["test", 1234, true, {}].each do |val|
expect {
dsl.property val
dsl.property val, :string
}.to raise_error PropertiesDSL::InvalidName
end
end
it "will not accept other types for type" do
["test", 1234, true, {}].each do |val|
expect {
dsl.property :fail, val
}.to raise_error PropertiesDSL::InvalidType
end
end
it "accepts only supported types" do
%i(text number foobar).each do |val|
expect {
dsl.property :fail, val
}.to raise_error PropertiesDSL::InvalidType
end
end
it "can define multiple properties" do
dsl.property :test
dsl.property :asdf
dsl.property :zzzz
dsl.property :test, :string
dsl.property :asdf, :string
dsl.property :zzzz, :string
properties = dsl.class_props
expect(properties).to have(3).items
expect(properties.keys).to include(:test, :asdf, :zzzz)
properties.values.each {|type| expect(type).to eq(String) }
properties.values.each {|type| expect(type).to eq(:string) }
end
it "can add an xml name to simple properties with a symbol" do
dsl.property :test, xml_name: :xml_test
dsl.property :test, :string, xml_name: :xml_test
properties = dsl.class_props
expect(properties).to have(1).item
expect(properties[:test]).to eq(String)
expect(properties[:test]).to eq(:string)
expect(dsl.xml_names[:test]).to eq(:xml_test)
end
it "will not accept other types for xml names" do
["test", 1234, true, {}].each do |val|
expect {
dsl.property :test, xml_name: val
dsl.property :test, :string, xml_name: val
}.to raise_error PropertiesDSL::InvalidName, "invalid xml_name"
end
end
@ -91,13 +107,13 @@ module DiasporaFederation
describe ".default_values" do
it "can accept default values" do
dsl.property :test, default: :foobar
dsl.property :test, :string, default: :foobar
defaults = dsl.default_values
expect(defaults[:test]).to eq(:foobar)
end
it "can accept default blocks" do
dsl.property :test, default: -> { "default" }
dsl.property :test, :string, default: -> { "default" }
defaults = dsl.default_values
expect(defaults[:test]).to eq("default")
end
@ -105,27 +121,27 @@ module DiasporaFederation
describe ".resolv_aliases" do
it "resolves the defined alias" do
dsl.property :test, alias: :test_alias
dsl.property :test, :string, alias: :test_alias
data = dsl.resolv_aliases(test_alias: "foo")
expect(data[:test]).to eq("foo")
expect(data).not_to have_key(:test_alias)
end
it "raises when alias and original property are present" do
dsl.property :test, alias: :test_alias
dsl.property :test, :string, alias: :test_alias
expect {
dsl.resolv_aliases(test_alias: "foo", test: "bar")
}.to raise_error PropertiesDSL::InvalidData, "only use 'test_alias' OR 'test'"
end
it "returns original data if no alias is defined" do
dsl.property :test, alias: :test_alias
dsl.property :test, :string, alias: :test_alias
data = dsl.resolv_aliases(test: "foo")
expect(data[:test]).to eq("foo")
end
it "returns original data if alias is defined, but not present" do
dsl.property :test
dsl.property :test, :string
data = dsl.resolv_aliases(test: "foo")
expect(data[:test]).to eq("foo")
expect(data).not_to have_key(:test_alias)
@ -134,17 +150,17 @@ module DiasporaFederation
describe ".find_property_for_xml_name" do
it "finds property by xml_name" do
dsl.property :test, xml_name: :xml_test
dsl.property :test, :string, xml_name: :xml_test
expect(dsl.find_property_for_xml_name("xml_test")).to eq(:test)
end
it "finds property by name" do
dsl.property :test, xml_name: :xml_test
dsl.property :test, :string, xml_name: :xml_test
expect(dsl.find_property_for_xml_name("test")).to eq(:test)
end
it "returns nil if property is not defined" do
dsl.property :test, xml_name: :xml_test
dsl.property :test, :string, xml_name: :xml_test
expect(dsl.find_property_for_xml_name("unknown")).to be_nil
end
end

View file

@ -38,6 +38,14 @@ def expect_callback(*opts)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(*opts)
end
def add_signatures(entity)
properties = entity.send(:enriched_properties)
entity.to_h.tap do |hash|
hash[:author_signature] = properties[:author_signature]
hash[:parent_author_signature] = properties[:parent_author_signature]
end
end
# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
fixture_builder_file = "#{File.dirname(__FILE__)}/support/fixture_builder.rb"

View file

@ -75,14 +75,22 @@ shared_examples "an XML Entity" do |ignored_props=[]|
def validate_values(value, parsed_value, type, ignored_props)
if value.nil?
expect(parsed_value).to be_nil
elsif type == String
expect(parsed_value.to_s).to eq(value.to_s)
elsif type.instance_of?(Symbol)
validate_property(value, parsed_value)
elsif type.instance_of?(Array)
value.each_with_index {|entity, index| check_entity(entity, parsed_value[index], ignored_props) }
elsif type.ancestors.include?(DiasporaFederation::Entity)
check_entity(value, parsed_value, ignored_props)
end
end
def validate_property(value, parsed_value)
if value.is_a?(Time)
expect(parsed_value).to eq(value.change(usec: 0))
else
expect(parsed_value).to eq(value)
end
end
end
shared_examples "a relayable Entity" do