flexmls_api 0.3.6 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/Gemfile +6 -6
  2. data/Gemfile.lock +6 -6
  3. data/README.md +5 -3
  4. data/Rakefile +2 -1
  5. data/VERSION +1 -1
  6. data/lib/flexmls_api/authentication.rb +25 -54
  7. data/lib/flexmls_api/authentication/api_auth.rb +100 -0
  8. data/lib/flexmls_api/authentication/base_auth.rb +47 -0
  9. data/lib/flexmls_api/authentication/oauth2.rb +219 -0
  10. data/lib/flexmls_api/client.rb +7 -1
  11. data/lib/flexmls_api/configuration.rb +5 -2
  12. data/lib/flexmls_api/faraday.rb +6 -2
  13. data/lib/flexmls_api/models.rb +2 -0
  14. data/lib/flexmls_api/models/base.rb +5 -1
  15. data/lib/flexmls_api/models/contact.rb +1 -0
  16. data/lib/flexmls_api/models/custom_fields.rb +2 -2
  17. data/lib/flexmls_api/models/finders.rb +2 -2
  18. data/lib/flexmls_api/models/idx_link.rb +1 -1
  19. data/lib/flexmls_api/models/listing.rb +31 -5
  20. data/lib/flexmls_api/models/market_statistics.rb +1 -1
  21. data/lib/flexmls_api/models/note.rb +43 -0
  22. data/lib/flexmls_api/models/standard_fields.rb +43 -0
  23. data/lib/flexmls_api/models/subresource.rb +5 -2
  24. data/lib/flexmls_api/models/system_info.rb +7 -0
  25. data/lib/flexmls_api/models/tour_of_home.rb +24 -0
  26. data/lib/flexmls_api/request.rb +13 -28
  27. data/spec/fixtures/add_note.json +11 -0
  28. data/spec/fixtures/agent_shared_note.json +11 -0
  29. data/spec/fixtures/agent_shared_note_empty.json +7 -0
  30. data/spec/fixtures/authentication_failure.json +7 -0
  31. data/spec/fixtures/count.json +10 -0
  32. data/spec/fixtures/errors/expired.json +7 -0
  33. data/spec/fixtures/generic_delete.json +1 -0
  34. data/spec/fixtures/generic_failure.json +5 -0
  35. data/spec/fixtures/oauth2_access.json +3 -0
  36. data/spec/fixtures/oauth2_error.json +3 -0
  37. data/spec/fixtures/session.json +1 -1
  38. data/spec/fixtures/standardfields.json +188 -0
  39. data/spec/fixtures/standardfields_city.json +1031 -0
  40. data/spec/fixtures/standardfields_nearby.json +53 -0
  41. data/spec/fixtures/standardfields_stateorprovince.json +36 -0
  42. data/spec/fixtures/tour_of_homes.json +23 -0
  43. data/spec/spec_helper.rb +22 -5
  44. data/spec/unit/flexmls_api/authentication/api_auth_spec.rb +159 -0
  45. data/spec/unit/flexmls_api/authentication/oauth2_spec.rb +183 -0
  46. data/spec/unit/flexmls_api/authentication_spec.rb +10 -2
  47. data/spec/unit/flexmls_api/configuration_spec.rb +2 -2
  48. data/spec/unit/flexmls_api/faraday_spec.rb +3 -7
  49. data/spec/unit/flexmls_api/models/base_spec.rb +1 -1
  50. data/spec/unit/flexmls_api/models/contact_spec.rb +8 -4
  51. data/spec/unit/flexmls_api/models/document_spec.rb +2 -5
  52. data/spec/unit/flexmls_api/models/listing_spec.rb +46 -9
  53. data/spec/unit/flexmls_api/models/note_spec.rb +90 -0
  54. data/spec/unit/flexmls_api/models/photo_spec.rb +2 -2
  55. data/spec/unit/flexmls_api/models/system_info_spec.rb +37 -3
  56. data/spec/unit/flexmls_api/models/tour_of_home_spec.rb +43 -0
  57. data/spec/unit/flexmls_api/models/video_spec.rb +2 -4
  58. data/spec/unit/flexmls_api/models/virtual_tour_spec.rb +2 -2
  59. data/spec/unit/flexmls_api/paginate_spec.rb +11 -8
  60. data/spec/unit/flexmls_api/request_spec.rb +31 -16
  61. data/spec/unit/flexmls_api/standard_fields_spec.rb +86 -0
  62. data/spec/unit/flexmls_api_spec.rb +6 -27
  63. metadata +119 -76
