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.
Files changed (257) hide show
  1. data/History.txt +139 -0
  2. data/LICENSE +14 -0
  3. data/README.md +153 -0
  4. data/Rakefile +18 -0
  5. data/VERSION +1 -0
  6. data/bin/spark_api +8 -0
  7. data/bin/spark_api~ +8 -0
  8. data/lib/spark_api.rb +46 -0
  9. data/lib/spark_api/authentication.rb +55 -0
  10. data/lib/spark_api/authentication/api_auth.rb +104 -0
  11. data/lib/spark_api/authentication/api_auth.rb~ +104 -0
  12. data/lib/spark_api/authentication/base_auth.rb +47 -0
  13. data/lib/spark_api/authentication/base_auth.rb~ +47 -0
  14. data/lib/spark_api/authentication/oauth2.rb +198 -0
  15. data/lib/spark_api/authentication/oauth2.rb~ +199 -0
  16. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb +87 -0
  17. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +87 -0
  18. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb +48 -0
  19. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +49 -0
  20. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb +44 -0
  21. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +45 -0
  22. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb +35 -0
  23. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +36 -0
  24. data/lib/spark_api/authentication/oauth2_impl/middleware.rb +38 -0
  25. data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +39 -0
  26. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb +24 -0
  27. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +25 -0
  28. data/lib/spark_api/cli.rb +158 -0
  29. data/lib/spark_api/cli.rb~ +158 -0
  30. data/lib/spark_api/cli/api_auth.rb +8 -0
  31. data/lib/spark_api/cli/api_auth.rb~ +8 -0
  32. data/lib/spark_api/cli/oauth2.rb +14 -0
  33. data/lib/spark_api/cli/oauth2.rb~ +14 -0
  34. data/lib/spark_api/cli/setup.rb +47 -0
  35. data/lib/spark_api/cli/setup.rb~ +47 -0
  36. data/lib/spark_api/client.rb +27 -0
  37. data/lib/spark_api/configuration.rb +54 -0
  38. data/lib/spark_api/configuration.rb~ +54 -0
  39. data/lib/spark_api/configuration/yaml.rb +101 -0
  40. data/lib/spark_api/configuration/yaml.rb~ +101 -0
  41. data/lib/spark_api/connection.rb +42 -0
  42. data/lib/spark_api/faraday.rb +64 -0
  43. data/lib/spark_api/faraday.rb~ +64 -0
  44. data/lib/spark_api/models.rb +33 -0
  45. data/lib/spark_api/models.rb~ +33 -0
  46. data/lib/spark_api/models/account.rb +115 -0
  47. data/lib/spark_api/models/account.rb~ +115 -0
  48. data/lib/spark_api/models/base.rb +118 -0
  49. data/lib/spark_api/models/base.rb~ +118 -0
  50. data/lib/spark_api/models/connect_prefs.rb +10 -0
  51. data/lib/spark_api/models/connect_prefs.rb~ +10 -0
  52. data/lib/spark_api/models/constraint.rb +16 -0
  53. data/lib/spark_api/models/constraint.rb~ +16 -0
  54. data/lib/spark_api/models/contact.rb +49 -0
  55. data/lib/spark_api/models/contact.rb~ +49 -0
  56. data/lib/spark_api/models/custom_fields.rb +12 -0
  57. data/lib/spark_api/models/custom_fields.rb~ +12 -0
  58. data/lib/spark_api/models/document.rb +11 -0
  59. data/lib/spark_api/models/document.rb~ +11 -0
  60. data/lib/spark_api/models/finders.rb +45 -0
  61. data/lib/spark_api/models/finders.rb~ +45 -0
  62. data/lib/spark_api/models/idx_link.rb +47 -0
  63. data/lib/spark_api/models/idx_link.rb~ +47 -0
  64. data/lib/spark_api/models/listing.rb +197 -0
  65. data/lib/spark_api/models/listing.rb~ +197 -0
  66. data/lib/spark_api/models/listing_cart.rb +72 -0
  67. data/lib/spark_api/models/listing_cart.rb~ +72 -0
  68. data/lib/spark_api/models/market_statistics.rb +33 -0
  69. data/lib/spark_api/models/market_statistics.rb~ +33 -0
  70. data/lib/spark_api/models/message.rb +21 -0
  71. data/lib/spark_api/models/message.rb~ +21 -0
  72. data/lib/spark_api/models/note.rb +41 -0
  73. data/lib/spark_api/models/note.rb~ +41 -0
  74. data/lib/spark_api/models/notification.rb +42 -0
  75. data/lib/spark_api/models/notification.rb~ +42 -0
  76. data/lib/spark_api/models/open_house.rb +24 -0
  77. data/lib/spark_api/models/open_house.rb~ +24 -0
  78. data/lib/spark_api/models/photo.rb +70 -0
  79. data/lib/spark_api/models/photo.rb~ +70 -0
  80. data/lib/spark_api/models/property_types.rb +7 -0
  81. data/lib/spark_api/models/property_types.rb~ +7 -0
  82. data/lib/spark_api/models/saved_search.rb +16 -0
  83. data/lib/spark_api/models/saved_search.rb~ +16 -0
  84. data/lib/spark_api/models/shared_listing.rb +35 -0
  85. data/lib/spark_api/models/shared_listing.rb~ +35 -0
  86. data/lib/spark_api/models/standard_fields.rb +50 -0
  87. data/lib/spark_api/models/standard_fields.rb~ +50 -0
  88. data/lib/spark_api/models/subresource.rb +19 -0
  89. data/lib/spark_api/models/subresource.rb~ +19 -0
  90. data/lib/spark_api/models/system_info.rb +14 -0
  91. data/lib/spark_api/models/system_info.rb~ +14 -0
  92. data/lib/spark_api/models/tour_of_home.rb +24 -0
  93. data/lib/spark_api/models/tour_of_home.rb~ +24 -0
  94. data/lib/spark_api/models/video.rb +16 -0
  95. data/lib/spark_api/models/video.rb~ +16 -0
  96. data/lib/spark_api/models/virtual_tour.rb +18 -0
  97. data/lib/spark_api/models/virtual_tour.rb~ +18 -0
  98. data/lib/spark_api/multi_client.rb +59 -0
  99. data/lib/spark_api/multi_client.rb~ +59 -0
  100. data/lib/spark_api/paginate.rb +109 -0
  101. data/lib/spark_api/paginate.rb~ +109 -0
  102. data/lib/spark_api/primary_array.rb +29 -0
  103. data/lib/spark_api/primary_array.rb~ +29 -0
  104. data/lib/spark_api/request.rb +96 -0
  105. data/lib/spark_api/request.rb~ +96 -0
  106. data/lib/spark_api/response.rb +70 -0
  107. data/lib/spark_api/response.rb~ +70 -0
  108. data/lib/spark_api/version.rb +4 -0
  109. data/lib/spark_api/version.rb~ +4 -0
  110. data/script/console +6 -0
  111. data/script/console~ +6 -0
  112. data/script/example.rb +27 -0
  113. data/script/example.rb~ +27 -0
  114. data/spec/fixtures/accounts/all.json +160 -0
  115. data/spec/fixtures/accounts/my.json +74 -0
  116. data/spec/fixtures/accounts/my_portal.json +20 -0
  117. data/spec/fixtures/accounts/my_put.json +5 -0
  118. data/spec/fixtures/accounts/my_save.json +5 -0
  119. data/spec/fixtures/accounts/office.json +142 -0
  120. data/spec/fixtures/accounts/password_save.json +6 -0
  121. data/spec/fixtures/authentication_failure.json +7 -0
  122. data/spec/fixtures/base.json +13 -0
  123. data/spec/fixtures/contacts/contacts.json +28 -0
  124. data/spec/fixtures/contacts/my.json +19 -0
  125. data/spec/fixtures/contacts/new.json +11 -0
  126. data/spec/fixtures/contacts/new_empty.json +8 -0
  127. data/spec/fixtures/contacts/new_notify.json +11 -0
  128. data/spec/fixtures/contacts/post.json +10 -0
  129. data/spec/fixtures/contacts/tags.json +11 -0
  130. data/spec/fixtures/count.json +10 -0
  131. data/spec/fixtures/empty.json +3 -0
  132. data/spec/fixtures/errors/expired.json +7 -0
  133. data/spec/fixtures/errors/failure.json +5 -0
  134. data/spec/fixtures/errors/failure_with_constraint.json +17 -0
  135. data/spec/fixtures/errors/failure_with_msg.json +7 -0
  136. data/spec/fixtures/generic_delete.json +1 -0
  137. data/spec/fixtures/generic_failure.json +5 -0
  138. data/spec/fixtures/listing_carts/add_listing.json +13 -0
  139. data/spec/fixtures/listing_carts/add_listing_post.json +5 -0
  140. data/spec/fixtures/listing_carts/empty.json +5 -0
  141. data/spec/fixtures/listing_carts/listing_cart.json +19 -0
  142. data/spec/fixtures/listing_carts/new.json +12 -0
  143. data/spec/fixtures/listing_carts/post.json +10 -0
  144. data/spec/fixtures/listing_carts/remove_listing.json +13 -0
  145. data/spec/fixtures/listings/constraints.json +18 -0
  146. data/spec/fixtures/listings/constraints_with_pagination.json +24 -0
  147. data/spec/fixtures/listings/document_index.json +19 -0
  148. data/spec/fixtures/listings/multiple.json +69 -0
  149. data/spec/fixtures/listings/no_subresources.json +38 -0
  150. data/spec/fixtures/listings/open_houses.json +21 -0
  151. data/spec/fixtures/listings/photos/index.json +469 -0
  152. data/spec/fixtures/listings/photos/new.json +12 -0
  153. data/spec/fixtures/listings/photos/post.json +20 -0
  154. data/spec/fixtures/listings/put.json +5 -0
  155. data/spec/fixtures/listings/put_expiration_date.json +5 -0
  156. data/spec/fixtures/listings/saved_search.json +17 -0
  157. data/spec/fixtures/listings/shared_listing_get.json +14 -0
  158. data/spec/fixtures/listings/shared_listing_new.json +9 -0
  159. data/spec/fixtures/listings/shared_listing_post.json +10 -0
  160. data/spec/fixtures/listings/tour_of_homes.json +23 -0
  161. data/spec/fixtures/listings/videos_index.json +18 -0
  162. data/spec/fixtures/listings/virtual_tours_index.json +42 -0
  163. data/spec/fixtures/listings/with_documents.json +52 -0
  164. data/spec/fixtures/listings/with_permissions.json +44 -0
  165. data/spec/fixtures/listings/with_photos.json +110 -0
  166. data/spec/fixtures/listings/with_supplement.json +39 -0
  167. data/spec/fixtures/listings/with_videos.json +54 -0
  168. data/spec/fixtures/listings/with_vtour.json +48 -0
  169. data/spec/fixtures/logo_fbs.png +0 -0
  170. data/spec/fixtures/messages/new.json +14 -0
  171. data/spec/fixtures/messages/new_empty.json +7 -0
  172. data/spec/fixtures/messages/new_with_recipients.json +15 -0
  173. data/spec/fixtures/messages/post.json +5 -0
  174. data/spec/fixtures/notes/add.json +11 -0
  175. data/spec/fixtures/notes/agent_shared.json +11 -0
  176. data/spec/fixtures/notes/agent_shared_empty.json +7 -0
  177. data/spec/fixtures/notes/new.json +5 -0
  178. data/spec/fixtures/notifications/mark_read.json +1 -0
  179. data/spec/fixtures/notifications/new.json +8 -0
  180. data/spec/fixtures/notifications/new_empty.json +7 -0
  181. data/spec/fixtures/notifications/notifications.json +43 -0
  182. data/spec/fixtures/notifications/post.json +10 -0
  183. data/spec/fixtures/notifications/unread.json +10 -0
  184. data/spec/fixtures/oauth2/access.json +3 -0
  185. data/spec/fixtures/oauth2/access_with_old_refresh.json +5 -0
  186. data/spec/fixtures/oauth2/access_with_refresh.json +5 -0
  187. data/spec/fixtures/oauth2/authorization_code_body.json +7 -0
  188. data/spec/fixtures/oauth2/error.json +3 -0
  189. data/spec/fixtures/oauth2/password_body.json +7 -0
  190. data/spec/fixtures/oauth2/refresh_body.json +7 -0
  191. data/spec/fixtures/oauth2_error.json +3 -0
  192. data/spec/fixtures/property_types/property_types.json +31 -0
  193. data/spec/fixtures/session.json +10 -0
  194. data/spec/fixtures/standardfields/city.json +1031 -0
  195. data/spec/fixtures/standardfields/nearby.json +53 -0
  196. data/spec/fixtures/standardfields/standardfields.json +188 -0
  197. data/spec/fixtures/standardfields/stateorprovince.json +36 -0
  198. data/spec/fixtures/success.json +5 -0
  199. data/spec/json_helper.rb +76 -0
  200. data/spec/mock_helper.rb +124 -0
  201. data/spec/oauth2_helper.rb +68 -0
  202. data/spec/spec_helper.rb +48 -0
  203. data/spec/unit/flexmls_api_spec.rb~ +23 -0
  204. data/spec/unit/spark_api/authentication/api_auth_spec.rb +169 -0
  205. data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +169 -0
  206. data/spec/unit/spark_api/authentication/base_auth_spec.rb +10 -0
  207. data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +10 -0
  208. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb +10 -0
  209. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +10 -0
  210. data/spec/unit/spark_api/authentication/oauth2_spec.rb +205 -0
  211. data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +205 -0
  212. data/spec/unit/spark_api/authentication_spec.rb +38 -0
  213. data/spec/unit/spark_api/authentication_spec.rb~ +38 -0
  214. data/spec/unit/spark_api/configuration/yaml_spec.rb +72 -0
  215. data/spec/unit/spark_api/configuration/yaml_spec.rb~ +72 -0
  216. data/spec/unit/spark_api/configuration_spec.rb +122 -0
  217. data/spec/unit/spark_api/configuration_spec.rb~ +122 -0
  218. data/spec/unit/spark_api/faraday_spec.rb +90 -0
  219. data/spec/unit/spark_api/faraday_spec.rb~ +90 -0
  220. data/spec/unit/spark_api/models/account_spec.rb +176 -0
  221. data/spec/unit/spark_api/models/base_spec.rb +106 -0
  222. data/spec/unit/spark_api/models/connect_prefs_spec.rb +9 -0
  223. data/spec/unit/spark_api/models/constraint_spec.rb +19 -0
  224. data/spec/unit/spark_api/models/contact_spec.rb +108 -0
  225. data/spec/unit/spark_api/models/contact_spec.rb~ +108 -0
  226. data/spec/unit/spark_api/models/document_spec.rb +32 -0
  227. data/spec/unit/spark_api/models/listing_cart_spec.rb +127 -0
  228. data/spec/unit/spark_api/models/listing_cart_spec.rb~ +127 -0
  229. data/spec/unit/spark_api/models/listing_spec.rb +320 -0
  230. data/spec/unit/spark_api/models/listing_spec.rb~ +320 -0
  231. data/spec/unit/spark_api/models/message_spec.rb +47 -0
  232. data/spec/unit/spark_api/models/message_spec.rb~ +47 -0
  233. data/spec/unit/spark_api/models/note_spec.rb +63 -0
  234. data/spec/unit/spark_api/models/note_spec.rb~ +63 -0
  235. data/spec/unit/spark_api/models/notification_spec.rb +62 -0
  236. data/spec/unit/spark_api/models/notification_spec.rb~ +62 -0
  237. data/spec/unit/spark_api/models/open_house_spec.rb +39 -0
  238. data/spec/unit/spark_api/models/photo_spec.rb +92 -0
  239. data/spec/unit/spark_api/models/property_types_spec.rb +33 -0
  240. data/spec/unit/spark_api/models/saved_search_spec.rb +40 -0
  241. data/spec/unit/spark_api/models/shared_listing_spec.rb +45 -0
  242. data/spec/unit/spark_api/models/shared_listing_spec.rb~ +45 -0
  243. data/spec/unit/spark_api/models/standard_fields_spec.rb +60 -0
  244. data/spec/unit/spark_api/models/system_info_spec.rb +83 -0
  245. data/spec/unit/spark_api/models/tour_of_home_spec.rb +44 -0
  246. data/spec/unit/spark_api/models/video_spec.rb +36 -0
  247. data/spec/unit/spark_api/models/virtual_tour_spec.rb +44 -0
  248. data/spec/unit/spark_api/multi_client_spec.rb +56 -0
  249. data/spec/unit/spark_api/multi_client_spec.rb~ +56 -0
  250. data/spec/unit/spark_api/paginate_spec.rb +224 -0
  251. data/spec/unit/spark_api/paginate_spec.rb~ +224 -0
  252. data/spec/unit/spark_api/primary_array_spec.rb +41 -0
  253. data/spec/unit/spark_api/primary_array_spec.rb~ +41 -0
  254. data/spec/unit/spark_api/request_spec.rb +344 -0
  255. data/spec/unit/spark_api/request_spec.rb~ +344 -0
  256. data/spec/unit/spark_api_spec.rb +23 -0
  257. metadata +725 -0
