flexmls_api 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/Gemfile +20 -0
  2. data/Gemfile.lock +60 -0
  3. data/LICENSE +14 -0
  4. data/README.md +128 -0
  5. data/Rakefile +78 -0
  6. data/VERSION +1 -0
  7. data/lib/flexmls_api/authentication.rb +104 -0
  8. data/lib/flexmls_api/client.rb +20 -0
  9. data/lib/flexmls_api/configuration.rb +40 -0
  10. data/lib/flexmls_api/faraday.rb +52 -0
  11. data/lib/flexmls_api/models/base.rb +76 -0
  12. data/lib/flexmls_api/models/connect_prefs.rb +10 -0
  13. data/lib/flexmls_api/models/contact.rb +25 -0
  14. data/lib/flexmls_api/models/custom_fields.rb +12 -0
  15. data/lib/flexmls_api/models/document.rb +11 -0
  16. data/lib/flexmls_api/models/idx_link.rb +45 -0
  17. data/lib/flexmls_api/models/listing.rb +110 -0
  18. data/lib/flexmls_api/models/market_statistics.rb +33 -0
  19. data/lib/flexmls_api/models/photo.rb +15 -0
  20. data/lib/flexmls_api/models/property_types.rb +7 -0
  21. data/lib/flexmls_api/models/standard_fields.rb +7 -0
  22. data/lib/flexmls_api/models/subresource.rb +13 -0
  23. data/lib/flexmls_api/models/system_info.rb +7 -0
  24. data/lib/flexmls_api/models/video.rb +16 -0
  25. data/lib/flexmls_api/models/virtual_tour.rb +18 -0
  26. data/lib/flexmls_api/models.rb +21 -0
  27. data/lib/flexmls_api/paginate.rb +87 -0
  28. data/lib/flexmls_api/request.rb +172 -0
  29. data/lib/flexmls_api/version.rb +4 -0
  30. data/lib/flexmls_api.rb +41 -0
  31. data/spec/fixtures/contacts.json +25 -0
  32. data/spec/fixtures/listing_document_index.json +19 -0
  33. data/spec/fixtures/listing_no_subresources.json +38 -0
  34. data/spec/fixtures/listing_photos_index.json +469 -0
  35. data/spec/fixtures/listing_videos_index.json +18 -0
  36. data/spec/fixtures/listing_virtual_tours_index.json +42 -0
  37. data/spec/fixtures/listing_with_documents.json +52 -0
  38. data/spec/fixtures/listing_with_photos.json +110 -0
  39. data/spec/fixtures/listing_with_supplement.json +39 -0
  40. data/spec/fixtures/listing_with_videos.json +54 -0
  41. data/spec/fixtures/listing_with_vtour.json +48 -0
  42. data/spec/fixtures/session.json +10 -0
  43. data/spec/json_helper.rb +77 -0
  44. data/spec/spec_helper.rb +78 -0
  45. data/spec/unit/flexmls_api/configuration_spec.rb +97 -0
  46. data/spec/unit/flexmls_api/faraday_spec.rb +94 -0
  47. data/spec/unit/flexmls_api/models/base_spec.rb +62 -0
  48. data/spec/unit/flexmls_api/models/connect_prefs_spec.rb +9 -0
  49. data/spec/unit/flexmls_api/models/contact_spec.rb +70 -0
  50. data/spec/unit/flexmls_api/models/document_spec.rb +39 -0
  51. data/spec/unit/flexmls_api/models/listing_spec.rb +174 -0
  52. data/spec/unit/flexmls_api/models/photo_spec.rb +59 -0
  53. data/spec/unit/flexmls_api/models/property_types_spec.rb +20 -0
  54. data/spec/unit/flexmls_api/models/standard_fields_spec.rb +42 -0
  55. data/spec/unit/flexmls_api/models/system_info_spec.rb +37 -0
  56. data/spec/unit/flexmls_api/models/video_spec.rb +43 -0
  57. data/spec/unit/flexmls_api/models/virtual_tour_spec.rb +46 -0
  58. data/spec/unit/flexmls_api/paginate_spec.rb +221 -0
  59. data/spec/unit/flexmls_api/request_spec.rb +288 -0
  60. data/spec/unit/flexmls_api_spec.rb +44 -0
  61. metadata +315 -0
