flexmls_api 0.6.5 → 0.7.0
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.
- 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
|