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