yardi 4.0.8

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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +29 -0
  3. data/.gitignore +5 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +20 -0
  6. data/CODEOWNERS +1 -0
  7. data/CODE_OF_CONDUCT.md +13 -0
  8. data/Gemfile +4 -0
  9. data/README.md +212 -0
  10. data/Rakefile +7 -0
  11. data/bin/console +15 -0
  12. data/config/multi_xml.rb +4 -0
  13. data/docs/contributing.md +24 -0
  14. data/docs/getting_started.md +14 -0
  15. data/lib/yardi.rb +54 -0
  16. data/lib/yardi/document_parser.rb +6 -0
  17. data/lib/yardi/document_parser/base.rb +85 -0
  18. data/lib/yardi/document_parser/guest_card_import_response_object.rb +72 -0
  19. data/lib/yardi/document_parser/prospects.rb +79 -0
  20. data/lib/yardi/document_parser/residents.rb +59 -0
  21. data/lib/yardi/error/base.rb +7 -0
  22. data/lib/yardi/error/connection_error.rb +12 -0
  23. data/lib/yardi/error/empty_response.rb +11 -0
  24. data/lib/yardi/error/error_response.rb +11 -0
  25. data/lib/yardi/error/fault_response.rb +14 -0
  26. data/lib/yardi/error/guests_not_found.rb +10 -0
  27. data/lib/yardi/error/invalid_configuration.rb +11 -0
  28. data/lib/yardi/error/missing_property.rb +10 -0
  29. data/lib/yardi/error/no_results.rb +10 -0
  30. data/lib/yardi/error/resource_not_found.rb +9 -0
  31. data/lib/yardi/error/service_unavailable.rb +11 -0
  32. data/lib/yardi/error/unparsable_response.rb +11 -0
  33. data/lib/yardi/model/event.rb +18 -0
  34. data/lib/yardi/model/guest_card_response.rb +12 -0
  35. data/lib/yardi/model/prospect.rb +49 -0
  36. data/lib/yardi/model/resident.rb +36 -0
  37. data/lib/yardi/parameter/agent.rb +13 -0
  38. data/lib/yardi/parameter/contact_info.rb +13 -0
  39. data/lib/yardi/parameter/credential.rb +16 -0
  40. data/lib/yardi/parameter/property.rb +35 -0
  41. data/lib/yardi/parameter/prospect.rb +25 -0
  42. data/lib/yardi/parameter/user.rb +64 -0
  43. data/lib/yardi/request/base.rb +99 -0
  44. data/lib/yardi/request/get_residents.rb +39 -0
  45. data/lib/yardi/request/get_yardi_guest_activity.rb +85 -0
  46. data/lib/yardi/request/import_yardi_guest.rb +73 -0
  47. data/lib/yardi/request_section.rb +24 -0
  48. data/lib/yardi/request_section/authentication.rb +24 -0
  49. data/lib/yardi/request_section/lead_management.rb +148 -0
  50. data/lib/yardi/request_section/prospect.rb +27 -0
  51. data/lib/yardi/request_section/residents.rb +18 -0
  52. data/lib/yardi/utils.rb +6 -0
  53. data/lib/yardi/utils/configuration_validator.rb +17 -0
  54. data/lib/yardi/utils/phone_parser.rb +23 -0
  55. data/lib/yardi/utils/request_fetcher.rb +47 -0
  56. data/lib/yardi/utils/request_generator.rb +88 -0
  57. data/lib/yardi/validator.rb +6 -0
  58. data/lib/yardi/validator/empty_response.rb +43 -0
  59. data/lib/yardi/validator/error_response.rb +87 -0
  60. data/lib/yardi/validator/fault_response.rb +40 -0
  61. data/lib/yardi/validator/missing_property.rb +60 -0
  62. data/lib/yardi/version.rb +5 -0
  63. data/yardi.gemspec +31 -0
  64. metadata +246 -0