@@ -0,0 +1,78 @@
1
+ require "rubygems"
2
+ require "json"
3
+ require "rspec"
4
+ require 'webmock/rspec'
5
+
6
+
7
+ Rspec.configure do |config|
8
+ config.include WebMock::API
9
+ end
10
+
11
+ begin require "redgreen" unless ENV['TM_CURRENT_LINE']; rescue LoadError; end
12
+ path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
13
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
14
+ require path + '/flexmls_api'
15
+
16
+ require 'flexmls_api'
17
+ require File.expand_path('../json_helper', __FILE__)
18
+
19
+
20
+ FileUtils.mkdir 'log' unless File.exists? 'log'
21
+
22
+ # TODO, really we should change the library to support configuration without overriding
23
+ module FlexmlsApi
24
+ def self.logger
25
+ if @logger.nil?
26
+ @logger = Logger.new('log/test.log')
27
+ @logger.level = Logger::DEBUG
28
+ end
29
+ @logger
30
+ end
31
+ end
32
+
33
+ FlexmlsApi.logger.info("Setup gem for rspec testing")
34
+
35
+ def mock_session()
36
+ FlexmlsApi::Authentication::Session.new("AuthToken" => "1234", "Expires" => (Time.now + 3600).to_s, "Roles" => "['idx']")
37
+ end
38
+
39
+
40
+ class MockClient < FlexmlsApi::Client
41
+ attr_accessor :connection, :session
42
+ end
43
+
44
+ def mock_client(stubs)
45
+ c = MockClient.new
46
+ c.session = mock_session()
47
+ c.connection = test_connection(stubs)
48
+ c
49
+ end
50
+
51
+ def mock_expired_session()
52
+ FlexmlsApi::Authentication::Session.new("AuthToken" => "1234", "Expires" => (Time.now - 60).to_s, "Roles" => "['idx']")
53
+ end
54
+
55
+ def test_connection(stubs)
56
+ Faraday::Connection.new(nil, {:headers => FlexmlsApi::Client.new.headers}) do |builder|
57
+ builder.adapter :test, stubs
58
+ builder.use Faraday::Response::ParseJson
59
+ builder.use FlexmlsApi::FaradayExt::FlexmlsMiddleware
60
+ end
61
+ end
62
+
63
+
64
+ def stub_auth_request()
65
+ stub_request(:post, "https://api.flexmls.com/#{FlexmlsApi.version}/session").
66
+ with(:query => {:ApiKey => "", :ApiSig => "806737984ab19be2fd08ba36030549ac"}).
67
+ to_return(:body => fixture("session.json"))
68
+ end
69
+
70
+
71
+
72
+
73
+ def fixture(file)
74
+ File.new(File.expand_path("../fixtures", __FILE__) + '/' + file)
75
+ end
76
+
77
+
78
+ include FlexmlsApi::Models
@@ -0,0 +1,97 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe FlexmlsApi::Client, "Client config" do
4
+ after(:each) do
5
+ FlexmlsApi.reset
6
+ end
7
+
8
+ describe "default settings" do
9
+ it "should return the proper defaults when called with no arguments" do
10
+ FlexmlsApi.api_key.should be_nil
11
+ FlexmlsApi.api_secret.should be_nil
12
+ FlexmlsApi.version.should match "v1"
13
+ FlexmlsApi.endpoint.should match "api.flexmls.com"
14
+
15
+ FlexmlsApi.api_key = "my_api_key"
16
+ FlexmlsApi.api_key.should match "my_api_key"
17
+ end
18
+ end
19
+
20
+ describe "instance config" do
21
+ it "should return a properly configured client" do
22
+ client = FlexmlsApi::Client.new(:api_key => "key_of_wade",
23
+ :api_secret => "TopSecret",
24
+ :endpoint => "http://api.wade.dev.fbsdata.com")
25
+
26
+ client.api_key.should match "key_of_wade"
27
+ client.api_secret.should match "TopSecret"
28
+ client.endpoint.should match "http://api.wade.dev.fbsdata.com"
29
+ client.version.should match "v1"
30
+ end
31
+ end
32
+
33
+ describe "block config" do
34
+ it "should correctly set up the client" do
35
+ FlexmlsApi.configure do |config|
36
+ config.api_key = "my_key"
37
+ config.api_secret = "my_secret"
38
+ config.version = "veleventy"
39
+ config.endpoint = "test.api.flexmls.com"
40
+ config.user_agent = "my useragent"
41
+ end
42
+
43
+ FlexmlsApi.api_key.should match "my_key"
44
+ FlexmlsApi.api_secret.should match "my_secret"
45
+ FlexmlsApi.version.should match "veleventy"
46
+ FlexmlsApi.endpoint.should match "test.api.flexmls.com"
47
+ FlexmlsApi.user_agent.should match "my useragent"
48
+
49
+ end
50
+
51
+ it "should reset" do
52
+ FlexmlsApi.configure do |config|
53
+ config.api_key = "my_key"
54
+ config.api_secret = "my_secret"
55
+ config.version = "veleventy"
56
+ config.endpoint = "test.api.flexmls.com"
57
+ config.user_agent = "my useragent"
58
+ end
59
+
60
+ FlexmlsApi.api_key.should match "my_key"
61
+ FlexmlsApi.reset
62
+ FlexmlsApi.api_key.should == FlexmlsApi::Configuration::DEFAULT_API_KEY
63
+
64
+ end
65
+ end
66
+
67
+ describe "connections" do
68
+ it "should use http by default" do
69
+ stub_auth_request
70
+ stub_request(:get, "#{FlexmlsApi.endpoint}/#{FlexmlsApi.version}/connections").
71
+ with(:query => {
72
+ :ApiSig => "951e5ba0496b0758356d3cc7676f8b21",
73
+ :AuthToken => "c401736bf3d3f754f07c04e460e09573"
74
+ }).
75
+ to_return(:body => '{"D":{"Success": true,"Results": [{"SSL":false}]}}')
76
+
77
+ FlexmlsApi.client.get('/connections')[0]["SSL"].should eq false
78
+
79
+ end
80
+
81
+ it "should use https when ssl is enabled" do
82
+ stub_auth_request
83
+ stub_request(:get, "https://api.flexmls.com/#{FlexmlsApi.version}/connections").
84
+ with(:query => {
85
+ :ApiSig => "951e5ba0496b0758356d3cc7676f8b21",
86
+ :AuthToken => "c401736bf3d3f754f07c04e460e09573"
87
+ }).
88
+ to_return(:body => '{"D":{"Success": true,"Results": [{"SSL":true}]}}')
89
+ c = FlexmlsApi::Client.new(:endpoint => "https://api.flexmls.com",
90
+ :ssl => true)
91
+ c.get('/connections')[0]["SSL"].should eq true
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
@@ -0,0 +1,94 @@
1
+ require './spec/spec_helper'
2
+
3
+ # Test out the faraday connection stack.
4
+ describe FlexmlsApi do
5
+ describe "FlexmlsMiddleware" do
6
+ before(:all) do
7
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
8
+ stub.post('/session') { [200, {}, '{"D": {
9
+ "Success": true,
10
+ "Results": [{
11
+ "AuthToken": "xxxxx",
12
+ "Expires": "2010-10-30T15:49:01-05:00",
13
+ "Roles": ["idx"]
14
+ }]
15
+ }}']
16
+ }
17
+ stub.get('/system') { [200, {}, '{"D": {
18
+ "Success": true,
19
+ "Results": [{
20
+ "Name": "My User",
21
+ "OfficeId": "20070830184014994915000000",
22
+ "Configuration": [],
23
+ "Id": "20101202170654111629000000",
24
+ "MlsId": "20000426143505724628000000",
25
+ "Office": "test office",
26
+ "Mls": "flexmls Web Demonstration Database"
27
+ }]}
28
+ }']
29
+ }
30
+ stub.get('/expired') { [401, {}, '{"D": {
31
+ "Success": false,
32
+ "Message": "Session token has expired",
33
+ "Code": 1020
34
+ }}']
35
+ }
36
+ stub.get('/methodnotallowed') { [405, {}, '{"D": {
37
+ "Success": false,
38
+ "Message": "Method Not Allowed",
39
+ "Code": "1234"
40
+ }}']
41
+ }
42
+ stub.get('/epicfail') { [500, {}, '{"D": {
43
+ "Success": false,
44
+ "Message": "EPIC FAIL",
45
+ "Code": "0000"
46
+ }}']
47
+ }
48
+ stub.get('/unknownerror') { [499, {}, '{"D": {
49
+ "Success": false,
50
+ "Message": "Some random status error",
51
+ "Code": "0000"
52
+ }}']
53
+ }
54
+ stub.get('/invalidjson') { [200, {}, '{"OMG": "THIS IS NOT THE API JSON THAT I KNOW AND <3!!!"}'] }
55
+ stub.get('/garbage') { [200, {}, 'THIS IS TOTAL GARBAGE!'] }
56
+ end
57
+
58
+ @connection = test_connection(stubs)
59
+
60
+ end
61
+
62
+ it "should raised exception when token is expired" do
63
+ expect { @connection.get('/expired')}.to raise_error(FlexmlsApi::PermissionDenied){ |e| e.code.should == FlexmlsApi::ResponseCodes::SESSION_TOKEN_EXPIRED }
64
+ end
65
+
66
+ it "should raised exception on error" do
67
+ expect { @connection.get('/methodnotallowed')}.to raise_error(FlexmlsApi::NotAllowed){ |e| e.message.should == "Method Not Allowed" }
68
+ expect { @connection.get('/epicfail')}.to raise_error(FlexmlsApi::ClientError){ |e| e.status.should be 500 }
69
+ expect { @connection.get('/unknownerror')}.to raise_error(FlexmlsApi::ClientError){ |e| e.status.should be 499 }
70
+ end
71
+
72
+ it "should raised exception on invalid responses" do
73
+ expect { @connection.get('/invalidjson')}.to raise_error(FlexmlsApi::InvalidResponse)
74
+ # This should be caught in the request code
75
+ expect { @connection.get('/garbage')}.to raise_error(MultiJson::DecodeError)
76
+ end
77
+
78
+ it "should give me a session response" do
79
+ response = @connection.post('/session').body
80
+ response.success.should eq(true)
81
+ session = FlexmlsApi::Authentication::Session.new(response.results[0])
82
+ session.auth_token.should eq("xxxxx")
83
+ end
84
+
85
+ it "should give me an api response" do
86
+ response = @connection.get('/system').body
87
+ response.success.should eq(true)
88
+ response.results.length.should be > 0
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
@@ -0,0 +1,62 @@
1
+ require './spec/spec_helper'
2
+
3
+ # Sample resource models for testing the base class
4
+ class MyExampleModel < Base
5
+ self.element_name = "example"
6
+ self.prefix = "/test/"
7
+ def self.connection
8
+ @connection ||= Base.connection
9
+ end
10
+ def self.connection=(con)
11
+ @connection = con
12
+ end
13
+ end
14
+
15
+ class MyDefaultModel < Base
16
+ end
17
+
18
+
19
+ describe Base, "Base model" do
20
+ describe "class methods" do
21
+ it "should set the element name" do
22
+ MyExampleModel.element_name.should eq("example")
23
+ MyDefaultModel.element_name.should eq("resource")
24
+ end
25
+ it "should set the prefix" do
26
+ MyExampleModel.prefix.should eq("/test/")
27
+ MyDefaultModel.prefix.should eq("/")
28
+ end
29
+ it "should set the path" do
30
+ MyExampleModel.path.should eq("/test/example")
31
+ MyDefaultModel.path.should eq("/resource")
32
+ end
33
+ describe "finders" do
34
+ before(:all) do
35
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
36
+ stub.get('/v1/test/example?ApiSig=0637dccf93be3774c9c7c554bb0b1d9a&AuthToken=1234') { [200, {}, '{"D": {
37
+ "Success": true,
38
+ "Results": [{
39
+ "Id": 1,
40
+ "Name": "My Example",
41
+ "Test": true
42
+ },
43
+ {
44
+ "Id": 2,
45
+ "Name": "My Example2",
46
+ "Test": false
47
+ }]}
48
+ }']
49
+ }
50
+ end
51
+ MyExampleModel.connection = mock_client(stubs)
52
+ end
53
+ it "should get all results" do
54
+ MyExampleModel.get.length.should == 2
55
+ end
56
+ it "should get first result" do
57
+ MyExampleModel.first.Id.should == 1
58
+ end
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,9 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe Connect do
4
+
5
+ it "should respond to prefs" do
6
+ Connect.should respond_to(:prefs)
7
+ end
8
+
9
+ end
@@ -0,0 +1,70 @@
1
+ require './spec/spec_helper'
2
+
3
+
4
+ class Contact
5
+ class << self
6
+ # Neato trick, using the accessor function nested here acts on the class methods!
7
+ attr_accessor :connection
8
+ end
9
+ end
10
+
11
+ describe Contact do
12
+ before(:all) do
13
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
14
+ stub.get('/v1/contacts?ApiSig=735774295be070a27f7cf859fde90740&AuthToken=1234') { [200, {}, fixture('contacts.json')]
15
+ }
16
+ stub.post('/v1/contacts?ApiSig=7f0a7b0f648f87aabd4d4393913a10ba&AuthToken=1234', '{"D":{"Contacts":[{"DisplayName":"Contact Four","PrimaryEmail":"contact4@fbsdata.com"}]}}') { [201, {}, '{"D": {
17
+ "Success": true,
18
+ "Results": [
19
+ {
20
+ "ResourceUri":"/v1/contacts/20101230223226074204000000"
21
+ }]}
22
+ }']
23
+ }
24
+ stub.post('/v1/contacts?ApiSig=76f6ee7032f7038d737f9b73457f06e2&AuthToken=1234', '{"D":{"Contacts":[{}]}}') { [400, {}, '{"D": {
25
+ "Success": false}
26
+ }']
27
+ }
28
+ end
29
+ Contact.connection = mock_client(stubs)
30
+ end
31
+
32
+ it "should get all my contacts" do
33
+ contacts = Contact.get
34
+ contacts.should be_an Array
35
+ contacts.length.should eq 3
36
+ contacts.first.Id.should eq "20101230223226074201000000"
37
+ end
38
+
39
+ it "should save a new contact" do
40
+ c=Contact.new
41
+ c.attributes["DisplayName"] = "Contact Four"
42
+ c.attributes["PrimaryEmail"] = "contact4@fbsdata.com"
43
+ c.save.should be true
44
+ c.Id.should eq '20101230223226074204000000'
45
+ end
46
+
47
+ it "should fail saving" do
48
+ c=Contact.new
49
+ c.save.should be false
50
+ expect{ c.save! }.to raise_error(FlexmlsApi::ClientError){ |e| e.status.should == 400 }
51
+ end
52
+
53
+ context "on an epic fail" do
54
+ before(:all) do
55
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
56
+ stub.post('/v1/contacts?ApiSig=76f6ee7032f7038d737f9b73457f06e2&AuthToken=1234', '{"D":{"Contacts":[{}]}}') { [500, {}, '{"D": {
57
+ "Success": false}
58
+ }']
59
+ }
60
+ end
61
+ Contact.connection = mock_client(stubs)
62
+ end
63
+ it "should fail saving and asplode" do
64
+ c=Contact.new()
65
+ expect{ c.save! }.to raise_error(FlexmlsApi::ClientError){ |e| e.status.should == 500 }
66
+ expect{ c.save }.to raise_error(FlexmlsApi::ClientError){ |e| e.status.should == 500 }
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,39 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe Document do
4
+ before(:each) do
5
+ @document = Document.new({
6
+ :Uri => "http://images.dev.fbsdata.com/documents/cda/20060725224801143085000000.pdf",
7
+ :ResourceUri => "/v1/listings/20060725224713296297000000/documents/20060725224801143085000000",
8
+ :Name => "Disclosure",
9
+ :Id => "20110105165843978012000000",
10
+ })
11
+ end
12
+
13
+ it "should respond to a few methods" do
14
+ Document.should respond_to(:find_by_listing_key)
15
+ end
16
+
17
+
18
+ it "should get documents for a listing" do
19
+ stub_auth_request
20
+ stub_request(:get, "#{FlexmlsApi.endpoint}/#{FlexmlsApi.version}/listings/1234/documents").
21
+ with( :query => {
22
+ :ApiSig => "82f62b685e4318a1ab6bc3fc5a031c5a",
23
+ :AuthToken => "c401736bf3d3f754f07c04e460e09573",
24
+ :ApiUser => "foobar"
25
+ }).
26
+ to_return(:body => fixture('listing_document_index.json'))
27
+
28
+ v = Document.find_by_listing_key('1234', "foobar")
29
+ v.should be_an Array
30
+ v.length.should == 2
31
+ end
32
+
33
+
34
+
35
+ after(:each) do
36
+ @document = nil
37
+ end
38
+
39
+ end