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,64 +0,0 @@
1
- module FlexmlsApi
2
- module FaradayExt
3
- #=Flexmls API Faraday middleware
4
- # HTTP Response after filter to package api responses and bubble up basic api errors.
5
- class FlexmlsMiddleware < Faraday::Response::ParseJson
6
- include FlexmlsApi::PaginateHelper
7
- # Handles pretty much all the api response parsing and error handling. All responses that
8
- # indicate a failure will raise a FlexmlsApi::ClientError exception
9
- def on_complete(finished_env)
10
- body = parse(finished_env[:body])
11
- FlexmlsApi.logger.debug("Response Body: #{body.inspect}")
12
- unless body.is_a?(Hash) && body.key?("D")
13
- raise InvalidResponse, "The server response could not be understood"
14
- end
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
26
- case finished_env[:status]
27
- when 400
28
- hash = {:message => response.message, :code => response.code, :status => finished_env[:status]}
29
-
30
- # constraint violation
31
- if response.code == 1053
32
- details = body['D']['Details']
33
- hash[:details] = details
34
- end
35
- raise BadResourceRequest,hash
36
- when 401
37
- # Handle the WWW-Authenticate Response Header Field if present. This can be returned by
38
- # OAuth2 implementations and wouldn't hurt to log.
39
- auth_header_error = finished_env[:request_headers]["WWW-Authenticate"]
40
- FlexmlsApi.logger.warn("Authentication error #{auth_header_error}") unless auth_header_error.nil?
41
- raise PermissionDenied, {:message => response.message, :code => response.code, :status => finished_env[:status]}
42
- when 404
43
- raise NotFound, {:message => response.message, :code => response.code, :status => finished_env[:status]}
44
- when 405
45
- raise NotAllowed, {:message => response.message, :code => response.code, :status => finished_env[:status]}
46
- when 409
47
- raise BadResourceRequest, {:message => response.message, :code => response.code, :status => finished_env[:status]}
48
- when 500
49
- raise ClientError, {:message => response.message, :code => response.code, :status => finished_env[:status]}
50
- when 200..299
51
- FlexmlsApi.logger.debug("Success!")
52
- else
53
- raise ClientError, {:message => response.message, :code => response.code, :status => finished_env[:status]}
54
- end
55
- finished_env[:body] = results
56
- end
57
-
58
- def initialize(app)
59
- super(app)
60
- end
61
-
62
- end
63
- end
64
- end
@@ -1,33 +0,0 @@
1
- require 'flexmls_api/models/base'
2
- require 'flexmls_api/models/constraint'
3
- require 'flexmls_api/models/finders'
4
- require 'flexmls_api/models/subresource'
5
-
6
- require 'flexmls_api/models/account'
7
- require 'flexmls_api/models/connect_prefs'
8
- require 'flexmls_api/models/contact'
9
- require 'flexmls_api/models/custom_fields'
10
- require 'flexmls_api/models/document'
11
- require 'flexmls_api/models/idx_link'
12
- require 'flexmls_api/models/listing'
13
- require 'flexmls_api/models/listing_cart'
14
- require 'flexmls_api/models/market_statistics'
15
- require 'flexmls_api/models/message'
16
- require 'flexmls_api/models/note'
17
- require 'flexmls_api/models/notification'
18
- require 'flexmls_api/models/open_house'
19
- require 'flexmls_api/models/photo'
20
- require 'flexmls_api/models/property_types'
21
- require 'flexmls_api/models/saved_search'
22
- require 'flexmls_api/models/shared_listing'
23
- require 'flexmls_api/models/standard_fields'
24
- require 'flexmls_api/models/system_info'
25
- require 'flexmls_api/models/tour_of_home'
26
- require 'flexmls_api/models/video'
27
- require 'flexmls_api/models/virtual_tour'
28
-
29
- module FlexmlsApi
30
- module Models
31
-
32
- end
33
- end
@@ -1,115 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Account < Base
4
- extend Finders
5
- self.element_name="accounts"
6
-
7
- SUBELEMENTS = [:emails, :phones, :websites, :addresses, :images]
8
- attr_accessor :my_account, *SUBELEMENTS
9
-
10
- def initialize(attributes={})
11
- @emails = subresource(Email, "Emails", attributes)
12
- @phones = subresource(Phone, "Phones", attributes)
13
- @websites = subresource(Website, "Websites", attributes)
14
- @addresses = subresource(Address, "Addresses", attributes)
15
- if attributes["Images"]
16
- @images = []
17
- attributes["Images"].each { |i| @images << Image.new(i) }
18
- else
19
- @images = nil
20
- end
21
- @my_account = false
22
- super(attributes)
23
- end
24
-
25
- def self.my(arguments={})
26
- account = collect(connection.get("/my/account", arguments)).first
27
- account.my_account = true
28
- account
29
- end
30
-
31
- def my_account?
32
- @my_account
33
- end
34
-
35
- def self.by_office(office_id, arguments={})
36
- collect(connection.get("#{self.path()}/by/office/#{office_id}", arguments))
37
- end
38
-
39
- def primary_img(typ)
40
- if @images.is_a?(Array)
41
- matches = @images.select {|i| i.Type == typ}
42
- matches.sort {|a,b| a.Name <=> b.Name }.first
43
- else
44
- nil
45
- end
46
- end
47
-
48
- def save(arguments={})
49
- self.errors = [] # clear the errors hash
50
- begin
51
- return save!(arguments)
52
- rescue NotFound, BadResourceRequest => e
53
- self.errors << {:code => e.code, :message => e.message}
54
- FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
55
- end
56
- false
57
- end
58
- def save!(arguments={})
59
- # The long-term idea is that any setting in the user's account could be updated by including
60
- # an attribute and calling PUT /my/account, but for now only the GetEmailUpdates attribute
61
- # is supported
62
-
63
- save_path = "/accounts/"+self.Id
64
-
65
- ojbsome = {}
66
- if attributes['GetEmailUpdates']
67
- save_path = my_account? ? "/my/account" : self.class.path
68
- ojbsome["GetEmailUpdates"] = attributes['GetEmailUpdates']
69
- end
70
- if attributes['PasswordValidation']
71
- ojbsome["PasswordValidation"] = attributes['PasswordValidation']
72
- end
73
- if attributes['Password']
74
- ojbsome["Password"] = attributes['Password']
75
- end
76
-
77
- results = connection.put save_path, ojbsome, arguments
78
- true
79
- end
80
-
81
-
82
- private
83
-
84
- def subresource(klass, key, attributes)
85
- return nil unless attributes.key?(key)
86
- array = attributes[key]
87
- result = PrimaryArray.new()
88
- array.each do |i|
89
- result << klass.new(i)
90
- end
91
- result
92
- end
93
-
94
- class Email < Base
95
- include Primary
96
- end
97
-
98
- class Phone < Base
99
- include Primary
100
- end
101
-
102
- class Website < Base
103
- include Primary
104
- end
105
-
106
- class Address < Base
107
- include Primary
108
- end
109
-
110
- class Image < Base
111
- end
112
- end
113
- end
114
-
115
- end
@@ -1,118 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- # =API Model Base class
4
- # Intended to be a lot like working with ActiveResource, this class adds most of the basic
5
- # active model type niceties.
6
- class Base
7
- extend Paginate
8
-
9
- attr_accessor :attributes, :errors
10
- attr_reader :changed
11
-
12
- # Name of the resource as related to the path name
13
- def self.element_name
14
- # TODO I'd love to pull in active model at this point to provide default naming
15
- @element_name ||= "resource"
16
- end
17
-
18
- def self.element_name=(name)
19
- @element_name = name
20
- end
21
-
22
- # Resource path prefix, prepended to the url
23
- def self.prefix
24
- @prefix ||= "/"
25
- end
26
- def self.prefix=(prefix)
27
- @prefix = prefix
28
- end
29
- def self.path
30
- "#{prefix}#{element_name}"
31
- end
32
-
33
- def self.connection
34
- FlexmlsApi.client
35
- end
36
- def connection
37
- self.class.connection
38
- end
39
-
40
- def initialize(attributes={})
41
- @attributes = {}
42
- @errors = []
43
- @changed = []
44
- load(attributes)
45
- end
46
-
47
- def load(attributes)
48
- attributes.each do |key,val|
49
- @attributes[key.to_s] = val
50
- end
51
- end
52
-
53
- def self.get(options={})
54
- collect(connection.get(path, options))
55
- end
56
-
57
- def self.first(options={})
58
- get(options).first
59
- end
60
-
61
- def self.count(options={})
62
- connection.get(path, options.merge({:_pagination=>"count"}))
63
- end
64
-
65
- def method_missing(method_symbol, *arguments)
66
- method_name = method_symbol.to_s
67
-
68
- if method_name =~ /(=|\?)$/
69
- case $1
70
- when "="
71
- write_attribute($`, arguments.first)
72
- # TODO figure out a nice way to present setters for the standard fields
73
- when "?"
74
- if attributes.include?($`)
75
- attributes[$`] ? true : false
76
- else
77
- raise NoMethodError
78
- end
79
- end
80
- else
81
- return attributes[method_name] if attributes.include?(method_name)
82
- super # GTFO
83
- end
84
- end
85
-
86
- def respond_to?(method_symbol, include_private=false)
87
- if super
88
- return true
89
- else
90
- method_name = method_symbol.to_s
91
-
92
- if method_name =~ /=$/
93
- true
94
- elsif method_name =~ /(\?)$/
95
- attributes.include?($`)
96
- else
97
- attributes.include?(method_name)
98
- end
99
-
100
- end
101
- end
102
-
103
- def parse_id(uri)
104
- uri[/\/.*\/(.+)$/, 1]
105
- end
106
-
107
- protected
108
-
109
- def write_attribute(attribute, value)
110
- unless attributes[attribute] == value
111
- attributes[attribute] = value
112
- @changed << attribute unless @changed.include?(attribute)
113
- end
114
- end
115
-
116
- end
117
- end
118
- end
@@ -1,10 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Connect < Base
4
- self.element_name="connect"
5
- def self.prefs
6
- connection.get("#{path}/prefs")
7
- end
8
- end
9
- end
10
- end
@@ -1,16 +0,0 @@
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
-
@@ -1,49 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Contact < Base
4
- extend Finders
5
- self.element_name="contacts"
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
- def save!(arguments={})
20
- results = connection.post self.class.path, {"Contacts" => [ attributes ], "Notify" => notify? }, arguments
21
- result = results.first
22
- attributes['ResourceUri'] = result['ResourceUri']
23
- attributes['Id'] = parse_id(result['ResourceUri'])
24
- true
25
- end
26
-
27
- def self.by_tag(tag_name, arguments={})
28
- collect(connection.get("#{path}/tags/#{tag_name}", arguments))
29
- end
30
-
31
- def self.tags(arguments={})
32
- connection.get("#{path}/tags", arguments)
33
- end
34
-
35
- def self.my(arguments={})
36
- new(connection.get('/my/contact', arguments).first)
37
- end
38
-
39
- # Notify the agent of contact creation via a flexmls message.
40
- def notify?
41
- @notify == true
42
- end
43
- def notify=(notify_me=true)
44
- @notify = notify_me
45
- end
46
-
47
- end
48
- end
49
- end
@@ -1,12 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class CustomFields < Base
4
- self.element_name="customfields"
5
-
6
-
7
- def self.find_by_property_type(card_fmt, arguments={})
8
- collect(connection.get("#{self.path}/#{card_fmt}", arguments))
9
- end
10
- end
11
- end
12
- end
@@ -1,11 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- class Document < Base
4
- extend Subresource
5
-
6
- self.element_name="documents"
7
-
8
-
9
- end
10
- end
11
- end
@@ -1,45 +0,0 @@
1
- module FlexmlsApi
2
- module Models
3
- # =Rails-like finders module
4
- # Adds the base set of finder class methods to the models that support them (not all of them do)
5
- module Finders
6
-
7
- def find(*arguments)
8
- scope = arguments.slice!(0)
9
- options = arguments.slice!(0) || {}
10
- case scope
11
- when :all then find_every(options)
12
- when :first then find_every(options).first
13
- when :last then find_every(options).last
14
- when :one then find_one(options)
15
- else find_single(scope, options)
16
- end
17
- end
18
-
19
- def first(*arguments)
20
- find(:first, *arguments)
21
- end
22
-
23
- def last(*arguments)
24
- find(:last, *arguments)
25
- end
26
-
27
- private
28
-
29
- def find_every(options)
30
- collect(connection.get("/#{element_name}", options))
31
- end
32
-
33
- def find_one(options)
34
- raise NotImplementedError # TODO um... what?
35
- end
36
-
37
- def find_single(scope, options)
38
- resp = connection.get("/#{element_name}/#{scope}", options)
39
- new(resp.first)
40
- end
41
-
42
- end
43
- end
44
- end
45
-