tentd 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +9 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +8 -0
- data/bin/tent-server +3 -0
- data/lib/tentd.rb +31 -0
- data/lib/tentd/api.rb +58 -0
- data/lib/tentd/api/apps.rb +196 -0
- data/lib/tentd/api/authentication_finalize.rb +12 -0
- data/lib/tentd/api/authentication_lookup.rb +27 -0
- data/lib/tentd/api/authentication_verification.rb +50 -0
- data/lib/tentd/api/authorizable.rb +21 -0
- data/lib/tentd/api/authorization.rb +14 -0
- data/lib/tentd/api/core_profile_data.rb +45 -0
- data/lib/tentd/api/followers.rb +218 -0
- data/lib/tentd/api/followings.rb +241 -0
- data/lib/tentd/api/groups.rb +161 -0
- data/lib/tentd/api/middleware.rb +32 -0
- data/lib/tentd/api/posts.rb +373 -0
- data/lib/tentd/api/profile.rb +78 -0
- data/lib/tentd/api/router.rb +123 -0
- data/lib/tentd/api/router/caching_headers.rb +49 -0
- data/lib/tentd/api/router/extract_params.rb +88 -0
- data/lib/tentd/api/router/serialize_response.rb +38 -0
- data/lib/tentd/api/user_lookup.rb +10 -0
- data/lib/tentd/core_ext/hash/slice.rb +29 -0
- data/lib/tentd/datamapper/array_property.rb +23 -0
- data/lib/tentd/datamapper/query.rb +19 -0
- data/lib/tentd/json_patch.rb +181 -0
- data/lib/tentd/model.rb +30 -0
- data/lib/tentd/model/app.rb +68 -0
- data/lib/tentd/model/app_authorization.rb +113 -0
- data/lib/tentd/model/follower.rb +105 -0
- data/lib/tentd/model/following.rb +100 -0
- data/lib/tentd/model/group.rb +24 -0
- data/lib/tentd/model/mention.rb +19 -0
- data/lib/tentd/model/notification_subscription.rb +56 -0
- data/lib/tentd/model/permissible.rb +227 -0
- data/lib/tentd/model/permission.rb +28 -0
- data/lib/tentd/model/post.rb +178 -0
- data/lib/tentd/model/post_attachment.rb +27 -0
- data/lib/tentd/model/post_version.rb +64 -0
- data/lib/tentd/model/profile_info.rb +80 -0
- data/lib/tentd/model/random_public_id.rb +46 -0
- data/lib/tentd/model/serializable.rb +58 -0
- data/lib/tentd/model/type_properties.rb +36 -0
- data/lib/tentd/model/user.rb +39 -0
- data/lib/tentd/model/user_scoped.rb +14 -0
- data/lib/tentd/notifications.rb +13 -0
- data/lib/tentd/notifications/girl_friday.rb +30 -0
- data/lib/tentd/notifications/sidekiq.rb +50 -0
- data/lib/tentd/tent_type.rb +20 -0
- data/lib/tentd/tent_version.rb +41 -0
- data/lib/tentd/version.rb +3 -0
- data/spec/fabricators/app_authorizations_fabricator.rb +5 -0
- data/spec/fabricators/apps_fabricator.rb +11 -0
- data/spec/fabricators/followers_fabricator.rb +14 -0
- data/spec/fabricators/followings_fabricator.rb +17 -0
- data/spec/fabricators/groups_fabricator.rb +3 -0
- data/spec/fabricators/mentions_fabricator.rb +3 -0
- data/spec/fabricators/notification_subscriptions_fabricator.rb +4 -0
- data/spec/fabricators/permissions_fabricator.rb +1 -0
- data/spec/fabricators/post_attachments_fabricator.rb +8 -0
- data/spec/fabricators/post_versions_fabricator.rb +12 -0
- data/spec/fabricators/posts_fabricator.rb +12 -0
- data/spec/fabricators/profile_infos_fabricator.rb +30 -0
- data/spec/integration/api/apps_spec.rb +466 -0
- data/spec/integration/api/followers_spec.rb +535 -0
- data/spec/integration/api/followings_spec.rb +688 -0
- data/spec/integration/api/groups_spec.rb +207 -0
- data/spec/integration/api/posts_spec.rb +874 -0
- data/spec/integration/api/profile_spec.rb +285 -0
- data/spec/integration/api/router_spec.rb +102 -0
- data/spec/integration/model/app_authorization_spec.rb +59 -0
- data/spec/integration/model/app_spec.rb +63 -0
- data/spec/integration/model/follower_spec.rb +344 -0
- data/spec/integration/model/following_spec.rb +97 -0
- data/spec/integration/model/group_spec.rb +39 -0
- data/spec/integration/model/notification_subscription_spec.rb +145 -0
- data/spec/integration/model/post_spec.rb +658 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/expect_server.rb +3 -0
- data/spec/support/json_request.rb +54 -0
- data/spec/support/with_constant.rb +23 -0
- data/spec/support/with_warnings.rb +6 -0
- data/spec/unit/api/authentication_finalize_spec.rb +45 -0
- data/spec/unit/api/authentication_lookup_spec.rb +65 -0
- data/spec/unit/api/authentication_verification_spec.rb +50 -0
- data/spec/unit/api/authorizable_spec.rb +50 -0
- data/spec/unit/api/authorization_spec.rb +44 -0
- data/spec/unit/api/caching_headers_spec.rb +121 -0
- data/spec/unit/core_profile_data_spec.rb +64 -0
- data/spec/unit/json_patch_spec.rb +407 -0
- data/spec/unit/tent_type_spec.rb +28 -0
- data/spec/unit/tent_version_spec.rb +68 -0
- data/tentd.gemspec +36 -0
- metadata +435 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module TentD
|
4
|
+
module Model
|
5
|
+
class Post
|
6
|
+
include DataMapper::Resource
|
7
|
+
include Permissible
|
8
|
+
include RandomPublicId
|
9
|
+
include Serializable
|
10
|
+
include TypeProperties
|
11
|
+
include UserScoped
|
12
|
+
|
13
|
+
storage_names[:default] = "posts"
|
14
|
+
|
15
|
+
property :id, Serial
|
16
|
+
property :entity, Text, :lazy => false, :unique_index => :upublic_id
|
17
|
+
property :public, Boolean, :default => false
|
18
|
+
property :licenses, Array, :default => []
|
19
|
+
property :content, Json, :default => {}
|
20
|
+
property :views, Json, :default => {}
|
21
|
+
property :published_at, DateTime, :default => lambda { |*args| Time.now }
|
22
|
+
property :received_at, DateTime, :default => lambda { |*args| Time.now }
|
23
|
+
property :updated_at, DateTime
|
24
|
+
property :deleted_at, ParanoidDateTime
|
25
|
+
property :app_name, Text, :lazy => false
|
26
|
+
property :app_url, Text, :lazy => false
|
27
|
+
property :original, Boolean, :default => false
|
28
|
+
|
29
|
+
has n, :permissions, 'TentD::Model::Permission', :constraint => :destroy
|
30
|
+
has n, :attachments, 'TentD::Model::PostAttachment', :constraint => :destroy
|
31
|
+
has n, :mentions, 'TentD::Model::Mention', :constraint => :destroy
|
32
|
+
belongs_to :app, 'TentD::Model::App', :required => false
|
33
|
+
belongs_to :following, 'TentD::Model::Following', :required => false
|
34
|
+
|
35
|
+
has n, :versions, 'TentD::Model::PostVersion', :constraint => :destroy
|
36
|
+
|
37
|
+
after :create, :create_version!
|
38
|
+
|
39
|
+
def create_version!(post = self)
|
40
|
+
attrs = post.attributes
|
41
|
+
attrs.delete(:id)
|
42
|
+
latest = post.versions.all(:order => :version.desc, :fields => [:version]).first
|
43
|
+
attrs[:version] = latest ? latest.version + 1 : 1
|
44
|
+
version = post.versions.create(attrs)
|
45
|
+
end
|
46
|
+
|
47
|
+
def latest_version(options = {})
|
48
|
+
versions.all({ :order => :version.desc }.merge(options)).first
|
49
|
+
end
|
50
|
+
|
51
|
+
def update(data)
|
52
|
+
mentions = data.delete(:mentions)
|
53
|
+
last_version = latest_version(:fields => [:id])
|
54
|
+
res = super(data)
|
55
|
+
|
56
|
+
create_version! # after update hook doe not fire
|
57
|
+
|
58
|
+
current_version = latest_version(:fields => [:id])
|
59
|
+
|
60
|
+
if mentions.to_a.any?
|
61
|
+
Mention.all(:post_id => self.id).update(:post_id => nil, :post_version_id => last_version.id)
|
62
|
+
mentions.each do |mention|
|
63
|
+
next unless mention[:entity]
|
64
|
+
self.mentions.create(:entity => mention[:entity], :mentioned_post_id => mention[:post], :post_version_id => current_version.id)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
res
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.create(data)
|
72
|
+
mentions = data.delete(:mentions)
|
73
|
+
post = super(data)
|
74
|
+
|
75
|
+
mentions.to_a.each do |mention|
|
76
|
+
next unless mention[:entity]
|
77
|
+
post.mentions.create(:entity => mention[:entity], :mentioned_post_id => mention[:post], :post_version_id => post.latest_version(:fields => [:id]).id)
|
78
|
+
end
|
79
|
+
|
80
|
+
if post.mentions.to_a.any? && post.original
|
81
|
+
post.mentions.each do |mention|
|
82
|
+
follower = Follower.first(:entity => mention.entity)
|
83
|
+
next if follower && NotificationSubscription.first(:follower => follower, :type_base => post.type.base)
|
84
|
+
|
85
|
+
Notifications.notify_entity(:entity => mention.entity, :post_id => post.id)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
post
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.fetch_with_permissions(params, current_auth)
|
93
|
+
super do |params, query, query_bindings|
|
94
|
+
if params.since_time
|
95
|
+
query << "AND posts.published_at > ?"
|
96
|
+
query_bindings << Time.at(params.since_time.to_i)
|
97
|
+
end
|
98
|
+
|
99
|
+
if params.before_time
|
100
|
+
query << "AND posts.published_at < ?"
|
101
|
+
query_bindings << Time.at(params.before_time.to_i)
|
102
|
+
end
|
103
|
+
|
104
|
+
if params.post_types
|
105
|
+
params.post_types = params.post_types.split(',').map { |url| URI.unescape(url) }
|
106
|
+
if params.post_types.any?
|
107
|
+
query << "AND posts.type_base IN ?"
|
108
|
+
query_bindings << params.post_types.map { |t| TentType.new(t).base }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if params.mentioned_post && params.mentioned_entity
|
113
|
+
select = query.shift
|
114
|
+
query.unshift "INNER JOIN mentions ON mentions.post_id = posts.id"
|
115
|
+
query.unshift select
|
116
|
+
|
117
|
+
query << "AND mentions.entity = ? AND mentions.mentioned_post_id = ?"
|
118
|
+
query_bindings << params.mentioned_entity
|
119
|
+
query_bindings << params.mentioned_post
|
120
|
+
end
|
121
|
+
|
122
|
+
unless params.return_count
|
123
|
+
query << "ORDER BY posts.published_at DESC"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.public_attributes
|
129
|
+
[:app_name, :app_url, :entity, :type, :licenses, :content, :published_at]
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.write_attributes
|
133
|
+
public_attributes + [:following_id, :original, :public, :mentions, :views]
|
134
|
+
end
|
135
|
+
|
136
|
+
def can_notify?(app_or_follow)
|
137
|
+
return true if public && original
|
138
|
+
case app_or_follow
|
139
|
+
when AppAuthorization
|
140
|
+
app_or_follow.scopes && app_or_follow.scopes.map(&:to_sym).include?(:read_posts) ||
|
141
|
+
app_or_follow.post_types && app_or_follow.post_types.include?(type.base)
|
142
|
+
when Follower
|
143
|
+
return false unless original
|
144
|
+
q = permissions.all(:follower_access_id => app_or_follow.id)
|
145
|
+
q += permissions.all(:group_public_id => app_or_follow.groups) if app_or_follow.groups.any?
|
146
|
+
q.any?
|
147
|
+
when Following
|
148
|
+
return false unless original
|
149
|
+
q = permissions.all(:following => app_or_follow)
|
150
|
+
q += permissions.all(:group_public_id => app_or_follow.groups) if app_or_follow.groups.any?
|
151
|
+
q.any?
|
152
|
+
else
|
153
|
+
false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def as_json(options = {})
|
158
|
+
attributes = super
|
159
|
+
attributes[:type] = type.uri
|
160
|
+
attributes[:version] = latest_version(:fields => [:version]).version
|
161
|
+
attributes[:app] = { :url => attributes.delete(:app_url), :name => attributes.delete(:app_name) }
|
162
|
+
|
163
|
+
attributes[:mentions] = mentions.map do |mention|
|
164
|
+
h = { :entity => mention.entity }
|
165
|
+
h[:post] = mention.mentioned_post_id if mention.mentioned_post_id
|
166
|
+
h
|
167
|
+
end
|
168
|
+
|
169
|
+
if options[:app]
|
170
|
+
attributes[:following_id] = following.public_id if following
|
171
|
+
end
|
172
|
+
|
173
|
+
Array(options[:exclude]).each { |k| attributes.delete(k) if k }
|
174
|
+
attributes
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module TentD
|
2
|
+
module Model
|
3
|
+
class PostAttachment
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
storage_names[:default] = "post_attachments"
|
7
|
+
|
8
|
+
property :id, Serial
|
9
|
+
property :type, Text, :required => true, :lazy => false
|
10
|
+
property :category, Text, :required => true, :lazy => false
|
11
|
+
property :name, Text, :required => true, :lazy => false
|
12
|
+
property :data, Text, :required => true
|
13
|
+
property :size, Integer, :required => true
|
14
|
+
timestamps :at
|
15
|
+
|
16
|
+
belongs_to :post, 'TentD::Model::Post', :required => false
|
17
|
+
belongs_to :post_version, 'TentD::Model::PostVersion', :required => false
|
18
|
+
|
19
|
+
validates_presence_of :post_id, :if => lambda { |m| m.post_version_id.nil? }
|
20
|
+
validates_presence_of :post_version_id, :if => lambda { |m| m.post_id.nil? }
|
21
|
+
|
22
|
+
def as_json(options = {})
|
23
|
+
super({ :exclude => [:id, :data, :post_id, :created_at, :updated_at] }.merge(options))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module TentD
|
2
|
+
module Model
|
3
|
+
class PostVersion
|
4
|
+
include DataMapper::Resource
|
5
|
+
include Permissible
|
6
|
+
include Serializable
|
7
|
+
include TypeProperties
|
8
|
+
include UserScoped
|
9
|
+
|
10
|
+
storage_names[:default] = "post_versions"
|
11
|
+
|
12
|
+
belongs_to :post, 'TentD::Model::Post', :required => true
|
13
|
+
property :version, Integer, :required => true
|
14
|
+
|
15
|
+
property :id, Serial
|
16
|
+
property :entity, Text, :lazy => false
|
17
|
+
property :public_id, String, :required => true
|
18
|
+
property :public, Boolean, :default => false
|
19
|
+
property :licenses, Array, :default => []
|
20
|
+
property :content, Json, :default => {}
|
21
|
+
property :views, Json, :default => {}
|
22
|
+
property :published_at, DateTime, :default => lambda { |*args| Time.now }
|
23
|
+
property :received_at, DateTime, :default => lambda { |*args| Time.now }
|
24
|
+
property :updated_at, DateTime
|
25
|
+
property :deleted_at, ParanoidDateTime
|
26
|
+
property :app_name, Text, :lazy => false
|
27
|
+
property :app_url, Text, :lazy => false
|
28
|
+
property :original, Boolean, :default => false
|
29
|
+
|
30
|
+
has n, :attachments, 'TentD::Model::PostAttachment', :constraint => :destroy
|
31
|
+
has n, :mentions, 'TentD::Model::Mention', :constraint => :destroy
|
32
|
+
belongs_to :app, 'TentD::Model::App', :required => false
|
33
|
+
belongs_to :following, 'TentD::Model::Following', :required => false
|
34
|
+
|
35
|
+
def self.public_attributes
|
36
|
+
Post.public_attributes
|
37
|
+
end
|
38
|
+
|
39
|
+
def as_json(options = {})
|
40
|
+
attributes = super
|
41
|
+
post_attrs = post.as_json(options)
|
42
|
+
|
43
|
+
attributes[:type] = type.uri
|
44
|
+
attributes[:version] = version
|
45
|
+
attributes[:app] = { :url => attributes.delete(:app_url), :name => attributes.delete(:app_name) }
|
46
|
+
|
47
|
+
attributes[:mentions] = mentions.map do |mention|
|
48
|
+
h = { :entity => mention.entity }
|
49
|
+
h[:post] = mention.mentioned_post_id if mention.mentioned_post_id
|
50
|
+
h
|
51
|
+
end
|
52
|
+
|
53
|
+
if options[:app]
|
54
|
+
attributes[:following_id] = following.public_id if following
|
55
|
+
end
|
56
|
+
|
57
|
+
attributes[:permissions] = post_attrs[:permissions]
|
58
|
+
|
59
|
+
Array(options[:exclude]).each { |k| attributes.delete(k) if k }
|
60
|
+
attributes
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module TentD
|
4
|
+
module Model
|
5
|
+
class ProfileInfo
|
6
|
+
include DataMapper::Resource
|
7
|
+
include TypeProperties
|
8
|
+
include UserScoped
|
9
|
+
include Permissible
|
10
|
+
|
11
|
+
TENT_PROFILE_TYPE_URI = 'https://tent.io/types/info/core/v0.1.0'
|
12
|
+
TENT_PROFILE_TYPE = TentType.new(TENT_PROFILE_TYPE_URI)
|
13
|
+
|
14
|
+
self.raise_on_save_failure = true
|
15
|
+
|
16
|
+
storage_names[:default] = 'profile_info'
|
17
|
+
|
18
|
+
property :id, Serial
|
19
|
+
property :public, Boolean, :default => false
|
20
|
+
property :content, Json, :default => {}, :lazy => false
|
21
|
+
property :created_at, DateTime
|
22
|
+
property :updated_at, DateTime
|
23
|
+
property :deleted_at, ParanoidDateTime
|
24
|
+
|
25
|
+
has n, :permissions, 'TentD::Model::Permission'
|
26
|
+
|
27
|
+
|
28
|
+
def self.tent_info
|
29
|
+
first(:type_base => TENT_PROFILE_TYPE.base, :order => :type_version.desc) || Hashie::Mash.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.get_profile(authorized_scopes = [], current_auth = nil)
|
33
|
+
h = if (authorized_scopes.include?(:read_profile) || authorized_scopes.include?(:write_profile)) && current_auth.respond_to?(:profile_info_types)
|
34
|
+
current_auth.profile_info_types.include?('all') ? all : all(:type_base => current_auth.profile_info_types.map { |t| TentType.new(t).base }) + all(:public => true)
|
35
|
+
else
|
36
|
+
fetch_with_permissions({}, current_auth)
|
37
|
+
end.inject({}) do |memo, info|
|
38
|
+
memo[info.type.uri] = info.content.merge(:permissions => info.permissions_json(authorized_scopes.include?(:read_permissions)))
|
39
|
+
memo
|
40
|
+
end
|
41
|
+
h
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.update_profile(type, data)
|
45
|
+
data = Hashie::Mash.new(data) unless data.kind_of?(Hashie::Mash)
|
46
|
+
type = TentType.new(type)
|
47
|
+
perms = data.delete(:permissions)
|
48
|
+
if (infos = all(:type_base => type.base)) && (info = infos.pop)
|
49
|
+
infos.to_a.each(&:destroy)
|
50
|
+
info.type = type
|
51
|
+
info.public = data.delete(:public)
|
52
|
+
info.content = data
|
53
|
+
info.save
|
54
|
+
else
|
55
|
+
info = create(:type => type, :public => data.delete(:public), :content => data)
|
56
|
+
end
|
57
|
+
info.assign_permissions(perms)
|
58
|
+
info
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.create_update_post(id)
|
62
|
+
first(:id => id).create_update_post
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_update_post
|
66
|
+
post = user.posts.create(
|
67
|
+
:type => 'https://tent.io/types/post/profile/v0.1.0',
|
68
|
+
:entity => user.profile_entity,
|
69
|
+
:original => true,
|
70
|
+
:content => {
|
71
|
+
:action => 'update',
|
72
|
+
:types => [self.type.uri],
|
73
|
+
}
|
74
|
+
)
|
75
|
+
Permission.copy(self, post)
|
76
|
+
Notifications.trigger(:type => post.type.uri, :post_id => post.id)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module TentD
|
2
|
+
module Model
|
3
|
+
module RandomPublicId
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.class_eval do
|
7
|
+
property :public_id, String, :required => true, :unique_index => :upublic_id, :default => lambda { |*args| random_id }
|
8
|
+
self.raise_on_save_failure = true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def random_id
|
14
|
+
rand(36 ** 6).to_s(36)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# TODO: Debug DataMapper state issue
|
21
|
+
# # catch unique public_id validation and generate a new one
|
22
|
+
# def assert_save_successful(*args)
|
23
|
+
# super
|
24
|
+
# rescue DataMapper::SaveFailureError
|
25
|
+
# if self.class.all(:public_id => self.public_id).any?
|
26
|
+
# self.public_id = self.class.random_id
|
27
|
+
# save
|
28
|
+
# else
|
29
|
+
# raise
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # catch db unique constraint on public_id and generate a new one
|
34
|
+
# def _persist
|
35
|
+
# super
|
36
|
+
# rescue DataObjects::IntegrityError
|
37
|
+
# if self.class.all(:public_id => self.public_id).any?
|
38
|
+
# self.public_id = self.class.random_id
|
39
|
+
# save
|
40
|
+
# else
|
41
|
+
# raise
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module TentD
|
2
|
+
module Model
|
3
|
+
module Serializable
|
4
|
+
def as_json(options = {})
|
5
|
+
attributes = super(:only => self.class.public_attributes)
|
6
|
+
attributes.merge!(:permissions => permissions_json(options[:permissions])) if respond_to?(:permissions_json)
|
7
|
+
attributes[:id] = respond_to?(:public_id) ? public_id : id
|
8
|
+
|
9
|
+
if options[:app]
|
10
|
+
[:created_at, :updated_at, :published_at, :received_at].each { |key|
|
11
|
+
attributes[key] = send(key) if respond_to?(key)
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
[:published_at, :updated_at, :created_at, :received_at].each do |key|
|
16
|
+
attributes[key] = attributes[key].to_time.to_i if attributes[key].respond_to?(:to_time)
|
17
|
+
end
|
18
|
+
|
19
|
+
mac_fields = [:mac_key_id, :mac_key, :mac_algorithm]
|
20
|
+
if options[:mac] && mac_fields.select { |k| respond_to?(k) }.size == mac_fields.size
|
21
|
+
mac_fields.each { |k| attributes[k] = send(k) }
|
22
|
+
end
|
23
|
+
|
24
|
+
if options[:groups] && respond_to?(:groups)
|
25
|
+
attributes[:groups] = groups.to_a.uniq
|
26
|
+
end
|
27
|
+
|
28
|
+
if relationships.map(&:name).include?(:attachments)
|
29
|
+
if options[:view] && respond_to?(:views) && (conditions = (views[options[:view]] || {})['attachments'])
|
30
|
+
conditions.map! { |c| c.slice('category', 'name', 'type') }.reject! { |c| c.empty? }
|
31
|
+
attributes[:attachments] = conditions.inject(nil) { |memo, c|
|
32
|
+
q = attachments.all(c)
|
33
|
+
memo ? memo += q : q
|
34
|
+
}
|
35
|
+
else
|
36
|
+
attributes[:attachments] = attachments.all.map { |a| a.as_json } unless options[:view] == 'meta'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if options[:view] && respond_to?(:views) && respond_to?(:content)
|
41
|
+
if keypaths = (views[options[:view]] || {})['content']
|
42
|
+
attributes[:content] = keypaths.inject({}) do |memo, keypath|
|
43
|
+
pointer = JsonPatch::HashPointer.new(content, keypath)
|
44
|
+
memo[pointer.keys.last] = pointer.exists? ? pointer.value : nil
|
45
|
+
memo
|
46
|
+
end
|
47
|
+
elsif options[:view] == 'meta'
|
48
|
+
attributes.delete(:content)
|
49
|
+
elsif options[:view] != 'full'
|
50
|
+
attributes[:content] = {}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
attributes
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|