spark_api 1.0.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.
- data/History.txt +139 -0
- data/LICENSE +14 -0
- data/README.md +153 -0
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/bin/spark_api +8 -0
- data/bin/spark_api~ +8 -0
- data/lib/spark_api.rb +46 -0
- data/lib/spark_api/authentication.rb +55 -0
- data/lib/spark_api/authentication/api_auth.rb +104 -0
- data/lib/spark_api/authentication/api_auth.rb~ +104 -0
- data/lib/spark_api/authentication/base_auth.rb +47 -0
- data/lib/spark_api/authentication/base_auth.rb~ +47 -0
- data/lib/spark_api/authentication/oauth2.rb +198 -0
- data/lib/spark_api/authentication/oauth2.rb~ +199 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb +87 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +87 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb +48 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +49 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb +44 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +45 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb +35 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +36 -0
- data/lib/spark_api/authentication/oauth2_impl/middleware.rb +38 -0
- data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +39 -0
- data/lib/spark_api/authentication/oauth2_impl/password_provider.rb +24 -0
- data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +25 -0
- data/lib/spark_api/cli.rb +158 -0
- data/lib/spark_api/cli.rb~ +158 -0
- data/lib/spark_api/cli/api_auth.rb +8 -0
- data/lib/spark_api/cli/api_auth.rb~ +8 -0
- data/lib/spark_api/cli/oauth2.rb +14 -0
- data/lib/spark_api/cli/oauth2.rb~ +14 -0
- data/lib/spark_api/cli/setup.rb +47 -0
- data/lib/spark_api/cli/setup.rb~ +47 -0
- data/lib/spark_api/client.rb +27 -0
- data/lib/spark_api/configuration.rb +54 -0
- data/lib/spark_api/configuration.rb~ +54 -0
- data/lib/spark_api/configuration/yaml.rb +101 -0
- data/lib/spark_api/configuration/yaml.rb~ +101 -0
- data/lib/spark_api/connection.rb +42 -0
- data/lib/spark_api/faraday.rb +64 -0
- data/lib/spark_api/faraday.rb~ +64 -0
- data/lib/spark_api/models.rb +33 -0
- data/lib/spark_api/models.rb~ +33 -0
- data/lib/spark_api/models/account.rb +115 -0
- data/lib/spark_api/models/account.rb~ +115 -0
- data/lib/spark_api/models/base.rb +118 -0
- data/lib/spark_api/models/base.rb~ +118 -0
- data/lib/spark_api/models/connect_prefs.rb +10 -0
- data/lib/spark_api/models/connect_prefs.rb~ +10 -0
- data/lib/spark_api/models/constraint.rb +16 -0
- data/lib/spark_api/models/constraint.rb~ +16 -0
- data/lib/spark_api/models/contact.rb +49 -0
- data/lib/spark_api/models/contact.rb~ +49 -0
- data/lib/spark_api/models/custom_fields.rb +12 -0
- data/lib/spark_api/models/custom_fields.rb~ +12 -0
- data/lib/spark_api/models/document.rb +11 -0
- data/lib/spark_api/models/document.rb~ +11 -0
- data/lib/spark_api/models/finders.rb +45 -0
- data/lib/spark_api/models/finders.rb~ +45 -0
- data/lib/spark_api/models/idx_link.rb +47 -0
- data/lib/spark_api/models/idx_link.rb~ +47 -0
- data/lib/spark_api/models/listing.rb +197 -0
- data/lib/spark_api/models/listing.rb~ +197 -0
- data/lib/spark_api/models/listing_cart.rb +72 -0
- data/lib/spark_api/models/listing_cart.rb~ +72 -0
- data/lib/spark_api/models/market_statistics.rb +33 -0
- data/lib/spark_api/models/market_statistics.rb~ +33 -0
- data/lib/spark_api/models/message.rb +21 -0
- data/lib/spark_api/models/message.rb~ +21 -0
- data/lib/spark_api/models/note.rb +41 -0
- data/lib/spark_api/models/note.rb~ +41 -0
- data/lib/spark_api/models/notification.rb +42 -0
- data/lib/spark_api/models/notification.rb~ +42 -0
- data/lib/spark_api/models/open_house.rb +24 -0
- data/lib/spark_api/models/open_house.rb~ +24 -0
- data/lib/spark_api/models/photo.rb +70 -0
- data/lib/spark_api/models/photo.rb~ +70 -0
- data/lib/spark_api/models/property_types.rb +7 -0
- data/lib/spark_api/models/property_types.rb~ +7 -0
- data/lib/spark_api/models/saved_search.rb +16 -0
- data/lib/spark_api/models/saved_search.rb~ +16 -0
- data/lib/spark_api/models/shared_listing.rb +35 -0
- data/lib/spark_api/models/shared_listing.rb~ +35 -0
- data/lib/spark_api/models/standard_fields.rb +50 -0
- data/lib/spark_api/models/standard_fields.rb~ +50 -0
- data/lib/spark_api/models/subresource.rb +19 -0
- data/lib/spark_api/models/subresource.rb~ +19 -0
- data/lib/spark_api/models/system_info.rb +14 -0
- data/lib/spark_api/models/system_info.rb~ +14 -0
- data/lib/spark_api/models/tour_of_home.rb +24 -0
- data/lib/spark_api/models/tour_of_home.rb~ +24 -0
- data/lib/spark_api/models/video.rb +16 -0
- data/lib/spark_api/models/video.rb~ +16 -0
- data/lib/spark_api/models/virtual_tour.rb +18 -0
- data/lib/spark_api/models/virtual_tour.rb~ +18 -0
- data/lib/spark_api/multi_client.rb +59 -0
- data/lib/spark_api/multi_client.rb~ +59 -0
- data/lib/spark_api/paginate.rb +109 -0
- data/lib/spark_api/paginate.rb~ +109 -0
- data/lib/spark_api/primary_array.rb +29 -0
- data/lib/spark_api/primary_array.rb~ +29 -0
- data/lib/spark_api/request.rb +96 -0
- data/lib/spark_api/request.rb~ +96 -0
- data/lib/spark_api/response.rb +70 -0
- data/lib/spark_api/response.rb~ +70 -0
- data/lib/spark_api/version.rb +4 -0
- data/lib/spark_api/version.rb~ +4 -0
- data/script/console +6 -0
- data/script/console~ +6 -0
- data/script/example.rb +27 -0
- data/script/example.rb~ +27 -0
- data/spec/fixtures/accounts/all.json +160 -0
- data/spec/fixtures/accounts/my.json +74 -0
- data/spec/fixtures/accounts/my_portal.json +20 -0
- data/spec/fixtures/accounts/my_put.json +5 -0
- data/spec/fixtures/accounts/my_save.json +5 -0
- data/spec/fixtures/accounts/office.json +142 -0
- data/spec/fixtures/accounts/password_save.json +6 -0
- data/spec/fixtures/authentication_failure.json +7 -0
- data/spec/fixtures/base.json +13 -0
- data/spec/fixtures/contacts/contacts.json +28 -0
- data/spec/fixtures/contacts/my.json +19 -0
- data/spec/fixtures/contacts/new.json +11 -0
- data/spec/fixtures/contacts/new_empty.json +8 -0
- data/spec/fixtures/contacts/new_notify.json +11 -0
- data/spec/fixtures/contacts/post.json +10 -0
- data/spec/fixtures/contacts/tags.json +11 -0
- data/spec/fixtures/count.json +10 -0
- data/spec/fixtures/empty.json +3 -0
- data/spec/fixtures/errors/expired.json +7 -0
- data/spec/fixtures/errors/failure.json +5 -0
- data/spec/fixtures/errors/failure_with_constraint.json +17 -0
- data/spec/fixtures/errors/failure_with_msg.json +7 -0
- data/spec/fixtures/generic_delete.json +1 -0
- data/spec/fixtures/generic_failure.json +5 -0
- data/spec/fixtures/listing_carts/add_listing.json +13 -0
- data/spec/fixtures/listing_carts/add_listing_post.json +5 -0
- data/spec/fixtures/listing_carts/empty.json +5 -0
- data/spec/fixtures/listing_carts/listing_cart.json +19 -0
- data/spec/fixtures/listing_carts/new.json +12 -0
- data/spec/fixtures/listing_carts/post.json +10 -0
- data/spec/fixtures/listing_carts/remove_listing.json +13 -0
- data/spec/fixtures/listings/constraints.json +18 -0
- data/spec/fixtures/listings/constraints_with_pagination.json +24 -0
- data/spec/fixtures/listings/document_index.json +19 -0
- data/spec/fixtures/listings/multiple.json +69 -0
- data/spec/fixtures/listings/no_subresources.json +38 -0
- data/spec/fixtures/listings/open_houses.json +21 -0
- data/spec/fixtures/listings/photos/index.json +469 -0
- data/spec/fixtures/listings/photos/new.json +12 -0
- data/spec/fixtures/listings/photos/post.json +20 -0
- data/spec/fixtures/listings/put.json +5 -0
- data/spec/fixtures/listings/put_expiration_date.json +5 -0
- data/spec/fixtures/listings/saved_search.json +17 -0
- data/spec/fixtures/listings/shared_listing_get.json +14 -0
- data/spec/fixtures/listings/shared_listing_new.json +9 -0
- data/spec/fixtures/listings/shared_listing_post.json +10 -0
- data/spec/fixtures/listings/tour_of_homes.json +23 -0
- data/spec/fixtures/listings/videos_index.json +18 -0
- data/spec/fixtures/listings/virtual_tours_index.json +42 -0
- data/spec/fixtures/listings/with_documents.json +52 -0
- data/spec/fixtures/listings/with_permissions.json +44 -0
- data/spec/fixtures/listings/with_photos.json +110 -0
- data/spec/fixtures/listings/with_supplement.json +39 -0
- data/spec/fixtures/listings/with_videos.json +54 -0
- data/spec/fixtures/listings/with_vtour.json +48 -0
- data/spec/fixtures/logo_fbs.png +0 -0
- data/spec/fixtures/messages/new.json +14 -0
- data/spec/fixtures/messages/new_empty.json +7 -0
- data/spec/fixtures/messages/new_with_recipients.json +15 -0
- data/spec/fixtures/messages/post.json +5 -0
- data/spec/fixtures/notes/add.json +11 -0
- data/spec/fixtures/notes/agent_shared.json +11 -0
- data/spec/fixtures/notes/agent_shared_empty.json +7 -0
- data/spec/fixtures/notes/new.json +5 -0
- data/spec/fixtures/notifications/mark_read.json +1 -0
- data/spec/fixtures/notifications/new.json +8 -0
- data/spec/fixtures/notifications/new_empty.json +7 -0
- data/spec/fixtures/notifications/notifications.json +43 -0
- data/spec/fixtures/notifications/post.json +10 -0
- data/spec/fixtures/notifications/unread.json +10 -0
- data/spec/fixtures/oauth2/access.json +3 -0
- data/spec/fixtures/oauth2/access_with_old_refresh.json +5 -0
- data/spec/fixtures/oauth2/access_with_refresh.json +5 -0
- data/spec/fixtures/oauth2/authorization_code_body.json +7 -0
- data/spec/fixtures/oauth2/error.json +3 -0
- data/spec/fixtures/oauth2/password_body.json +7 -0
- data/spec/fixtures/oauth2/refresh_body.json +7 -0
- data/spec/fixtures/oauth2_error.json +3 -0
- data/spec/fixtures/property_types/property_types.json +31 -0
- data/spec/fixtures/session.json +10 -0
- data/spec/fixtures/standardfields/city.json +1031 -0
- data/spec/fixtures/standardfields/nearby.json +53 -0
- data/spec/fixtures/standardfields/standardfields.json +188 -0
- data/spec/fixtures/standardfields/stateorprovince.json +36 -0
- data/spec/fixtures/success.json +5 -0
- data/spec/json_helper.rb +76 -0
- data/spec/mock_helper.rb +124 -0
- data/spec/oauth2_helper.rb +68 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/unit/flexmls_api_spec.rb~ +23 -0
- data/spec/unit/spark_api/authentication/api_auth_spec.rb +169 -0
- data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +169 -0
- data/spec/unit/spark_api/authentication/base_auth_spec.rb +10 -0
- data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +10 -0
- data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb +10 -0
- data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +10 -0
- data/spec/unit/spark_api/authentication/oauth2_spec.rb +205 -0
- data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +205 -0
- data/spec/unit/spark_api/authentication_spec.rb +38 -0
- data/spec/unit/spark_api/authentication_spec.rb~ +38 -0
- data/spec/unit/spark_api/configuration/yaml_spec.rb +72 -0
- data/spec/unit/spark_api/configuration/yaml_spec.rb~ +72 -0
- data/spec/unit/spark_api/configuration_spec.rb +122 -0
- data/spec/unit/spark_api/configuration_spec.rb~ +122 -0
- data/spec/unit/spark_api/faraday_spec.rb +90 -0
- data/spec/unit/spark_api/faraday_spec.rb~ +90 -0
- data/spec/unit/spark_api/models/account_spec.rb +176 -0
- data/spec/unit/spark_api/models/base_spec.rb +106 -0
- data/spec/unit/spark_api/models/connect_prefs_spec.rb +9 -0
- data/spec/unit/spark_api/models/constraint_spec.rb +19 -0
- data/spec/unit/spark_api/models/contact_spec.rb +108 -0
- data/spec/unit/spark_api/models/contact_spec.rb~ +108 -0
- data/spec/unit/spark_api/models/document_spec.rb +32 -0
- data/spec/unit/spark_api/models/listing_cart_spec.rb +127 -0
- data/spec/unit/spark_api/models/listing_cart_spec.rb~ +127 -0
- data/spec/unit/spark_api/models/listing_spec.rb +320 -0
- data/spec/unit/spark_api/models/listing_spec.rb~ +320 -0
- data/spec/unit/spark_api/models/message_spec.rb +47 -0
- data/spec/unit/spark_api/models/message_spec.rb~ +47 -0
- data/spec/unit/spark_api/models/note_spec.rb +63 -0
- data/spec/unit/spark_api/models/note_spec.rb~ +63 -0
- data/spec/unit/spark_api/models/notification_spec.rb +62 -0
- data/spec/unit/spark_api/models/notification_spec.rb~ +62 -0
- data/spec/unit/spark_api/models/open_house_spec.rb +39 -0
- data/spec/unit/spark_api/models/photo_spec.rb +92 -0
- data/spec/unit/spark_api/models/property_types_spec.rb +33 -0
- data/spec/unit/spark_api/models/saved_search_spec.rb +40 -0
- data/spec/unit/spark_api/models/shared_listing_spec.rb +45 -0
- data/spec/unit/spark_api/models/shared_listing_spec.rb~ +45 -0
- data/spec/unit/spark_api/models/standard_fields_spec.rb +60 -0
- data/spec/unit/spark_api/models/system_info_spec.rb +83 -0
- data/spec/unit/spark_api/models/tour_of_home_spec.rb +44 -0
- data/spec/unit/spark_api/models/video_spec.rb +36 -0
- data/spec/unit/spark_api/models/virtual_tour_spec.rb +44 -0
- data/spec/unit/spark_api/multi_client_spec.rb +56 -0
- data/spec/unit/spark_api/multi_client_spec.rb~ +56 -0
- data/spec/unit/spark_api/paginate_spec.rb +224 -0
- data/spec/unit/spark_api/paginate_spec.rb~ +224 -0
- data/spec/unit/spark_api/primary_array_spec.rb +41 -0
- data/spec/unit/spark_api/primary_array_spec.rb~ +41 -0
- data/spec/unit/spark_api/request_spec.rb +344 -0
- data/spec/unit/spark_api/request_spec.rb~ +344 -0
- data/spec/unit/spark_api_spec.rb +23 -0
- metadata +725 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require './spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe SparkApi::Authentication::BaseAuth do
|
|
4
|
+
subject {SparkApi::Authentication::BaseAuth.new(nil) }
|
|
5
|
+
it "should raise an error" do
|
|
6
|
+
expect {subject.authenticate()}.to raise_error(){ |e| e.message.should == "Implement me!"}
|
|
7
|
+
expect {subject.logout()}.to raise_error(){ |e| e.message.should == "Implement me!"}
|
|
8
|
+
expect {subject.request(nil, nil, nil, nil)}.to raise_error(){ |e| e.message.should == "Implement me!"}
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require './spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe FlexmlsApi::Authentication::BaseAuth do
|
|
4
|
+
subject {FlexmlsApi::Authentication::BaseAuth.new(nil) }
|
|
5
|
+
it "should raise an error" do
|
|
6
|
+
expect {subject.authenticate()}.to raise_error(){ |e| e.message.should == "Implement me!"}
|
|
7
|
+
expect {subject.logout()}.to raise_error(){ |e| e.message.should == "Implement me!"}
|
|
8
|
+
expect {subject.request(nil, nil, nil, nil)}.to raise_error(){ |e| e.message.should == "Implement me!"}
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require './spec/spec_helper'
|
|
2
|
+
require './spec/oauth2_helper'
|
|
3
|
+
|
|
4
|
+
describe SparkApi::Authentication::OAuth2Impl::GrantTypeBase do
|
|
5
|
+
subject { SparkApi::Authentication::OAuth2Impl::GrantTypeBase }
|
|
6
|
+
# Make sure the client boostraps the right plugin based on configuration.
|
|
7
|
+
it "create should " do
|
|
8
|
+
expect {subject.create(nil, InvalidAuth2Provider.new())}.to raise_error(SparkApi::ClientError){ |e| e.message.should == "Unsupported grant type [not_a_real_type]" }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require './spec/spec_helper'
|
|
2
|
+
require './spec/oauth2_helper'
|
|
3
|
+
|
|
4
|
+
describe FlexmlsApi::Authentication::OAuth2Impl::GrantTypeBase do
|
|
5
|
+
subject { FlexmlsApi::Authentication::OAuth2Impl::GrantTypeBase }
|
|
6
|
+
# Make sure the client boostraps the right plugin based on configuration.
|
|
7
|
+
it "create should " do
|
|
8
|
+
expect {subject.create(nil, InvalidAuth2Provider.new())}.to raise_error(FlexmlsApi::ClientError){ |e| e.message.should == "Unsupported grant type [not_a_real_type]" }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
require './spec/spec_helper'
|
|
2
|
+
require './spec/oauth2_helper'
|
|
3
|
+
|
|
4
|
+
describe SparkApi::Authentication::OAuth2 do
|
|
5
|
+
let(:provider) { TestOAuth2Provider.new() }
|
|
6
|
+
let(:client) { SparkApi::Client.new({:authentication_mode => SparkApi::Authentication::OAuth2,:oauth2_provider => provider}) }
|
|
7
|
+
subject {client.authenticator }
|
|
8
|
+
# Make sure the client boostraps the right plugin based on configuration.
|
|
9
|
+
describe "plugin" do
|
|
10
|
+
it "should load the oauth2 authenticator" do
|
|
11
|
+
client.authenticator.class.should eq(SparkApi::Authentication::OAuth2)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
describe "authenticate" do
|
|
15
|
+
it "should authenticate the api credentials" do
|
|
16
|
+
stub_request(:post, provider.access_uri).
|
|
17
|
+
with(:body =>
|
|
18
|
+
'{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}'
|
|
19
|
+
).
|
|
20
|
+
to_return(:body => fixture("oauth2/access.json"), :status=>200)
|
|
21
|
+
subject.authenticate.access_token.should eq("04u7h-4cc355-70k3n")
|
|
22
|
+
subject.authenticate.expires_in.should eq(7200)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should raise an error when api credentials are invalid" do
|
|
26
|
+
s=stub_request(:post, provider.access_uri).
|
|
27
|
+
with(:body =>
|
|
28
|
+
'{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}'
|
|
29
|
+
).
|
|
30
|
+
to_return(:body => fixture("oauth2/error.json"), :status=>400)
|
|
31
|
+
expect {subject.authenticate()}.to raise_error(SparkApi::ClientError){ |e| e.status.should == 400 }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "authenticated?" do
|
|
37
|
+
let(:session) { Object.new }
|
|
38
|
+
it "should return true when session is active" do
|
|
39
|
+
subject.session = session
|
|
40
|
+
session.stub(:expired?) { false }
|
|
41
|
+
subject.authenticated?.should eq(true)
|
|
42
|
+
end
|
|
43
|
+
it "should return false when session is expired" do
|
|
44
|
+
subject.session = session
|
|
45
|
+
session.stub(:expired?) { true }
|
|
46
|
+
subject.authenticated?.should eq(false)
|
|
47
|
+
end
|
|
48
|
+
it "should return false when session is uninitialized" do
|
|
49
|
+
subject.authenticated?.should eq(false)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
describe "logout" do
|
|
55
|
+
let(:session) { mock_oauth_session }
|
|
56
|
+
it "should logout when there is an active session" do
|
|
57
|
+
subject.session = session
|
|
58
|
+
subject.logout
|
|
59
|
+
subject.session.should eq(nil)
|
|
60
|
+
end
|
|
61
|
+
it "should skip logging out when there is no active session information" do
|
|
62
|
+
client.stub(:delete) { raise "Should not be called" }
|
|
63
|
+
subject.logout.should eq(nil)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "request" do
|
|
68
|
+
let(:session) { mock_oauth_session }
|
|
69
|
+
it "should handle a get request" do
|
|
70
|
+
subject.session = session
|
|
71
|
+
args = {
|
|
72
|
+
:_limit => '10',
|
|
73
|
+
:_page => '1',
|
|
74
|
+
:_pagination => '1'
|
|
75
|
+
}
|
|
76
|
+
c = stub_request(:get, "https://api.sparkapi.com/#{SparkApi.version}/listings").
|
|
77
|
+
with(:query => args).
|
|
78
|
+
to_return(:body => fixture("listings/no_subresources.json"))
|
|
79
|
+
subject.session = session
|
|
80
|
+
subject.request(:get, "/#{SparkApi.version}/listings", nil, args).status.should eq(200)
|
|
81
|
+
end
|
|
82
|
+
it "should handle a post request" do
|
|
83
|
+
subject.session = session
|
|
84
|
+
args = {}
|
|
85
|
+
contact = '{"D":{"Contacts":[{"DisplayName":"Contact Four","PrimaryEmail":"contact4@fbsdata.com"}]}}'
|
|
86
|
+
stub_request(:post, "https://api.sparkapi.com/#{SparkApi.version}/contacts").
|
|
87
|
+
with(:body => contact).
|
|
88
|
+
to_return(:body => '{"D": {
|
|
89
|
+
"Success": true,
|
|
90
|
+
"Results": [
|
|
91
|
+
{
|
|
92
|
+
"ResourceUri":"/v1/contacts/20101230223226074204000000"
|
|
93
|
+
}]}
|
|
94
|
+
}',
|
|
95
|
+
:status=>201)
|
|
96
|
+
subject.request(:post, "/#{SparkApi.version}/contacts", contact, args).status.should eq(201)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context "with an expired session" do
|
|
101
|
+
context "and a valid refresh token" do
|
|
102
|
+
it "should reset the session and reauthenticate" do
|
|
103
|
+
count = 0
|
|
104
|
+
refresh_count = 0
|
|
105
|
+
stub_request(:post, provider.access_uri).
|
|
106
|
+
with(:body => '{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}').
|
|
107
|
+
to_return do
|
|
108
|
+
count += 1
|
|
109
|
+
{:body => fixture("oauth2/access_with_old_refresh.json"), :status=>200}
|
|
110
|
+
end
|
|
111
|
+
stub_request(:post, provider.access_uri).
|
|
112
|
+
with(:body => '{"client_id":"example-id","client_secret":"example-password","grant_type":"refresh_token","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","refresh_token":"0ld-r3fr35h-70k3n"}').
|
|
113
|
+
to_return do
|
|
114
|
+
refresh_count += 1
|
|
115
|
+
{:body => fixture("oauth2/access_with_refresh.json"), :status=>200}
|
|
116
|
+
end
|
|
117
|
+
# Make sure the auth request goes out twice.
|
|
118
|
+
# Fail the first time, but then return the correct value after reauthentication
|
|
119
|
+
stub_request(:get, "https://api.sparkapi.com/#{SparkApi.version}/listings/1234").
|
|
120
|
+
to_return(:body => fixture('errors/expired.json'), :status => 401).times(1).then.
|
|
121
|
+
to_return(:body => fixture('listings/with_documents.json'))
|
|
122
|
+
client.get("/listings/1234")
|
|
123
|
+
count.should eq(1)
|
|
124
|
+
refresh_count.should eq(1)
|
|
125
|
+
client.session.expired?.should eq(false)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
context "and an invalid refresh token" do
|
|
129
|
+
it "should reset the session and reauthenticate" do
|
|
130
|
+
count = 0
|
|
131
|
+
stub_request(:post, provider.access_uri).
|
|
132
|
+
with(:body => '{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}').
|
|
133
|
+
to_return do
|
|
134
|
+
count += 1
|
|
135
|
+
{:body => fixture("oauth2/access.json"), :status=>200}
|
|
136
|
+
end
|
|
137
|
+
# Make sure the auth request goes out twice.
|
|
138
|
+
# Fail the first time, but then return the correct value after reauthentication
|
|
139
|
+
stub_request(:get, "https://api.sparkapi.com/#{SparkApi.version}/listings/1234").
|
|
140
|
+
to_return(:body => fixture('errors/expired.json'), :status => 401).times(1).then.
|
|
141
|
+
to_return(:body => fixture('listings/with_documents.json'))
|
|
142
|
+
|
|
143
|
+
client.get("/listings/1234")
|
|
144
|
+
count.should eq(2)
|
|
145
|
+
client.session.expired?.should eq(false)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
describe SparkApi::Authentication::BaseOAuth2Provider do
|
|
152
|
+
context "session_timeout" do
|
|
153
|
+
it "should provide a default" do
|
|
154
|
+
subject.session_timeout.should eq(86400)
|
|
155
|
+
end
|
|
156
|
+
describe TestOAuth2Provider do
|
|
157
|
+
subject { TestOAuth2Provider.new }
|
|
158
|
+
it "should be able to override the session timeout" do
|
|
159
|
+
subject.session_timeout.should eq(7200)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
describe "password authentication" do
|
|
166
|
+
let(:provider) { TestCLIOAuth2Provider.new() }
|
|
167
|
+
let(:client) { SparkApi::Client.new({:authentication_mode => SparkApi::Authentication::OAuth2,:oauth2_provider => provider}) }
|
|
168
|
+
subject {client.authenticator }
|
|
169
|
+
it "should authenticate the api credentials with username and password" do
|
|
170
|
+
stub_request(:post, provider.access_uri).
|
|
171
|
+
with(:body =>
|
|
172
|
+
'{"username":"example-user","client_secret":"example-secret","client_id":"example-id","password":"example-password","grant_type":"password"}'
|
|
173
|
+
).
|
|
174
|
+
to_return(:body => fixture("oauth2/access.json"), :status=>200)
|
|
175
|
+
subject.authenticate.access_token.should eq("04u7h-4cc355-70k3n")
|
|
176
|
+
subject.authenticate.expires_in.should eq(60)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
describe SparkApi::Authentication::OAuth2Impl do
|
|
180
|
+
it "should load a provider" do
|
|
181
|
+
example = "SparkApi::Authentication::OAuth2Impl::PasswordProvider"
|
|
182
|
+
SparkApi::Authentication::OAuth2Impl.load_provider(example,{}).class.to_s.should eq(example)
|
|
183
|
+
prefix = "::#{example}"
|
|
184
|
+
SparkApi::Authentication::OAuth2Impl.load_provider(prefix,{}).class.to_s.should eq(example)
|
|
185
|
+
bad_example = "Derp::Derp::Derp::DerpProvider"
|
|
186
|
+
expect{SparkApi::Authentication::OAuth2Impl.load_provider(bad_example,{}).class.to_s.should eq(bad_example)}.to raise_error(ArgumentError)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe SparkApi::Authentication::OAuthSession do
|
|
192
|
+
it "should serialize to json" do
|
|
193
|
+
args = {
|
|
194
|
+
"access_token" => "abc",
|
|
195
|
+
"expires_in" => 3600,
|
|
196
|
+
"refresh_token" => "123",
|
|
197
|
+
"refresh_timeout" => 10000,
|
|
198
|
+
"start_time" => "2012-01-01T00:00:00+00:00"
|
|
199
|
+
}
|
|
200
|
+
session = SparkApi::Authentication::OAuthSession.new(args)
|
|
201
|
+
session.start_time.should eq(DateTime.parse(args["start_time"]))
|
|
202
|
+
JSON.parse(session.to_json).should eq(args)
|
|
203
|
+
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
require './spec/spec_helper'
|
|
2
|
+
require './spec/oauth2_helper'
|
|
3
|
+
|
|
4
|
+
describe FlexmlsApi::Authentication::OAuth2 do
|
|
5
|
+
let(:provider) { TestOAuth2Provider.new() }
|
|
6
|
+
let(:client) { FlexmlsApi::Client.new({:authentication_mode => FlexmlsApi::Authentication::OAuth2,:oauth2_provider => provider}) }
|
|
7
|
+
subject {client.authenticator }
|
|
8
|
+
# Make sure the client boostraps the right plugin based on configuration.
|
|
9
|
+
describe "plugin" do
|
|
10
|
+
it "should load the oauth2 authenticator" do
|
|
11
|
+
client.authenticator.class.should eq(FlexmlsApi::Authentication::OAuth2)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
describe "authenticate" do
|
|
15
|
+
it "should authenticate the api credentials" do
|
|
16
|
+
stub_request(:post, provider.access_uri).
|
|
17
|
+
with(:body =>
|
|
18
|
+
'{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}'
|
|
19
|
+
).
|
|
20
|
+
to_return(:body => fixture("oauth2/access.json"), :status=>200)
|
|
21
|
+
subject.authenticate.access_token.should eq("04u7h-4cc355-70k3n")
|
|
22
|
+
subject.authenticate.expires_in.should eq(7200)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should raise an error when api credentials are invalid" do
|
|
26
|
+
s=stub_request(:post, provider.access_uri).
|
|
27
|
+
with(:body =>
|
|
28
|
+
'{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}'
|
|
29
|
+
).
|
|
30
|
+
to_return(:body => fixture("oauth2/error.json"), :status=>400)
|
|
31
|
+
expect {subject.authenticate()}.to raise_error(FlexmlsApi::ClientError){ |e| e.status.should == 400 }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "authenticated?" do
|
|
37
|
+
let(:session) { Object.new }
|
|
38
|
+
it "should return true when session is active" do
|
|
39
|
+
subject.session = session
|
|
40
|
+
session.stub(:expired?) { false }
|
|
41
|
+
subject.authenticated?.should eq(true)
|
|
42
|
+
end
|
|
43
|
+
it "should return false when session is expired" do
|
|
44
|
+
subject.session = session
|
|
45
|
+
session.stub(:expired?) { true }
|
|
46
|
+
subject.authenticated?.should eq(false)
|
|
47
|
+
end
|
|
48
|
+
it "should return false when session is uninitialized" do
|
|
49
|
+
subject.authenticated?.should eq(false)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
describe "logout" do
|
|
55
|
+
let(:session) { mock_oauth_session }
|
|
56
|
+
it "should logout when there is an active session" do
|
|
57
|
+
subject.session = session
|
|
58
|
+
subject.logout
|
|
59
|
+
subject.session.should eq(nil)
|
|
60
|
+
end
|
|
61
|
+
it "should skip logging out when there is no active session information" do
|
|
62
|
+
client.stub(:delete) { raise "Should not be called" }
|
|
63
|
+
subject.logout.should eq(nil)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "request" do
|
|
68
|
+
let(:session) { mock_oauth_session }
|
|
69
|
+
it "should handle a get request" do
|
|
70
|
+
subject.session = session
|
|
71
|
+
args = {
|
|
72
|
+
:_limit => '10',
|
|
73
|
+
:_page => '1',
|
|
74
|
+
:_pagination => '1'
|
|
75
|
+
}
|
|
76
|
+
c = stub_request(:get, "https://api.flexmls.com/#{FlexmlsApi.version}/listings").
|
|
77
|
+
with(:query => args).
|
|
78
|
+
to_return(:body => fixture("listings/no_subresources.json"))
|
|
79
|
+
subject.session = session
|
|
80
|
+
subject.request(:get, "/#{FlexmlsApi.version}/listings", nil, args).status.should eq(200)
|
|
81
|
+
end
|
|
82
|
+
it "should handle a post request" do
|
|
83
|
+
subject.session = session
|
|
84
|
+
args = {}
|
|
85
|
+
contact = '{"D":{"Contacts":[{"DisplayName":"Contact Four","PrimaryEmail":"contact4@fbsdata.com"}]}}'
|
|
86
|
+
stub_request(:post, "https://api.flexmls.com/#{FlexmlsApi.version}/contacts").
|
|
87
|
+
with(:body => contact).
|
|
88
|
+
to_return(:body => '{"D": {
|
|
89
|
+
"Success": true,
|
|
90
|
+
"Results": [
|
|
91
|
+
{
|
|
92
|
+
"ResourceUri":"/v1/contacts/20101230223226074204000000"
|
|
93
|
+
}]}
|
|
94
|
+
}',
|
|
95
|
+
:status=>201)
|
|
96
|
+
subject.request(:post, "/#{FlexmlsApi.version}/contacts", contact, args).status.should eq(201)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context "with an expired session" do
|
|
101
|
+
context "and a valid refresh token" do
|
|
102
|
+
it "should reset the session and reauthenticate" do
|
|
103
|
+
count = 0
|
|
104
|
+
refresh_count = 0
|
|
105
|
+
stub_request(:post, provider.access_uri).
|
|
106
|
+
with(:body => '{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}').
|
|
107
|
+
to_return do
|
|
108
|
+
count += 1
|
|
109
|
+
{:body => fixture("oauth2/access_with_old_refresh.json"), :status=>200}
|
|
110
|
+
end
|
|
111
|
+
stub_request(:post, provider.access_uri).
|
|
112
|
+
with(:body => '{"client_id":"example-id","client_secret":"example-password","grant_type":"refresh_token","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","refresh_token":"0ld-r3fr35h-70k3n"}').
|
|
113
|
+
to_return do
|
|
114
|
+
refresh_count += 1
|
|
115
|
+
{:body => fixture("oauth2/access_with_refresh.json"), :status=>200}
|
|
116
|
+
end
|
|
117
|
+
# Make sure the auth request goes out twice.
|
|
118
|
+
# Fail the first time, but then return the correct value after reauthentication
|
|
119
|
+
stub_request(:get, "https://api.flexmls.com/#{FlexmlsApi.version}/listings/1234").
|
|
120
|
+
to_return(:body => fixture('errors/expired.json'), :status => 401).times(1).then.
|
|
121
|
+
to_return(:body => fixture('listings/with_documents.json'))
|
|
122
|
+
client.get("/listings/1234")
|
|
123
|
+
count.should eq(1)
|
|
124
|
+
refresh_count.should eq(1)
|
|
125
|
+
client.session.expired?.should eq(false)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
context "and an invalid refresh token" do
|
|
129
|
+
it "should reset the session and reauthenticate" do
|
|
130
|
+
count = 0
|
|
131
|
+
stub_request(:post, provider.access_uri).
|
|
132
|
+
with(:body => '{"code":"my_code","client_secret":"example-password","client_id":"example-id","redirect_uri":"https://exampleapp.fbsdata.com/oauth-callback","grant_type":"authorization_code"}').
|
|
133
|
+
to_return do
|
|
134
|
+
count += 1
|
|
135
|
+
{:body => fixture("oauth2/access.json"), :status=>200}
|
|
136
|
+
end
|
|
137
|
+
# Make sure the auth request goes out twice.
|
|
138
|
+
# Fail the first time, but then return the correct value after reauthentication
|
|
139
|
+
stub_request(:get, "https://api.flexmls.com/#{FlexmlsApi.version}/listings/1234").
|
|
140
|
+
to_return(:body => fixture('errors/expired.json'), :status => 401).times(1).then.
|
|
141
|
+
to_return(:body => fixture('listings/with_documents.json'))
|
|
142
|
+
|
|
143
|
+
client.get("/listings/1234")
|
|
144
|
+
count.should eq(2)
|
|
145
|
+
client.session.expired?.should eq(false)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
describe FlexmlsApi::Authentication::BaseOAuth2Provider do
|
|
152
|
+
context "session_timeout" do
|
|
153
|
+
it "should provide a default" do
|
|
154
|
+
subject.session_timeout.should eq(86400)
|
|
155
|
+
end
|
|
156
|
+
describe TestOAuth2Provider do
|
|
157
|
+
subject { TestOAuth2Provider.new }
|
|
158
|
+
it "should be able to override the session timeout" do
|
|
159
|
+
subject.session_timeout.should eq(7200)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
describe "password authentication" do
|
|
166
|
+
let(:provider) { TestCLIOAuth2Provider.new() }
|
|
167
|
+
let(:client) { FlexmlsApi::Client.new({:authentication_mode => FlexmlsApi::Authentication::OAuth2,:oauth2_provider => provider}) }
|
|
168
|
+
subject {client.authenticator }
|
|
169
|
+
it "should authenticate the api credentials with username and password" do
|
|
170
|
+
stub_request(:post, provider.access_uri).
|
|
171
|
+
with(:body =>
|
|
172
|
+
'{"username":"example-user","client_secret":"example-secret","client_id":"example-id","password":"example-password","grant_type":"password"}'
|
|
173
|
+
).
|
|
174
|
+
to_return(:body => fixture("oauth2/access.json"), :status=>200)
|
|
175
|
+
subject.authenticate.access_token.should eq("04u7h-4cc355-70k3n")
|
|
176
|
+
subject.authenticate.expires_in.should eq(60)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
describe FlexmlsApi::Authentication::OAuth2Impl do
|
|
180
|
+
it "should load a provider" do
|
|
181
|
+
example = "FlexmlsApi::Authentication::OAuth2Impl::PasswordProvider"
|
|
182
|
+
FlexmlsApi::Authentication::OAuth2Impl.load_provider(example,{}).class.to_s.should eq(example)
|
|
183
|
+
prefix = "::#{example}"
|
|
184
|
+
FlexmlsApi::Authentication::OAuth2Impl.load_provider(prefix,{}).class.to_s.should eq(example)
|
|
185
|
+
bad_example = "Derp::Derp::Derp::DerpProvider"
|
|
186
|
+
expect{FlexmlsApi::Authentication::OAuth2Impl.load_provider(bad_example,{}).class.to_s.should eq(bad_example)}.to raise_error(ArgumentError)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe FlexmlsApi::Authentication::OAuthSession do
|
|
192
|
+
it "should serialize to json" do
|
|
193
|
+
args = {
|
|
194
|
+
"access_token" => "abc",
|
|
195
|
+
"expires_in" => 3600,
|
|
196
|
+
"refresh_token" => "123",
|
|
197
|
+
"refresh_timeout" => 10000,
|
|
198
|
+
"start_time" => "2012-01-01T00:00:00+00:00"
|
|
199
|
+
}
|
|
200
|
+
session = FlexmlsApi::Authentication::OAuthSession.new(args)
|
|
201
|
+
session.start_time.should eq(DateTime.parse(args["start_time"]))
|
|
202
|
+
JSON.parse(session.to_json).should eq(args)
|
|
203
|
+
|
|
204
|
+
end
|
|
205
|
+
end
|