livefyre 1.1.4 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +6 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vagrant.xml +8 -0
- data/.idea/vcs.xml +7 -0
- data/.project +11 -0
- data/CHANGELOG +12 -0
- data/README.md +3 -3
- data/lib/livefyre.rb +1 -1
- data/lib/livefyre/api/personalized_stream.rb +271 -0
- data/lib/livefyre/core/network.rb +75 -0
- data/lib/livefyre/core/site.rb +102 -0
- data/lib/livefyre/entity/subscription.rb +32 -0
- data/lib/livefyre/entity/timeline_cursor.rb +41 -0
- data/lib/livefyre/entity/topic.rb +46 -0
- data/lib/livefyre/factory/cursor_factory.rb +15 -0
- data/lib/livefyre/version.rb +1 -1
- data/spec/livefyre/api/personalized_stream_spec.rb +65 -0
- data/spec/livefyre/core/network_spec.rb +24 -0
- data/spec/livefyre/core/site_spec.rb +73 -0
- data/spec/livefyre/test_spec.rb +7 -0
- metadata +26 -5
- data/lib/livefyre/core.rb +0 -129
- data/spec/livefyre/core_spec.rb +0 -79
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZGUyOTkxOWQwN2MzZWRmYTZiMjhlMjhiNTliNTU4NTg2N2M5YjVkYg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZTlmOWRlMWU4NTI0NzI5ODM1YmJlN2U1NzkzODA2M2MxMzExOGJmYQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YjM4ZWIyMWM2YjdhMzVhMjRiZTI4YTZjMzMwNzNjNjFhZjIwMTFkZDE3OTkx
|
10
|
+
YmRkZDIzZTY3MjlmOGM4NmY0ZDEzNmMxZDA2YWYyZDBlMmQxZDU0YTg1MzZi
|
11
|
+
ZGQxNmJlOTA0ZGEwMDBkZGQ0MjNmYzFiOTA4MTg0N2UwNzhmZjE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MmY1NjUwNjk3OTE4Nzc2ZTI4ZWI3MWFkMzFjYmFkZmIwNzJjZGMyMjJiODZh
|
14
|
+
OGRmNjc2NDlmNDRhODM4N2NhNzgzYmZkZGI5NWQyODdmMGU3ZDQ3YWJiNTcy
|
15
|
+
OTAxOTZhMTgxYzA3MzA5ODM5N2E5MmI3NmEzNzI4ZmVjODc4MjQ=
|
data/.gitignore
CHANGED
data/.idea/.name
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
livefyre-ruby-utils
|
data/.idea/.rakeTasks
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Settings><!--This file was automatically generated by Ruby plugin.
|
3
|
+
You are allowed to:
|
4
|
+
1. Remove rake task
|
5
|
+
2. Add existing rake tasks
|
6
|
+
To add existing rake tasks automatically delete this file and reload the project.
|
7
|
+
--><RakeGroup description="" fullCmd="" taksId="rake"><RakeTask description="Build livefyre-1.1.4.gem into the pkg directory" fullCmd="build" taksId="build" /><RakeTask description="Build and install livefyre-1.1.4.gem into system gems" fullCmd="install" taksId="install" /><RakeTask description="Create tag v1.1.4 and build and push livefyre-1.1.4.gem to Rubygems" fullCmd="release" taksId="release" /><RakeTask description="" fullCmd="run_tests" taksId="run_tests" /></RakeGroup></Settings>
|
data/.idea/encodings.xml
ADDED
data/.idea/misc.xml
ADDED
data/.idea/modules.xml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="ProjectModuleManager">
|
4
|
+
<modules>
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/livefyre-ruby-utils.iml" filepath="$PROJECT_DIR$/.idea/livefyre-ruby-utils.iml" />
|
6
|
+
</modules>
|
7
|
+
</component>
|
8
|
+
</project>
|
9
|
+
|
data/.idea/vagrant.xml
ADDED
data/.idea/vcs.xml
ADDED
data/.project
ADDED
data/CHANGELOG
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== 1.3.0 2014-07-30
|
2
|
+
|
3
|
+
*** CAUTION: THIS VERSION HAS UPDATED METHODS THAT MAKE PREVIOUS FUNCTION CALLS OBSOLETE ***
|
4
|
+
|
5
|
+
* Jumped from 1.1.0 to 1.3.0 to match other libraries.
|
6
|
+
* Added personal stream and timeline support.
|
7
|
+
* Updated most API to use SSL.
|
8
|
+
* Added create_collection method.
|
9
|
+
* Updated checksum formula to match other libraries.
|
10
|
+
* Refactored core objects.
|
11
|
+
* Updated create_collection_meta to take in a dict of optional params.
|
12
|
+
|
1
13
|
=== 1.1.4 2014-05-07
|
2
14
|
|
3
15
|
* Added changelog.
|
data/README.md
CHANGED
@@ -53,10 +53,10 @@ site = network.get_site('site_id', 'site_key')
|
|
53
53
|
```
|
54
54
|
|
55
55
|
Building a collection meta token:
|
56
|
-
*The
|
56
|
+
*The {options} argument is optional.*
|
57
57
|
|
58
58
|
```ruby
|
59
|
-
site.build_collection_meta_token('title', 'article_id', 'url',
|
59
|
+
site.build_collection_meta_token('title', 'article_id', 'url', {options})
|
60
60
|
```
|
61
61
|
|
62
62
|
Building a checksum:
|
@@ -78,7 +78,7 @@ To get a content collection's id:
|
|
78
78
|
site.get_collection_id('article_id')
|
79
79
|
```
|
80
80
|
|
81
|
-
## Documentation
|
81
|
+
## Additional Documentation
|
82
82
|
|
83
83
|
Located [here](http://answers.livefyre.com/developers/libraries).
|
84
84
|
|
data/lib/livefyre.rb
CHANGED
@@ -0,0 +1,271 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'jwt'
|
3
|
+
require 'rest-client'
|
4
|
+
require 'addressable/uri'
|
5
|
+
|
6
|
+
require 'livefyre/entity/topic'
|
7
|
+
require 'livefyre/entity/subscription'
|
8
|
+
|
9
|
+
module Livefyre
|
10
|
+
class PersonalizedStream
|
11
|
+
# Topic API
|
12
|
+
def self.get_topic(core, topic_id)
|
13
|
+
url = self.base_url(core) + self.topic_path(core, topic_id)
|
14
|
+
|
15
|
+
response = RestClient.get(url, self.get_headers(core))
|
16
|
+
data = JSON.parse(response)['data']
|
17
|
+
|
18
|
+
Topic::serialize_from_json(data['topic'])
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create_or_update_topic(core, topic_id, label)
|
22
|
+
PersonalizedStream::create_or_update_topics(core, { "#{topic_id}" => label })[0]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.delete_topic(core, topic)
|
26
|
+
PersonalizedStream::delete_topics(core, [topic]) == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
# Multiple Topic API
|
30
|
+
def self.get_topics(core, limit=100, offset=0)
|
31
|
+
url = self.base_url(core) + self.multiple_topic_path(core)
|
32
|
+
url += "?limit=#{limit}&offset=#{offset}"
|
33
|
+
|
34
|
+
response = RestClient.get(url, self.get_headers(core))
|
35
|
+
data = JSON.parse(response)['data']
|
36
|
+
|
37
|
+
topics = []
|
38
|
+
data['topics'].each do |topic|
|
39
|
+
topics << Topic::serialize_from_json(topic)
|
40
|
+
end
|
41
|
+
|
42
|
+
topics
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.create_or_update_topics(core, topic_map)
|
46
|
+
topics = []
|
47
|
+
|
48
|
+
topic_map.each do |key, value|
|
49
|
+
if not value or value.length > 128
|
50
|
+
raise ArgumentError, 'label cannot be longer than 128 chars.'
|
51
|
+
end
|
52
|
+
topics << Topic::create(core, key, value)
|
53
|
+
end
|
54
|
+
|
55
|
+
url = self.base_url(core) + self.multiple_topic_path(core)
|
56
|
+
headers = self.get_headers(core)
|
57
|
+
headers[:content_type] = :json
|
58
|
+
|
59
|
+
topics_json = []
|
60
|
+
topics.each do |topic|
|
61
|
+
topics_json << topic.to_dict
|
62
|
+
end
|
63
|
+
|
64
|
+
response = RestClient.post(url, {topics: topics_json}.to_json, headers)
|
65
|
+
JSON.parse(response)['data']
|
66
|
+
|
67
|
+
return topics
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.delete_topics(core, topics)
|
71
|
+
url = self.base_url(core) + self.multiple_topic_path(core)
|
72
|
+
headers = self.get_headers(core)
|
73
|
+
headers[:content_type] = :json
|
74
|
+
form = {delete: self.get_ids(topics)}
|
75
|
+
|
76
|
+
response = RestClient.patch(url, form.to_json, headers)
|
77
|
+
data = JSON.parse(response)['data']
|
78
|
+
|
79
|
+
data.has_key?('deleted') ? data['deleted'] : 0
|
80
|
+
end
|
81
|
+
|
82
|
+
# Collection Topic API
|
83
|
+
def self.get_collection_topics(site, collection_id)
|
84
|
+
url = self.base_url(site) + self.collection_topics_path(site, collection_id)
|
85
|
+
|
86
|
+
response = RestClient.get(url, self.get_headers(site))
|
87
|
+
data = JSON.parse(response)['data']
|
88
|
+
|
89
|
+
data.has_key?('topicIds') ? data['topicIds'] : []
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.add_collection_topics(site, collection_id, topics)
|
93
|
+
url = self.base_url(site) + self.collection_topics_path(site, collection_id)
|
94
|
+
headers = self.get_headers(site)
|
95
|
+
headers[:content_type] = :json
|
96
|
+
form = {topicIds: self.get_ids(topics)}
|
97
|
+
|
98
|
+
response = RestClient.post(url, form.to_json, headers)
|
99
|
+
data = JSON.parse(response)['data']
|
100
|
+
|
101
|
+
data.has_key?('added') ? data['added'] : 0
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.replace_collection_topics(site, collection_id, topics)
|
105
|
+
url = self.base_url(site) + self.collection_topics_path(site, collection_id)
|
106
|
+
headers = self.get_headers(site)
|
107
|
+
headers[:content_type] = :json
|
108
|
+
form = {topicIds: self.get_ids(topics)}
|
109
|
+
|
110
|
+
response = RestClient.put(url, form.to_json, headers)
|
111
|
+
data = JSON.parse(response)['data']
|
112
|
+
|
113
|
+
return data.has_key?('added') ? data['added'] : 0, data.has_key?('removed') ? data['removed'] : 0
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.remove_collection_topics(site, collection_id, topics)
|
117
|
+
url = self.base_url(site) + self.collection_topics_path(site, collection_id)
|
118
|
+
headers = self.get_headers(site)
|
119
|
+
headers[:content_type] = :json
|
120
|
+
form = {delete: self.get_ids(topics)}
|
121
|
+
|
122
|
+
response = RestClient.patch(url, form.to_json, headers)
|
123
|
+
data = JSON.parse(response)['data']
|
124
|
+
|
125
|
+
data.has_key?('removed') ? data['removed'] : 0
|
126
|
+
end
|
127
|
+
|
128
|
+
# Subscription API
|
129
|
+
def self.get_subscriptions(network, user_id)
|
130
|
+
url = self.base_url(network) + self.user_subscription_path(network.get_user_urn(user_id))
|
131
|
+
|
132
|
+
response = RestClient.get(url, self.get_headers(network))
|
133
|
+
data = JSON.parse(response)['data']
|
134
|
+
|
135
|
+
subscriptions = []
|
136
|
+
if data.has_key?('subscriptions')
|
137
|
+
data['subscriptions'].each do |sub_json|
|
138
|
+
subscriptions << Subscription::serialize_from_json(sub_json)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
subscriptions
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.add_subscriptions(network, user_token, topics)
|
146
|
+
user_id = JWT.decode(user_token, network.key)['user_id']
|
147
|
+
user_urn = network.get_user_urn(user_id)
|
148
|
+
url = self.base_url(network) + self.user_subscription_path(user_urn)
|
149
|
+
headers = self.get_headers(network, user_token)
|
150
|
+
headers[:content_type] = :json
|
151
|
+
form = {subscriptions: self.to_subscriptions(topics, user_urn)}
|
152
|
+
|
153
|
+
response = RestClient.post(url, form.to_json, headers)
|
154
|
+
data = JSON.parse(response)['data']
|
155
|
+
|
156
|
+
data.has_key?('added') ? data['added'] : 0
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.replace_subscriptions(network, user_token, topics)
|
160
|
+
user_id = JWT.decode(user_token, network.key)['user_id']
|
161
|
+
user_urn = network.get_user_urn(user_id)
|
162
|
+
url = self.base_url(network) + self.user_subscription_path(user_urn)
|
163
|
+
headers = self.get_headers(network, user_token)
|
164
|
+
headers[:content_type] = :json
|
165
|
+
form = {subscriptions: self.to_subscriptions(topics, user_urn)}
|
166
|
+
|
167
|
+
response = RestClient.put(url, form.to_json, headers)
|
168
|
+
data = JSON.parse(response)['data']
|
169
|
+
|
170
|
+
return data.has_key?('added') ? data['added'] : 0, data.has_key?('removed') ? data['removed'] : 0
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.remove_subscriptions(network, user_token, topics)
|
174
|
+
user_id = JWT.decode(user_token, network.key)['user_id']
|
175
|
+
user_urn = network.get_user_urn(user_id)
|
176
|
+
url = self.base_url(network) + self.user_subscription_path(user_urn)
|
177
|
+
headers = self.get_headers(network, user_token)
|
178
|
+
headers[:content_type] = :json
|
179
|
+
form = {delete: self.to_subscriptions(topics, user_urn)}
|
180
|
+
|
181
|
+
response = RestClient.patch(url, form.to_json, headers)
|
182
|
+
data = JSON.parse(response)['data']
|
183
|
+
|
184
|
+
data.has_key?('removed') ? data['removed'] : 0
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.get_subscribers(network, topic, limit=100, offset=0)
|
188
|
+
url = self.base_url(network) + self.topic_subscription_path(topic)
|
189
|
+
url += "?limit=#{limit}&offset=#{offset}"
|
190
|
+
|
191
|
+
response = RestClient.get(url, self.get_headers(network))
|
192
|
+
data = JSON.parse(response)['data']
|
193
|
+
|
194
|
+
subscriptions = []
|
195
|
+
if data.has_key?('subscriptions')
|
196
|
+
data['subscriptions'].each do |sub_json|
|
197
|
+
subscriptions << Subscription::serialize_from_json(sub_json)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
subscriptions
|
202
|
+
end
|
203
|
+
|
204
|
+
# Stream API
|
205
|
+
def self.get_timeline_stream(core, resource, limit=50, t_until=nil, t_since=nil)
|
206
|
+
url = STREAM_BASE_URL + TIMELINE_PATH
|
207
|
+
url += "?resource=#{resource}&limit=#{limit}"
|
208
|
+
|
209
|
+
if t_until != nil
|
210
|
+
url += "&until=#{t_until}"
|
211
|
+
elsif t_since != nil
|
212
|
+
url += "&since=#{t_since}"
|
213
|
+
end
|
214
|
+
|
215
|
+
response = RestClient.get(url, self.get_headers(core))
|
216
|
+
|
217
|
+
JSON.parse(response)
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def self.base_url(core)
|
223
|
+
"https://#{core.network_name}.quill.fyre.co/api/v4"
|
224
|
+
end
|
225
|
+
|
226
|
+
STREAM_BASE_URL = 'https://bootstrap.livefyre.com/api/v4'
|
227
|
+
|
228
|
+
def self.topic_path(core, topic_id)
|
229
|
+
"/#{Topic.generate_urn(core, topic_id)}/"
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.multiple_topic_path(core)
|
233
|
+
"/#{core.get_urn}:topics/"
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.collection_topics_path(site, collection_id)
|
237
|
+
"/#{site.get_urn}:collection=#{collection_id}:topics/"
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.user_subscription_path(user_urn)
|
241
|
+
"/#{user_urn}:subscriptions/"
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.topic_subscription_path(topic)
|
245
|
+
"/#{topic.id}:subscribers/"
|
246
|
+
end
|
247
|
+
|
248
|
+
TIMELINE_PATH = '/timeline/'
|
249
|
+
|
250
|
+
def self.get_headers(core, user_token=nil)
|
251
|
+
{:accepts => :json, :authorization => 'lftoken ' + (user_token == nil ? core.build_livefyre_token : user_token)}
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.get_ids(topics)
|
255
|
+
ids = []
|
256
|
+
topics.each do |topic|
|
257
|
+
ids << topic.id
|
258
|
+
end
|
259
|
+
|
260
|
+
ids
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.to_subscriptions(topics, user)
|
264
|
+
subscriptions = []
|
265
|
+
topics.each do |topic|
|
266
|
+
subscriptions << Subscription.new(topic.id, user, SubscriptionType::PERSONAL_STREAM).to_dict
|
267
|
+
end
|
268
|
+
subscriptions
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'jwt'
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
require 'livefyre/core/site'
|
6
|
+
|
7
|
+
module Livefyre
|
8
|
+
class Network
|
9
|
+
DEFAULT_USER = 'system'
|
10
|
+
DEFAULT_EXPIRES = 86400
|
11
|
+
|
12
|
+
def initialize(name, key)
|
13
|
+
@name = name
|
14
|
+
@key = key
|
15
|
+
@network_name = name.split('.')[0]
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :name
|
19
|
+
attr_reader :key
|
20
|
+
attr_reader :network_name
|
21
|
+
|
22
|
+
def set_user_sync_url(url_template)
|
23
|
+
raise ArgumentError, 'url_template should contain {id}' if !url_template.include?('{id}')
|
24
|
+
|
25
|
+
response = RestClient.post(
|
26
|
+
"http://#{@name}",
|
27
|
+
{ actor_token: build_livefyre_token, pull_profile_url: url_template }
|
28
|
+
)
|
29
|
+
response.code == 204
|
30
|
+
end
|
31
|
+
|
32
|
+
def sync_user(user_id)
|
33
|
+
response = RestClient.post(
|
34
|
+
"http://#{@name}/api/v3_0/user/#{user_id}/refresh",
|
35
|
+
{ lftoken: build_livefyre_token }
|
36
|
+
)
|
37
|
+
response.code == 200
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_livefyre_token
|
41
|
+
build_user_auth_token(DEFAULT_USER, DEFAULT_USER, DEFAULT_EXPIRES)
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_user_auth_token(user_id, display_name, expires)
|
45
|
+
raise ArgumentError, 'user_id must be alphanumeric' if !(user_id =~ /\A\p{Alnum}+\z/)
|
46
|
+
|
47
|
+
JWT.encode({
|
48
|
+
domain: @name,
|
49
|
+
user_id: user_id,
|
50
|
+
display_name: display_name,
|
51
|
+
expires: Time.new.to_i + expires},
|
52
|
+
@key)
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_livefyre_token(lf_token)
|
56
|
+
token_attributes = JWT.decode(lf_token, @key)
|
57
|
+
|
58
|
+
token_attributes['domain'] == @name \
|
59
|
+
&& token_attributes['user_id'] == DEFAULT_USER \
|
60
|
+
&& token_attributes['expires'] >= Time.new.to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_site(site_id, site_key)
|
64
|
+
Site.new(self, site_id, site_key)
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_urn
|
68
|
+
"urn:livefyre:#{@name}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_user_urn(user)
|
72
|
+
"#{get_urn}:user=#{user}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'digest'
|
3
|
+
require 'json'
|
4
|
+
require 'jwt'
|
5
|
+
require 'rest-client'
|
6
|
+
require 'addressable/uri'
|
7
|
+
|
8
|
+
module Livefyre
|
9
|
+
class Site
|
10
|
+
TYPE = %w(reviews sidenotes ratings counting liveblog livechat livecomments)
|
11
|
+
|
12
|
+
def initialize(network, id, key)
|
13
|
+
@network = network
|
14
|
+
@id = id
|
15
|
+
@key = key
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :network
|
19
|
+
attr_reader :id
|
20
|
+
attr_reader :key
|
21
|
+
|
22
|
+
def build_collection_meta_token(title, article_id, url, options={})
|
23
|
+
raise ArgumentError, 'provided url is not a valid url' if !uri?(url)
|
24
|
+
raise ArgumentError, 'title length should be under 255 char' if title.length > 255
|
25
|
+
|
26
|
+
collection_meta = {
|
27
|
+
url: url,
|
28
|
+
title: title,
|
29
|
+
articleId: article_id
|
30
|
+
}
|
31
|
+
|
32
|
+
if options.has_key?(:type) && !TYPE.include?(options[:type])
|
33
|
+
raise ArgumentError, 'type is not a recognized type. should be liveblog, livechat, livecomments, reviews, sidenotes, or an empty string'
|
34
|
+
end
|
35
|
+
|
36
|
+
JWT.encode(collection_meta.merge(options), @key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_checksum(title, url, tags='')
|
40
|
+
raise ArgumentError, 'provided url is not a valid url' if !uri?(url)
|
41
|
+
raise ArgumentError, 'title length should be under 255 char' if title.length > 255
|
42
|
+
|
43
|
+
collection_meta = { tags: tags, title: title, url: url }
|
44
|
+
Digest::MD5.new.update(collection_meta.to_json).hexdigest
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def create_collection(title, article_id, url, options={})
|
49
|
+
uri = "https://#{network_name}.quill.fyre.co/api/v3.0/site/#{@id}/collection/create/?sync=1"
|
50
|
+
data = {
|
51
|
+
articleId: article_id,
|
52
|
+
collectionMeta: build_collection_meta_token(title, article_id, url, options),
|
53
|
+
checksum: build_checksum(title, url, options.has_key?(:tags) ? options[:tags] : '')
|
54
|
+
}
|
55
|
+
headers = {:accepts => :json, :content_type => :json}
|
56
|
+
|
57
|
+
response = RestClient.post(uri, data.to_json, headers)
|
58
|
+
|
59
|
+
if response.code == 200
|
60
|
+
return JSON.parse(response)['data']['collectionId']
|
61
|
+
end
|
62
|
+
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_collection_content(article_id)
|
67
|
+
response = RestClient.get(
|
68
|
+
"https://bootstrap.livefyre.com/bs3/#{@network.name}/#{@id}/#{Base64.encode64(article_id.to_s).chomp}/init",
|
69
|
+
:accepts => :json
|
70
|
+
)
|
71
|
+
response.code == 200 ? JSON.parse(response) : nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_collection_id(article_id)
|
75
|
+
content = get_collection_content(article_id)
|
76
|
+
if content
|
77
|
+
content['collectionSettings']['collectionId']
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def network_name
|
82
|
+
@network.network_name
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_livefyre_token
|
86
|
+
@network.build_livefyre_token
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_urn
|
90
|
+
"#{@network.get_urn}:site=#{@id}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def uri?(string)
|
94
|
+
uri = Addressable::URI.parse(string)
|
95
|
+
%w( ftp ftps http https ).include?(uri.scheme)
|
96
|
+
rescue Addressable::URI::BadURIError
|
97
|
+
false
|
98
|
+
rescue Addressable::URI::InvalidURIError
|
99
|
+
false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Livefyre
|
2
|
+
class Subscription
|
3
|
+
def initialize(to, by, type, created_at=nil)
|
4
|
+
@to = to
|
5
|
+
@by = by
|
6
|
+
@type = type
|
7
|
+
@created_at = created_at
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :to
|
11
|
+
attr_reader :by
|
12
|
+
attr_reader :type
|
13
|
+
attr_reader :created_at
|
14
|
+
|
15
|
+
def self.serialize_from_json(json)
|
16
|
+
new(json['to'], json['by'], json['type'], json['createdAt'])
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_dict
|
20
|
+
dict = { :to => @to, :by => @by, :type => @type }
|
21
|
+
if @created_at != nil
|
22
|
+
dict[:createdAt] = @created_at
|
23
|
+
end
|
24
|
+
dict
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
module SubscriptionType
|
30
|
+
PERSONAL_STREAM = 1
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'livefyre/api/personalized_stream'
|
2
|
+
|
3
|
+
module Livefyre
|
4
|
+
class TimelineCursor
|
5
|
+
def initialize(core, resource, limit, date)
|
6
|
+
@core = core
|
7
|
+
@resource = resource
|
8
|
+
@limit = limit
|
9
|
+
@next = false
|
10
|
+
@previous = false
|
11
|
+
|
12
|
+
@cursor_time = date.utc.iso8601(3)
|
13
|
+
end
|
14
|
+
|
15
|
+
def next(limit=@limit)
|
16
|
+
data = PersonalizedStream::get_timeline_stream(@core, @resource, limit, nil, @cursor_time)
|
17
|
+
cursor = data['meta']['cursor']
|
18
|
+
|
19
|
+
@next = cursor['hasNext']
|
20
|
+
@previous = cursor['next'] != nil
|
21
|
+
@cursor_time = cursor['next']
|
22
|
+
|
23
|
+
data
|
24
|
+
end
|
25
|
+
|
26
|
+
def previous(limit=@limit)
|
27
|
+
data = PersonalizedStream::get_timeline_stream(@core, @resource, limit, @cursor_time, nil)
|
28
|
+
cursor = data['meta']['cursor']
|
29
|
+
|
30
|
+
@previous = cursor['hasPrev']
|
31
|
+
@next = cursor['prev'] != nil
|
32
|
+
@cursor_time = cursor['prev']
|
33
|
+
|
34
|
+
data
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_cursor_time(time)
|
38
|
+
@cursor_time = time.utc.iso8601(3)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Livefyre
|
2
|
+
class Topic
|
3
|
+
TOPIC_IDENTIFIER = ':topic='
|
4
|
+
|
5
|
+
def initialize(id, label, created_at=nil, modified_at=nil)
|
6
|
+
@id = id
|
7
|
+
@label = label
|
8
|
+
@created_at = created_at
|
9
|
+
@modified_at = modified_at
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :id
|
13
|
+
attr_reader :label
|
14
|
+
attr_reader :created_at
|
15
|
+
attr_reader :modified_at
|
16
|
+
|
17
|
+
def self.create(core, id, label)
|
18
|
+
new(Topic::generate_urn(core, id), label)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.serialize_from_json(json)
|
22
|
+
new(json['id'], json['label'], json['createdAt'], json['modifiedAt'])
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_dict
|
26
|
+
json = { :id => @id, :label => @label }
|
27
|
+
if @created_at != nil
|
28
|
+
json[:createdAt] = @created_at
|
29
|
+
end
|
30
|
+
|
31
|
+
if @modified_at != nil
|
32
|
+
json[:modifiedAt] = @modified_at
|
33
|
+
end
|
34
|
+
|
35
|
+
json
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.generate_urn(core, id)
|
39
|
+
"#{core.get_urn}#{TOPIC_IDENTIFIER}#{id}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_truncated_id
|
43
|
+
@id[@id.index(TOPIC_IDENTIFIER) + TOPIC_IDENTIFIER.length]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'livefyre/entity/timeline_cursor'
|
2
|
+
|
3
|
+
module Livefyre
|
4
|
+
class CursorFactory
|
5
|
+
def self.get_topic_stream_cursor(core, topic, limit=50, date=Time.new)
|
6
|
+
resource = "#{topic.id}:topicStream"
|
7
|
+
TimelineCursor.new(core, resource, limit, date)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.get_personal_stream_cursor(network, user, limit=50, date=Time.new)
|
11
|
+
resource = "#{network.get_user_urn(user)}:personalStream"
|
12
|
+
TimelineCursor.new(network, resource, limit, date)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/livefyre/version.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'livefyre'
|
2
|
+
require 'jwt'
|
3
|
+
require 'livefyre/api/personalized_stream'
|
4
|
+
require 'livefyre/factory/cursor_factory'
|
5
|
+
|
6
|
+
RSpec.configure do |c|
|
7
|
+
c.filter_run_excluding :broken => true
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Livefyre::Network, :broken => true do
|
11
|
+
before(:each) do
|
12
|
+
@network = Livefyre.get_network(NETWORK_NAME, NETWORK_KEY)
|
13
|
+
@site = @network.get_site(SITE_ID, SITE_KEY)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should test that personalized streams api work for topics' do
|
17
|
+
Livefyre::PersonalizedStream::create_or_update_topic(@network, 1, 'EINS')
|
18
|
+
topic = Livefyre::PersonalizedStream::get_topic(@network, 1)
|
19
|
+
Livefyre::PersonalizedStream::delete_topic(@network, topic).should == true
|
20
|
+
|
21
|
+
Livefyre::PersonalizedStream::create_or_update_topics(@network, {1 => 'EINS', 2 => 'ZWEI'})
|
22
|
+
topics = Livefyre::PersonalizedStream::get_topics(@network)
|
23
|
+
Livefyre::PersonalizedStream::delete_topics(@network, topics)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should test that personalized streams api work for subscriptions' do
|
27
|
+
topics = Livefyre::PersonalizedStream::create_or_update_topics(@network, {1 => 'EINS', 2 => 'ZWEI'})
|
28
|
+
user_token = @network.build_user_auth_token(USER_ID, "#{USER_ID}@#{NETWORK_NAME}", Livefyre::Network::DEFAULT_EXPIRES)
|
29
|
+
|
30
|
+
Livefyre::PersonalizedStream::add_subscriptions(@network, user_token, topics)
|
31
|
+
Livefyre::PersonalizedStream::get_subscriptions(@network, USER_ID)
|
32
|
+
Livefyre::PersonalizedStream::replace_subscriptions(@network, user_token, [topics[1]])
|
33
|
+
Livefyre::PersonalizedStream::get_subscribers(@network, topics[1])
|
34
|
+
Livefyre::PersonalizedStream::remove_subscriptions(@network, user_token, [topics[1]])
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should test that personalized streams api work for timelines and cursors' do
|
38
|
+
topic = Livefyre::PersonalizedStream::create_or_update_topic(@network, 1, 'EINS')
|
39
|
+
cursor = Livefyre::CursorFactory::get_topic_stream_cursor(@network, topic)
|
40
|
+
|
41
|
+
cursor.next
|
42
|
+
cursor.previous
|
43
|
+
|
44
|
+
Livefyre::PersonalizedStream::delete_topic(@network, topic)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should test that personalized streams api work for topics' do
|
48
|
+
Livefyre::PersonalizedStream::create_or_update_topic(@site, 1, 'EINS')
|
49
|
+
topic = Livefyre::PersonalizedStream::get_topic(@site, 1)
|
50
|
+
Livefyre::PersonalizedStream::delete_topic(@site, topic).should == true
|
51
|
+
|
52
|
+
Livefyre::PersonalizedStream::create_or_update_topics(@site, {1 => 'EINS', 2 => 'ZWEI'})
|
53
|
+
topics = Livefyre::PersonalizedStream::get_topics(@site)
|
54
|
+
Livefyre::PersonalizedStream::delete_topics(@site, topics)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should test that personalized streams api work for collections' do
|
58
|
+
topics = Livefyre::PersonalizedStream::create_or_update_topics(@site, {1 => 'EINS', 2 => 'ZWEI'})
|
59
|
+
|
60
|
+
Livefyre::PersonalizedStream::add_collection_topics(@site, COLLECTION_ID, topics)
|
61
|
+
Livefyre::PersonalizedStream::get_collection_topics(@site, COLLECTION_ID)
|
62
|
+
Livefyre::PersonalizedStream::replace_collection_topics(@site, COLLECTION_ID, [topics[1]])
|
63
|
+
Livefyre::PersonalizedStream::remove_collection_topics(@site, COLLECTION_ID, [topics[1]])
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'livefyre'
|
2
|
+
require 'jwt'
|
3
|
+
|
4
|
+
RSpec.configure do |c|
|
5
|
+
c.filter_run_excluding :broken => true
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Livefyre::Network do
|
9
|
+
before(:each) do
|
10
|
+
@network = Livefyre.get_network(NETWORK_NAME, NETWORK_KEY)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should raise ArgumentError if url_template does not contain {id}' do
|
14
|
+
expect{ @network.set_user_sync_url('blah.com/') }.to raise_error(ArgumentError)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should raise ArgumentError if user_id is not alphanumeric' do
|
18
|
+
expect{ @network.build_user_auth_token('fjoiwje.1fj', 'test', 100) }.to raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should validate a livefyre token' do
|
22
|
+
@network.validate_livefyre_token(@network.build_livefyre_token).should == true
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'livefyre'
|
4
|
+
require 'jwt'
|
5
|
+
|
6
|
+
RSpec.configure do |c|
|
7
|
+
c.filter_run_excluding :broken => true
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Livefyre::Site do
|
11
|
+
before(:each) do
|
12
|
+
@site = Livefyre.get_network(NETWORK_NAME, NETWORK_KEY).get_site(SITE_ID, SITE_KEY)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should raise ArgumentError if url is not a valid url for cmt' do
|
16
|
+
expect{ @site.build_collection_meta_token('test', 'test', 'blah.com/', 'test') }.to raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should raise ArgumentError if title is more than 255 characters for cmt' do
|
20
|
+
expect{ @site.build_collection_meta_token('1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456', 'test', 'http://test.com', 'test') }.to raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should raise ArgumentError if not a valid type is passed in when building a collection meta token' do
|
24
|
+
expect{ @site.build_collection_meta_token('', '', 'http://livefyre.com', {type: 'bad type'}) }.to raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should check type and assign them to the correct field in the collection meta token' do
|
28
|
+
@token = @site.build_collection_meta_token('', '', 'http://livefyre.com', {tags: '', type: 'reviews'})
|
29
|
+
@decoded = JWT.decode(@token, SITE_KEY)
|
30
|
+
|
31
|
+
expect(@decoded['type']).to eq('reviews')
|
32
|
+
|
33
|
+
@token = @site.build_collection_meta_token('', '', 'http://livefyre.com', {type: 'liveblog'})
|
34
|
+
@decoded = JWT.decode(@token, SITE_KEY)
|
35
|
+
|
36
|
+
expect(@decoded['type']).to eq('liveblog')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should return a collection meta token' do
|
40
|
+
expect{ @site.build_collection_meta_token('title', 'article_id', 'https://www.url.com', 'tags') }.to be_true
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should raise ArgumentError if url is not a valid url for checksum' do
|
44
|
+
expect{ @site.build_checksum('test', 'blah.com/', 'test') }.to raise_error(ArgumentError)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should raise ArgumentError if title is more than 255 characters for checksum' do
|
48
|
+
expect{ @site.build_checksum('1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456', 'http://test.com', 'test') }.to raise_error(ArgumentError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should return a valid checksum' do
|
52
|
+
expect(@site.build_checksum('title', 'https://www.url.com', 'tags')).to eq('4464458a10c305693b5bf4d43a384be7')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should check for valid and invalid urls' do
|
56
|
+
expect{ @site.build_checksum('', 'test.com', '') }.to raise_error(ArgumentError)
|
57
|
+
|
58
|
+
@site.build_checksum('', 'http://localhost:8000', '')
|
59
|
+
@site.build_checksum('', 'http://清华大学.cn', '')
|
60
|
+
@site.build_checksum('', 'http://www.mysite.com/myresumé.html', '')
|
61
|
+
@site.build_checksum('', 'https://test.com/', '')
|
62
|
+
@site.build_checksum('', 'ftp://test.com/', '')
|
63
|
+
@site.build_checksum('', "https://test.com/path/test.-_~!$&'()*+,;=:@/dash", '')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should test basic site api', :broken => true do
|
67
|
+
@site.get_collection_content(ARTICLE_ID)
|
68
|
+
|
69
|
+
name = "RubyCreateCollection#{Time.new}"
|
70
|
+
id = @site.create_collection(name, name, 'http://answers.livefyre.com/RUBY')
|
71
|
+
expect(@site.get_collection_id(name)).to eq(id)
|
72
|
+
end
|
73
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: livefyre
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Livefyre
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -126,16 +126,34 @@ extensions: []
|
|
126
126
|
extra_rdoc_files: []
|
127
127
|
files:
|
128
128
|
- .gitignore
|
129
|
+
- .idea/.name
|
130
|
+
- .idea/.rakeTasks
|
131
|
+
- .idea/encodings.xml
|
132
|
+
- .idea/misc.xml
|
133
|
+
- .idea/modules.xml
|
134
|
+
- .idea/scopes/scope_settings.xml
|
135
|
+
- .idea/vagrant.xml
|
136
|
+
- .idea/vcs.xml
|
137
|
+
- .project
|
129
138
|
- CHANGELOG
|
130
139
|
- Gemfile
|
131
140
|
- LICENSE.txt
|
132
141
|
- README.md
|
133
142
|
- Rakefile
|
134
143
|
- lib/livefyre.rb
|
135
|
-
- lib/livefyre/
|
144
|
+
- lib/livefyre/api/personalized_stream.rb
|
145
|
+
- lib/livefyre/core/network.rb
|
146
|
+
- lib/livefyre/core/site.rb
|
147
|
+
- lib/livefyre/entity/subscription.rb
|
148
|
+
- lib/livefyre/entity/timeline_cursor.rb
|
149
|
+
- lib/livefyre/entity/topic.rb
|
150
|
+
- lib/livefyre/factory/cursor_factory.rb
|
136
151
|
- lib/livefyre/version.rb
|
137
152
|
- livefyre.gemspec
|
138
|
-
- spec/livefyre/
|
153
|
+
- spec/livefyre/api/personalized_stream_spec.rb
|
154
|
+
- spec/livefyre/core/network_spec.rb
|
155
|
+
- spec/livefyre/core/site_spec.rb
|
156
|
+
- spec/livefyre/test_spec.rb
|
139
157
|
homepage: http://github.com/livefyre/livefyre-ruby-utils
|
140
158
|
licenses:
|
141
159
|
- MIT
|
@@ -164,5 +182,8 @@ signing_key:
|
|
164
182
|
specification_version: 4
|
165
183
|
summary: Livefyre Ruby utility classes
|
166
184
|
test_files:
|
167
|
-
- spec/livefyre/
|
185
|
+
- spec/livefyre/api/personalized_stream_spec.rb
|
186
|
+
- spec/livefyre/core/network_spec.rb
|
187
|
+
- spec/livefyre/core/site_spec.rb
|
188
|
+
- spec/livefyre/test_spec.rb
|
168
189
|
has_rdoc:
|
data/lib/livefyre/core.rb
DELETED
@@ -1,129 +0,0 @@
|
|
1
|
-
require 'base64'
|
2
|
-
require 'digest'
|
3
|
-
require 'json'
|
4
|
-
require 'jwt'
|
5
|
-
require 'rest-client'
|
6
|
-
require 'addressable/uri'
|
7
|
-
|
8
|
-
module Livefyre
|
9
|
-
class Network
|
10
|
-
DEFAULT_USER = 'system'
|
11
|
-
DEFAULT_EXPIRES = 86400
|
12
|
-
|
13
|
-
def initialize(network_name, network_key)
|
14
|
-
@network_name = network_name
|
15
|
-
@network_key = network_key
|
16
|
-
end
|
17
|
-
|
18
|
-
def set_user_sync_url(url_template)
|
19
|
-
raise ArgumentError, 'url_template should contain {id}' if !url_template.include?('{id}')
|
20
|
-
|
21
|
-
response =
|
22
|
-
RestClient.post(
|
23
|
-
"http://#{@network_name}",
|
24
|
-
{ actor_token: build_livefyre_token, pull_profile_url: url_template }
|
25
|
-
)
|
26
|
-
response.code == 204
|
27
|
-
end
|
28
|
-
|
29
|
-
def sync_user(user_id)
|
30
|
-
response =
|
31
|
-
RestClient.post(
|
32
|
-
"http://#{@network_name}/api/v3_0/user/#{user_id}/refresh",
|
33
|
-
{ lftoken: build_livefyre_token }
|
34
|
-
)
|
35
|
-
response.code == 200
|
36
|
-
end
|
37
|
-
|
38
|
-
def build_livefyre_token
|
39
|
-
build_user_auth_token(DEFAULT_USER, DEFAULT_USER, DEFAULT_EXPIRES)
|
40
|
-
end
|
41
|
-
|
42
|
-
def build_user_auth_token(user_id, display_name, expires)
|
43
|
-
raise ArgumentError, 'user_id must be alphanumeric' if !(user_id =~ /\A\p{Alnum}+\z/)
|
44
|
-
|
45
|
-
JWT.encode({
|
46
|
-
domain: @network_name,
|
47
|
-
user_id: user_id,
|
48
|
-
display_name: display_name,
|
49
|
-
expires: Time.new.to_i + expires},
|
50
|
-
@network_key)
|
51
|
-
end
|
52
|
-
|
53
|
-
def validate_livefyre_token(lf_token)
|
54
|
-
token_attributes = JWT.decode(lf_token, @network_key)
|
55
|
-
|
56
|
-
token_attributes['domain'] == @network_name \
|
57
|
-
&& token_attributes['user_id'] == DEFAULT_USER \
|
58
|
-
&& token_attributes['expires'] >= Time.new.to_i
|
59
|
-
end
|
60
|
-
|
61
|
-
def get_site(site_id, site_key)
|
62
|
-
Site.new(@network_name, site_id, site_key)
|
63
|
-
end
|
64
|
-
|
65
|
-
class Site
|
66
|
-
TYPE = ['reviews', 'sidenotes', 'ratings', 'counting', 'liveblog', 'livechat', 'livecomments']
|
67
|
-
|
68
|
-
def initialize(network_name, site_id, site_key)
|
69
|
-
@network_name = network_name
|
70
|
-
@site_id = site_id
|
71
|
-
@site_key = site_key
|
72
|
-
end
|
73
|
-
|
74
|
-
def build_collection_meta_token(title, article_id, url, tags='', type=nil)
|
75
|
-
raise ArgumentError, 'provided url is not a valid url' if !uri?(url)
|
76
|
-
raise ArgumentError, 'title length should be under 255 char' if title.length > 255
|
77
|
-
|
78
|
-
collection_meta = {
|
79
|
-
url: url,
|
80
|
-
tags: tags,
|
81
|
-
title: title,
|
82
|
-
articleId: article_id
|
83
|
-
}
|
84
|
-
if type
|
85
|
-
if TYPE.include? type
|
86
|
-
collection_meta[:type] = type
|
87
|
-
else
|
88
|
-
raise ArgumentError, 'type is not a recognized type. should be liveblog, livechat, livecomments, reviews, sidenotes, or an empty string'
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
JWT.encode(collection_meta, @site_key)
|
93
|
-
end
|
94
|
-
|
95
|
-
def build_checksum(title, url, tags='')
|
96
|
-
raise ArgumentError, 'provided url is not a valid url' if !uri?(url)
|
97
|
-
raise ArgumentError, 'title length should be under 255 char' if title.length > 255
|
98
|
-
|
99
|
-
collection_meta = { url: url, tags: tags, title: title }
|
100
|
-
Digest::MD5.new.update(collection_meta.to_json).hexdigest
|
101
|
-
end
|
102
|
-
|
103
|
-
def get_collection_content(article_id)
|
104
|
-
response =
|
105
|
-
RestClient.get(
|
106
|
-
"http://bootstrap.#{@network_name}/bs3/#{@network_name}/#{@site_id}/#{Base64.encode64(article_id.to_s()).chomp}/init",
|
107
|
-
:accepts => :json
|
108
|
-
)
|
109
|
-
response.code == 200 ? JSON.parse(response) : nil
|
110
|
-
end
|
111
|
-
|
112
|
-
def get_collection_id(article_id)
|
113
|
-
content = get_collection_content(article_id)
|
114
|
-
if content
|
115
|
-
content['collectionSettings']['collectionId']
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def uri?(string)
|
120
|
-
uri = Addressable::URI.parse(string)
|
121
|
-
%w( ftp ftps http https ).include?(uri.scheme)
|
122
|
-
rescue Addressable::URI::BadURIError
|
123
|
-
false
|
124
|
-
rescue Addressable::URI::InvalidURIError
|
125
|
-
false
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
data/spec/livefyre/core_spec.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
require 'livefyre'
|
4
|
-
require 'jwt'
|
5
|
-
|
6
|
-
describe Livefyre::Network do
|
7
|
-
before(:each) do
|
8
|
-
@network = Livefyre.get_network('networkName', 'networkKey')
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'should raise ArgumentError if url_template does not contain {id}' do
|
12
|
-
expect{ @network.set_user_sync_url('blah.com/') }.to raise_error(ArgumentError)
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'should raise ArgumentError if user_id is not alphanumeric' do
|
16
|
-
expect{ @network.build_user_auth_token('fjoiwje.1fj', 'test', 100) }.to raise_error(ArgumentError)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'should validate a livefyre token' do
|
20
|
-
@network.validate_livefyre_token(@network.build_livefyre_token).should == true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe Livefyre::Network::Site do
|
25
|
-
before(:each) do
|
26
|
-
@site = Livefyre.get_network('networkName', 'networkKey').get_site('siteId', 'siteKey')
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'should raise ArgumentError if url is not a valid url for cmt' do
|
30
|
-
expect{ @site.build_collection_meta_token('test', 'test', 'blah.com/', 'test') }.to raise_error(ArgumentError)
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should raise ArgumentError if title is more than 255 characters for cmt' do
|
34
|
-
expect{ @site.build_collection_meta_token('1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456', 'test', 'http://test.com', 'test') }.to raise_error(ArgumentError)
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'should raise ArgumentError if not a valid type is passed in when building a collection meta token' do
|
38
|
-
expect{ @site.build_collection_meta_token('', '', 'http://livefyre.com', '', 'bad type') }.to raise_error(ArgumentError)
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'should check type and assign them to the correct field in the collection meta token' do
|
42
|
-
@token = @site.build_collection_meta_token('', '', 'http://livefyre.com', '', 'reviews')
|
43
|
-
@decoded = JWT.decode(@token, 'siteKey')
|
44
|
-
|
45
|
-
expect(@decoded['type']).to eq('reviews')
|
46
|
-
|
47
|
-
@token = @site.build_collection_meta_token('', '', 'http://livefyre.com', '', 'liveblog')
|
48
|
-
@decoded = JWT.decode(@token, 'siteKey')
|
49
|
-
|
50
|
-
expect(@decoded['type']).to eq('liveblog')
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'should return a collection meta token' do
|
54
|
-
expect{ @site.build_collection_meta_token('title', 'article_id', 'https://www.url.com', 'tags') }.to be_true
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'should raise ArgumentError if url is not a valid url for checksum' do
|
58
|
-
expect{ @site.build_checksum('test', 'blah.com/', 'test') }.to raise_error(ArgumentError)
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'should raise ArgumentError if title is more than 255 characters for checksum' do
|
62
|
-
expect{ @site.build_checksum('1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456', 'http://test.com', 'test') }.to raise_error(ArgumentError)
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'should return a valid checksum' do
|
66
|
-
expect(@site.build_checksum('title', 'https://www.url.com', 'tags')).to eq('6e2e4faf7b95f896260fe695eafb34ba')
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'should check for valid and invalid urls' do
|
70
|
-
expect{ @site.build_checksum('', 'test.com', '') }.to raise_error(ArgumentError)
|
71
|
-
|
72
|
-
@site.build_checksum('', 'http://localhost:8000', '')
|
73
|
-
@site.build_checksum('', 'http://清华大学.cn', '')
|
74
|
-
@site.build_checksum('', 'http://www.mysite.com/myresumé.html', '')
|
75
|
-
@site.build_checksum('', 'https://test.com/', '')
|
76
|
-
@site.build_checksum('', 'ftp://test.com/', '')
|
77
|
-
@site.build_checksum('', "https://test.com/path/test.-_~!$&'()*+,;=:@/dash", '')
|
78
|
-
end
|
79
|
-
end
|