flexmls_api 0.6.5 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +12 -1
- data/VERSION +1 -1
- data/lib/flexmls_api.rb +13 -11
- data/lib/flexmls_api/authentication/api_auth.rb +5 -3
- data/lib/flexmls_api/authentication/oauth2.rb +23 -17
- data/lib/flexmls_api/authentication/oauth2_impl/password_provider.rb +25 -0
- data/lib/flexmls_api/cli.rb +51 -25
- data/lib/flexmls_api/cli/oauth2.rb +1 -30
- data/lib/flexmls_api/cli/setup.rb +4 -1
- data/lib/flexmls_api/client.rb +1 -1
- data/lib/flexmls_api/configuration.rb +10 -0
- data/lib/flexmls_api/configuration/yaml.rb +81 -0
- data/lib/flexmls_api/faraday.rb +12 -2
- data/lib/flexmls_api/models.rb +25 -23
- data/lib/flexmls_api/models/constraint.rb +16 -0
- data/lib/flexmls_api/models/listing.rb +45 -2
- data/lib/flexmls_api/models/photo.rb +57 -2
- data/lib/flexmls_api/models/subresource.rb +5 -2
- data/lib/flexmls_api/multi_client.rb +21 -6
- data/lib/flexmls_api/paginate.rb +18 -1
- data/lib/flexmls_api/request.rb +1 -71
- data/lib/flexmls_api/response.rb +69 -0
- data/spec/fixtures/{contacts.json → contacts/contacts.json} +0 -0
- data/spec/fixtures/{contact_my.json → contacts/my.json} +0 -0
- data/spec/fixtures/{contact_new.json → contacts/new.json} +0 -0
- data/spec/fixtures/{contact_new_empty.json → contacts/new_empty.json} +0 -0
- data/spec/fixtures/{contact_new_notify.json → contacts/new_notify.json} +0 -0
- data/spec/fixtures/{contacts_post.json → contacts/post.json} +0 -0
- data/spec/fixtures/{contact_tags.json → contacts/tags.json} +0 -0
- data/spec/fixtures/{listing_cart_add_listing.json → listing_carts/add_listing.json} +0 -0
- data/spec/fixtures/{listing_cart_add_listing_post.json → listing_carts/add_listing_post.json} +0 -0
- data/spec/fixtures/{listing_cart_empty.json → listing_carts/empty.json} +0 -0
- data/spec/fixtures/{listing_cart.json → listing_carts/listing_cart.json} +0 -0
- data/spec/fixtures/{listing_cart_new.json → listing_carts/new.json} +0 -0
- data/spec/fixtures/{listing_cart_post.json → listing_carts/post.json} +0 -0
- data/spec/fixtures/{listing_cart_remove_listing.json → listing_carts/remove_listing.json} +0 -0
- data/spec/fixtures/listings/constraints.json +18 -0
- data/spec/fixtures/listings/constraints_with_pagination.json +24 -0
- data/spec/fixtures/{listing_document_index.json → listings/document_index.json} +0 -0
- data/spec/fixtures/{listing_no_subresources.json → listings/no_subresources.json} +0 -0
- data/spec/fixtures/{open_houses.json → listings/open_houses.json} +0 -0
- data/spec/fixtures/{listing_photos_index.json → listings/photos/index.json} +0 -0
- data/spec/fixtures/listings/photos/new.json +12 -0
- data/spec/fixtures/listings/photos/post.json +20 -0
- data/spec/fixtures/listings/put.json +5 -0
- data/spec/fixtures/{saved_search.json → listings/saved_search.json} +0 -0
- data/spec/fixtures/{shared_listing_new.json → listings/shared_listing_new.json} +0 -0
- data/spec/fixtures/{shared_listing_post.json → listings/shared_listing_post.json} +0 -0
- data/spec/fixtures/{tour_of_homes.json → listings/tour_of_homes.json} +0 -0
- data/spec/fixtures/{listing_videos_index.json → listings/videos_index.json} +0 -0
- data/spec/fixtures/{listing_virtual_tours_index.json → listings/virtual_tours_index.json} +0 -0
- data/spec/fixtures/{listing_with_documents.json → listings/with_documents.json} +0 -0
- data/spec/fixtures/listings/with_permissions.json +44 -0
- data/spec/fixtures/{listing_with_photos.json → listings/with_photos.json} +0 -0
- data/spec/fixtures/{listing_with_supplement.json → listings/with_supplement.json} +0 -0
- data/spec/fixtures/{listing_with_videos.json → listings/with_videos.json} +0 -0
- data/spec/fixtures/{listing_with_vtour.json → listings/with_vtour.json} +0 -0
- data/spec/fixtures/logo_fbs.png +0 -0
- data/spec/fixtures/{add_note.json → notes/add.json} +0 -0
- data/spec/fixtures/{agent_shared_note.json → notes/agent_shared.json} +0 -0
- data/spec/fixtures/{agent_shared_note_empty.json → notes/agent_shared_empty.json} +0 -0
- data/spec/fixtures/{note_new.json → notes/new.json} +0 -0
- data/spec/fixtures/{standardfields_city.json → standardfields/city.json} +0 -0
- data/spec/fixtures/{standardfields_nearby.json → standardfields/nearby.json} +0 -0
- data/spec/fixtures/{standardfields.json → standardfields/standardfields.json} +0 -0
- data/spec/fixtures/{standardfields_stateorprovince.json → standardfields/stateorprovince.json} +0 -0
- data/spec/mock_helper.rb +5 -3
- data/spec/unit/flexmls_api/authentication/api_auth_spec.rb +11 -2
- data/spec/unit/flexmls_api/authentication/base_auth_spec.rb +10 -0
- data/spec/unit/flexmls_api/authentication/oauth2_spec.rb +3 -3
- data/spec/unit/flexmls_api/configuration/yaml_spec.rb +70 -0
- data/spec/unit/flexmls_api/models/constraint_spec.rb +19 -0
- data/spec/unit/flexmls_api/models/contact_spec.rb +8 -8
- data/spec/unit/flexmls_api/models/document_spec.rb +1 -1
- data/spec/unit/flexmls_api/models/listing_cart_spec.rb +15 -15
- data/spec/unit/flexmls_api/models/listing_spec.rb +78 -13
- data/spec/unit/flexmls_api/models/note_spec.rb +4 -4
- data/spec/unit/flexmls_api/models/open_house_spec.rb +1 -1
- data/spec/unit/flexmls_api/models/photo_spec.rb +73 -40
- data/spec/unit/flexmls_api/models/saved_search_spec.rb +3 -3
- data/spec/unit/flexmls_api/models/shared_listing_spec.rb +1 -1
- data/spec/unit/flexmls_api/models/standard_fields_spec.rb +4 -7
- data/spec/unit/flexmls_api/models/tour_of_home_spec.rb +1 -1
- data/spec/unit/flexmls_api/models/video_spec.rb +1 -1
- data/spec/unit/flexmls_api/models/virtual_tour_spec.rb +1 -1
- data/spec/unit/flexmls_api/multi_client_spec.rb +9 -1
- data/spec/unit/flexmls_api/paginate_spec.rb +1 -1
- data/spec/unit/flexmls_api/request_spec.rb +2 -2
- metadata +182 -152
data/lib/flexmls_api/faraday.rb
CHANGED
@@ -3,7 +3,7 @@ module FlexmlsApi
|
|
3
3
|
#=Flexmls API Faraday middleware
|
4
4
|
# HTTP Response after filter to package api responses and bubble up basic api errors.
|
5
5
|
class FlexmlsMiddleware < Faraday::Response::ParseJson
|
6
|
-
|
6
|
+
include FlexmlsApi::PaginateHelper
|
7
7
|
# Handles pretty much all the api response parsing and error handling. All responses that
|
8
8
|
# indicate a failure will raise a FlexmlsApi::ClientError exception
|
9
9
|
def on_complete(finished_env)
|
@@ -13,6 +13,16 @@ module FlexmlsApi
|
|
13
13
|
raise InvalidResponse, "The server response could not be understood"
|
14
14
|
end
|
15
15
|
response = ApiResponse.new body
|
16
|
+
paging = response.pagination
|
17
|
+
if paging.nil?
|
18
|
+
results = response
|
19
|
+
else
|
20
|
+
if finished_env[:url].query_values["_pagination"] == "count"
|
21
|
+
results = paging['TotalRows']
|
22
|
+
else
|
23
|
+
results = paginate_response(response, paging)
|
24
|
+
end
|
25
|
+
end
|
16
26
|
case finished_env[:status]
|
17
27
|
when 400, 409
|
18
28
|
raise BadResourceRequest, {:message => response.message, :code => response.code, :status => finished_env[:status]}
|
@@ -33,7 +43,7 @@ module FlexmlsApi
|
|
33
43
|
else
|
34
44
|
raise ClientError, {:message => response.message, :code => response.code, :status => finished_env[:status]}
|
35
45
|
end
|
36
|
-
finished_env[:body] =
|
46
|
+
finished_env[:body] = results
|
37
47
|
end
|
38
48
|
|
39
49
|
def initialize(app)
|
data/lib/flexmls_api/models.rb
CHANGED
@@ -1,26 +1,28 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
1
|
+
require 'flexmls_api/models/base'
|
2
|
+
require 'flexmls_api/models/finders'
|
3
|
+
require 'flexmls_api/models/constraint'
|
4
|
+
|
5
|
+
require 'flexmls_api/models/account'
|
6
|
+
require 'flexmls_api/models/listing'
|
7
|
+
require 'flexmls_api/models/subresource'
|
8
|
+
require 'flexmls_api/models/photo'
|
9
|
+
require 'flexmls_api/models/system_info'
|
10
|
+
require 'flexmls_api/models/standard_fields'
|
11
|
+
require 'flexmls_api/models/custom_fields'
|
12
|
+
require 'flexmls_api/models/property_types'
|
13
|
+
require 'flexmls_api/models/connect_prefs'
|
14
|
+
require 'flexmls_api/models/contact'
|
15
|
+
require 'flexmls_api/models/idx_link'
|
16
|
+
require 'flexmls_api/models/market_statistics'
|
17
|
+
require 'flexmls_api/models/video'
|
18
|
+
require 'flexmls_api/models/open_house'
|
19
|
+
require 'flexmls_api/models/tour_of_home'
|
20
|
+
require 'flexmls_api/models/virtual_tour'
|
21
|
+
require 'flexmls_api/models/document'
|
22
|
+
require 'flexmls_api/models/note'
|
23
|
+
require 'flexmls_api/models/listing_cart'
|
24
|
+
require 'flexmls_api/models/shared_listing'
|
25
|
+
require 'flexmls_api/models/saved_search'
|
24
26
|
|
25
27
|
module FlexmlsApi
|
26
28
|
module Models
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FlexmlsApi
|
2
|
+
module Models
|
3
|
+
class Constraint
|
4
|
+
ATTRIBUTES = ["RuleValue","Value","RuleFieldValue","RuleField","RuleName"]
|
5
|
+
attr_accessor *ATTRIBUTES
|
6
|
+
def initialize(args)
|
7
|
+
ATTRIBUTES.each { |f| send("#{f}=", args[f]) if args.include?(f) || args.include?(f.to_sym) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{self.RuleName}: Field(#{self.RuleField},#{self.RuleFieldValue}) Value(#{self.RuleValue},#{self.Value})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -3,14 +3,17 @@ module FlexmlsApi
|
|
3
3
|
class Listing < Base
|
4
4
|
extend Finders
|
5
5
|
attr_accessor :photos, :videos, :virtual_tours, :documents
|
6
|
+
attr_accessor :constraints
|
6
7
|
self.element_name="listings"
|
8
|
+
DATA_MASK = "********"
|
7
9
|
|
8
10
|
def initialize(attributes={})
|
9
11
|
@photos = []
|
10
12
|
@videos = []
|
11
13
|
@virtual_tours = []
|
12
14
|
@documents = []
|
13
|
-
|
15
|
+
@constraints = []
|
16
|
+
|
14
17
|
if attributes.has_key?('StandardFields')
|
15
18
|
pics, vids, tours, docs = attributes['StandardFields'].values_at('Photos','Videos', 'VirtualTours', 'Documents')
|
16
19
|
end
|
@@ -19,7 +22,7 @@ module FlexmlsApi
|
|
19
22
|
pics.collect { |pic| @photos.push(Photo.new(pic)) }
|
20
23
|
attributes['StandardFields'].delete('Photos')
|
21
24
|
end
|
22
|
-
|
25
|
+
|
23
26
|
if vids != nil
|
24
27
|
vids.collect { |vid| @videos.push(Video.new(vid)) }
|
25
28
|
attributes['StandardFields'].delete('Videos')
|
@@ -88,6 +91,46 @@ module FlexmlsApi
|
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
94
|
+
def street_address
|
95
|
+
"#{self.StreetNumber} #{self.StreetDirPrefix} #{self.StreetName} #{self.StreetSuffix} #{self.StreetDirSuffix} #{self.StreetAdditionalInfo}".delete(DATA_MASK).strip().gsub(/\s{2,}/, ' ')
|
96
|
+
end
|
97
|
+
|
98
|
+
def region_address
|
99
|
+
"#{self.City}, #{self.StateOrProvince} #{self.PostalCode}".delete(DATA_MASK).strip().gsub(/^,\s/, '').gsub(/,$/, '')
|
100
|
+
end
|
101
|
+
|
102
|
+
def full_address
|
103
|
+
"#{self.street_address}, #{self.region_address}".strip().gsub(/^,\s/, '').gsub(/,$/, '')
|
104
|
+
end
|
105
|
+
|
106
|
+
def save(arguments={})
|
107
|
+
begin
|
108
|
+
return save!(arguments)
|
109
|
+
rescue NotFound, BadResourceRequest => e
|
110
|
+
FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
|
111
|
+
end
|
112
|
+
false
|
113
|
+
end
|
114
|
+
def save!(arguments={})
|
115
|
+
results = connection.put "#{self.class.path}/#{self.Id}", {"ListPrice"=> self.ListPrice}, arguments
|
116
|
+
@contstraints = []
|
117
|
+
results.details.each do |detail|
|
118
|
+
detail.each_pair do |k,v|
|
119
|
+
v.each { |constraint| @constraints << Constraint.new(constraint)}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def editable?(editable_settings = [])
|
126
|
+
settings = Array(editable_settings)
|
127
|
+
editable = attributes.include?("Permissions") && self.Permissions["Editable"] == true
|
128
|
+
if editable
|
129
|
+
settings.each{ |setting| editable = false unless self.Permissions["EditableSettings"][setting.to_s] == true }
|
130
|
+
end
|
131
|
+
editable
|
132
|
+
end
|
133
|
+
|
91
134
|
|
92
135
|
private
|
93
136
|
|
@@ -2,13 +2,68 @@ module FlexmlsApi
|
|
2
2
|
module Models
|
3
3
|
class Photo < Base
|
4
4
|
extend Subresource
|
5
|
-
|
6
5
|
self.element_name = "photos"
|
6
|
+
|
7
|
+
attr_accessor :update_path
|
8
|
+
|
9
|
+
EDITABLE_FIELDS = [:Picture, :FileName, :Name, :Caption, :Primary]
|
10
|
+
|
11
|
+
def initialize(opts={})
|
12
|
+
defaulted_opts = {}
|
13
|
+
EDITABLE_FIELDS.each do |k|
|
14
|
+
key = k.to_s()
|
15
|
+
defaulted_opts[key] = opts[key] || nil
|
16
|
+
end
|
17
|
+
super(opts.merge(defaulted_opts))
|
18
|
+
end
|
7
19
|
|
8
20
|
def primary?
|
9
21
|
@attributes["Primary"] == true
|
10
22
|
end
|
11
|
-
|
23
|
+
|
24
|
+
def save(arguments={})
|
25
|
+
begin
|
26
|
+
return save!(arguments)
|
27
|
+
rescue NotFound, BadResourceRequest => e
|
28
|
+
FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
|
29
|
+
end
|
30
|
+
false
|
31
|
+
end
|
32
|
+
def save!(arguments={})
|
33
|
+
payload = {"Photos" => [ build_photo_hash]}
|
34
|
+
if exists?
|
35
|
+
results = connection.put "#{update_path}/#{self.Id}", payload, arguments
|
36
|
+
else
|
37
|
+
results = connection.post update_path, payload, arguments
|
38
|
+
end
|
39
|
+
result = results.first
|
40
|
+
load(result)
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_picture(file_name)
|
45
|
+
self.Picture = Base64.encode64(File.open(file_name, 'rb').read).gsub(/\n/, '')
|
46
|
+
self.FileName = File.basename(file_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete(args={})
|
50
|
+
connection.delete("#{update_path}/#{self.Id}", args)
|
51
|
+
end
|
52
|
+
|
53
|
+
def exists?
|
54
|
+
@attributes.include?("Id")
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def build_photo_hash
|
60
|
+
results_hash = {}
|
61
|
+
EDITABLE_FIELDS.each do |k|
|
62
|
+
key = k.to_s
|
63
|
+
results_hash[key] = @attributes[key] unless @attributes[key].nil?
|
64
|
+
end
|
65
|
+
results_hash
|
66
|
+
end
|
12
67
|
|
13
68
|
end
|
14
69
|
end
|
@@ -6,11 +6,14 @@ module FlexmlsApi
|
|
6
6
|
Class.new(self)
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
9
|
def find_by_listing_key(key, arguments={})
|
11
10
|
collect(connection.get("/listings/#{key}#{self.path}", arguments))
|
12
11
|
end
|
13
|
-
|
12
|
+
|
13
|
+
def find_by_id(id, parent_id, arguments={})
|
14
|
+
collect(connection.get("/listings/#{parent_id}#{self.path}/#{id}", arguments)).first
|
15
|
+
end
|
16
|
+
|
14
17
|
end
|
15
18
|
end
|
16
19
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module FlexmlsApi
|
2
2
|
#===Active support for multiple clients
|
3
3
|
module MultiClient
|
4
|
+
include FlexmlsApi::Configuration
|
4
5
|
|
5
6
|
# Activate a specific instance of the client (with appropriate config settings). Each client
|
6
7
|
# is lazily instanciated by calling the matching FlexmlsApi.symbol_name method on the
|
@@ -13,8 +14,8 @@ module FlexmlsApi
|
|
13
14
|
def activate(sym)
|
14
15
|
if block_given?
|
15
16
|
original_client = Thread.current[:flexmls_api_client]
|
16
|
-
activate_client(sym)
|
17
17
|
begin
|
18
|
+
activate_client(sym)
|
18
19
|
yield
|
19
20
|
ensure
|
20
21
|
Thread.current[:flexmls_api_client] = original_client
|
@@ -27,11 +28,25 @@ module FlexmlsApi
|
|
27
28
|
private
|
28
29
|
|
29
30
|
# set the active client for the symbol
|
30
|
-
def activate_client(
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
def activate_client(symbol)
|
32
|
+
if !Thread.current[symbol].nil?
|
33
|
+
active_client = Thread.current[symbol]
|
34
|
+
elsif FlexmlsApi.respond_to? symbol
|
35
|
+
active_client = FlexmlsApi.send(symbol)
|
36
|
+
elsif YamlConfig.exists? symbol.to_s
|
37
|
+
active_client = activate_client_from_config(symbol)
|
38
|
+
else
|
39
|
+
raise ArgumentError, "The symbol #{symbol} is not setup correctly as a multi client key."
|
40
|
+
end
|
41
|
+
Thread.current[symbol] = active_client
|
42
|
+
Thread.current[:flexmls_api_client] = active_client
|
35
43
|
end
|
44
|
+
|
45
|
+
def activate_client_from_config(symbol)
|
46
|
+
FlexmlsApi.logger.debug("Loading multiclient [#{symbol.to_s}] from config")
|
47
|
+
yaml = YamlConfig.build(symbol.to_s)
|
48
|
+
Client.new(yaml.client_keys)
|
49
|
+
end
|
50
|
+
|
36
51
|
end
|
37
52
|
end
|
data/lib/flexmls_api/paginate.rb
CHANGED
@@ -55,13 +55,28 @@ module FlexmlsApi
|
|
55
55
|
def per_page
|
56
56
|
DEFAULT_PAGE_SIZE
|
57
57
|
end
|
58
|
+
|
59
|
+
|
58
60
|
end
|
59
61
|
|
60
62
|
# ==Paginate Api Responses
|
61
|
-
# Module
|
63
|
+
# Module used by the request layer to decorate the response's results array with paging support.
|
62
64
|
# Pagination only happens if the response includes the pagination information as specified by the
|
63
65
|
# API.
|
64
66
|
module PaginateResponse
|
67
|
+
attr_accessor :results
|
68
|
+
def method_missing(method_symbol, *arguments)
|
69
|
+
if results.respond_to?(method_symbol)
|
70
|
+
arguments.empty? ? self.results.send(method_symbol) : self.results.send(method_symbol, arguments)
|
71
|
+
else
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# ==Pagination Helpers
|
78
|
+
# Helpers to create the pagination collection
|
79
|
+
module PaginateHelper
|
65
80
|
# ==Enable pagination
|
66
81
|
# * results -- array of hashes representing api resources
|
67
82
|
# * paging_hash -- the pagination response information from the api representing paging state.
|
@@ -73,6 +88,8 @@ module FlexmlsApi
|
|
73
88
|
paged_results = WillPaginate::Collection.create(pager.current_page, pager.page_size, pager.total_rows) do |p|
|
74
89
|
p.replace(results)
|
75
90
|
end
|
91
|
+
paged_results.extend PaginateResponse
|
92
|
+
paged_results.results = results
|
76
93
|
paged_results
|
77
94
|
end
|
78
95
|
end
|
data/lib/flexmls_api/request.rb
CHANGED
@@ -3,7 +3,6 @@ require 'cgi'
|
|
3
3
|
module FlexmlsApi
|
4
4
|
# HTTP request wrapper. Performs all the api session mumbo jumbo so that the models don't have to.
|
5
5
|
module Request
|
6
|
-
include PaginateResponse
|
7
6
|
# Perform an HTTP GET request
|
8
7
|
#
|
9
8
|
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
@@ -89,78 +88,9 @@ module FlexmlsApi
|
|
89
88
|
FlexmlsApi.logger.error("Authentication failed or server is sending us expired tokens, nothing we can do here.")
|
90
89
|
raise
|
91
90
|
end
|
92
|
-
|
93
|
-
paging = response.body.pagination
|
94
|
-
unless paging.nil?
|
95
|
-
if request_opts[:_pagination] == "count"
|
96
|
-
results = paging['TotalRows']
|
97
|
-
else
|
98
|
-
results = paginate_response(results, paging)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
results
|
91
|
+
response.body
|
102
92
|
end
|
103
93
|
|
104
94
|
end
|
105
95
|
|
106
|
-
# All known response codes listed in the API
|
107
|
-
module ResponseCodes
|
108
|
-
NOT_FOUND = 404
|
109
|
-
METHOD_NOT_ALLOWED = 405
|
110
|
-
INVALID_KEY = 1000
|
111
|
-
DISABLED_KEY = 1010
|
112
|
-
API_USER_REQUIRED = 1015
|
113
|
-
SESSION_TOKEN_EXPIRED = 1020
|
114
|
-
SSL_REQUIRED = 1030
|
115
|
-
INVALID_JSON = 1035
|
116
|
-
INVALID_FIELD = 1040
|
117
|
-
MISSING_PARAMETER = 1050
|
118
|
-
INVALID_PARAMETER = 1053
|
119
|
-
CONFLICTING_DATA = 1055
|
120
|
-
NOT_AVAILABLE= 1500
|
121
|
-
RATE_LIMIT_EXCEEDED = 1550
|
122
|
-
end
|
123
|
-
|
124
|
-
# Errors built from API responses
|
125
|
-
class InvalidResponse < StandardError; end
|
126
|
-
class ClientError < StandardError
|
127
|
-
attr_reader :code, :status
|
128
|
-
def initialize (options = {})
|
129
|
-
# Support the standard initializer for errors
|
130
|
-
opts = options.is_a?(Hash) ? options : {:message => options.to_s}
|
131
|
-
@code = opts[:code]
|
132
|
-
@status = opts[:status]
|
133
|
-
super(opts[:message])
|
134
|
-
end
|
135
|
-
|
136
|
-
end
|
137
|
-
class NotFound < ClientError; end
|
138
|
-
class PermissionDenied < ClientError; end
|
139
|
-
class NotAllowed < ClientError; end
|
140
|
-
class BadResourceRequest < ClientError; end
|
141
|
-
|
142
|
-
# Nice and handy class wrapper for the api response hash
|
143
|
-
class ApiResponse
|
144
|
-
attr_accessor :code, :message, :results, :success, :pagination
|
145
|
-
def initialize(d)
|
146
|
-
begin
|
147
|
-
hash = d["D"]
|
148
|
-
if hash.nil? || hash.empty?
|
149
|
-
raise InvalidResponse, "The server response could not be understood"
|
150
|
-
end
|
151
|
-
self.message = hash["Message"]
|
152
|
-
self.code = hash["Code"]
|
153
|
-
self.results = hash["Results"]
|
154
|
-
self.success = hash["Success"]
|
155
|
-
self.pagination = hash["Pagination"]
|
156
|
-
rescue Exception => e
|
157
|
-
FlexmlsApi.logger.error "Unable to understand the response! #{d}"
|
158
|
-
raise
|
159
|
-
end
|
160
|
-
end
|
161
|
-
def success?
|
162
|
-
@success
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
96
|
end
|
@@ -0,0 +1,69 @@
|
|
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
|
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
|
+
super(opts[:message])
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
class NotFound < ClientError; end
|
43
|
+
class PermissionDenied < ClientError; end
|
44
|
+
class NotAllowed < ClientError; end
|
45
|
+
class BadResourceRequest < ClientError; end
|
46
|
+
|
47
|
+
# Nice and handy class wrapper for the api response hash
|
48
|
+
class ApiResponse < ::Array
|
49
|
+
include FlexmlsApi::Response
|
50
|
+
def initialize(d)
|
51
|
+
begin
|
52
|
+
hash = d["D"]
|
53
|
+
if hash.nil? || hash.empty?
|
54
|
+
raise InvalidResponse, "The server response could not be understood"
|
55
|
+
end
|
56
|
+
self.message = hash["Message"]
|
57
|
+
self.code = hash["Code"]
|
58
|
+
self.results = Array(hash["Results"])
|
59
|
+
self.success = hash["Success"]
|
60
|
+
self.pagination = hash["Pagination"]
|
61
|
+
self.details = hash["Details"] || []
|
62
|
+
super(results)
|
63
|
+
rescue Exception => e
|
64
|
+
FlexmlsApi.logger.error "Unable to understand the response! #{d}"
|
65
|
+
raise
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|