spark_api 1.0.2 → 1.0.4

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 (90) hide show
  1. data/README.md +86 -3
  2. data/VERSION +1 -1
  3. data/lib/spark_api.rb +1 -0
  4. data/lib/spark_api/authentication/oauth2.rb +43 -2
  5. data/lib/spark_api/configuration/oauth2_configurable.rb +3 -1
  6. data/lib/spark_api/models.rb +1 -0
  7. data/lib/spark_api/models/listing.rb +17 -1
  8. data/lib/spark_api/models/rental_calendar.rb +26 -0
  9. data/lib/spark_api/options_hash.rb +18 -0
  10. data/script/combined_flow_example.rb +55 -0
  11. data/script/oauth2_example.rb +2 -2
  12. data/spec/fixtures/listings/rental_calendar.json +19 -0
  13. data/spec/fixtures/listings/with_rental_calendar.json +52 -0
  14. data/spec/unit/spark_api/authentication/oauth2_spec.rb +79 -1
  15. data/spec/unit/spark_api/models/listing_spec.rb +7 -0
  16. data/spec/unit/spark_api/models/rental_calendar_spec.rb +30 -0
  17. data/spec/unit/spark_api/options_hash_spec.rb +14 -0
  18. metadata +15 -96
  19. data/bin/spark_api~ +0 -8
  20. data/lib/spark_api/authentication/api_auth.rb~ +0 -104
  21. data/lib/spark_api/authentication/base_auth.rb~ +0 -47
  22. data/lib/spark_api/authentication/oauth2.rb~ +0 -199
  23. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +0 -87
  24. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +0 -49
  25. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +0 -45
  26. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +0 -36
  27. data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +0 -39
  28. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +0 -25
  29. data/lib/spark_api/cli.rb~ +0 -158
  30. data/lib/spark_api/cli/api_auth.rb~ +0 -8
  31. data/lib/spark_api/cli/oauth2.rb~ +0 -14
  32. data/lib/spark_api/cli/setup.rb~ +0 -47
  33. data/lib/spark_api/configuration.rb~ +0 -54
  34. data/lib/spark_api/configuration/yaml.rb~ +0 -101
  35. data/lib/spark_api/faraday.rb~ +0 -64
  36. data/lib/spark_api/models.rb~ +0 -33
  37. data/lib/spark_api/models/account.rb~ +0 -115
  38. data/lib/spark_api/models/base.rb~ +0 -118
  39. data/lib/spark_api/models/connect_prefs.rb~ +0 -10
  40. data/lib/spark_api/models/constraint.rb~ +0 -16
  41. data/lib/spark_api/models/contact.rb~ +0 -49
  42. data/lib/spark_api/models/custom_fields.rb~ +0 -12
  43. data/lib/spark_api/models/document.rb~ +0 -11
  44. data/lib/spark_api/models/finders.rb~ +0 -45
  45. data/lib/spark_api/models/idx_link.rb~ +0 -47
  46. data/lib/spark_api/models/listing.rb~ +0 -197
  47. data/lib/spark_api/models/listing_cart.rb~ +0 -72
  48. data/lib/spark_api/models/market_statistics.rb~ +0 -33
  49. data/lib/spark_api/models/message.rb~ +0 -21
  50. data/lib/spark_api/models/note.rb~ +0 -41
  51. data/lib/spark_api/models/notification.rb~ +0 -42
  52. data/lib/spark_api/models/open_house.rb~ +0 -24
  53. data/lib/spark_api/models/photo.rb~ +0 -70
  54. data/lib/spark_api/models/property_types.rb~ +0 -7
  55. data/lib/spark_api/models/saved_search.rb~ +0 -16
  56. data/lib/spark_api/models/shared_listing.rb~ +0 -35
  57. data/lib/spark_api/models/standard_fields.rb~ +0 -50
  58. data/lib/spark_api/models/subresource.rb~ +0 -19
  59. data/lib/spark_api/models/system_info.rb~ +0 -14
  60. data/lib/spark_api/models/tour_of_home.rb~ +0 -24
  61. data/lib/spark_api/models/video.rb~ +0 -16
  62. data/lib/spark_api/models/virtual_tour.rb~ +0 -18
  63. data/lib/spark_api/multi_client.rb~ +0 -59
  64. data/lib/spark_api/paginate.rb~ +0 -109
  65. data/lib/spark_api/primary_array.rb~ +0 -29
  66. data/lib/spark_api/request.rb~ +0 -96
  67. data/lib/spark_api/response.rb~ +0 -70
  68. data/lib/spark_api/version.rb~ +0 -4
  69. data/script/console~ +0 -6
  70. data/script/example.rb~ +0 -27
  71. data/spec/unit/flexmls_api_spec.rb~ +0 -23
  72. data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +0 -169
  73. data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +0 -10
  74. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +0 -10
  75. data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +0 -205
  76. data/spec/unit/spark_api/authentication_spec.rb~ +0 -38
  77. data/spec/unit/spark_api/configuration/yaml_spec.rb~ +0 -72
  78. data/spec/unit/spark_api/configuration_spec.rb~ +0 -122
  79. data/spec/unit/spark_api/faraday_spec.rb~ +0 -90
  80. data/spec/unit/spark_api/models/contact_spec.rb~ +0 -108
  81. data/spec/unit/spark_api/models/listing_cart_spec.rb~ +0 -127
  82. data/spec/unit/spark_api/models/listing_spec.rb~ +0 -320
  83. data/spec/unit/spark_api/models/message_spec.rb~ +0 -47
  84. data/spec/unit/spark_api/models/note_spec.rb~ +0 -63
  85. data/spec/unit/spark_api/models/notification_spec.rb~ +0 -62
  86. data/spec/unit/spark_api/models/shared_listing_spec.rb~ +0 -45
  87. data/spec/unit/spark_api/multi_client_spec.rb~ +0 -56
  88. data/spec/unit/spark_api/paginate_spec.rb~ +0 -224
  89. data/spec/unit/spark_api/primary_array_spec.rb~ +0 -41
  90. data/spec/unit/spark_api/request_spec.rb~ +0 -344