@@ -0,0 +1,68 @@
1
+ # Lightweight example of an oauth2 provider used by the ruby client.
2
+ class TestOAuth2Provider < SparkApi::Authentication::BaseOAuth2Provider
3
+
4
+ def initialize
5
+ @authorization_uri = "https://test.fbsdata.com/r/oauth2"
6
+ @access_uri = "https://api.test.fbsdata.com/v1/oauth2/grant"
7
+ @redirect_uri = "https://exampleapp.fbsdata.com/oauth-callback"
8
+ @client_id="example-id"
9
+ @client_secret="example-password"
10
+ @session_cache = {}
11
+ end
12
+
13
+ def redirect(url)
14
+ # User redirected to url, signs in, and gets code sent to callback
15
+ self.code="my_code"
16
+ end
17
+
18
+ def load_session()
19
+ @session_cache["test_user_session"]
20
+ end
21
+
22
+ def save_session(session)
23
+ @session_cache["test_user_session"] = session
24
+ nil
25
+ end
26
+
27
+ def session_timeout; 7200; end
28
+
29
+ end
30
+
31
+
32
+ class TestCLIOAuth2Provider < SparkApi::Authentication::BaseOAuth2Provider
33
+ def initialize
34
+ @authorization_uri = "https://test.fbsdata.com/r/oauth2"
35
+ @access_uri = "https://api.test.fbsdata.com/v1/oauth2/grant"
36
+ @client_id="example-id"
37
+ @client_secret="example-secret"
38
+ @username="example-user"
39
+ @password="example-password"
40
+ @session_cache = {}
41
+ end
42
+
43
+ def grant_type
44
+ :password
45
+ end
46
+
47
+ def redirect(url)
48
+ raise "Unsupported in oauth grant_type=password mode"
49
+ end
50
+
51
+ def load_session()
52
+ @session_cache["test_user_session"]
53
+ end
54
+ def save_session(session)
55
+ @session_cache["test_user_session"] = session
56
+ nil
57
+ end
58
+ def session_timeout; 60; end
59
+ end
60
+
61
+
62
+ class InvalidAuth2Provider < SparkApi::Authentication::BaseOAuth2Provider
63
+
64
+ def grant_type
65
+ :not_a_real_type
66
+ end
67
+
68
+ end
@@ -0,0 +1,48 @@
1
+ require "rubygems"
2
+ require "json"
3
+ require "rspec"
4
+ require 'rspec/autorun'
5
+ require 'webmock/rspec'
6
+
7
+ begin require "redgreen" unless ENV['TM_CURRENT_LINE']; rescue LoadError; end
8
+ path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
9
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
10
+ require path + '/spark_api'
11
+
12
+ require 'spark_api'
13
+ require File.expand_path('../mock_helper', __FILE__)
14
+ require File.expand_path('../json_helper', __FILE__)
15
+
16
+
17
+ FileUtils.mkdir 'log' unless File.exists? 'log'
18
+
19
+ # TODO, really we should change the library to support configuration without overriding
20
+ module SparkApi
21
+ def self.logger
22
+ if @logger.nil?
23
+ @logger = Logger.new('log/test.log')
24
+ @logger.level = Logger::DEBUG
25
+ end
26
+ @logger
27
+ end
28
+ end
29
+
30
+ SparkApi.logger.info("Setup gem for rspec testing")
31
+
32
+ def reset_config()
33
+ SparkApi.reset
34
+ SparkApi.configure do |config|
35
+ config.api_user = "foobar"
36
+ end
37
+ end
38
+ reset_config
39
+
40
+ include SparkApi::Models
41
+
42
+ RSpec.configure do |config|
43
+ config.treat_symbols_as_metadata_keys_with_true_values = true
44
+ config.alias_example_to :on_get_it, :method => 'GET'
45
+ config.alias_example_to :on_put_it, :method => 'PUT'
46
+ config.alias_example_to :on_post_it, :method => 'POST'
47
+ config.alias_example_to :on_delete_it, :method => 'DELETE'
48
+ end
@@ -0,0 +1,23 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe FlexmlsApi do
4
+ after(:each) do
5
+ reset_config
6
+ end
7
+
8
+ it "should load the version" do
9
+ subject::VERSION.should match(/\d+\.\d+\.\d+/)
10
+ end
11
+
12
+ it "should give me a client connection" do
13
+ subject.client.should be_a(FlexmlsApi::Client)
14
+ end
15
+
16
+ it "should reset my connection" do
17
+ c1 = subject.client
18
+ subject.reset
19
+ subject.client.should_not eq(c1)
20
+ end
21
+
22
+ end
23
+
@@ -0,0 +1,169 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe SparkApi::Authentication::ApiAuth do
4
+ subject {SparkApi::Authentication::ApiAuth.new(nil) }
5
+ describe "build_param_hash" do
6
+ it "should return a blank string when passed nil" do
7
+ subject.build_param_string(nil).should be_empty
8
+ end
9
+ it "should return a correct param string for one item" do
10
+ subject.build_param_string({:foo => "bar"}).should match("foobar")
11
+ end
12
+ it "should alphabatize the param names by key first, then by value" do
13
+ subject.build_param_string({:zoo => "zar", :ooo => "car"}).should match("ooocarzoozar")
14
+ subject.build_param_string({:Akey => "aValue", :aNotherkey => "AnotherValue"}).should
15
+ match "AkeyaValueaNotherkeyAnotherValue"
16
+ end
17
+ end
18
+
19
+ describe "authenticate" do
20
+ let(:client) { SparkApi::Client.new({:api_key => "my_key", :api_secret => "my_secret"}) }
21
+ subject do
22
+ s = SparkApi::Authentication::ApiAuth.new(client)
23
+ client.authenticator = s
24
+ s
25
+ end
26
+ it "should authenticate the api credentials" do
27
+ stub_request(:post, "https://api.sparkapi.com/#{SparkApi.version}/session").
28
+ with(:query => {:ApiKey => "my_key", :ApiSig => "c731cf2455fbc7a4ef937b2301108d7a"}).
29
+ to_return(:body => fixture("session.json"))
30
+ subject.authenticate()
31
+ end
32
+ it "should raise an error when api credentials are invalid" do
33
+ stub_request(:post, "https://api.sparkapi.com/#{SparkApi.version}/session").
34
+ with(:query => {:ApiKey => "my_key", :ApiSig => "c731cf2455fbc7a4ef937b2301108d7a"}).
35
+ to_return(:body => fixture("authentication_failure.json"), :status=>401)
36
+ expect {subject.authenticate()}.to raise_error(SparkApi::ClientError){ |e| e.status.should == 401 }
37
+ end
38
+ end
39
+
40
+ describe "authenticated?" do
41
+ let(:session) { Object.new }
42
+ it "should return true when session is active" do
43
+ subject.session = session
44
+ session.stub(:expired?) { false }
45
+ subject.authenticated?.should eq(true)
46
+ end
47
+ it "should return false when session is expired" do
48
+ subject.session = session
49
+ session.stub(:expired?) { true }
50
+ subject.authenticated?.should eq(false)
51
+ end
52
+ it "should return false when session is uninitialized" do
53
+ subject.authenticated?.should eq(false)
54
+ end
55
+ end
56
+
57
+ describe "logout" do
58
+ let(:session) { mock_session }
59
+ let(:client) { Object.new }
60
+ subject {SparkApi::Authentication::ApiAuth.new(client) }
61
+ it "should logout when there is an active session" do
62
+ logged_out = false
63
+ subject.session = session
64
+ client.stub(:delete).with("/session/1234") { logged_out = true }
65
+ subject.logout
66
+ subject.session.should eq(nil)
67
+ logged_out.should eq(true)
68
+ end
69
+ it "should skip logging out when there is no active session information" do
70
+ client.stub(:delete) { raise "Should not be called" }
71
+ subject.logout.should eq(nil)
72
+ end
73
+ end
74
+
75
+ # Since the request method is overly complex, the following tests just go through the whole stack
76
+ # with some semi realistic requests. Performing this type of test here should allow us to safely
77
+ # mock out authentication for the rest of our unit tests and still have some decent coverage.
78
+ describe "request" do
79
+ let(:client) { SparkApi::Client.new({:api_key => "my_key", :api_secret => "my_secret"}) }
80
+ let(:session) { mock_session }
81
+ subject do
82
+ s = SparkApi::Authentication::ApiAuth.new(client)
83
+ client.authenticator = s
84
+ s.session = session
85
+ s
86
+ end
87
+ it "should handle a get request" do
88
+ stub_auth_request
89
+ args = {
90
+ :ApiUser => "foobar",
91
+ :_limit => '10',
92
+ :_page => '1',
93
+ :_pagination => '1'
94
+ }
95
+ stub_request(:get, "#{SparkApi.endpoint}/#{SparkApi.version}/listings").
96
+ with(:query => {
97
+ :ApiSig => "1cb789831f8f4c6925dc708c93762a2c",
98
+ :AuthToken => "1234"}.merge(args)).
99
+ to_return(:body => fixture("listings/no_subresources.json"))
100
+ subject.session = session
101
+ subject.request(:get, "/#{SparkApi.version}/listings", nil, args).status.should eq(200)
102
+ end
103
+ it "should handle a post request" do
104
+ stub_auth_request
105
+ args = {:ApiUser => "foobar"}
106
+ contact = '{"D":{"Contacts":[{"DisplayName":"Contact Four","PrimaryEmail":"contact4@fbsdata.com"}]}}'
107
+ stub_request(:post, "#{SparkApi.endpoint}/#{SparkApi.version}/contacts").
108
+ with(:query => {
109
+ :ApiSig => "82898ef88d22e1b31bd2e2ea6bb8efe7",
110
+ :AuthToken => "1234"}.merge(args),
111
+ :body => contact
112
+ ).
113
+ to_return(:body => '{"D": {
114
+ "Success": true,
115
+ "Results": [
116
+ {
117
+ "ResourceUri":"/v1/contacts/20101230223226074204000000"
118
+ }]}
119
+ }',
120
+ :status=>201)
121
+ subject.request(:post, "/#{SparkApi.version}/contacts", contact, args).status.should eq(201)
122
+ end
123
+ end
124
+
125
+ describe "sign" do
126
+ it "should sign the auth parameters correctly" do
127
+ sign_token = "my_secretApiKeymy_key"
128
+ subject.sign(sign_token).should eq("c731cf2455fbc7a4ef937b2301108d7a")
129
+ end
130
+ end
131
+
132
+ describe "sign_token" do
133
+ let(:client) { SparkApi::Client.new({:api_key => "my_key", :api_secret => "my_secret"}) }
134
+ subject {SparkApi::Authentication::ApiAuth.new(client) }
135
+ it "should fully sign the token" do
136
+ parms = {:AuthToken => "1234", :ApiUser => "CoolAsIce"}
137
+ subject.sign_token("/test", parms).should eq("7bbe3384a8b64368357f8551cab271e3")
138
+ end
139
+ end
140
+
141
+ context "when the server says the session is expired (even if we disagree)" do
142
+ it "should reset the session and reauthenticate" do
143
+ reset_config
144
+ count = 0
145
+ # Make sure the auth request goes out twice.
146
+ stub_request(:post, "https://api.sparkapi.com/#{SparkApi.version}/session").
147
+ with(:query => {:ApiKey => "", :ApiSig => "806737984ab19be2fd08ba36030549ac"}).
148
+ to_return do |r|
149
+ count += 1
150
+ {:body => fixture("session.json")}
151
+ end
152
+ # Fail the first time, but then return the correct value after reauthentication
153
+ stub_request(:get, "#{SparkApi.endpoint}/#{SparkApi.version}/listings/1234").
154
+ with(:query => {
155
+ :ApiSig => "554b6e2a3efec8719b782647c19d238d",
156
+ :AuthToken => "c401736bf3d3f754f07c04e460e09573",
157
+ :ApiUser => "foobar",
158
+ :_expand => "Documents"
159
+ }).
160
+ to_return(:body => fixture('errors/expired.json'), :status => 401).times(1).then.
161
+ to_return(:body => fixture('listings/with_documents.json'))
162
+ l = Listing.find('1234', :_expand => "Documents")
163
+
164
+ count.should eq(2)
165
+ SparkApi.client.session.expired?.should eq(false)
166
+ end
167
+ end
168
+
169
+ end
@@ -0,0 +1,169 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe FlexmlsApi::Authentication::ApiAuth do
4
+ subject {FlexmlsApi::Authentication::ApiAuth.new(nil) }
5
+ describe "build_param_hash" do
6
+ it "should return a blank string when passed nil" do
7
+ subject.build_param_string(nil).should be_empty
8
+ end
9
+ it "should return a correct param string for one item" do
10
+ subject.build_param_string({:foo => "bar"}).should match("foobar")
11
+ end
12
+ it "should alphabatize the param names by key first, then by value" do
13
+ subject.build_param_string({:zoo => "zar", :ooo => "car"}).should match("ooocarzoozar")
14
+ subject.build_param_string({:Akey => "aValue", :aNotherkey => "AnotherValue"}).should
15
+ match "AkeyaValueaNotherkeyAnotherValue"
16
+ end
17
+ end
18
+
19
+ describe "authenticate" do
20
+ let(:client) { FlexmlsApi::Client.new({:api_key => "my_key", :api_secret => "my_secret"}) }
21
+ subject do
22
+ s = FlexmlsApi::Authentication::ApiAuth.new(client)
23
+ client.authenticator = s
24
+ s
25
+ end
26
+ it "should authenticate the api credentials" do
27
+ stub_request(:post, "https://api.flexmls.com/#{FlexmlsApi.version}/session").
28
+ with(:query => {:ApiKey => "my_key", :ApiSig => "c731cf2455fbc7a4ef937b2301108d7a"}).
29
+ to_return(:body => fixture("session.json"))
30
+ subject.authenticate()
31
+ end
32
+ it "should raise an error when api credentials are invalid" do
33
+ stub_request(:post, "https://api.flexmls.com/#{FlexmlsApi.version}/session").
34
+ with(:query => {:ApiKey => "my_key", :ApiSig => "c731cf2455fbc7a4ef937b2301108d7a"}).
35
+ to_return(:body => fixture("authentication_failure.json"), :status=>401)
36
+ expect {subject.authenticate()}.to raise_error(FlexmlsApi::ClientError){ |e| e.status.should == 401 }
37
+ end
38
+ end
39
+
40
+ describe "authenticated?" do
41
+ let(:session) { Object.new }
42
+ it "should return true when session is active" do
43
+ subject.session = session
44
+ session.stub(:expired?) { false }
45
+ subject.authenticated?.should eq(true)
46
+ end
47
+ it "should return false when session is expired" do
48
+ subject.session = session
49
+ session.stub(:expired?) { true }
50
+ subject.authenticated?.should eq(false)
51
+ end
52
+ it "should return false when session is uninitialized" do
53
+ subject.authenticated?.should eq(false)
54
+ end
55
+ end
56
+
57
+ describe "logout" do
58
+ let(:session) { mock_session }
59
+ let(:client) { Object.new }
60
+ subject {FlexmlsApi::Authentication::ApiAuth.new(client) }
61
+ it "should logout when there is an active session" do
62
+ logged_out = false
63
+ subject.session = session
64
+ client.stub(:delete).with("/session/1234") { logged_out = true }
65
+ subject.logout
66
+ subject.session.should eq(nil)
67
+ logged_out.should eq(true)
68
+ end
69
+ it "should skip logging out when there is no active session information" do
70
+ client.stub(:delete) { raise "Should not be called" }
71
+ subject.logout.should eq(nil)
72
+ end
73
+ end
74
+
75
+ # Since the request method is overly complex, the following tests just go through the whole stack
76
+ # with some semi realistic requests. Performing this type of test here should allow us to safely
77
+ # mock out authentication for the rest of our unit tests and still have some decent coverage.
78
+ describe "request" do
79
+ let(:client) { FlexmlsApi::Client.new({:api_key => "my_key", :api_secret => "my_secret"}) }
80
+ let(:session) { mock_session }
81
+ subject do
82
+ s = FlexmlsApi::Authentication::ApiAuth.new(client)
83
+ client.authenticator = s
84
+ s.session = session
85
+ s
86
+ end
87
+ it "should handle a get request" do
88
+ stub_auth_request
89
+ args = {
90
+ :ApiUser => "foobar",
91
+ :_limit => '10',
92
+ :_page => '1',
93
+ :_pagination => '1'
94
+ }
95
+ stub_request(:get, "#{FlexmlsApi.endpoint}/#{FlexmlsApi.version}/listings").
96
+ with(:query => {
97
+ :ApiSig => "1cb789831f8f4c6925dc708c93762a2c",
98
+ :AuthToken => "1234"}.merge(args)).
99
+ to_return(:body => fixture("listings/no_subresources.json"))
100
+ subject.session = session
101
+ subject.request(:get, "/#{FlexmlsApi.version}/listings", nil, args).status.should eq(200)
102
+ end
103
+ it "should handle a post request" do
104
+ stub_auth_request
105
+ args = {:ApiUser => "foobar"}
106
+ contact = '{"D":{"Contacts":[{"DisplayName":"Contact Four","PrimaryEmail":"contact4@fbsdata.com"}]}}'
107
+ stub_request(:post, "#{FlexmlsApi.endpoint}/#{FlexmlsApi.version}/contacts").
108
+ with(:query => {
109
+ :ApiSig => "82898ef88d22e1b31bd2e2ea6bb8efe7",
110
+ :AuthToken => "1234"}.merge(args),
111
+ :body => contact
112
+ ).
113
+ to_return(:body => '{"D": {
114
+ "Success": true,
115
+ "Results": [
116
+ {
117
+ "ResourceUri":"/v1/contacts/20101230223226074204000000"
118
+ }]}
119
+ }',
120
+ :status=>201)
121
+ subject.request(:post, "/#{FlexmlsApi.version}/contacts", contact, args).status.should eq(201)
122
+ end
123
+ end
124
+
125
+ describe "sign" do
126
+ it "should sign the auth parameters correctly" do
127
+ sign_token = "my_secretApiKeymy_key"
128
+ subject.sign(sign_token).should eq("c731cf2455fbc7a4ef937b2301108d7a")
129
+ end
130
+ end
131
+
132
+ describe "sign_token" do
133
+ let(:client) { FlexmlsApi::Client.new({:api_key => "my_key", :api_secret => "my_secret"}) }
134
+ subject {FlexmlsApi::Authentication::ApiAuth.new(client) }
135
+ it "should fully sign the token" do
136
+ parms = {:AuthToken => "1234", :ApiUser => "CoolAsIce"}
137
+ subject.sign_token("/test", parms).should eq("7bbe3384a8b64368357f8551cab271e3")
138
+ end
139
+ end
140
+
141
+ context "when the server says the session is expired (even if we disagree)" do
142
+ it "should reset the session and reauthenticate" do
143
+ reset_config
144
+ count = 0
145
+ # Make sure the auth request goes out twice.
146
+ stub_request(:post, "https://api.flexmls.com/#{FlexmlsApi.version}/session").
147
+ with(:query => {:ApiKey => "", :ApiSig => "806737984ab19be2fd08ba36030549ac"}).
148
+ to_return do |r|
149
+ count += 1
150
+ {:body => fixture("session.json")}
151
+ end
152
+ # Fail the first time, but then return the correct value after reauthentication
153
+ stub_request(:get, "#{FlexmlsApi.endpoint}/#{FlexmlsApi.version}/listings/1234").
154
+ with(:query => {
155
+ :ApiSig => "554b6e2a3efec8719b782647c19d238d",
156
+ :AuthToken => "c401736bf3d3f754f07c04e460e09573",
157
+ :ApiUser => "foobar",
158
+ :_expand => "Documents"
159
+ }).
160
+ to_return(:body => fixture('errors/expired.json'), :status => 401).times(1).then.
161
+ to_return(:body => fixture('listings/with_documents.json'))
162
+ l = Listing.find('1234', :_expand => "Documents")
163
+
164
+ count.should eq(2)
165
+ FlexmlsApi.client.session.expired?.should eq(false)
166
+ end
167
+ end
168
+
169
+ end