spark_api 1.0.2 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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