@@ -0,0 +1,87 @@
1
+ require 'yardi/validator'
2
+
3
+ module Yardi
4
+ module Validator
5
+ # Ensure that the response has no Messages with messageType=Error.
6
+ class ErrorResponse
7
+ # @param parsed_response [Hash<String, Object>] XML response parsed into a
8
+ # Hash
9
+ def initialize(action:, parsed_response:)
10
+ @action = action
11
+ @response = parsed_response
12
+ end
13
+
14
+ # @raise [Yardi::Error::ErrorResponse] if the response has an error
15
+ def validate!
16
+ return unless error?
17
+
18
+ guest_err_msg = "Error: No guests exist with the given search criteria."
19
+
20
+ if error_messages.include?(guest_err_msg)
21
+ raise Error::GuestsNotFound
22
+ end
23
+ raise Error::ErrorResponse, error_messages.join('. ')
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :action, :response
29
+
30
+ def error?
31
+ messages.any? do |message_node|
32
+ message_node['messageType'].downcase == 'error'
33
+ end
34
+ end
35
+
36
+ # We have seen a few different types of errors from Yardi.
37
+ # The content inside <Messages><Message> can be a hash, an array of
38
+ # hashes, or an array that has both a string and a hash.
39
+ # This string case is the example in missing_node_error.xml; they have
40
+ # two <Message> nodes, both of which have the same content, but only one
41
+ # of which has the 'messageType' attribute to tell us this is an error
42
+ # case. When XML is parsed into a hash, this becomes:
43
+ # [
44
+ # "XSD Error:The element 'Name' has incomplete...",
45
+ # {
46
+ # "messageType" => "Error",
47
+ # "__content__" => "XSD Error:The element 'Name' has incomplete..."
48
+ # }
49
+ # ]
50
+ # We convert the string into a hash that matches the rest of the error
51
+ # messages so that we don't have to worry about type checks when pulling
52
+ # out the errors.
53
+ def messages
54
+ body = response['soap:Envelope']['soap:Body']
55
+ # This will be picked up by either the EmptyResponse or the
56
+ # FaultResponse check. Whatever it is, it's not an ErrorResponse issue.
57
+ return [] if body["#{action}Response"].nil?
58
+ result = body["#{action}Response"]["#{action}Result"]
59
+ # There are no Messages nodes in a successful
60
+ # GetYardiGuestActivity_Search response
61
+ return [] if result['Messages'].nil?
62
+ nodes = result['Messages']['Message']
63
+ nodes = nodes.is_a?(Array) ? nodes : [nodes]
64
+ # see method comment for why this normalization is needed
65
+ nodes.map do |node|
66
+ if node.is_a?(Hash)
67
+ node
68
+ elsif node.is_a?(String)
69
+ { 'messageType' => 'Error', '__content__' => node }
70
+ end
71
+ end
72
+ end
73
+
74
+ # We have seen a few types of error messages from Yardi so far.
75
+ # 1) there is only one Message node.
76
+ # 2) error response contains two message nodes that have the same
77
+ # content. (this is why we need the .uniq!)
78
+ # 3) error response contains two (or more) nodes with different content
79
+ # PRECONDITION: #error? evaluates to true.
80
+ def error_messages
81
+ messages.map do |message|
82
+ "#{message['messageType']}: #{message['__content__']}".strip
83
+ end.uniq
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,40 @@
1
+ require 'yardi/validator'
2
+
3
+ module Yardi
4
+ module Validator
5
+ # Ensure that the response has no Fault nodes
6
+ class FaultResponse
7
+ # @param parsed_response [Hash<String, Object>] XML response parsed into a
8
+ # Hash
9
+ def initialize(action:, parsed_response:)
10
+ @action = action
11
+ @response = parsed_response
12
+ end
13
+
14
+ # @raise [Yardi::Error::FaultResponse] if the response has a Fault node
15
+ def validate!
16
+ return unless error?
17
+ raise Error::FaultResponse, error_message
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :action, :response
23
+
24
+ def body
25
+ response['soap:Envelope']['soap:Body']
26
+ end
27
+
28
+ def error?
29
+ !body['soap:Fault'].nil?
30
+ end
31
+
32
+ def error_message
33
+ fault = body['soap:Fault']
34
+ fault_message = "#{fault['faultcode']}: #{fault['faultstring']}."
35
+ details = fault['detail']
36
+ details.nil? ? fault_message : "#{fault_message} Details: #{details}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,60 @@
1
+ require 'yardi/validator'
2
+
3
+ module Yardi
4
+ module Validator
5
+ # Make sure the property we're trying to send a guestcard for is configured
6
+ # in Yardi's system. For an example, @see invalid_property.xml
7
+ class MissingProperty
8
+ UNCONFIGURED_PROPERTY_REGEX = /Invalid Yardi Property Code/i
9
+
10
+ # @param parsed_response [Hash<String, Object>] the XML response parsed
11
+ # into a Hash
12
+ def initialize(action:, parsed_response:)
13
+ @action = action
14
+ @response = parsed_response
15
+ end
16
+
17
+ # @raise [Yardi::Error::ErrorResponse] if the response is effectively
18
+ # empty
19
+ def validate!
20
+ return if valid_property?
21
+ raise Error::MissingProperty, message
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :action, :response
27
+
28
+ def valid_property?
29
+ message !~ UNCONFIGURED_PROPERTY_REGEX
30
+ end
31
+
32
+ # This looks like:
33
+ # test0123:Invalid Yardi Property Code.
34
+ # In the example, the remote property ID that was invalid is 'test0123'.
35
+ #
36
+ # Successful guestcard insertion responses have multiple Message nodes,
37
+ # which become an array after parsing.
38
+ #
39
+ # Successful prospect search responses don't have any Message nodes at
40
+ # all.
41
+ #
42
+ # If there is only one Message node (in the case where we have an error,
43
+ # perhaps an invalid property), it will be a Hash. When we have an array,
44
+ # we know it isn't an invalid property error so we return an empty string
45
+ # which will fail the comparison to the regex.
46
+ def message
47
+ body = response['soap:Envelope']['soap:Body']
48
+ # This will be picked up by either the EmptyResponse or the
49
+ # FaultResponse check. Whatever it is, it's not a MissingProperty issue.
50
+ return '' if body["#{action}Response"].nil?
51
+ result = body["#{action}Response"]["#{action}Result"]
52
+ # There are no Messages nodes in a successful
53
+ # GetYardiGuestActivity_Search response
54
+ return '' if result['Messages'].nil?
55
+ messages = result['Messages']['Message']
56
+ messages.is_a?(Hash) ? messages['__content__'] : ''
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Yardi
4
+ VERSION = '4.0.8'
5
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'yardi/version'
5
+ UNWANTED = %r{^(test|spec|features)/}
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'yardi'
9
+ spec.version = Yardi::VERSION
10
+ spec.authors = ['Heidi Galbraith']
11
+ spec.email = ['heidi@apartmentlist.com']
12
+
13
+ spec.summary = %(A ruby client for v4 of Yardi's API)
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(UNWANTED) }
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_runtime_dependency 'faraday', '~> 0.9'
21
+ spec.add_runtime_dependency 'multi_xml', '~> 0.5' # Parse XML responses
22
+ spec.add_runtime_dependency 'nokogiri', '~> 1.6' # Build XML for requests
23
+ spec.add_runtime_dependency 'ox', '~> 2.3' # Parser we want with MultiXML
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.10'
26
+ spec.add_development_dependency 'pry-byebug', '~> 3.4'
27
+ spec.add_development_dependency 'rake', '~> 11.2'
28
+ spec.add_development_dependency 'rspec', '~> 3.4'
29
+ spec.add_development_dependency 'rubocop', '~> 0.65'
30
+ spec.add_development_dependency 'webmock', '~> 1.21'
31
+ end
metadata ADDED
@@ -0,0 +1,246 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yardi
3
+ version: !ruby/object:Gem::Version
4
+ version: 4.0.8
5
+ platform: ruby
6
+ authors:
7
+ - Heidi Galbraith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-01-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_xml
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ox
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.10'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.10'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '11.2'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '11.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.4'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.4'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.65'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.65'
139
+ - !ruby/object:Gem::Dependency
140
+ name: webmock
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.21'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.21'
153
+ description:
154
+ email:
155
+ - heidi@apartmentlist.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".circleci/config.yml"
161
+ - ".gitignore"
162
+ - ".rspec"
163
+ - ".rubocop.yml"
164
+ - CODEOWNERS
165
+ - CODE_OF_CONDUCT.md
166
+ - Gemfile
167
+ - README.md
168
+ - Rakefile
169
+ - bin/console
170
+ - config/multi_xml.rb
171
+ - docs/contributing.md
172
+ - docs/getting_started.md
173
+ - lib/yardi.rb
174
+ - lib/yardi/document_parser.rb
175
+ - lib/yardi/document_parser/base.rb
176
+ - lib/yardi/document_parser/guest_card_import_response_object.rb
177
+ - lib/yardi/document_parser/prospects.rb
178
+ - lib/yardi/document_parser/residents.rb
179
+ - lib/yardi/error/base.rb
180
+ - lib/yardi/error/connection_error.rb
181
+ - lib/yardi/error/empty_response.rb
182
+ - lib/yardi/error/error_response.rb
183
+ - lib/yardi/error/fault_response.rb
184
+ - lib/yardi/error/guests_not_found.rb
185
+ - lib/yardi/error/invalid_configuration.rb
186
+ - lib/yardi/error/missing_property.rb
187
+ - lib/yardi/error/no_results.rb
188
+ - lib/yardi/error/resource_not_found.rb
189
+ - lib/yardi/error/service_unavailable.rb
190
+ - lib/yardi/error/unparsable_response.rb
191
+ - lib/yardi/model/event.rb
192
+ - lib/yardi/model/guest_card_response.rb
193
+ - lib/yardi/model/prospect.rb
194
+ - lib/yardi/model/resident.rb
195
+ - lib/yardi/parameter/agent.rb
196
+ - lib/yardi/parameter/contact_info.rb
197
+ - lib/yardi/parameter/credential.rb
198
+ - lib/yardi/parameter/property.rb
199
+ - lib/yardi/parameter/prospect.rb
200
+ - lib/yardi/parameter/user.rb
201
+ - lib/yardi/request/base.rb
202
+ - lib/yardi/request/get_residents.rb
203
+ - lib/yardi/request/get_yardi_guest_activity.rb
204
+ - lib/yardi/request/import_yardi_guest.rb
205
+ - lib/yardi/request_section.rb
206
+ - lib/yardi/request_section/authentication.rb
207
+ - lib/yardi/request_section/lead_management.rb
208
+ - lib/yardi/request_section/prospect.rb
209
+ - lib/yardi/request_section/residents.rb
210
+ - lib/yardi/utils.rb
211
+ - lib/yardi/utils/configuration_validator.rb
212
+ - lib/yardi/utils/phone_parser.rb
213
+ - lib/yardi/utils/request_fetcher.rb
214
+ - lib/yardi/utils/request_generator.rb
215
+ - lib/yardi/validator.rb
216
+ - lib/yardi/validator/empty_response.rb
217
+ - lib/yardi/validator/error_response.rb
218
+ - lib/yardi/validator/fault_response.rb
219
+ - lib/yardi/validator/missing_property.rb
220
+ - lib/yardi/version.rb
221
+ - yardi.gemspec
222
+ homepage:
223
+ licenses:
224
+ - MIT
225
+ metadata: {}
226
+ post_install_message:
227
+ rdoc_options: []
228
+ require_paths:
229
+ - lib
230
+ required_ruby_version: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - ">="
233
+ - !ruby/object:Gem::Version
234
+ version: '0'
235
+ required_rubygems_version: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - ">="
238
+ - !ruby/object:Gem::Version
239
+ version: '0'
240
+ requirements: []
241
+ rubyforge_project:
242
+ rubygems_version: 2.5.0
243
+ signing_key:
244
+ specification_version: 4
245
+ summary: A ruby client for v4 of Yardi's API
246
+ test_files: []