data/Gemfile CHANGED
@@ -1,11 +1,11 @@
1
1
  source :rubygems
2
2
 
3
- gem 'faraday'
4
- gem 'curb'
5
- gem 'faraday_middleware'
6
- gem 'multi_json'
7
- gem 'json'
8
- gem 'yajl-ruby'
3
+ gem 'curb', '0.7.8'
4
+ gem 'faraday', '0.5.3'
5
+ gem 'faraday_middleware', '0.3.1'
6
+ gem 'multi_json', '0.0.5'
7
+ gem 'json', '1.4.6'
8
+ gem 'yajl-ruby', '0.7.8'
9
9
  gem 'builder', '2.1.2'
10
10
 
11
11
  gem 'will_paginate', '~> 3.0.pre2'
data/Gemfile.lock CHANGED
@@ -46,15 +46,15 @@ PLATFORMS
46
46
  DEPENDENCIES
47
47
  builder (= 2.1.2)
48
48
  ci_reporter (>= 1.6.3)
49
- curb
50
- faraday
51
- faraday_middleware
49
+ curb (= 0.7.8)
50
+ faraday (= 0.5.3)
51
+ faraday_middleware (= 0.3.1)
52
52
  jeweler
53
- json
54
- multi_json
53
+ json (= 1.4.6)
54
+ multi_json (= 0.0.5)
55
55
  rcov
56
56
  rspec
57
57
  typhoeus
58
58
  webmock
59
59
  will_paginate (~> 3.0.pre2)
60
- yajl-ruby
60
+ yajl-ruby (= 0.7.8)
data/README.md CHANGED
@@ -34,9 +34,13 @@ Usage Examples
34
34
 
35
35
  Authentication
36
36
  --------------
37
- Authentication is handled transparently by the request framework in the gem, so you should never need to manually make an authentication request.
37
+ Authentication is handled transparently by the request framework in the gem, so you should never need to manually make an authentication request. More than one mode of authentication is supported, so the client needs to be configured accordingly.
38
38
 
39
+ #### API Authentication (Default)
40
+ Usually supplied for a single user, this authentication mode is the simplest, and is setup as the default. The example usage above demonstrates how to get started using this authentication mode.
39
41
 
42
+ #### OAuth2 Authentication
43
+ Authentication mode the separates application and user authentication. This mode requires further setup which is described in lib/flexmls_api/authentication/oauth2.rb
40
44
 
41
45
  Error Codes
42
46
  ---------------------
@@ -124,5 +128,3 @@ Error Codes
124
128
  </tbody>
125
129
  </table>
126
130
 
127
-
128
-
data/Rakefile CHANGED
@@ -14,7 +14,8 @@ begin
14
14
  gemspec.email = "api-support@flexmls.com"
15
15
  gemspec.homepage = "https://github.com/flexmls/flexmls_api"
16
16
  gemspec.authors = ["Brandon Hornseth", "Wade McEwen"]
17
- gemspec.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
17
+ # Need to skip spec/reports for CI builds
18
+ gemspec.files = FileList["[A-Z]*", "{lib,spec/fixtures,spec/unit}/**/*", "spec/*.rb"]
18
19
  gemspec.add_development_dependency "rspec"
19
20
  gemspec.add_development_dependency "jeweler"
20
21
  gemspec.add_development_dependency "curb"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.6
1
+ 0.4.5
@@ -1,11 +1,18 @@
1
+
1
2
  require 'openssl'
2
3
  require 'faraday'
3
4
  require 'faraday_middleware'
4
5
  require 'yajl'
5
6
  require 'date'
7
+
8
+ require File.expand_path('../authentication/base_auth', __FILE__)
9
+ require File.expand_path('../authentication/api_auth', __FILE__)
10
+ require File.expand_path('../authentication/oauth2', __FILE__)
11
+
6
12
  module FlexmlsApi
7
- # =API Authentication
8
- # Handles authentication and reauthentication to the flexmls api.
13
+ # =Authentication
14
+ # Mixin module for handling client authentication and reauthentication to the flexmls api. Makes
15
+ # use of the configured authentication mode (API Auth by default).
9
16
  module Authentication
10
17
 
11
18
  # Main authentication step. Run before any api request unless the user session exists and is
