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
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source :rubygems
2
+
3
+ gem 'faraday'
4
+ gem 'curb'
5
+ gem 'faraday_middleware'
6
+ gem 'multi_json'
7
+ gem 'json'
8
+ gem 'yajl-ruby'
9
+ gem 'builder', '2.1.2'
10
+
11
+ gem 'will_paginate', '~> 3.0.pre2'
12
+
13
+ group :test do
14
+ gem 'rspec'
15
+ gem 'webmock'
16
+ gem 'jeweler'
17
+ gem 'typhoeus'
18
+ gem 'ci_reporter', '>=1.6.3'
19
+ gem 'rcov'
20
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,60 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.2.2)
5
+ builder (2.1.2)
6
+ ci_reporter (1.6.3)
7
+ builder (>= 2.1.2)
8
+ crack (0.1.8)
9
+ curb (0.7.8)
10
+ diff-lcs (1.1.2)
11
+ faraday (0.5.3)
12
+ addressable (~> 2.2.2)
13
+ multipart-post (~> 1.0.1)
14
+ rack (>= 1.1.0, < 2)
15
+ faraday_middleware (0.3.1)
16
+ faraday (~> 0.5.3)
17
+ git (1.2.5)
18
+ jeweler (1.5.2)
19
+ bundler (~> 1.0.0)
20
+ git (>= 1.2.5)
21
+ rake
22
+ json (1.4.6)
23
+ multi_json (0.0.5)
24
+ multipart-post (1.0.1)
25
+ rack (1.2.1)
26
+ rake (0.8.7)
27
+ rcov (0.9.9)
28
+ rspec (2.3.0)
29
+ rspec-core (~> 2.3.0)
30
+ rspec-expectations (~> 2.3.0)
31
+ rspec-mocks (~> 2.3.0)
32
+ rspec-core (2.3.1)
33
+ rspec-expectations (2.3.0)
34
+ diff-lcs (~> 1.1.2)
35
+ rspec-mocks (2.3.0)
36
+ typhoeus (0.2.0)
37
+ webmock (1.4.0)
38
+ addressable (>= 2.2.2)
39
+ crack (>= 0.1.7)
40
+ will_paginate (3.0.pre2)
41
+ yajl-ruby (0.7.8)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ builder (= 2.1.2)
48
+ ci_reporter (>= 1.6.3)
49
+ curb
50
+ faraday
51
+ faraday_middleware
52
+ jeweler
53
+ json
54
+ multi_json
55
+ rcov
56
+ rspec
57
+ typhoeus
58
+ webmock
59
+ will_paginate (~> 3.0.pre2)
60
+ yajl-ruby
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright 2011 Financial Business Systems, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ flexmls API
2
+ =====================
3
+ A Ruby wrapper for the flexmls REST API. Loosely based on ActiveResource to provide models to interact with remote services.
4
+
5
+
6
+ Documentation
7
+ -------------
8
+ For full information on the API, see [http://www.flexmls.com/developers/](http://www.flexmls.com/developers/)
9
+
10
+
11
+ Installation
12
+ ---------
13
+ gem install flexmls_api
14
+
15
+ Usage Examples
16
+ ------------------------
17
+ # initialize the gem with your key/secret
18
+ # api_key and _api_secret are the only required settings
19
+ # other options and their defaults:
20
+ # - endpoint: 'http://api.flexmls.com'
21
+ # - version: 'v1'
22
+ # - ssl: false
23
+ # - user_agent: 'flexmls API Ruby Gem'
24
+ FlexmlsApi.configure do |config|
25
+ config.api_key = 'your_api_key'
26
+ config.api_secret = 'your_api_secret'
27
+ end
28
+
29
+ # mixin the models so you can use them without prefix
30
+ include FlexmlsApi::Models
31
+
32
+ # Grab your listings!
33
+ my_listings = Listing.my()
34
+
35
+ Authentication
36
+ --------------
37
+ Authentication is handled transparently by the request framework in the gem, so you should never need to manually make an authentication request.
38
+
39
+
40
+
41
+ Error Codes
42
+ ---------------------
43
+ <table>
44
+ <thead>
45
+ <tr>
46
+ <th>HTTP Code</th>
47
+ <th>flexmls API Error Code</th>
48
+ <th>Exception Raised</th>
49
+ <th>Description</th>
50
+ </tr>
51
+ </thead>
52
+ <tbody>
53
+ <tr>
54
+ <td><tt>401</tt></td>
55
+ <td><tt>1000</tt></td>
56
+ <td><tt></tt></td>
57
+ <td>Invalid API Key and/or Request signed improperly</td>
58
+ </tr>
59
+ <tr>
60
+ <td><tt>401</tt></td>
61
+ <td><tt>1010</tt></td>
62
+ <td><tt></tt></td>
63
+ <td>API key is disabled</td>
64
+ </tr>
65
+ <tr>
66
+ <td><tt>403</tt></td>
67
+ <td><tt>1015</tt></td>
68
+ <td><tt></tt></td>
69
+ <td><tt>ApiUser</tt> must be supplied, or the provided key does not have access to the supplied user</td>
70
+ </tr>
71
+ <tr>
72
+ <td><tt>401</tt></td>
73
+ <td><tt>1020</tt></td>
74
+ <td><tt></tt></td>
75
+ <td>Session token has expired</td>
76
+ </tr>
77
+ <tr>
78
+ <td><tt>403</tt></td>
79
+ <td><tt>1030</tt></td>
80
+ <td><tt></tt></td>
81
+ <td>SSL required for this type of request</td>
82
+ </tr>
83
+ <tr>
84
+ <td><tt>400</tt></td>
85
+ <td><tt>1035</tt></td>
86
+ <td><tt></tt></td>
87
+ <td>POST data not supplied as valid JSON. Issued if the <tt>Content-Type</tt> header is not <tt>application/json/</tt> and/or if the POST data is not in valid JSON format.</td>
88
+ </tr>
89
+ <tr>
90
+ <td><tt>400</tt></td>
91
+ <td><tt>1040</tt></td>
92
+ <td><tt></tt></td>
93
+ <td>The <tt>_filter</tt> syntax was invalid or a specified field to search on does not exist</td>
94
+ </tr>
95
+ <tr>
96
+ <td><tt>400</tt></td>
97
+ <td><tt>1050</tt></td>
98
+ <td><tt></tt></td>
99
+ <td>(message varies) A required parameter was not provided</td>
100
+ </tr>
101
+ <tr>
102
+ <td><tt>400</tt></td>
103
+ <td><tt>1053</tt></td>
104
+ <td><tt></tt></td>
105
+ <td>(message varies) A parameter was provided but does not adhere to constraints</td>
106
+ </tr>
107
+ <tr>
108
+ <td><tt>409</tt></td>
109
+ <td><tt>1055</tt></td>
110
+ <td><tt></tt></td>
111
+ <td>(message varies)Issued when a write is requested that will conflict with existing data. For example, adding a new contact with an e-mail that already exists.</td>
112
+ </tr>
113
+ <tr>
114
+ <td><tt>403</tt></td>
115
+ <td><tt>1500</tt></td>
116
+ <td><tt></tt></td>
117
+ <td>The resource is not available at the current API key's service level. For example, this error applies if a user attempts to access the IDX Links API via a free API key. </td>
118
+ </tr>
119
+ <tr>
120
+ <td><tt>503</tt></td>
121
+ <td><tt>1550</tt></td>
122
+ <td><tt></tt></td>
123
+ <td>Over rate limit</td>
124
+ </tbody>
125
+ </table>
126
+
127
+
128
+
data/Rakefile ADDED
@@ -0,0 +1,78 @@
1
+ require "rubygems"
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+ require 'ci/reporter/rake/rspec'
5
+ require "rspec"
6
+ require 'rspec/core/rake_task'
7
+
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |gemspec|
11
+ gemspec.name = "flexmls_api"
12
+ gemspec.summary = "A library for interacting with the flexmls web services."
13
+ gemspec.description = "A library for interacting with the flexmls web services."
14
+ gemspec.email = "api-support@flexmls.com"
15
+ gemspec.homepage = "https://github.com/flexmls/flexmls_api"
16
+ gemspec.authors = ["Brandon Hornseth"]
17
+ gemspec.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
18
+ gemspec.add_development_dependency "rspec"
19
+ gemspec.add_development_dependency "jeweler"
20
+ gemspec.add_development_dependency "curb"
21
+ gemspec.add_development_dependency "json"
22
+ end
23
+ Jeweler::GemcutterTasks.new
24
+ rescue LoadError
25
+ puts "Jeweler not available. Install it with: gem install jeweler"
26
+ end
27
+
28
+ RSpec::Core::RakeTask.new do |t|
29
+ t.rspec_opts = ["-c", "-f progress"]
30
+ t.pattern = 'spec/**/*_spec.rb'
31
+ t.rcov = true
32
+ t.rcov_opts = %w{--exclude /usr/local/rvm/gems/,bundle,spec}
33
+ end
34
+
35
+ task :install do
36
+ require './lib/flexmls_api'
37
+ rm_rf "*.gem"
38
+ puts `gem build flexmls_api.gemspec`
39
+ puts `sudo gem install flexmls_api-#{FlexmlsApi::VERSION}.gem`
40
+ end
41
+
42
+ desc "Run all the tests"
43
+ task :default => :spec
44
+
45
+ Rake::TaskManager.class_eval do
46
+ def remove_task(task_name)
47
+ @tasks.delete(task_name.to_s)
48
+ end
49
+ end
50
+
51
+ def remove_task(task_name)
52
+ Rake.application.remove_task(task_name)
53
+ end
54
+
55
+ Rake::RDocTask.new do |rdoc|
56
+ files =['lib/**/*.rb']
57
+ rdoc.rdoc_files.add(files)
58
+ rdoc.main = "README.md" # page to start on
59
+ rdoc.title = "flexmls_api Docs"
60
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
61
+ rdoc.options << '--line-numbers'
62
+ end
63
+
64
+ spec = Gem::Specification::load("flexmls_api.gemspec")
65
+ Rake::GemPackageTask.new(spec) do |p|
66
+ p.gem_spec = spec
67
+ p.need_tar = true
68
+ p.need_zip = true
69
+ end
70
+
71
+ task :deploy do
72
+ gems = FileList['pkg/*.gem']
73
+ FileUtils.cp gems, '/opt/gems/dev/gems'
74
+ require 'rubygems'
75
+ require 'rubygems/indexer'
76
+ i=Gem::Indexer.new '/opt/gems/dev'
77
+ i.generate_index
78
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.2
@@ -0,0 +1,104 @@
1
+ require 'openssl'
2
+ require 'faraday'
3
+ require 'faraday_middleware'
4
+ require 'yajl'
5
+ require 'date'
6
+ module FlexmlsApi
7
+ # =API Authentication
8
+ # Handles authentication and reauthentication to the flexmls api.
9
+ module Authentication
10
+
11
+ # Main authentication step. Run before any api request unless the user session exists and is
12
+ # still valid.
13
+ #
14
+ # :returns:
15
+ # The user session object when authentication succeeds
16
+ # :raises:
17
+ # FlexmlsApi::ClientError when authentication fails
18
+ def authenticate
19
+ sig = sign("#{@api_secret}ApiKey#{@api_key}")
20
+ FlexmlsApi.logger.debug("Authenticating to #{@endpoint}")
21
+ start_time = Time.now
22
+ request_path = "/#{version}/session?ApiKey=#{api_key}&ApiSig=#{sig}"
23
+ resp = connection(true).post request_path, ""
24
+ request_time = Time.now - start_time
25
+ FlexmlsApi.logger.info("[#{request_time}s] Api: POST #{request_path}")
26
+ @session = Session.new(resp.body.results[0])
27
+ FlexmlsApi.logger.debug("Authentication: #{@session.inspect}")
28
+ @session
29
+ end
30
+
31
+ # Builds an ordered list of key value pairs and concatenates it all as one big string. Used
32
+ # specifically for signing a request.
33
+ def build_param_string(param_hash)
34
+ return "" if param_hash.nil?
35
+ sorted = param_hash.sort do |a,b|
36
+ a.to_s <=> b.to_s
37
+ end
38
+ params = ""
39
+ sorted.each do |key,val|
40
+ params += key.to_s + val.to_s
41
+ end
42
+ params
43
+ end
44
+
45
+ # ==Session class
46
+ # Handle on the api user session information as return by the api session service, including
47
+ # roles, tokens and expiration
48
+ class Session
49
+ attr_accessor :auth_token, :expires, :roles
50
+ def initialize(options={})
51
+ @auth_token = options["AuthToken"]
52
+ @expires = DateTime.parse options["Expires"]
53
+ @roles = options["Roles"]
54
+ end
55
+ # Is the user session token expired?
56
+ def expired?
57
+ DateTime.now > @expires
58
+ end
59
+ end
60
+
61
+ # Main connection object for running requests. Bootstraps the Faraday abstraction layer with
62
+ # our client configuration.
63
+ def connection(force_ssl = false)
64
+ opts = {
65
+ :headers => headers
66
+ }
67
+ domain = @endpoint
68
+ if(force_ssl || self.ssl)
69
+ opts[:ssl] = {:verify => false }
70
+ opts[:url] = @endpoint.sub /^http:/, "https:"
71
+ else
72
+ opts[:url] = @endpoint.sub /^https:/, "http:"
73
+ end
74
+ conn = Faraday::Connection.new(opts) do |builder|
75
+ builder.adapter Faraday.default_adapter
76
+ builder.use Faraday::Response::ParseJson
77
+ builder.use FlexmlsApi::FaradayExt::FlexmlsMiddleware
78
+ end
79
+ FlexmlsApi.logger.debug("Connection: #{conn.inspect}")
80
+ conn
81
+ end
82
+
83
+ # HTTP request headers
84
+ def headers
85
+ {
86
+ :accept => 'application/json',
87
+ :content_type => 'application/json',
88
+ :user_agent => user_agent,
89
+ 'X-flexmlsApi-User-Agent' => user_agent
90
+ }
91
+ end
92
+
93
+ # Sign a request
94
+ def sign(sig)
95
+ Digest::MD5.hexdigest(sig)
96
+ end
97
+
98
+ # Sign a request with request data.
99
+ def sign_token(path, params = {}, post_data="")
100
+ sign("#{@api_secret}ApiKey#{@api_key}ServicePath/#{version}#{path}#{build_param_string(params)}#{post_data}")
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,20 @@
1
+ module FlexmlsApi
2
+ # =API Client
3
+ # Main class to setup and run requests on the API. A default client is accessible globally as
4
+ # FlexmlsApi::client if the global configuration has been set as well. Otherwise, this class may
5
+ # be instanciated separately with the configuration information.
6
+ class Client
7
+ include Authentication
8
+ include Request
9
+
10
+ attr_accessor *Configuration::VALID_OPTION_KEYS
11
+
12
+ def initialize(options={})
13
+ options = FlexmlsApi.options.merge(options)
14
+ Configuration::VALID_OPTION_KEYS.each do |key|
15
+ send("#{key}=", options[key])
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ module FlexmlsApi
2
+ module Configuration
3
+ # valid configuration options
4
+ VALID_OPTION_KEYS = [:api_key, :api_secret, :endpoint, :user_agent, :version, :ssl].freeze
5
+
6
+ DEFAULT_API_KEY = nil
7
+ DEFAULT_API_SECRET = nil
8
+ DEFAULT_ENDPOINT = 'http://api.flexmls.com'
9
+ DEFAULT_VERSION = 'v1'
10
+ DEFAULT_USER_AGENT = "flexmls API Ruby Gem #{VERSION}"
11
+ DEFAULT_SSL = false
12
+
13
+ attr_accessor *VALID_OPTION_KEYS
14
+ def configure
15
+ yield self
16
+ end
17
+
18
+ def self.extended(base)
19
+ base.reset_configuration
20
+ end
21
+
22
+
23
+ def options
24
+ VALID_OPTION_KEYS.inject({}) do |opt,key|
25
+ opt.merge(key => send(key))
26
+ end
27
+ end
28
+
29
+
30
+ def reset_configuration
31
+ self.api_key = DEFAULT_API_KEY
32
+ self.api_secret = DEFAULT_API_SECRET
33
+ self.endpoint = DEFAULT_ENDPOINT
34
+ self.version = DEFAULT_VERSION
35
+ self.user_agent = DEFAULT_USER_AGENT
36
+ self.ssl = DEFAULT_SSL
37
+ self
38
+ end
39
+ end
40
+ end