flexmls_api 0.3.2

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 (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