@@ -16,58 +23,32 @@ module FlexmlsApi
16
23
  # *raises*
17
24
  # FlexmlsApi::ClientError when authentication fails
18
25
  def authenticate
19
- sig = sign("#{@api_secret}ApiKey#{@api_key}")
20
- FlexmlsApi.logger.debug("Authenticating to #{@endpoint}")
21
26
  start_time = Time.now
22
- request_path = "/#{version}/session?ApiKey=#{api_key}&ApiSig=#{sig}"
23
- resp = connection(true).post request_path, ""
24
27
  request_time = Time.now - start_time
25
- FlexmlsApi.logger.info("[#{(request_time * 1000).to_i}ms] Api: POST #{request_path}")
26
- @session = Session.new(resp.body.results[0])
27
- FlexmlsApi.logger.debug("Authentication: #{@session.inspect}")
28
- @session
28
+ new_session = @authenticator.authenticate
29
+ FlexmlsApi.logger.info("[#{(request_time * 1000).to_i}ms]")
30
+ FlexmlsApi.logger.debug("Session: #{new_session.inspect}")
31
+ new_session
32
+ end
33
+
34
+ # Test to see if there is an active session
35
+ def authenticated?
36
+ @authenticator.authenticated?
29
37
  end
30
38
 
31
39
  # Delete the current session
32
40
  def logout
33
41
  FlexmlsApi.logger.info("Logging out.")
34
- delete("/session/#{@session.auth_token}") unless @session.nil?
35
- @session = nil
42
+ @authenticator.logout
36
43
  end
37
44
 
38
- # Active session object
45
+ # Fetch the active session object
39
46
  def session
40
- @session
41
- end
42
-
43
- # Builds an ordered list of key value pairs and concatenates it all as one big string. Used
44
- # specifically for signing a request.
45
- def build_param_string(param_hash)
46
- return "" if param_hash.nil?
47
- sorted = param_hash.sort do |a,b|
48
- a.to_s <=> b.to_s
49
- end
50
- params = ""
51
- sorted.each do |key,val|
52
- params += key.to_s + val.to_s
53
- end
54
- params
47
+ @authenticator.session
55
48
  end
56
-
57
- # ==Session class
58
- # Handle on the api user session information as return by the api session service, including
59
- # roles, tokens and expiration
60
- class Session
61
- attr_accessor :auth_token, :expires, :roles
62
- def initialize(options={})
63
- @auth_token = options["AuthToken"]
64
- @expires = DateTime.parse options["Expires"]
65
- @roles = options["Roles"]
66
- end
67
- # Is the user session token expired?
68
- def expired?
69
- DateTime.now > @expires
70
- end
49
+ # Save the active session object
50
+ def session=(active_session)
51
+ @authenticator.session=active_session
71
52
  end
72
53
 
73
54
  # Main connection object for running requests. Bootstraps the Faraday abstraction layer with
@@ -92,7 +73,7 @@ module FlexmlsApi
92
73
  conn
93
74
  end
94
75
 
95
- # HTTP request headers
76
+ # HTTP request headers for client requests
96
77
  def headers
97
78
  {
98
79
  :accept => 'application/json',
@@ -102,15 +83,5 @@ module FlexmlsApi
102
83
  }
103
84
  end
104
85
 
105
- # Sign a request
106
- def sign(sig)
107
- Digest::MD5.hexdigest(sig)
108
- end
109
-
110
- # Sign a request with request data.
111
- def sign_token(path, params = {}, post_data="")
112
- sign("#{@api_secret}ApiKey#{@api_key}ServicePath/#{version}#{path}#{build_param_string(params)}#{post_data}")
113
- end
114
-
115
86
  end
116
87
  end
