tentd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +9 -0
  5. data/Guardfile +6 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +49 -0
  8. data/Rakefile +8 -0
  9. data/bin/tent-server +3 -0
  10. data/lib/tentd.rb +31 -0
  11. data/lib/tentd/api.rb +58 -0
  12. data/lib/tentd/api/apps.rb +196 -0
  13. data/lib/tentd/api/authentication_finalize.rb +12 -0
  14. data/lib/tentd/api/authentication_lookup.rb +27 -0
  15. data/lib/tentd/api/authentication_verification.rb +50 -0
  16. data/lib/tentd/api/authorizable.rb +21 -0
  17. data/lib/tentd/api/authorization.rb +14 -0
  18. data/lib/tentd/api/core_profile_data.rb +45 -0
  19. data/lib/tentd/api/followers.rb +218 -0
  20. data/lib/tentd/api/followings.rb +241 -0
  21. data/lib/tentd/api/groups.rb +161 -0
  22. data/lib/tentd/api/middleware.rb +32 -0
  23. data/lib/tentd/api/posts.rb +373 -0
  24. data/lib/tentd/api/profile.rb +78 -0
  25. data/lib/tentd/api/router.rb +123 -0
  26. data/lib/tentd/api/router/caching_headers.rb +49 -0
  27. data/lib/tentd/api/router/extract_params.rb +88 -0
  28. data/lib/tentd/api/router/serialize_response.rb +38 -0
  29. data/lib/tentd/api/user_lookup.rb +10 -0
  30. data/lib/tentd/core_ext/hash/slice.rb +29 -0
  31. data/lib/tentd/datamapper/array_property.rb +23 -0
  32. data/lib/tentd/datamapper/query.rb +19 -0
  33. data/lib/tentd/json_patch.rb +181 -0
  34. data/lib/tentd/model.rb +30 -0
  35. data/lib/tentd/model/app.rb +68 -0
  36. data/lib/tentd/model/app_authorization.rb +113 -0
  37. data/lib/tentd/model/follower.rb +105 -0
  38. data/lib/tentd/model/following.rb +100 -0
  39. data/lib/tentd/model/group.rb +24 -0
  40. data/lib/tentd/model/mention.rb +19 -0
  41. data/lib/tentd/model/notification_subscription.rb +56 -0
  42. data/lib/tentd/model/permissible.rb +227 -0
  43. data/lib/tentd/model/permission.rb +28 -0
  44. data/lib/tentd/model/post.rb +178 -0
  45. data/lib/tentd/model/post_attachment.rb +27 -0
  46. data/lib/tentd/model/post_version.rb +64 -0
  47. data/lib/tentd/model/profile_info.rb +80 -0
  48. data/lib/tentd/model/random_public_id.rb +46 -0
  49. data/lib/tentd/model/serializable.rb +58 -0
  50. data/lib/tentd/model/type_properties.rb +36 -0
  51. data/lib/tentd/model/user.rb +39 -0
  52. data/lib/tentd/model/user_scoped.rb +14 -0
  53. data/lib/tentd/notifications.rb +13 -0
  54. data/lib/tentd/notifications/girl_friday.rb +30 -0
  55. data/lib/tentd/notifications/sidekiq.rb +50 -0
  56. data/lib/tentd/tent_type.rb +20 -0
  57. data/lib/tentd/tent_version.rb +41 -0
  58. data/lib/tentd/version.rb +3 -0
  59. data/spec/fabricators/app_authorizations_fabricator.rb +5 -0
  60. data/spec/fabricators/apps_fabricator.rb +11 -0
  61. data/spec/fabricators/followers_fabricator.rb +14 -0
  62. data/spec/fabricators/followings_fabricator.rb +17 -0
  63. data/spec/fabricators/groups_fabricator.rb +3 -0
  64. data/spec/fabricators/mentions_fabricator.rb +3 -0
  65. data/spec/fabricators/notification_subscriptions_fabricator.rb +4 -0
  66. data/spec/fabricators/permissions_fabricator.rb +1 -0
  67. data/spec/fabricators/post_attachments_fabricator.rb +8 -0
  68. data/spec/fabricators/post_versions_fabricator.rb +12 -0
  69. data/spec/fabricators/posts_fabricator.rb +12 -0
  70. data/spec/fabricators/profile_infos_fabricator.rb +30 -0
  71. data/spec/integration/api/apps_spec.rb +466 -0
  72. data/spec/integration/api/followers_spec.rb +535 -0
  73. data/spec/integration/api/followings_spec.rb +688 -0
  74. data/spec/integration/api/groups_spec.rb +207 -0
  75. data/spec/integration/api/posts_spec.rb +874 -0
  76. data/spec/integration/api/profile_spec.rb +285 -0
  77. data/spec/integration/api/router_spec.rb +102 -0
  78. data/spec/integration/model/app_authorization_spec.rb +59 -0
  79. data/spec/integration/model/app_spec.rb +63 -0
  80. data/spec/integration/model/follower_spec.rb +344 -0
  81. data/spec/integration/model/following_spec.rb +97 -0
  82. data/spec/integration/model/group_spec.rb +39 -0
  83. data/spec/integration/model/notification_subscription_spec.rb +145 -0
  84. data/spec/integration/model/post_spec.rb +658 -0
  85. data/spec/spec_helper.rb +37 -0
  86. data/spec/support/expect_server.rb +3 -0
  87. data/spec/support/json_request.rb +54 -0
  88. data/spec/support/with_constant.rb +23 -0
  89. data/spec/support/with_warnings.rb +6 -0
  90. data/spec/unit/api/authentication_finalize_spec.rb +45 -0
  91. data/spec/unit/api/authentication_lookup_spec.rb +65 -0
  92. data/spec/unit/api/authentication_verification_spec.rb +50 -0
  93. data/spec/unit/api/authorizable_spec.rb +50 -0
  94. data/spec/unit/api/authorization_spec.rb +44 -0
  95. data/spec/unit/api/caching_headers_spec.rb +121 -0
  96. data/spec/unit/core_profile_data_spec.rb +64 -0
  97. data/spec/unit/json_patch_spec.rb +407 -0
  98. data/spec/unit/tent_type_spec.rb +28 -0
  99. data/spec/unit/tent_version_spec.rb +68 -0
  100. data/tentd.gemspec +36 -0
  101. metadata +435 -0