@@ -1,29 +0,0 @@
1
- module FlexmlsApi
2
- class PrimaryArray < Array
3
-
4
- def primary
5
- find_primary
6
- end
7
-
8
- private
9
-
10
- # This is a very simplistic but reliable implementation.
11
- def find_primary
12
- self.each do |arg|
13
- if arg.primary?
14
- return arg
15
- end
16
- end
17
- nil
18
- end
19
- end
20
-
21
- #=== Primary: interface to implement for elements that are added to a "PrimaryArray" collection
22
- module Primary
23
- # Return true if the element is the primary resource in a collection.
24
- # Default implementation looks for a "Primary" attribute
25
- def primary?
26
- @attributes.key?("Primary") && self.Primary == true
27
- end
28
- end
29
- end
@@ -1,96 +0,0 @@
1
- require 'cgi'
2
-
3
- module FlexmlsApi
4
- # HTTP request wrapper. Performs all the api session mumbo jumbo so that the models don't have to.
5
- module Request
6
- # Perform an HTTP GET request
7
- #
8
- # * path - Path of an api resource, excluding version and endpoint (domain) information
9
- # * options - Resource request options as specified being supported via and api resource
10
- # :returns:
11
- # Hash of the json results as documented in the api.
12
- # :raises:
13
- # FlexmlsApi::ClientError or subclass if the request failed.
14
- def get(path, options={})
15
- request(:get, path, nil, options)
16
- end
17
-
18
- # Perform an HTTP POST request
19
- #
20
- # * path - Path of an api resource, excluding version and endpoint (domain) information
21
- # * body - Hash for post body data
22
- # * options - Resource request options as specified being supported via and api resource
23
- # :returns:
24
- # Hash of the json results as documented in the api.
25
- # :raises:
26
- # FlexmlsApi::ClientError or subclass if the request failed.
27
- def post(path, body={}, options={})
28
- request(:post, path, body, options)
29
- end
30
-
31
- # Perform an HTTP PUT request
32
- #
33
- # * path - Path of an api resource, excluding version and endpoint (domain) information
34
- # * body - Hash for post body data
35
- # * options - Resource request options as specified being supported via and api resource
36
- # :returns:
37
- # Hash of the json results as documented in the api.
38
- # :raises:
39
- # FlexmlsApi::ClientError or subclass if the request failed.
40
- def put(path, body={}, options={})
41
- request(:put, path, body, options)
42
- end
43
-
44
- # Perform an HTTP DELETE request
45
- #
46
- # * path - Path of an api resource, excluding version and endpoint (domain) information
47
- # * options - Resource request options as specified being supported via and api resource
48
- # :returns:
49
- # Hash of the json results as documented in the api.
50
- # :raises:
51
- # FlexmlsApi::ClientError or subclass if the request failed.
52
- def delete(path, options={})
53
- request(:delete, path, nil, options)
54
- end
55
-
56
- private
57
-
58
- # Perform an HTTP request (no data)
59
- def request(method, path, body, options)
60
- unless authenticated?
61
- authenticate
62
- end
63
- attempts = 0
64
- begin
65
- request_opts = {}
66
- request_opts.merge!(options)
67
- post_data = body.nil? ? nil : {"D" => body }.to_json
68
- request_path = "/#{version}#{path}"
69
- start_time = Time.now
70
- FlexmlsApi.logger.debug("#{method.to_s.upcase} Request: #{request_path}")
71
- if post_data.nil?
72
- response = authenticator.request(method, request_path, nil, request_opts)
73
- else
74
- FlexmlsApi.logger.debug("#{method.to_s.upcase} Data: #{post_data}")
75
- response = authenticator.request(method, request_path, post_data, request_opts)
76
- end
77
- request_time = Time.now - start_time
78
- FlexmlsApi.logger.info("[#{(request_time * 1000).to_i}ms] Api: #{method.to_s.upcase} #{request_path}")
79
- rescue PermissionDenied => e
80
- if(ResponseCodes::SESSION_TOKEN_EXPIRED == e.code)
81
- unless (attempts +=1) > 1
82
- FlexmlsApi.logger.debug("Retrying authentication")
83
- authenticate
84
- retry
85
- end
86
- end
87
- # No luck authenticating... KABOOM!
88
- FlexmlsApi.logger.error("Authentication failed or server is sending us expired tokens, nothing we can do here.")
89
- raise
90
- end
91
- response.body
92
- end
93
-
94
- end
95
-
96
- end
@@ -1,70 +0,0 @@
1
- module FlexmlsApi
2
- # API Response interface
3
- module Response
4
- ATTRIBUTES = [:code, :message, :results, :success, :pagination, :details]
5
- attr_accessor *ATTRIBUTES
6
- def success?
7
- @success
8
- end
9
- end
10
-
11
- # All known response codes listed in the API
12
- module ResponseCodes
13
- NOT_FOUND = 404
14
- METHOD_NOT_ALLOWED = 405
15
- INVALID_KEY = 1000
16
- DISABLED_KEY = 1010
17
- API_USER_REQUIRED = 1015
18
- SESSION_TOKEN_EXPIRED = 1020
19
- SSL_REQUIRED = 1030
20
- INVALID_JSON = 1035
21
- INVALID_FIELD = 1040
22
- MISSING_PARAMETER = 1050
23
- INVALID_PARAMETER = 1053
24
- CONFLICTING_DATA = 1055
25
- NOT_AVAILABLE= 1500
26
- RATE_LIMIT_EXCEEDED = 1550
27
- end
28
-
29
- # Errors built from API responses
30
- class InvalidResponse < StandardError; end
31
- class ClientError < StandardError
32
- attr_reader :code, :status, :details
33
- def initialize (options = {})
34
- # Support the standard initializer for errors
35
- opts = options.is_a?(Hash) ? options : {:message => options.to_s}
36
- @code = opts[:code]
37
- @status = opts[:status]
38
- @details = opts[:details]
39
- super(opts[:message])
40
- end
41
-
42
- end
43
- class NotFound < ClientError; end
44
- class PermissionDenied < ClientError; end
45
- class NotAllowed < ClientError; end
46
- class BadResourceRequest < ClientError; end
47
-
48
- # Nice and handy class wrapper for the api response hash
49
- class ApiResponse < ::Array
50
- include FlexmlsApi::Response
51
- def initialize(d)
52
- begin
53
- hash = d["D"]
54
- if hash.nil? || hash.empty?
55
- raise InvalidResponse, "The server response could not be understood"
56
- end
57
- self.message = hash["Message"]
58
- self.code = hash["Code"]
59
- self.results = Array(hash["Results"])
60
- self.success = hash["Success"]
61
- self.pagination = hash["Pagination"]
62
- self.details = hash["Details"] || []
63
- super(results)
64
- rescue Exception => e
65
- FlexmlsApi.logger.error "Unable to understand the response! #{d}"
66
- raise
67
- end
68
- end
69
- end
70
- end
@@ -1,4 +0,0 @@
1
- # Gem version information
2
- module FlexmlsApi
3
- VERSION = File.read(File.dirname(__FILE__) + "/../../VERSION").chomp
4
- end
data/script/console~ DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # File: script/console
3
-
4
- require File.dirname(__FILE__) + '/../lib/flexmls_api/cli'
5
- ENV["FLEXMLS_API_CONSOLE"] = "true"
6
- FlexmlsApi::CLI::ConsoleCLI.execute(STDOUT, ARGV)
data/script/example.rb~ DELETED
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require "rubygems"
3
-
4
- Bundler.require(:default, "development") if defined?(Bundler)
5
-
6
- path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
7
- $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
8
- require path + '/flexmls_api'
9
-
10
- FlexmlsApi.logger.info("Hello!")
11
-
12
- FlexmlsApi.configure do |config|
13
- config.api_key = "agent_key"
14
- config.api_secret = "agent_secret"
15
- config.version = "v1"
16
- config.endpoint = "https://api.flexmls.com"
17
- end
18
-
19
- client = FlexmlsApi.client
20
-
21
- list = client.get '/contacts'
22
- puts "client: #{list.inspect}"
23
- list = FlexmlsApi::Models::Contact.get
24
- puts "model: #{list.inspect}"
25
-
26
-
27
-
@@ -1,23 +0,0 @@
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
-
@@ -1,169 +0,0 @@
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
@@ -1,10 +0,0 @@
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