@@ -0,0 +1,100 @@
1
+ module FlexmlsApi
2
+
3
+ module Authentication
4
+
5
+ #=API Authentication
6
+ # Auth implementation for the API's original hash based authentication design. This is the
7
+ # default authentication strategy used by the client. API Auth rely's on the user's API key
8
+ # and secret and the active user is tied to the key owner.
9
+
10
+ #==ApiAuth
11
+ # Implementation the BaseAuth interface for API style authentication
12
+ class ApiAuth < BaseAuth
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ def authenticate
19
+ sig = sign("#{@client.api_secret}ApiKey#{@client.api_key}")
20
+ FlexmlsApi.logger.debug("Authenticating to #{@client.endpoint}")
21
+ start_time = Time.now
22
+ request_path = "/#{@client.version}/session?ApiKey=#{@client.api_key}&ApiSig=#{sig}"
23
+ resp = @client.connection(true).post request_path, ""
24
+ request_time = Time.now - start_time
25
+ FlexmlsApi.logger.info("[#{(request_time * 1000).to_i}ms] Api: POST #{request_path}")
26
+ @session = Session.new(resp.body.results.first)
27
+ FlexmlsApi.logger.debug("Authentication: #{@session.inspect}")
28
+ @session
29
+ end
30
+
31
+ def logout
32
+ @client.delete("/session/#{@session.auth_token}") unless @session.nil?
33
+ @session = nil
34
+ end
35
+
36
+ # Builds an ordered list of key value pairs and concatenates it all as one big string. Used
37
+ # specifically for signing a request.
38
+ def build_param_string(param_hash)
39
+ return "" if param_hash.nil?
40
+ sorted = param_hash.sort do |a,b|
41
+ a.to_s <=> b.to_s
42
+ end
43
+ params = ""
44
+ sorted.each do |key,val|
45
+ params += key.to_s + val.to_s
46
+ end
47
+ params
48
+ end
49
+
50
+ # Sign a request
51
+ def sign(sig)
52
+ Digest::MD5.hexdigest(sig)
53
+ end
54
+
55
+ # Sign a request with request data.
56
+ def sign_token(path, params = {}, post_data="")
57
+ sign("#{@client.api_secret}ApiKey#{@client.api_key}ServicePath#{path}#{build_param_string(params)}#{post_data}")
58
+ end
59
+
60
+ # Perform an HTTP request (no data)
61
+ def request(method, path, body, options)
62
+ request_opts = {
63
+ "AuthToken" => @session.auth_token
64
+ }
65
+ unless @client.api_user.nil?
66
+ request_opts.merge!(:ApiUser => "#{@client.api_user}")
67
+ end
68
+ request_opts.merge!(options)
69
+ sig = sign_token(path, request_opts, body)
70
+ request_path = "#{path}?#{build_url_parameters({"ApiSig"=>sig}.merge(request_opts))}"
71
+ FlexmlsApi.logger.debug("Request: #{request_path}")
72
+ if body.nil?
73
+ response = @client.connection.send(method, request_path)
74
+ else
75
+ FlexmlsApi.logger.debug("Data: #{body}")
76
+ response = @client.connection.send(method, request_path, body)
77
+ end
78
+ response
79
+ end
80
+
81
+ end
82
+
83
+ # ==Session class
84
+ # Handle on the api user session information as return by the api session service, including
85
+ # roles, tokens and expiration
86
+ class Session
87
+ attr_accessor :auth_token, :expires, :roles
88
+ def initialize(options={})
89
+ @auth_token = options["AuthToken"]
90
+ @expires = DateTime.parse options["Expires"]
91
+ @roles = options["Roles"]
92
+ end
93
+ # Is the user session token expired?
94
+ def expired?
95
+ DateTime.now > @expires
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,47 @@
1
+ module FlexmlsApi
2
+
3
+ module Authentication
4
+ #=Authentication Base
5
+ # This base class defines the basic interface supported by all client authentication
6
+ # implementations.
7
+ class BaseAuth
8
+ attr_accessor :session
9
+ # All ihheriting classes should accept the flexmls_api client as a part of initialization
10
+ def initialize(client)
11
+ @client = client
12
+ end
13
+
14
+ # Perform requests to authenticate the client with the API
15
+ def authenticate
16
+ raise "Implement me!"
17
+ end
18
+
19
+ # Called prior to running authenticate (except in case of api authentication errors)
20
+ def authenticated?
21
+ !(session.nil? || session.expired?)
22
+ end
23
+
24
+ # Terminate the active session
25
+ def logout
26
+ raise "Implement me!"
27
+ end
28
+
29
+ # Perform an HTTP request (no data)
30
+ def request(method, path, body, options)
31
+ raise "Implement me!"
32
+ end
33
+
34
+ # Format a hash as request parameters
35
+ #
36
+ # :returns:
37
+ # Stringized form of the parameters as needed for an HTTP request
38
+ def build_url_parameters(parameters={})
39
+ array = parameters.map do |key,value|
40
+ escaped_value = CGI.escape("#{value}")
41
+ "#{key}=#{escaped_value}"
42
+ end
43
+ array.join "&"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,219 @@
1
+ require 'uri'
2
+
3
+ module FlexmlsApi
4
+
5
+ module Authentication
6
+ #=OAuth2 Authentication
7
+ # Auth implementation to the API using the OAuth2 service endpoint. Current adheres to the 10
8
+ # draft of the OAuth2 specification. With OAuth2, the application supplies credentials for the
9
+ # application, and a separate a user authentication flow dictactes the active user for
10
+ # requests.
11
+ #
12
+ #===Setup
13
+ # When using this authentication method, there is a bit more setup involved to make the client
14
+ # work. All applications need to extend the BaseOAuth2Provider class to supply the application
15
+ # specific configuration. Also depending on the application type (command line, native, or web
16
+ # based), the user authentication step will be handled differently.
17
+
18
+ #==OAuth2
19
+ # Implementation the BaseAuth interface for API style authentication
20
+ class OAuth2 < BaseAuth
21
+
22
+ def session
23
+ @provider.load_session()
24
+ end
25
+ def session=(s)
26
+ @provider.save_session(s)
27
+ end
28
+
29
+ def initialize(client)
30
+ @client = client
31
+ @provider = client.oauth2_provider
32
+ end
33
+
34
+ # Generate the appropriate request uri for authorizing this application for current user.
35
+ def authorization_url
36
+ params = {
37
+ "client_id" => @provider.client_id,
38
+ "response_type" => "code",
39
+ "redirect_uri" => @provider.redirect_uri
40
+ }
41
+ "#{@provider.authorization_uri}?#{build_url_parameters(params)}"
42
+ end
43
+
44
+ def token_params
45
+ params = {
46
+ "client_id" => @provider.client_id,
47
+ "client_secret" => @provider.client_secret,
48
+ "grant_type" => "authorization_code",
49
+ "code" => @provider.code,
50
+ "redirect_uri" => @provider.redirect_uri
51
+ }
52
+ "?#{build_url_parameters(params)}"
53
+ end
54
+
55
+ def authenticate
56
+ if(@provider.code.nil?)
57
+ FlexmlsApi.logger.debug("Redirecting to provider to get the authorization code")
58
+ @provider.redirect(authorization_url)
59
+ end
60
+ FlexmlsApi.logger.debug("Authenticating to #{@provider.access_uri}")
61
+ uri = URI.parse(@provider.access_uri)
62
+ request_path = "#{uri.path}#{token_params}"
63
+ response = oauth_access_connection("#{uri.scheme}://#{uri.host}").post(request_path, "").body
64
+ response.expires_in = @provider.session_timeout if response.expires_in.nil?
65
+ self.session=response
66
+ response
67
+ end
68
+
69
+ # Perform an HTTP request (no data)
70
+ def request(method, path, body, options)
71
+ connection = @client.connection(true) # SSL Only!
72
+ connection.headers.merge!(self.auth_header)
73
+ request_opts = {}
74
+ request_opts.merge!(options)
75
+ request_path = "#{path}?#{build_url_parameters({:access_token => session.access_token}.merge(options))}"
76
+ FlexmlsApi.logger.debug("Request: #{request_path}")
77
+ if body.nil?
78
+ response = connection.send(method, request_path)
79
+ else
80
+ FlexmlsApi.logger.debug("Data: #{body}")
81
+ response = connection.send(method, request_path, body)
82
+ end
83
+ response
84
+ end
85
+
86
+ def logout
87
+ @provider.save_session(nil)
88
+ end
89
+
90
+
91
+ protected
92
+
93
+ def auth_header
94
+ {"Authorization"=> "OAuth #{session.access_token}"}
95
+ end
96
+
97
+ # Setup a faraday connection for dealing with an OAuth2 endpoint
98
+ def oauth_access_connection(endpoint)
99
+ opts = {
100
+ :headers => @client.headers
101
+ }
102
+ opts[:ssl] = {:verify => false }
103
+ opts[:url] = endpoint
104
+ conn = Faraday::Connection.new(opts) do |builder|
105
+ builder.adapter Faraday.default_adapter
106
+ builder.use Faraday::Response::ParseJson
107
+ builder.use FlexmlsApi::Authentication::FlexmlsOAuth2Middleware
108
+ end
109
+ end
110
+ end
111
+
112
+ # Representation of a session with the api using oauth2
113
+ class OAuthSession
114
+ attr_accessor :access_token, :expires_in, :scope, :refresh_token
115
+ def initialize(options={})
116
+ @access_token = options["access_token"]
117
+ # TODO The current oauth2 service does not send an expiration time. I'm setting it to default to 1 hour.
118
+ @expires_in = options["expires_in"]
119
+ @scope = options["scope"]
120
+ @refresh_token = options["refresh_token"]
121
+ @start_time = DateTime.now
122
+ end
123
+ # Is the user session token expired?
124
+ def expired?
125
+ @start_time + Rational(@expires_in, 24*60*60) < DateTime.now
126
+ end
127
+ end
128
+
129
+ #=OAuth2 configuration provider for applications
130
+ # Applications planning to use OAuth2 authentication with the API must extend this class as
131
+ # part of the client configuration, providing values for the following attributes:
132
+ # @authorization_uri - User oauth2 login page for flexmls
133
+ # @access_uri - Location of the OAuth2 access token resource for the api. OAuth2 code and
134
+ # credentials will be sent to this uri to generate an access token.
135
+ # @redirect_uri - Application uri to redirect to
136
+ # @client_id - OAuth2 provided application identifier
137
+ # @client_secret - OAuth2 provided password for the client id
138
+ class BaseOAuth2Provider
139
+
140
+ attr_accessor :authorization_uri, :code, :access_uri, :redirect_uri, :client_id, :client_secret
141
+
142
+ # Application using the client must handle user redirect for user authentication. For
143
+ # command line applications, this method is called prior to initial client requests so that
144
+ # the process can notify the user to go to the url and retrieve the access_code for the app.
145
+ # In a web based web application, this method can be mostly ignored. However, the web based
146
+ # application is then responsible for ensuring the code is saved to the the provider instance
147
+ # prior to any client requests are performed (or the error below will be thrown).
148
+ def redirect(url)
149
+ raise "To be implemented by client application"
150
+ end
151
+
152
+ #==For any persistence to be supported outside application process, the application shall
153
+ # implement the following methods for storing and retrieving the user OAuth2 session
154
+ # (e.g. to and from memcached).
155
+
156
+ # Load the current OAuth session
157
+ # returns - active OAuthSession or nil
158
+ def load_session
159
+ nil
160
+ end
161
+
162
+ # Save current session
163
+ # session - active OAuthSession
164
+ def save_session(session)
165
+
166
+ end
167
+
168
+ # Provides a default session time out
169
+ # returns - the session timeout length (in seconds)
170
+ def session_timeout
171
+ 3600
172
+ end
173
+
174
+ end
175
+
176
+ #==OAuth2 Faraday response middleware
177
+ # HTTP Response after filter to package oauth2 responses and bubble up basic api errors.
178
+ class FlexmlsOAuth2Middleware < Faraday::Response::Middleware
179
+ begin
180
+ def self.register_on_complete(env)
181
+ env[:response].on_complete do |finished_env|
182
+ validate_and_build_response(finished_env)
183
+ end
184
+ end
185
+ rescue LoadError, NameError => e
186
+ self.load_error = e
187
+ end
188
+
189
+ def self.validate_and_build_response(finished_env)
190
+ body = finished_env[:body]
191
+ FlexmlsApi.logger.debug("Response Body: #{body.inspect}")
192
+ unless body.is_a?(Hash)
193
+ raise InvalidResponse, "The server response could not be understood"
194
+ end
195
+ case finished_env[:status]
196
+ when 200..299
197
+ FlexmlsApi.logger.debug("Success!")
198
+ session = OAuthSession.new(body)
199
+ else
200
+ # Handle the WWW-Authenticate Response Header Field if present. This can be returned by
201
+ # OAuth2 implementations and wouldn't hurt to log.
202
+ auth_header_error = finished_env[:request_headers]["WWW-Authenticate"]
203
+ FlexmlsApi.logger.warn("Authentication error #{auth_header_error}") unless auth_header_error.nil?
204
+ raise ClientError.new(0, finished_env[:status]), body["error"]
205
+ end
206
+ FlexmlsApi.logger.debug("Session= #{session.inspect}")
207
+ finished_env[:body] = session
208
+ end
209
+
210
+ def initialize(app)
211
+ super
212
+ @parser = nil
213
+ end
214
+
215
+ end
216
+
217
+ end
218
+
219
+ end