spark_api 1.0.2 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/README.md +86 -3
  2. data/VERSION +1 -1
  3. data/lib/spark_api.rb +1 -0
  4. data/lib/spark_api/authentication/oauth2.rb +43 -2
  5. data/lib/spark_api/configuration/oauth2_configurable.rb +3 -1
  6. data/lib/spark_api/models.rb +1 -0
  7. data/lib/spark_api/models/listing.rb +17 -1
  8. data/lib/spark_api/models/rental_calendar.rb +26 -0
  9. data/lib/spark_api/options_hash.rb +18 -0
  10. data/script/combined_flow_example.rb +55 -0
  11. data/script/oauth2_example.rb +2 -2
  12. data/spec/fixtures/listings/rental_calendar.json +19 -0
  13. data/spec/fixtures/listings/with_rental_calendar.json +52 -0
  14. data/spec/unit/spark_api/authentication/oauth2_spec.rb +79 -1
  15. data/spec/unit/spark_api/models/listing_spec.rb +7 -0
  16. data/spec/unit/spark_api/models/rental_calendar_spec.rb +30 -0
  17. data/spec/unit/spark_api/options_hash_spec.rb +14 -0
  18. metadata +15 -96
  19. data/bin/spark_api~ +0 -8
  20. data/lib/spark_api/authentication/api_auth.rb~ +0 -104
  21. data/lib/spark_api/authentication/base_auth.rb~ +0 -47
  22. data/lib/spark_api/authentication/oauth2.rb~ +0 -199
  23. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +0 -87
  24. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +0 -49
  25. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +0 -45
  26. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +0 -36
  27. data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +0 -39
  28. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +0 -25
  29. data/lib/spark_api/cli.rb~ +0 -158
  30. data/lib/spark_api/cli/api_auth.rb~ +0 -8
  31. data/lib/spark_api/cli/oauth2.rb~ +0 -14
  32. data/lib/spark_api/cli/setup.rb~ +0 -47
  33. data/lib/spark_api/configuration.rb~ +0 -54
  34. data/lib/spark_api/configuration/yaml.rb~ +0 -101
  35. data/lib/spark_api/faraday.rb~ +0 -64
  36. data/lib/spark_api/models.rb~ +0 -33
  37. data/lib/spark_api/models/account.rb~ +0 -115
  38. data/lib/spark_api/models/base.rb~ +0 -118
  39. data/lib/spark_api/models/connect_prefs.rb~ +0 -10
  40. data/lib/spark_api/models/constraint.rb~ +0 -16
  41. data/lib/spark_api/models/contact.rb~ +0 -49
  42. data/lib/spark_api/models/custom_fields.rb~ +0 -12
  43. data/lib/spark_api/models/document.rb~ +0 -11
  44. data/lib/spark_api/models/finders.rb~ +0 -45
  45. data/lib/spark_api/models/idx_link.rb~ +0 -47
  46. data/lib/spark_api/models/listing.rb~ +0 -197
  47. data/lib/spark_api/models/listing_cart.rb~ +0 -72
  48. data/lib/spark_api/models/market_statistics.rb~ +0 -33
  49. data/lib/spark_api/models/message.rb~ +0 -21
  50. data/lib/spark_api/models/note.rb~ +0 -41
  51. data/lib/spark_api/models/notification.rb~ +0 -42
  52. data/lib/spark_api/models/open_house.rb~ +0 -24
  53. data/lib/spark_api/models/photo.rb~ +0 -70
  54. data/lib/spark_api/models/property_types.rb~ +0 -7
  55. data/lib/spark_api/models/saved_search.rb~ +0 -16
  56. data/lib/spark_api/models/shared_listing.rb~ +0 -35
  57. data/lib/spark_api/models/standard_fields.rb~ +0 -50
  58. data/lib/spark_api/models/subresource.rb~ +0 -19
  59. data/lib/spark_api/models/system_info.rb~ +0 -14
  60. data/lib/spark_api/models/tour_of_home.rb~ +0 -24
  61. data/lib/spark_api/models/video.rb~ +0 -16
  62. data/lib/spark_api/models/virtual_tour.rb~ +0 -18
  63. data/lib/spark_api/multi_client.rb~ +0 -59
  64. data/lib/spark_api/paginate.rb~ +0 -109
  65. data/lib/spark_api/primary_array.rb~ +0 -29
  66. data/lib/spark_api/request.rb~ +0 -96
  67. data/lib/spark_api/response.rb~ +0 -70
  68. data/lib/spark_api/version.rb~ +0 -4
  69. data/script/console~ +0 -6
  70. data/script/example.rb~ +0 -27
  71. data/spec/unit/flexmls_api_spec.rb~ +0 -23
  72. data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +0 -169
  73. data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +0 -10
  74. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +0 -10
  75. data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +0 -205
  76. data/spec/unit/spark_api/authentication_spec.rb~ +0 -38
  77. data/spec/unit/spark_api/configuration/yaml_spec.rb~ +0 -72
  78. data/spec/unit/spark_api/configuration_spec.rb~ +0 -122
  79. data/spec/unit/spark_api/faraday_spec.rb~ +0 -90
  80. data/spec/unit/spark_api/models/contact_spec.rb~ +0 -108
  81. data/spec/unit/spark_api/models/listing_cart_spec.rb~ +0 -127
  82. data/spec/unit/spark_api/models/listing_spec.rb~ +0 -320
  83. data/spec/unit/spark_api/models/message_spec.rb~ +0 -47
  84. data/spec/unit/spark_api/models/note_spec.rb~ +0 -63
  85. data/spec/unit/spark_api/models/notification_spec.rb~ +0 -62
  86. data/spec/unit/spark_api/models/shared_listing_spec.rb~ +0 -45
  87. data/spec/unit/spark_api/multi_client_spec.rb~ +0 -56
  88. data/spec/unit/spark_api/paginate_spec.rb~ +0 -224
  89. data/spec/unit/spark_api/primary_array_spec.rb~ +0 -41
  90. data/spec/unit/spark_api/request_spec.rb~ +0 -344
