livefyre 1.1.4 → 1.3.0
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.
- 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
|