tentd 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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