@@ -1,47 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class IdxLink < Base
4
- self.element_name="idxlinks"
5
-
6
- LINK_TYPES = ["QuickSearch", "SavedSearch", "MyListings", "Roster"]
7
-
8
- #TODO Work all below into common base class
9
- def self.find(*arguments)
10
- scope = arguments.slice!(0)
11
- options = arguments.slice!(0) || {}
12
-
13
- case scope
14
- when :all then find_every(options)
15
- when :first then find_every(options).first
16
- when :last then find_every(options).last
17
- when :one then find_one(options)
18
- else find_single(scope, options)
19
- end
20
- end
21
-
22
- def self.first(*arguments)
23
- find(:first, *arguments)
24
- end
25
-
26
- def self.last(*arguments)
27
- find(:last, *arguments)
28
- end
29
-
30
- private
31
-
32
- def self.find_every(options)
33
- raise NotImplementedError # TODO
34
- end
35
-
36
- def self.find_one(options)
37
- raise NotImplementedError # TODO
38
- end
39
-
40
- def self.find_single(scope, options)
41
- resp = FlexmlsApi.client.get("/idxlinks/#{scope}", options)
42
- new(resp.first)
43
- end
44
-
45
- end
46
- end
47
- end
@@ -1,197 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Listing < Base
4
- extend Finders
5
- attr_accessor :photos, :videos, :virtual_tours, :documents
6
- attr_accessor :constraints
7
- self.element_name="listings"
8
- DATA_MASK = "********"
9
- WRITEABLE_FIELDS = ["ListPrice", "ExpirationDate"]
10
-
11
- def initialize(attributes={})
12
- @photos = []
13
- @videos = []
14
- @virtual_tours = []
15
- @documents = []
16
- @constraints = []
17
-
18
- if attributes.has_key?('StandardFields')
19
- pics, vids, tours, docs = attributes['StandardFields'].values_at('Photos','Videos', 'VirtualTours', 'Documents')
20
- end
21
-
22
- if pics != nil
23
- pics.collect { |pic| @photos.push(Photo.new(pic)) }
24
- attributes['StandardFields'].delete('Photos')
25
- end
26
-
27
- if vids != nil
28
- vids.collect { |vid| @videos.push(Video.new(vid)) }
29
- attributes['StandardFields'].delete('Videos')
30
- end
31
-
32
- if tours != nil
33
- tours.collect { |tour| @virtual_tours.push(VirtualTour.new(tour)) }
34
- attributes['StandardFields'].delete('VirtualTours')
35
- end
36
-
37
- if docs != nil
38
- docs.collect { |doc| @documents.push(Document.new(doc)) }
39
- attributes['StandardFields'].delete('Documents')
40
- end
41
-
42
- super(attributes)
43
- end
44
-
45
- def self.find_by_cart_id(cart_id, options={})
46
- query = {:_filter => "ListingCart Eq '#{cart_id}'"}
47
- find(:all, options.merge(query))
48
- end
49
-
50
- def self.my(arguments={})
51
- collect(connection.get("/my/listings", arguments))
52
- end
53
-
54
- def self.office(arguments={})
55
- collect(connection.get("/office/listings", arguments))
56
- end
57
-
58
- def self.company(arguments={})
59
- collect(connection.get("/company/listings", arguments))
60
- end
61
-
62
- def self.nearby(latitude, longitude, arguments={})
63
- nearby_args = {:Lat => latitude, :Lon => longitude}.merge(arguments)
64
- collect(connection.get("/listings/nearby", nearby_args))
65
- end
66
-
67
- def tour_of_homes(arguments={})
68
- return @tour_of_homes unless @tour_of_homes.nil?
69
- @tour_of_homes = TourOfHome.find_by_listing_key(self.Id, arguments)
70
- end
71
-
72
- def open_houses(arguments={})
73
- return @open_houses unless @open_houses.nil?
74
- @open_houses = OpenHouse.find_by_listing_key(self.Id, arguments)
75
- end
76
-
77
- def my_notes
78
- Note.build_subclass.tap do |note|
79
- note.prefix = "/listings/#{self.ListingKey}"
80
- note.element_name = "/my/notes"
81
- FlexmlsApi.logger.info("Note.path: #{note.path}")
82
- end
83
- end
84
-
85
- # 'fore' is required when accessing an agent's shared
86
- # notes for a specific contact. If the ApiUser /is/ the
87
- # contact, then it can be inferred by the api, so it's
88
- # unecessary
89
- def shared_notes(fore=nil)
90
- Note.build_subclass.tap do |note|
91
- note.prefix = "/listings/#{self.ListingKey}"
92
- if fore.nil?
93
- note.element_name = "/shared/notes"
94
- else
95
- note.element_name = "/shared/notes/contacts/#{fore}"
96
- end
97
- end
98
- end
99
-
100
- def street_address
101
- "#{self.StreetNumber} #{self.StreetDirPrefix} #{self.StreetName} #{self.StreetSuffix} #{self.StreetDirSuffix} #{self.StreetAdditionalInfo}".delete(DATA_MASK).strip().gsub(/\s{2,}/, ' ')
102
- end
103
-
104
- def region_address
105
- "#{self.City}, #{self.StateOrProvince} #{self.PostalCode}".delete(DATA_MASK).strip().gsub(/^,\s/, '').gsub(/,$/, '')
106
- end
107
-
108
- def full_address
109
- "#{self.street_address}, #{self.region_address}".strip().gsub(/^,\s/, '').gsub(/,$/, '')
110
- end
111
-
112
- def save(arguments={})
113
- self.errors = []
114
- begin
115
- return save!(arguments)
116
- rescue BadResourceRequest => e
117
- self.errors << {:code => e.code, :message => e.message}
118
- FlexmlsApi.logger.debug("BHDEBUG: #{e.inspect}")
119
- if e.code == 1053
120
- @constraints = []
121
- e.details.each do |detail|
122
- detail.each_pair do |k,v|
123
- v.each { |constraint| @constraints << Constraint.new(constraint)}
124
- end
125
- end
126
- end
127
- FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
128
- rescue NotFound => e
129
- FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
130
- end
131
- false
132
- end
133
- def save!(arguments={})
134
- writable_changed_keys = changed & WRITEABLE_FIELDS
135
- if writable_changed_keys.empty?
136
- FlexmlsApi.logger.warn("No supported listing change detected")
137
- else
138
- results = connection.put "#{self.class.path}/#{self.Id}", build_hash(writable_changed_keys), arguments
139
- @contstraints = []
140
- results.details.each do |detail|
141
- detail.each_pair do |k,v|
142
- v.each { |constraint| @constraints << Constraint.new(constraint)}
143
- end
144
- end
145
- end
146
- true
147
- end
148
-
149
- def editable?(editable_settings = [])
150
- settings = Array(editable_settings)
151
- editable = attributes.include?("Permissions") && self.Permissions["Editable"] == true
152
- if editable
153
- settings.each{ |setting| editable = false unless self.Permissions["EditableSettings"][setting.to_s] == true }
154
- end
155
- editable
156
- end
157
-
158
- def ExpirationDate
159
- attributes["ExpirationDate"]
160
- end
161
- def ExpirationDate=(value)
162
- write_attribute("ExpirationDate", value)
163
- end
164
-
165
-
166
- private
167
-
168
- # TODO trim this down so we're only overriding the StandardFields access
169
- def method_missing(method_symbol, *arguments)
170
- method_name = method_symbol.to_s
171
-
172
- if method_name =~ /(=|\?)$/
173
- case $1
174
- when "="
175
- write_attribute($`,arguments.first)
176
- # TODO figure out a nice way to present setters for the standard fields
177
- when "?"
178
- attributes[$`]
179
- end
180
- else
181
- return attributes[method_name] if attributes.include?(method_name)
182
- return @attributes['StandardFields'][method_name] if attributes['StandardFields'].include?(method_name)
183
- super # GTFO
184
- end
185
- end
186
-
187
- def build_hash(keys)
188
- hash = {}
189
- keys.each do |key|
190
- hash[key] = attributes[key]
191
- end
192
- hash
193
- end
194
-
195
- end
196
- end
197
- end
@@ -1,72 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class ListingCart < Base
4
- extend Finders
5
- self.element_name="listingcarts"
6
-
7
- def ListingIds=(listing_ids)
8
- attributes["ListingIds"] = Array(listing_ids)
9
- end
10
- def Name=(name)
11
- attributes["Name"] = name
12
- end
13
-
14
- def add_listing(listing)
15
- id = listing.respond_to?(:Id) ? listing.Id : listing.to_s
16
- results = connection.post("#{self.class.path}/#{self.Id}", {"ListingIds" => [ listing ]})
17
- self.ListingCount = results.first["ListingCount"]
18
- end
19
-
20
- def remove_listing(listing)
21
- id = listing.respond_to?(:Id) ? listing.Id : listing.to_s
22
- results = connection.delete("#{self.class.path}/#{self.Id}/listings/#{id}")
23
- self.ListingCount = results.first["ListingCount"]
24
- end
25
-
26
- def self.for(listings,arguments={})
27
- keys = Array(listings).map { |l| l.respond_to?(:Id) ? l.Id : l.to_s }
28
- collect(connection.get("/#{self.element_name}/for/#{keys.join(",")}", arguments))
29
- end
30
-
31
- def self.my(arguments={})
32
- collect(connection.get("/my/#{self.element_name}", arguments))
33
- end
34
-
35
- def self.portal(arguments={})
36
- collect(connection.get("/#{self.element_name}/portal", arguments))
37
- end
38
-
39
- def save(arguments={})
40
- begin
41
- return save!(arguments)
42
- rescue BadResourceRequest => e
43
- rescue NotFound => e
44
- # log and leave
45
- FlexmlsApi.logger.error("Failed to save contact #{self}: #{e.message}")
46
- end
47
- false
48
- end
49
- def save!(arguments={})
50
- attributes['Id'].nil? ? create!(arguments) : update!(arguments)
51
- end
52
-
53
- def delete(args={})
54
- connection.delete("#{self.class.path}/#{self.Id}", args)
55
- end
56
-
57
- private
58
- def create!(arguments={})
59
- results = connection.post self.class.path, {"ListingCarts" => [ attributes ]}, arguments
60
- result = results.first
61
- attributes['ResourceUri'] = result['ResourceUri']
62
- attributes['Id'] = parse_id(result['ResourceUri'])
63
- true
64
- end
65
- def update!(arguments={})
66
- results = connection.put "#{self.class.path}/#{self.Id}", {"ListingCarts" => [ {"Name" => attributes["Name"], "ListingIds" => attributes["ListingIds"]} ] }, arguments
67
- true
68
- end
69
-
70
- end
71
- end
72
- end
@@ -1,33 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class MarketStatistics < Base
4
- self.element_name="marketstatistics"
5
-
6
- def self.absorption(parameters={})
7
- self.stat('absorption',parameters)
8
- end
9
- def self.inventory(parameters={})
10
- self.stat('inventory',parameters)
11
- end
12
- def self.price(parameters={})
13
- self.stat('price',parameters)
14
- end
15
- def self.ratio(parameters={})
16
- self.stat('ratio',parameters)
17
- end
18
- def self.dom(parameters={})
19
- self.stat('dom',parameters)
20
- end
21
- def self.volume(parameters={})
22
- self.stat('volume',parameters)
23
- end
24
-
25
- private
26
- def self.stat(stat_name, parameters={})
27
- resp = connection.get("#{path}/#{stat_name}", parameters)
28
- new(resp.first)
29
- end
30
-
31
- end
32
- end
33
- end
@@ -1,21 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Message < Base
4
- self.element_name="messages"
5
-
6
- def save(arguments={})
7
- begin
8
- return save!(arguments)
9
- rescue NotFound, BadResourceRequest => e
10
- FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
11
- end
12
- false
13
- end
14
- def save!(arguments={})
15
- results = connection.post self.class.path, {"Messages" => [ attributes ]}, arguments
16
- true
17
- end
18
-
19
- end
20
- end
21
- end
@@ -1,41 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Note < Base
4
- extend Subresource
5
- self.element_name = "notes" # not sure this is really of any use...
6
-
7
- def self.get(options={})
8
- ret = super(options)
9
- if ret.empty?
10
- return nil
11
- else
12
- return ret.first
13
- end
14
- end
15
-
16
- def save(arguments={})
17
- begin
18
- return save!(arguments)
19
- rescue BadResourceRequest => e
20
- rescue NotFound => e
21
- # log and leave
22
- FlexmlsApi.logger.error("Failed to save note #{self} (path: #{self.class.path}): #{e.message}")
23
- end
24
- false
25
- end
26
-
27
- def save!(args={})
28
- args.merge(:Notes => attributes['Note'])
29
- results = connection.put(self.class.path, {:Note => attributes['Note']}, args)
30
- result = results.first
31
- attributes['ResourceUri'] = result['ResourceUri']
32
- true
33
- end
34
-
35
- def delete(args={})
36
- connection.delete(self.class.path, args)
37
- end
38
-
39
- end
40
- end
41
- end
@@ -1,42 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Notification < Base
4
-
5
- self.element_name = 'notifications'
6
-
7
- def save(arguments={})
8
- self.errors = [] # clear the errors hash
9
- begin
10
- return save!(arguments)
11
- rescue BadResourceRequest => e
12
- self.errors << {:code => e.code, :message => e.message}
13
- FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
14
- rescue NotFound => e
15
- FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
16
- end
17
- false
18
- end
19
-
20
- def save!(arguments={})
21
- results = connection.post self.class.path, attributes, arguments
22
- result = results.first
23
- attributes['ResourceUri'] = result['ResourceUri']
24
- true
25
- end
26
-
27
- def self.unread()
28
- # force pagination so response knows to deal with returned pagination info
29
- result = connection.get "#{self.path}/unread", {:_pagination => 'count'}
30
- result
31
- end
32
-
33
- def self.mark_read(notifications, arguments={})
34
- notifications = Array(notifications)
35
-
36
- ids = notifications.map { |n| n.respond_to?('Id') ? n.Id : n }
37
- result = connection.put "#{self.path}/#{ids.join(',')}", {'Read' => true}, arguments
38
- end
39
-
40
- end
41
- end
42
- end