mogli 0.0.29 → 0.0.30
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.
- data/lib/mogli/client.rb +25 -14
- data/lib/mogli/education.rb +2 -1
- data/lib/mogli/page.rb +0 -3
- data/lib/mogli/user.rb +5 -1
- data/lib/mogli/work.rb +2 -1
- data/spec/app_client_spec.rb +41 -0
- data/spec/authenticator_spec.rb +92 -0
- data/spec/client/client_event_spec.rb +12 -0
- data/spec/client/client_user_spec.rb +12 -0
- data/spec/client_spec.rb +354 -0
- data/spec/fetching_array_spec.rb +98 -0
- data/spec/model_spec.rb +245 -0
- data/spec/page_spec.rb +19 -0
- data/spec/profile_spec.rb +16 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/test_user_spec.rb +25 -0
- data/spec/user_spec.rb +107 -0
- metadata +84 -18
- data/lib/mogli/subscription.rb +0 -6
data/lib/mogli/client.rb
CHANGED
@@ -10,7 +10,7 @@ module Mogli
|
|
10
10
|
include HTTParty
|
11
11
|
include Mogli::Client::Event
|
12
12
|
include Mogli::Client::User
|
13
|
-
|
13
|
+
|
14
14
|
class ClientException < Exception; end
|
15
15
|
class UnrecognizeableClassError < ClientException; end
|
16
16
|
class QueryParseException < ClientException; end
|
@@ -19,6 +19,7 @@ module Mogli
|
|
19
19
|
class OAuthException < ClientException; end
|
20
20
|
# represents case that the facebook limit on posts to a feed has been exceeded
|
21
21
|
class FeedActionRequestLimitExceeded < ClientException; end
|
22
|
+
class SessionInvalidatedDueToPasswordChange < ClientException; end
|
22
23
|
class HTTPException < ClientException; end
|
23
24
|
|
24
25
|
def api_path(path)
|
@@ -32,7 +33,7 @@ module Mogli
|
|
32
33
|
def fql_multiquery_path
|
33
34
|
"https://api.facebook.com/method/fql.multiquery"
|
34
35
|
end
|
35
|
-
|
36
|
+
|
36
37
|
def initialize(access_token = nil,expiration=nil)
|
37
38
|
@access_token = access_token
|
38
39
|
# nil expiration means extended access
|
@@ -49,7 +50,7 @@ module Mogli
|
|
49
50
|
post_data = get(authenticator.access_token_url(code)).parsed_response
|
50
51
|
if (response_is_error?(post_data))
|
51
52
|
raise_client_exception(post_data)
|
52
|
-
end
|
53
|
+
end
|
53
54
|
parts = post_data.split("&")
|
54
55
|
hash = {}
|
55
56
|
parts.each do |p| (k,v) = p.split("=")
|
@@ -61,10 +62,10 @@ module Mogli
|
|
61
62
|
else
|
62
63
|
expires = nil
|
63
64
|
end
|
64
|
-
|
65
|
+
|
65
66
|
new(hash["access_token"],expires)
|
66
67
|
end
|
67
|
-
|
68
|
+
|
68
69
|
def self.raise_client_exception(post_data)
|
69
70
|
raise_error_by_type_and_message(post_data["error"]["type"], post_data["error"]["message"])
|
70
71
|
end
|
@@ -72,13 +73,15 @@ module Mogli
|
|
72
73
|
def self.raise_error_by_type_and_message(type, message)
|
73
74
|
if type == 'OAuthException' && message =~ /Feed action request limit reached/
|
74
75
|
raise FeedActionRequestLimitExceeded.new(message)
|
76
|
+
elsif type == 'OAuthException' && message =~ /The session has been invalidated because the user has changed the password/
|
77
|
+
raise SessionInvalidatedDueToPasswordChange.new(message)
|
75
78
|
elsif Mogli::Client.const_defined?(type)
|
76
79
|
raise Mogli::Client.const_get(type).new(message)
|
77
80
|
else
|
78
81
|
raise ClientException.new("#{type}: #{message}")
|
79
82
|
end
|
80
83
|
end
|
81
|
-
|
84
|
+
|
82
85
|
def self.response_is_error?(post_data)
|
83
86
|
post_data.kind_of?(Hash) and
|
84
87
|
!post_data["error"].blank?
|
@@ -87,10 +90,19 @@ module Mogli
|
|
87
90
|
def self.create_from_session_key(session_key, client_id, secret)
|
88
91
|
authenticator = Mogli::Authenticator.new(client_id, secret, nil)
|
89
92
|
access_data = authenticator.get_access_token_for_session_key(session_key)
|
90
|
-
new(access_data
|
91
|
-
|
93
|
+
new(access_token_from_access_data(access_data),expiration_from_access_data(access_data))
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.access_token_from_access_data(access_data)
|
97
|
+
return nil if access_data.nil?
|
98
|
+
access_data['access_token']
|
92
99
|
end
|
93
100
|
|
101
|
+
def self.expiration_from_access_data(access_data)
|
102
|
+
return nil if access_data.nil? or access_data['expires'].nil?
|
103
|
+
Time.now.to_i + access_data['expires'].to_i
|
104
|
+
end
|
105
|
+
|
94
106
|
def self.create_and_authenticate_as_application(client_id, secret)
|
95
107
|
authenticator = Mogli::Authenticator.new(client_id, secret, nil)
|
96
108
|
access_data = authenticator.get_access_token_for_application
|
@@ -123,7 +135,7 @@ module Mogli
|
|
123
135
|
data = self.class.post(fql_multiquery_path,:body=>default_params.merge({:queries=>queries.to_json,:format=>"json"}))
|
124
136
|
map_data(data)
|
125
137
|
end
|
126
|
-
|
138
|
+
|
127
139
|
def get_and_map(path,klass=nil,body_args = {})
|
128
140
|
data = self.class.get(api_path(path),:query=>default_params.merge(body_args))
|
129
141
|
data = data.values if body_args.key?(:ids) && !data.key?('error')
|
@@ -146,13 +158,12 @@ module Mogli
|
|
146
158
|
|
147
159
|
def extract_hash_or_array(hash_or_array,klass)
|
148
160
|
hash_or_array = hash_or_array.parsed_response if hash_or_array.respond_to?(:parsed_response)
|
149
|
-
return nil if hash_or_array == false
|
150
|
-
|
161
|
+
return nil if hash_or_array == false
|
151
162
|
return hash_or_array if hash_or_array.nil? or hash_or_array.kind_of?(Array)
|
152
163
|
return extract_fetching_array(hash_or_array,klass) if is_fetching_array?(hash_or_array)
|
153
164
|
return hash_or_array
|
154
165
|
end
|
155
|
-
|
166
|
+
|
156
167
|
def is_fetching_array?(hash)
|
157
168
|
hash.has_key?("data") and hash["data"].instance_of?(Array)
|
158
169
|
end
|
@@ -185,7 +196,7 @@ module Mogli
|
|
185
196
|
end
|
186
197
|
klass_to_create.new(data,self)
|
187
198
|
end
|
188
|
-
|
199
|
+
|
189
200
|
def capitalize_if_required(string)
|
190
201
|
string.downcase == string ? string.capitalize : string
|
191
202
|
end
|
@@ -201,12 +212,12 @@ module Mogli
|
|
201
212
|
end
|
202
213
|
|
203
214
|
def raise_error_if_necessary(data)
|
215
|
+
raise HTTPException if data.respond_to?(:code) and data.code != 200
|
204
216
|
if data.kind_of?(Hash)
|
205
217
|
if data.keys.size == 1 and data["error"]
|
206
218
|
self.class.raise_error_by_type_and_message(data["error"]["type"], data["error"]["message"])
|
207
219
|
end
|
208
220
|
end
|
209
|
-
raise HTTPException if data.respond_to?(:code) and data.code != 200
|
210
221
|
end
|
211
222
|
|
212
223
|
def fields_to_serialize
|
data/lib/mogli/education.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Mogli
|
2
2
|
class Education < Model
|
3
3
|
|
4
|
-
define_properties :start_date, :end_date, :degree
|
4
|
+
define_properties :start_date, :end_date, :degree, :with
|
5
5
|
|
6
|
+
hash_populating_accessor :classes, "Page"
|
6
7
|
hash_populating_accessor :school, "Page"
|
7
8
|
hash_populating_accessor :year, "Page"
|
8
9
|
hash_populating_accessor :concentration, "Page"
|
data/lib/mogli/page.rb
CHANGED
@@ -16,9 +16,6 @@ module Mogli
|
|
16
16
|
# As a like
|
17
17
|
define_properties :created_time
|
18
18
|
|
19
|
-
# Number of checkins, not Checkin assocciation.
|
20
|
-
define_properties :checkins
|
21
|
-
|
22
19
|
def client_for_page
|
23
20
|
if access_token.blank?
|
24
21
|
raise MissingAccessToken.new("You can only get a client for this page if an access_token has been provided. i.e. via /me/accounts")
|
data/lib/mogli/user.rb
CHANGED
@@ -4,12 +4,16 @@ module Mogli
|
|
4
4
|
|
5
5
|
define_properties :username, :first_name, :last_name, :link, :about, :birthday, :gender,
|
6
6
|
:email, :website, :timezone, :updated_time, :verified, :political, :bio,
|
7
|
-
:relationship_status, :locale, :religion, :quotes, :third_party_id
|
7
|
+
:relationship_status, :locale, :religion, :quotes, :third_party_id,
|
8
|
+
:inspirational_people, :sports, :with, :middle_name
|
8
9
|
|
9
10
|
def self.recognize?(hash)
|
10
11
|
!hash.has_key?("category")
|
11
12
|
end
|
12
13
|
|
14
|
+
hash_populating_accessor :favorite_athletes, "Page"
|
15
|
+
hash_populating_accessor :favorite_teams, "Page"
|
16
|
+
|
13
17
|
hash_populating_accessor :work, "Work"
|
14
18
|
hash_populating_accessor :education, "Education"
|
15
19
|
|
data/lib/mogli/work.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Mogli
|
2
2
|
class Work < Model
|
3
3
|
|
4
|
-
define_properties :start_date, :end_date, :description
|
4
|
+
define_properties :start_date, :end_date, :description, :with, :from
|
5
5
|
|
6
|
+
hash_populating_accessor :projects, "Page"
|
6
7
|
hash_populating_accessor :employer, "Page"
|
7
8
|
hash_populating_accessor :location, "Page"
|
8
9
|
hash_populating_accessor :position, "Page"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
describe Mogli::AppClient do
|
3
|
+
describe "subscribing" do
|
4
|
+
let :client do
|
5
|
+
a=Mogli::AppClient.new('key')
|
6
|
+
a.application_id="123"
|
7
|
+
a
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should allow you to subscribe with callback url, fields and verify token" do
|
11
|
+
Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",an_instance_of(Hash))
|
12
|
+
client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"])
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should pass the type as the object field" do
|
16
|
+
Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:object=>"user")})
|
17
|
+
client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"])
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sends the list of fields comma separated" do
|
21
|
+
Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:fields=>"username,friends")})
|
22
|
+
client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username,friends"])
|
23
|
+
end
|
24
|
+
it "sends the callback url" do
|
25
|
+
Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:callback_url=>"http://localhost:3000")})
|
26
|
+
client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"])
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should send the verify token if provided" do
|
30
|
+
Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:verify_token=>"TOKEN")})
|
31
|
+
client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"],:verify_token=>"TOKEN")
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
it "Lists the existing subscriptions" do
|
36
|
+
client.should_receive(:get_and_map_url).with("https://graph.facebook.com/123/subscriptions","Subscription")
|
37
|
+
client.subscriptions
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
describe Mogli::Authenticator do
|
3
|
+
|
4
|
+
let :authenticator do
|
5
|
+
Mogli::Authenticator.new("123456","secret","http://example.com/url")
|
6
|
+
end
|
7
|
+
|
8
|
+
it "has the client id" do
|
9
|
+
authenticator.client_id.should == "123456"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has the secret" do
|
13
|
+
authenticator.secret.should == "secret"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has the callback url" do
|
17
|
+
authenticator.callback_url.should == "http://example.com/url"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "creates the authorize_url" do
|
21
|
+
authenticator.authorize_url.should == "https://graph.facebook.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "creates the authorize_url with scopes as an array" do
|
25
|
+
authenticator.authorize_url(:scope=>[:user_photos,:user_videos,:stream_publish]).should ==
|
26
|
+
"https://graph.facebook.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl&scope=user_photos,user_videos,stream_publish"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "creates the access_token_url" do
|
30
|
+
authenticator.access_token_url("mycode").should == "https://graph.facebook.com/oauth/access_token?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl&client_secret=secret&code=mycode"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "can trade session_keys for access_tokens" do
|
34
|
+
response = [{"access_token" => "myaccesstoken", "expires" => 5000},
|
35
|
+
{"access_token" => "youraccesstoken", "expires" => 5000}]
|
36
|
+
response.stub!(:code).and_return(200)
|
37
|
+
|
38
|
+
Mogli::Client.should_receive(:post).
|
39
|
+
with("https://graph.facebook.com/oauth/exchange_sessions",
|
40
|
+
:body => {:type => "client_cred", :client_id => "123456",
|
41
|
+
:client_secret => "secret",
|
42
|
+
:sessions => "mysessionkey,yoursessionkey"}).
|
43
|
+
and_return(response)
|
44
|
+
|
45
|
+
authenticator.
|
46
|
+
get_access_token_for_session_key(["mysessionkey","yoursessionkey"]).
|
47
|
+
should == [{"access_token" => "myaccesstoken", "expires" => 5000},
|
48
|
+
{"access_token" => "youraccesstoken", "expires" => 5000}]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "can trade one session_key for an access_tokens" do
|
52
|
+
response = {"access_token" => "myaccesstoken", "expires" => 5000}
|
53
|
+
response.stub!(:code).and_return(200)
|
54
|
+
Mogli::Client.should_receive(:post).
|
55
|
+
with("https://graph.facebook.com/oauth/exchange_sessions",
|
56
|
+
:body => {:type => "client_cred", :client_id => "123456",
|
57
|
+
:client_secret => "secret", :sessions => "mysessionkey"}).
|
58
|
+
and_return(response)
|
59
|
+
|
60
|
+
authenticator.
|
61
|
+
get_access_token_for_session_key("mysessionkey").
|
62
|
+
should == {"access_token" => "myaccesstoken", "expires" => 5000}
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can ask for an application token" do
|
66
|
+
response = "access_token=123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo"
|
67
|
+
response.stub!(:code).and_return(200)
|
68
|
+
Mogli::Client.should_receive(:post).
|
69
|
+
with("https://graph.facebook.com/oauth/access_token",
|
70
|
+
:body=> {
|
71
|
+
:grant_type => 'client_credentials',
|
72
|
+
:client_id => "123456",
|
73
|
+
:client_secret => "secret"
|
74
|
+
}).
|
75
|
+
and_return(response)
|
76
|
+
|
77
|
+
|
78
|
+
token = authenticator.get_access_token_for_application
|
79
|
+
token.should =="123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "raises an error if not a 200" do
|
83
|
+
response = "access_token=123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo"
|
84
|
+
response.stub!(:code).and_return(500)
|
85
|
+
Mogli::Client.should_receive(:post).and_return(response)
|
86
|
+
lambda do
|
87
|
+
token = authenticator.get_access_token_for_application
|
88
|
+
end.should raise_error(Mogli::Client::HTTPException)
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
describe Mogli::Client do
|
3
|
+
let :client do
|
4
|
+
Mogli::Client.new
|
5
|
+
end
|
6
|
+
|
7
|
+
it "fetches an event by id" do
|
8
|
+
client.should_receive(:get_and_map).with(118515001510745,Mogli::Event).and_return("event")
|
9
|
+
client.event(118515001510745).should == "event"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
describe Mogli::Client do
|
3
|
+
let :client do
|
4
|
+
Mogli::Client.new
|
5
|
+
end
|
6
|
+
|
7
|
+
it "fetches a user by id" do
|
8
|
+
client.should_receive(:get_and_map).with(12451752,Mogli::User).and_return("user")
|
9
|
+
client.user(12451752).should == "user"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,354 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
describe Mogli::Client do
|
3
|
+
|
4
|
+
let :client do
|
5
|
+
Mogli::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
describe "creation" do
|
10
|
+
it "allows creating with an access_token" do
|
11
|
+
client = Mogli::Client.new("myaccesstoken")
|
12
|
+
client.access_token.should == "myaccesstoken"
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when exchanging a session key" do
|
16
|
+
it "allows creating" do
|
17
|
+
Time.stub!(:now).and_return(1270000000)
|
18
|
+
authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
|
19
|
+
authenticator.should_receive(:get_access_token_for_session_key).
|
20
|
+
with('mysessionkey').
|
21
|
+
and_return({'access_token' => 'myaccesstoken',
|
22
|
+
'expires' => 5000})
|
23
|
+
Mogli::Authenticator.should_receive(:new).and_return(authenticator)
|
24
|
+
client = Mogli::Client.create_from_session_key(
|
25
|
+
'mysessionkey', '123456', 'secret')
|
26
|
+
client.access_token.should == 'myaccesstoken'
|
27
|
+
client.expiration.should == Time.at(1270005000)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allows creating and authenticating as an application" do
|
31
|
+
authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
|
32
|
+
authenticator.should_receive(:get_access_token_for_application).
|
33
|
+
and_return("123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo")
|
34
|
+
Mogli::Authenticator.should_receive(:new).and_return(authenticator)
|
35
|
+
client = Mogli::Client.create_and_authenticate_as_application(
|
36
|
+
'mysessionkey', '123456')
|
37
|
+
client.access_token.should == '123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "doesn't bail when the session key is stale" do
|
41
|
+
Time.stub!(:now).and_return(1270000000)
|
42
|
+
authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
|
43
|
+
authenticator.should_receive(:get_access_token_for_session_key).
|
44
|
+
with('mysessionkey').
|
45
|
+
and_return(nil)
|
46
|
+
Mogli::Authenticator.should_receive(:new).and_return(authenticator)
|
47
|
+
lambda {
|
48
|
+
client = Mogli::Client.create_from_session_key(
|
49
|
+
'mysessionkey', '123456', 'secret')
|
50
|
+
}.should_not raise_exception(NoMethodError, /nil/)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "treats missing expiries as nonexpiring access tokens" do
|
54
|
+
Time.stub!(:now).and_return(1270000000)
|
55
|
+
authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
|
56
|
+
authenticator.should_receive(:get_access_token_for_session_key).
|
57
|
+
with('mysessionkey').
|
58
|
+
and_return({'access_token' => 'myaccesstoken'})
|
59
|
+
Mogli::Authenticator.should_receive(:new).and_return(authenticator)
|
60
|
+
client = Mogli::Client.create_from_session_key(
|
61
|
+
'mysessionkey', '123456', 'secret')
|
62
|
+
client.expiration.should == Time.at(Time.now + 10*365*24*60*60)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "sets the access_token into the default params" do
|
67
|
+
client = Mogli::Client.new("myaccesstoken")
|
68
|
+
client.default_params.should == {:access_token=>"myaccesstoken"}
|
69
|
+
end
|
70
|
+
|
71
|
+
it "sets the expiration time if provided" do
|
72
|
+
old_now=Time.now
|
73
|
+
Time.stub!(:now).and_return(old_now)
|
74
|
+
client = Mogli::Client.new("myaccesstoken",old_now.to_i)
|
75
|
+
client.expiration.to_i.should == old_now.to_i
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should know if the session is expired" do
|
79
|
+
client = Mogli::Client.new("myaccesstoken",Time.now - 100)
|
80
|
+
client.should be_expired
|
81
|
+
end
|
82
|
+
|
83
|
+
it "allow creation with no access token" do
|
84
|
+
client = Mogli::Client.new
|
85
|
+
client.access_token.should be_nil
|
86
|
+
end
|
87
|
+
|
88
|
+
it "doesn't include the access_token param when not passed" do
|
89
|
+
client = Mogli::Client.new
|
90
|
+
client.default_params.should == {}
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
it "create get an unescaped access token from an authenticator and code" do
|
95
|
+
Mogli::Client.should_receive(:get).with("url").and_return(mock("response",:parsed_response=>
|
96
|
+
"access_token=114355055262088%7C2.6_s8VD_HRneAq3_tUEHJhA__.3600.1272920400-12451752|udZzWly7ptI7IMgX7KTdzaoDrhU.&expires=4168"))
|
97
|
+
client = Mogli::Client.create_from_code_and_authenticator("code",mock("auth",:access_token_url=>"url"))
|
98
|
+
client.access_token.should == "114355055262088|2.6_s8VD_HRneAq3_tUEHJhA__.3600.1272920400-12451752|udZzWly7ptI7IMgX7KTdzaoDrhU."
|
99
|
+
client.expiration.should_not be_nil
|
100
|
+
(client.expiration > Time.now).should be_true
|
101
|
+
end
|
102
|
+
|
103
|
+
it "create set the expiration into the future if there is on param" do
|
104
|
+
Mogli::Client.should_receive(:get).with("url").and_return(mock("response",:parsed_response=>"access_token=114355055262088%7C2.6_s8VD_HRneAq3_tUEHJhA__.3600.1272920400-12451752|udZzWly7ptI7IMgX7KTdzaoDrhU."))
|
105
|
+
client = Mogli::Client.create_from_code_and_authenticator("code",mock("auth",:access_token_url=>"url"))
|
106
|
+
(client.expiration > Time.now+365*24*60*60).should be_true
|
107
|
+
end
|
108
|
+
|
109
|
+
it "create throw exception if facebook returns exception" do
|
110
|
+
err_msg = "Some message"
|
111
|
+
err_type = "ErrorType"
|
112
|
+
response={"error"=>{"type"=>err_type,"message"=>err_msg}}
|
113
|
+
mock_response = mock("response",:parsed_response=>response,:is_a? => true,:[] => response["error"])
|
114
|
+
Mogli::Client.should_receive(:get).with("url").and_return(mock_response)
|
115
|
+
begin
|
116
|
+
client = Mogli::Client.create_from_code_and_authenticator("code",mock("auth",:access_token_url=>"url"))
|
117
|
+
rescue Exception => e
|
118
|
+
e.message.should == "#{err_type}: #{err_msg}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
describe "Making requests" do
|
126
|
+
it "posts with the parameters in the body" do
|
127
|
+
Mogli::Client.should_receive(:post).with("https://graph.facebook.com/1/feed",:body=>{:message=>"message",:access_token=>"1234"})
|
128
|
+
client = Mogli::Client.new("1234")
|
129
|
+
client.post("1/feed","Post",:message=>"message")
|
130
|
+
end
|
131
|
+
|
132
|
+
it "parses errors in the returns of posts" do
|
133
|
+
Mogli::Client.should_receive(:post).and_return({"error"=>{"type"=>"OAuthAccessTokenException","message"=>"An access token is required to request this resource."}})
|
134
|
+
client = Mogli::Client.new("1234")
|
135
|
+
lambda do
|
136
|
+
result = client.post("1/feed","Post",:message=>"message")
|
137
|
+
end.should raise_error(Mogli::Client::OAuthAccessTokenException, "An access token is required to request this resource.")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "parses http response errors" do
|
141
|
+
Mogli::Client.should_receive(:post).and_return(mock("httpresponse",:code=>500))
|
142
|
+
client = Mogli::Client.new("1234")
|
143
|
+
lambda do
|
144
|
+
result = client.post("1/feed","Post",:message=>"message")
|
145
|
+
end.should raise_error(Mogli::Client::HTTPException)
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
it "creates objects of the returned type" do
|
150
|
+
Mogli::Client.should_receive(:post).and_return({:id=>123434})
|
151
|
+
client = Mogli::Client.new("1234")
|
152
|
+
result = client.post("1/feed","Post",:message=>"message")
|
153
|
+
result.should == Mogli::Post.new(:id=>123434)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "creates object in a way that ignore invalid properties" do
|
157
|
+
Mogli::Client.stub!(:post).and_return({:id=>123434,:completely_invalid_property=>1})
|
158
|
+
client = Mogli::Client.new("1234")
|
159
|
+
lambda do
|
160
|
+
result = client.post("1/feed","Post",:message=>"message")
|
161
|
+
end.should_not raise_error
|
162
|
+
end
|
163
|
+
|
164
|
+
it "raises specific exception if Facebook-imposed posting limit exceeded for feed" do
|
165
|
+
error_message = "Feed action request limit reached"
|
166
|
+
Mogli::Client.should_receive(:post).and_return({"error"=>{"type"=>"OAuthException","message"=>error_message}})
|
167
|
+
client = Mogli::Client.new("1234")
|
168
|
+
lambda do
|
169
|
+
result = client.post("1/feed","Post",:message=>"message")
|
170
|
+
end.should raise_error(Mogli::Client::FeedActionRequestLimitExceeded, error_message)
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
it "allows deleting" do
|
176
|
+
Mogli::Client.should_receive(:delete).with("https://graph.facebook.com/1",:query=>{:access_token=>"1234"})
|
177
|
+
client = Mogli::Client.new("1234")
|
178
|
+
client.delete("1")
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
describe "fql queries" do
|
183
|
+
it "defaults to json" do
|
184
|
+
Mogli::Client.should_receive(:post).with("https://api.facebook.com/method/fql.query",:body=>{:query=>"query",:format=>"json",:access_token=>"1234"})
|
185
|
+
client = Mogli::Client.new("1234")
|
186
|
+
client.fql_query("query")
|
187
|
+
end
|
188
|
+
|
189
|
+
it "supports xml" do
|
190
|
+
Mogli::Client.should_receive(:post).with("https://api.facebook.com/method/fql.query",:body=>{:query=>"query",:format=>"xml",:access_token=>"1234"})
|
191
|
+
client = Mogli::Client.new("1234")
|
192
|
+
client.fql_query("query",nil,"xml")
|
193
|
+
end
|
194
|
+
|
195
|
+
it "creates objects if given a class" do
|
196
|
+
Mogli::Client.should_receive(:post).and_return({"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" })
|
197
|
+
client = Mogli::Client.new("1234")
|
198
|
+
client.fql_query("query","user").should be_an_instance_of(Mogli::User)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "returns a hash if no class is given" do
|
202
|
+
Mogli::Client.should_receive(:post).and_return({"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" })
|
203
|
+
client = Mogli::Client.new("1234")
|
204
|
+
client.fql_query("query").should be_an_instance_of(Hash)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "doesn't create objects if the format is xml" do
|
208
|
+
Mogli::Client.should_receive(:post).and_return({"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" })
|
209
|
+
client = Mogli::Client.new("1234")
|
210
|
+
client.fql_query("query","user","xml").should be_an_instance_of(Hash)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "fql multiqueries" do
|
215
|
+
it "defaults to json" do
|
216
|
+
fql = {"query1"=>"select uid from user"}
|
217
|
+
Mogli::Client.should_receive(:post).with("https://api.facebook.com/method/fql.multiquery",:body=>{:format=>"json",:queries=>fql.to_json,:access_token=>"1234"})
|
218
|
+
client = Mogli::Client.new("1234")
|
219
|
+
client.fql_multiquery(fql)
|
220
|
+
end
|
221
|
+
|
222
|
+
it "returns hash with query names and values matching desired classes" do
|
223
|
+
Mogli::Client.should_receive(:post).and_return([
|
224
|
+
{"name"=>"users", "fql_result_set"=>[{"uid"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino"}]},
|
225
|
+
{"name"=>"comment_user","fql_result_set"=>[{"fromid"=>12451752}]}
|
226
|
+
])
|
227
|
+
|
228
|
+
f = Mogli::FqlMultiquery.new(Mogli::Client.new("1234"))
|
229
|
+
f.add_named_query_for_class("comment_user", "SELECT fromid FROM comment WHERE post_id = 123343434", Mogli::Comment)
|
230
|
+
f.add_named_query_for_class("users", "SELECT uid, first_name, last_name FROM user WHERE uid IN (SELECT fromid FROM #comment_user)", Mogli::User)
|
231
|
+
results = f.results
|
232
|
+
results["users"].first.class.should == Mogli::User
|
233
|
+
results["comment_user"].first.class.should == Mogli::Comment
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "result mapping" do
|
238
|
+
|
239
|
+
let :user_data do
|
240
|
+
{"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" }
|
241
|
+
end
|
242
|
+
|
243
|
+
it "returns the raw value with no class specified" do
|
244
|
+
client.map_data(user_data).should be_an_instance_of(Hash)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "returns nil if we get a false response" do
|
248
|
+
client.map_data(false,Mogli::User).should be_false
|
249
|
+
end
|
250
|
+
it "returns false if we get a false response" do
|
251
|
+
client.map_to_class(false,Mogli::User).should be_false
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
it "returns the array if no class is specified and there is only a data parameter" do
|
256
|
+
client.map_data({"data"=>[user_data,user_data]}).should be_kind_of(Array)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "creates an instance of the class when specified" do
|
260
|
+
user = client.map_data(user_data,Mogli::User)
|
261
|
+
user.should be_an_instance_of(Mogli::User)
|
262
|
+
user.id.should == 12451752
|
263
|
+
end
|
264
|
+
|
265
|
+
it "creates an array of instances when the data is an array" do
|
266
|
+
users = client.map_data([user_data,user_data],Mogli::User)
|
267
|
+
users.should be_an_instance_of(Array)
|
268
|
+
users.each {|i| i.should be_an_instance_of(Mogli::User) }
|
269
|
+
users.size.should == 2
|
270
|
+
end
|
271
|
+
|
272
|
+
it "creates an array of instances when the data is just a hash with a single data parameter" do
|
273
|
+
users = client.map_data({"data"=>[user_data,user_data],"paging"=>{}},Mogli::User)
|
274
|
+
users.should be_kind_of(Array)
|
275
|
+
users.each {|i| i.should be_an_instance_of(Mogli::User) }
|
276
|
+
users.size.should == 2
|
277
|
+
end
|
278
|
+
|
279
|
+
it "create an instance of fetching array when there is a data element" do
|
280
|
+
users = client.map_data({"data"=>[user_data,user_data],"paging"=>{}},Mogli::User)
|
281
|
+
users.should be_an_instance_of(Mogli::FetchingArray)
|
282
|
+
end
|
283
|
+
|
284
|
+
it "sets the client on the array" do
|
285
|
+
users = client.map_data({"data"=>[user_data,user_data],"paging"=>{}},Mogli::User)
|
286
|
+
users.client.should == client
|
287
|
+
end
|
288
|
+
|
289
|
+
it "sets the next url on the array" do
|
290
|
+
users = client.map_data({"data"=>[user_data,user_data],"paging"=>{"next"=>"next"}},Mogli::User)
|
291
|
+
users.next_url.should == "next"
|
292
|
+
end
|
293
|
+
|
294
|
+
it "sets the previous url on the array" do
|
295
|
+
users = client.map_data({"data"=>[user_data,user_data],"paging"=>{"previous"=>"prev"}},Mogli::User)
|
296
|
+
users.previous_url.should == "prev"
|
297
|
+
end
|
298
|
+
|
299
|
+
it "sets the classes on the array" do
|
300
|
+
users = client.map_data({"data"=>[user_data,user_data],"paging"=>{"previous"=>"prev"}},Mogli::User)
|
301
|
+
users.classes.should == [Mogli::User]
|
302
|
+
end
|
303
|
+
|
304
|
+
it "sets the client in the newly created instance" do
|
305
|
+
user = client.map_data(user_data,Mogli::User)
|
306
|
+
user.client.should == client
|
307
|
+
end
|
308
|
+
|
309
|
+
it "returns nil if Facebook says false" do
|
310
|
+
Mogli::Client.should_receive(:get).and_return(false)
|
311
|
+
client.get_and_map(148800401968,"User").should be_nil
|
312
|
+
end
|
313
|
+
|
314
|
+
it "raises an exception with specific message when there is just an error" do
|
315
|
+
lambda do
|
316
|
+
client.map_data({"error"=>{"type"=>"OAuthAccessTokenException","message"=>"An access token is required to request this resource."}})
|
317
|
+
end.should raise_error(Mogli::Client::OAuthAccessTokenException, "An access token is required to request this resource.")
|
318
|
+
end
|
319
|
+
|
320
|
+
it "raises a OAuthException when a token is invalidated by a user logging out of Facebook" do
|
321
|
+
lambda do
|
322
|
+
client.map_data({"error"=>{"type"=>"OAuthException","message"=>"Error validating access token."}})
|
323
|
+
end.should raise_error(Mogli::Client::OAuthException, "Error validating access token.")
|
324
|
+
end
|
325
|
+
|
326
|
+
it "raises a generic ClientException when the exception type is not recorgnized" do
|
327
|
+
lambda do
|
328
|
+
client.map_data({"error"=>{"type"=>"Foo","message"=>"Lorem ipsum."}})
|
329
|
+
end.should raise_error(Mogli::Client::ClientException, "Foo: Lorem ipsum.")
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "Instance creation" do
|
333
|
+
it "will find the class in the Mogli namespace if given a string" do
|
334
|
+
client.create_instance("User",{:id=>1}).should be_an_instance_of(Mogli::User)
|
335
|
+
end
|
336
|
+
|
337
|
+
it "will NOT use the value from the type field if it exists" do
|
338
|
+
client.create_instance("Post",{:id=>1,"type"=>"status"}).should be_an_instance_of(Mogli::Post)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "call the recognize method on each class passing the data and will use the one that recognizes it" do
|
342
|
+
Mogli::User.should_receive(:recognize?).with(:id=>1).and_return(false)
|
343
|
+
Mogli::Post.should_receive(:recognize?).with(:id=>1).and_return(true)
|
344
|
+
client.create_instance(["User","Post"],{:id=>1}).should be_an_instance_of(Mogli::Post)
|
345
|
+
end
|
346
|
+
|
347
|
+
it "will use the first class if none are recognized" do
|
348
|
+
Mogli::Page.should_receive(:recognize?).with(:id=>1).and_return(false)
|
349
|
+
client.create_instance(["Page"],{:id=>1}).should be_an_instance_of(Mogli::Page)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
end
|