@@ -0,0 +1,344 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::Model::Follower do
4
+ describe 'find_with_permissions(id, current_auth)' do
5
+ public_expectations = proc do
6
+ it 'should return follower if public' do
7
+ follower = Fabricate(:follower, :public => true)
8
+ response = described_class.find_with_permissions(follower.id, current_auth)
9
+ expect(response).to eq(follower)
10
+ end
11
+
12
+ it 'should return nil if not public' do
13
+ follower = Fabricate(:follower, :public => false)
14
+ response = described_class.find_with_permissions(follower.id, current_auth)
15
+ expect(response).to be_nil
16
+ end
17
+ end
18
+
19
+ context 'without current_auth' do
20
+ let(:current_auth) { nil }
21
+
22
+ context '', &public_expectations
23
+ end
24
+
25
+ context 'with current_auth' do
26
+ current_auth_expectations = proc do
27
+ context 'when has permission' do
28
+ it 'should return follower' do
29
+ follower = Fabricate(:follower, :public => false)
30
+ TentD::Model::Permission.create(
31
+ :follower_visibility_id => follower.id, current_auth.permissible_foreign_key => current_auth.id)
32
+
33
+ response = described_class.find_with_permissions(follower.id, current_auth)
34
+ expect(response).to eq(follower)
35
+ end
36
+ end
37
+
38
+ context 'when does not have permission' do
39
+ context '', &public_expectations
40
+ end
41
+ end
42
+
43
+ context 'when Follower' do
44
+ let(:current_auth) { Fabricate(:follower) }
45
+
46
+ context '', &current_auth_expectations
47
+ end
48
+ end
49
+ end
50
+
51
+ describe 'fetch_all(params)' do
52
+ let(:params) { Hash.new }
53
+
54
+ it 'should return all followers' do
55
+ public_follower = Fabricate(:follower, :public => true)
56
+ private_follower = Fabricate(:follower, :public => false)
57
+
58
+ max_count = TentD::Model::Follower.count
59
+ with_constants "TentD::API::MAX_PER_PAGE" => max_count, "TentD::API::PER_PAGE" => max_count do
60
+ res = described_class.fetch_all(params)
61
+ expect(res).to include(public_follower)
62
+ expect(res).to include(private_follower)
63
+ end
64
+ end
65
+
66
+ context 'with params' do
67
+ context '[:since_id]' do
68
+ it 'should only return followers with id > :since_id' do
69
+ since_follower = Fabricate(:follower, :public => false)
70
+ follower = Fabricate(:follower, :public => false)
71
+
72
+ params['since_id'] = since_follower.id
73
+
74
+ res = described_class.fetch_all(params)
75
+ expect(res).to eq([follower])
76
+ end
77
+ end
78
+
79
+ context '[:before_id]' do
80
+ it 'should only return followers with id < :since_id' do
81
+ TentD::Model::Follower.all.destroy
82
+ follower = Fabricate(:follower, :public => false)
83
+ before_follower = Fabricate(:follower, :public => false)
84
+
85
+ params['before_id'] = before_follower.id
86
+
87
+ res = described_class.fetch_all(params)
88
+ expect(res).to eq([follower])
89
+ end
90
+ end
91
+
92
+ context '[:limit]' do
93
+ it 'should only return :limit number of followers' do
94
+ limit = 1
95
+ 0.upto(limit) { Fabricate(:follower, :public => false) }
96
+
97
+ params['limit'] = limit
98
+
99
+ res = described_class.fetch_all(params)
100
+ expect(res.size).to eq(limit)
101
+ end
102
+
103
+ it 'should never return more than TentD::API::MAX_PER_PAGE followers' do
104
+ limit = 1
105
+ Fabricate(:follower, :public => false)
106
+
107
+ params['limit'] = limit
108
+
109
+ with_constants "TentD::API::MAX_PER_PAGE" => 0 do
110
+ res = described_class.fetch_all(params)
111
+ expect(res.size).to eq(0)
112
+ end
113
+ end
114
+ end
115
+
116
+ context 'without [:limit]' do
117
+ it 'should never return more than TentD::API::MAX_PER_PAGE followers' do
118
+ Fabricate(:follower, :public => false)
119
+
120
+ with_constants "TentD::API::MAX_PER_PAGE" => 0 do
121
+ res = described_class.fetch_all(params)
122
+ expect(res.size).to eq(0)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ describe 'fetch_with_permissions(params, current_auth)' do
130
+ let(:params) { Hash.new }
131
+ let(:authorize_folower) { false }
132
+
133
+ with_params = proc do
134
+ before do
135
+ if current_auth && authorize_folower
136
+ @authorize_folower = lambda do |follower|
137
+ TentD::Model::Permission.create(
138
+ :follower_visibility_id => follower.id,
139
+ current_auth.permissible_foreign_key => current_auth.id
140
+ )
141
+ end
142
+ end
143
+ end
144
+
145
+ context '[:since_id]' do
146
+ it 'should only return followers with id > :since_id' do
147
+ since_follower = Fabricate(:follower, :public => !authorize_folower)
148
+ follower = Fabricate(:follower, :public => !authorize_folower)
149
+
150
+ params['since_id'] = since_follower.id
151
+
152
+ if authorize_folower
153
+ [since_follower, follower].each { |f| @authorize_folower.call(f) }
154
+ end
155
+
156
+ response = described_class.fetch_with_permissions(params, current_auth)
157
+ expect(response).to eq([follower])
158
+ end
159
+ end
160
+
161
+ context '[:before_id]' do
162
+ it 'should only return followers with id < :before_id' do
163
+ if current_auth.kind_of?(TentD::Model::Follower)
164
+ TentD::Model::Follower.all(:id.not => current_auth.id).destroy!
165
+ follower = current_auth
166
+ else
167
+ TentD::Model::Follower.all.destroy!
168
+ follower = Fabricate(:follower, :public => !authorize_folower)
169
+ end
170
+
171
+ before_follower = Fabricate(:follower, :public => !authorize_folower)
172
+
173
+ params['before_id'] = before_follower.id
174
+
175
+ if authorize_folower
176
+ [before_follower, follower].each { |f| @authorize_folower.call(f) }
177
+ end
178
+
179
+ response = described_class.fetch_with_permissions(params, current_auth)
180
+ expect(response).to eq([follower])
181
+ end
182
+ end
183
+
184
+ context '[:limit]' do
185
+ it 'should only return :limit number of followers' do
186
+ limit = 1
187
+ followers = 0.upto(limit).map { Fabricate(:follower, :public => !authorize_folower) }
188
+
189
+ if authorize_folower
190
+ followers.each { |f| @authorize_folower.call(f) }
191
+ end
192
+
193
+ params['limit'] = limit
194
+
195
+ response = described_class.fetch_with_permissions(params, current_auth)
196
+ expect(response.size).to eq(limit)
197
+ end
198
+
199
+ it 'should never return more than TentD::API::MAX_PER_PAGE followers' do
200
+ with_constants "TentD::API::MAX_PER_PAGE" => 0 do
201
+ followers = [Fabricate(:follower, :public => !authorize_folower)]
202
+
203
+ if authorize_folower
204
+ followers.each { |f| @authorize_folower.call(f) }
205
+ end
206
+
207
+ response = described_class.fetch_with_permissions(params, current_auth)
208
+ expect(response.size).to eq(0)
209
+ end
210
+ end
211
+ end
212
+
213
+ context 'without [:limit]' do
214
+ it 'should only return TentD::API::PER_PAGE number of followers' do
215
+ with_constants "TentD::API::PER_PAGE" => 1 do
216
+ followers = 2.times.map { Fabricate(:follower, :public => !authorize_folower) }
217
+
218
+ if authorize_folower
219
+ followers.each { |f| @authorize_folower.call(f) }
220
+ end
221
+
222
+ response = described_class.fetch_with_permissions(params, current_auth)
223
+ expect(response.size).to eq(1)
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ public_expectations = proc do
230
+ it 'should only return public followers' do
231
+ max_results = TentD::Model::Follower.count + 100
232
+ with_constants "TentD::API::MAX_PER_PAGE" => max_results, "TentD::API::PER_PAGE" => max_results do
233
+ public_follower = Fabricate(:follower, :public => true)
234
+ private_follower = Fabricate(:follower, :public => false)
235
+
236
+ response = described_class.fetch_with_permissions(params, current_auth)
237
+ expect(response).to include(public_follower)
238
+ expect(response).to_not include(private_follower)
239
+ end
240
+ end
241
+
242
+ context 'with params', &with_params
243
+ end
244
+
245
+ context 'without current_auth' do
246
+ let(:current_auth) { nil }
247
+
248
+ context '', &public_expectations
249
+ end
250
+
251
+ current_auth_expectations = proc do
252
+ context 'when has permissions' do
253
+ it 'should return permissible and public followers' do
254
+ public_follower = Fabricate(:follower, :public => true)
255
+ private_follower = Fabricate(:follower, :public => false)
256
+
257
+ TentD::Model::Permission.create(
258
+ :follower_visibility_id => private_follower.id,
259
+ current_auth.permissible_foreign_key => current_auth.id
260
+ )
261
+
262
+ response = described_class.fetch_with_permissions(params, current_auth)
263
+ expect(response).to include(public_follower)
264
+ expect(response).to include(private_follower)
265
+ end
266
+
267
+ context 'with params' do
268
+ context 'when private' do
269
+ let(:authorize_folower) { true }
270
+ context '', &with_params
271
+ end
272
+
273
+ context 'when public', &with_params
274
+ end
275
+ end
276
+
277
+ context 'when does not have permissions', &public_expectations
278
+ end
279
+
280
+ context 'with current_auth' do
281
+ context 'when Follower' do
282
+ let(:current_auth) { Fabricate(:follower) }
283
+
284
+ context '', &current_auth_expectations
285
+ end
286
+ end
287
+ end
288
+
289
+ describe "#as_json" do
290
+ let(:follower) { Fabricate(:follower) }
291
+ let(:public_attributes) do
292
+ {
293
+ :id => follower.public_id,
294
+ :entity => follower.entity,
295
+ :permissions => { :public => true }
296
+ }
297
+ end
298
+
299
+ context 'without options' do
300
+ it 'should return public attributes' do
301
+ expect(follower.as_json).to eq(public_attributes)
302
+ end
303
+ end
304
+
305
+ context 'with options[:mac]' do
306
+ it 'should return mac key' do
307
+ expect(follower.as_json(:mac => true)).to eq(public_attributes.merge(
308
+ :mac_key_id => follower.mac_key_id,
309
+ :mac_key => follower.mac_key,
310
+ :mac_algorithm => follower.mac_algorithm
311
+ ))
312
+ end
313
+ end
314
+
315
+ context 'with options[:app]' do
316
+ it 'should return additional attributes' do
317
+ expect(follower.as_json(:app => true)).to eq(public_attributes.merge(
318
+ :profile => follower.profile,
319
+ :licenses => follower.licenses,
320
+ :types => [],
321
+ :created_at => follower.created_at.to_time.to_i,
322
+ :updated_at => follower.updated_at.to_time.to_i
323
+ ))
324
+ end
325
+ end
326
+
327
+ context 'with options[:self]' do
328
+ it 'should return licenses and types' do
329
+ expect(follower.as_json(:self => true)).to eq(public_attributes.merge(
330
+ :licenses => follower.licenses,
331
+ :types => []
332
+ ))
333
+ end
334
+ end
335
+
336
+ context 'with options[:groups]' do
337
+ it 'should return groups' do
338
+ expect(follower.as_json(:groups => true)).to eq(public_attributes.merge(
339
+ :groups => follower.groups.uniq
340
+ ))
341
+ end
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::Model::Following do
4
+ let(:following) { Fabricate(:following) }
5
+
6
+ describe "#as_json" do
7
+ let(:public_attributes) do
8
+ {
9
+ :id => following.public_id,
10
+ :remote_id => following.remote_id,
11
+ :entity => following.entity,
12
+ :permissions => { :public => following.public }
13
+ }
14
+ end
15
+
16
+ context 'without options' do
17
+ it 'should return public attributes' do
18
+ expect(following.as_json).to eq(public_attributes)
19
+ end
20
+ end
21
+
22
+ context 'with options[:mac]' do
23
+ it 'should return mac key' do
24
+ expect(following.as_json(:mac => true)).to eq(public_attributes.merge(
25
+ :mac_key_id => following.mac_key_id,
26
+ :mac_key => following.mac_key,
27
+ :mac_algorithm => following.mac_algorithm
28
+ ))
29
+ end
30
+ end
31
+
32
+ context 'with options[:groups]' do
33
+ it 'should return groups' do
34
+ expect(following.as_json(:groups => true)).to eq(public_attributes.merge(
35
+ :groups => following.groups
36
+ ))
37
+ end
38
+ end
39
+
40
+ context 'with options[:permissions]' do
41
+ let(:follower) { Fabricate(:follower) }
42
+ let(:group) { Fabricate(:group) }
43
+ let(:entity_permission) { Fabricate(:permission, :follower_access => follower) }
44
+ let(:group_permission) { Fabricate(:permission, :group => group) }
45
+ let(:following) { Fabricate(:following, :permissions => [group_permission, entity_permission]) }
46
+
47
+ it 'should return detailed permissions' do
48
+ expect(following.as_json(:permissions => true)).to eq(public_attributes.merge(
49
+ :permissions => {
50
+ :public => following.public,
51
+ :groups => [group.public_id],
52
+ :entities => {
53
+ follower.entity => true
54
+ }
55
+ }
56
+ ))
57
+ end
58
+ end
59
+
60
+ context 'with options[:app]' do
61
+ it 'should return additional attribtues' do
62
+ expect(following.as_json(:app => true)).to eq(public_attributes.merge(
63
+ :profile => following.profile,
64
+ :licenses => following.licenses,
65
+ :updated_at => following.updated_at.to_time.to_i,
66
+ :created_at => following.updated_at.to_time.to_i
67
+ ))
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '.update_profile' do
73
+ let(:http_stubs) { Faraday::Adapter::Test::Stubs.new }
74
+ let(:updated_profile) {
75
+ {
76
+ TentD::Model::ProfileInfo::TENT_PROFILE_TYPE_URI => {
77
+ "licenses" => ["http://creativecommons.org/licenses/by/3.0/"],
78
+ "entity" => "https://new-server.example.org",
79
+ "servers" => ["https://new-server.example.org/tent"]
80
+ }
81
+ }
82
+ }
83
+ before { TentClient.any_instance.stubs(:faraday_adapter).returns([:test, http_stubs]) }
84
+
85
+ it 'should update a profile' do
86
+ http_stubs.get('/profile') {
87
+ [200, { 'Content-Type' => TentD::API::MEDIA_TYPE }, updated_profile.to_json]
88
+ }
89
+ described_class.update_profile(following.id)
90
+ following.reload
91
+
92
+ expect(following.profile).to eq(updated_profile)
93
+ expect(following.licenses).to eq(updated_profile.values.first['licenses'])
94
+ expect(following.entity).to eq(updated_profile.values.first['entity'])
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::Model::Group do
4
+ let(:group) { Fabricate(:group) }
5
+
6
+ it 'should set random_uid for public_id' do
7
+ expect(group.public_id).to be_a(String)
8
+ end
9
+
10
+ # TODO: Debug DataMapper, catching exception and updating public_id fails to save
11
+ xit 'should never set duplicate public_id' do
12
+ first_group = Fabricate(:group)
13
+ group = Fabricate(:group, :public_id => first_group.public_id)
14
+ expect(group).to be_saved
15
+ expect(group.public_id).to_not eq(first_group.public_id)
16
+ end
17
+
18
+ describe '#as_json' do
19
+ let(:public_attributes) do
20
+ {
21
+ :id => group.public_id,
22
+ :name => group.name
23
+ }
24
+ end
25
+
26
+ it 'should return public attributes' do
27
+ expect(group.as_json).to eq(public_attributes)
28
+ end
29
+
30
+ context 'with options[:app]' do
31
+ it 'should expose timestamps' do
32
+ expect(group.as_json(:app => true)).to eq(public_attributes.merge(
33
+ :created_at => group.created_at.to_time.to_i,
34
+ :updated_at => group.updated_at.to_time.to_i
35
+ ))
36
+ end
37
+ end
38
+ end
39
+ end